validate upstream version (#527)

This commit is contained in:
a10zn8
2024-07-21 18:55:15 +03:00
committed by GitHub
parent 366dbd3213
commit ac1e727e7f
48 changed files with 824 additions and 1083 deletions

View File

@@ -78,6 +78,7 @@ class GenericUpstreamMock extends GenericUpstream {
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&upstreamSettingsDetector,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&lowerBoundService,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&finalizationDetectorBuilder,
[get: { null }] as java.util.function.Supplier,
)
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
setLag(0)

View File

@@ -78,7 +78,8 @@ class FilteredApisSpec extends Specification {
cs.&validator,
cs.&upstreamSettingsDetector,
cs.&lowerBoundService,
cs.&finalizationDetectorBuilder
cs.&finalizationDetectorBuilder,
[get: { null }] as java.util.function.Supplier,
)
}
def matcher = new Selector.LabelMatcher("test", ["foo"])

View File

@@ -1,493 +0,0 @@
/**
* Copyright (c) 2022 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.upstream.ethereum
import io.emeraldpay.dshackle.foundation.ChainOptions
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.test.ApiReaderMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.ChainCallError
import io.emeraldpay.dshackle.upstream.ChainRequest
import io.emeraldpay.dshackle.upstream.ChainResponse
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.ethereum.domain.Address
import io.emeraldpay.dshackle.upstream.ethereum.hex.HexData
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcResponseError
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionCallJson
import reactor.core.publisher.Mono
import reactor.util.function.Tuples
import spock.lang.Specification
import java.time.Duration
import static io.emeraldpay.dshackle.Chain.BSC__MAINNET
import static io.emeraldpay.dshackle.Chain.ETHEREUM__MAINNET
import static io.emeraldpay.dshackle.Chain.OPTIMISM__MAINNET
import static io.emeraldpay.dshackle.upstream.UpstreamAvailability.*
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_VALID
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
class EthereumUpstreamValidatorSpec extends Specification {
def conf = ChainConfig.defaultWithContract("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def "Resolve to final availability"() {
setup:
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, Stub(Upstream), ChainOptions.PartialOptions.getDefaults().buildOptions(), conf)
expect:
validator.resolve(Tuples.of(sync, peers)) == exp
where:
exp | sync | peers
OK | OK | OK
IMMATURE | OK | IMMATURE
UNAVAILABLE | OK | UNAVAILABLE
SYNCING | SYNCING | OK
SYNCING | SYNCING | IMMATURE
UNAVAILABLE | SYNCING | UNAVAILABLE
UNAVAILABLE | UNAVAILABLE | OK
UNAVAILABLE | UNAVAILABLE | IMMATURE
UNAVAILABLE | UNAVAILABLE | UNAVAILABLE
}
def "Doesnt check eth_syncing when disabled"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = false
}.buildOptions()
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getIngressReader()
}
def "Syncing is OK when false returned from upstream"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = true
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("eth_syncing", [], false)
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == OK
}
def "Execute onSyncingNode with result of eth_syncing"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = true
}.buildOptions()
def up = Mock(Upstream) {
2 * getIngressReader() >> Mock(Reader) { reader ->
2 * reader.read(_) >>> [
Mono.just(new ChainResponse('true'.getBytes(), null)),
Mono.just(new ChainResponse('false'.getBytes(), null))
]
}
2 * getHead() >> Mock(Head) { head ->
1 * head.onSyncingNode(true)
1 * head.onSyncingNode(false)
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
def act2 = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == SYNCING
act2 == OK
}
def "Syncing is SYNCING when state returned from upstream"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = true
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("eth_syncing", [], [startingBlock: 100, currentBlock: 50])
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == SYNCING
}
def "Syncing is UNAVAILABLE when error returned from upstream"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = true
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("eth_syncing", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable"))
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == UNAVAILABLE
}
def "Doesnt validate peers when disabled"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = false
it.minPeers = 10
}.buildOptions()
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getApi()
}
def "Doesnt validate peers when zero peers is expected"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = true
it.minPeers = 0
}.buildOptions()
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getIngressReader()
}
def "Peers is IMMATURE when state returned too few peers"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = true
it.minPeers = 10
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("net_peerCount", [], "0x5")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == IMMATURE
}
def "Peers is OK when state returned exactly min peers"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = true
it.minPeers = 10
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("net_peerCount", [], "0xa")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == OK
}
def "Peers is OK when state returned more than enough peers"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = true
it.minPeers = 10
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("net_peerCount", [], "0xff")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == OK
}
def "Peers is UNAVAILABLE when state returned error"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validatePeers = true
it.minPeers = 10
}.buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("net_peerCount", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable"))
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == UNAVAILABLE
}
def "Doesnt validate chan and callLimit when disabled"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateCallLimit = false
it.validateChain = false
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
2 * getIngressReader() >>
Mock(Reader) {
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_VALID
}
def "Upstream is valid if not error from call limit check"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
3 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_call", new ListParams([new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240")
), "latest"]))) >> Mono.just(new ChainResponse("0x00000000000000000000".getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
//"0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_VALID
}
def "Upstream is not valid if error returned on call limit check"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
3 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_call", new ListParams([new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240")
), "latest"]))) >> Mono.just(new ChainResponse(null, new ChainCallError(1, "Too long")))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_SETTINGS_ERROR
}
def "Upstream is valid if gas price is equal to expected"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
it.validateCallLimit = false
}.buildOptions()
def conf = ChainConfig.defaultWithGasPriceCondition(["ne 3000000000", "ne 5000000000"])
def up = Mock(Upstream) {
3 * getIngressReader() >>
Mock(Reader) {
1 * read(new ChainRequest("eth_gasPrice", new ListParams())) >> Mono.just(new ChainResponse('"0x3b9aca00"'.getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(BSC__MAINNET, up, options, conf)
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_VALID
}
def "Upstream is NOT valid if gas price is different from expected"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
it.validateCallLimit = false
}.buildOptions()
def conf = ChainConfig.defaultWithGasPriceCondition(["eq 1000000000"])
def up = Mock(Upstream) {
3 * getIngressReader() >>
Mock(Reader) {
1 * read(new ChainRequest("eth_gasPrice", new ListParams())) >> Mono.just(new ChainResponse('"0xb2d05e00"'.getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(BSC__MAINNET, up, options, conf)
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_FATAL_SETTINGS_ERROR
}
def "Upstream is valid if chain settings are valid"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateCallLimit = false
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
4 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_chainId", new ListParams())) >> Mono.just(new ChainResponse('"0x1"'.getBytes(), null))
1 * read(new ChainRequest("net_version", new ListParams())) >> Mono.just(new ChainResponse('"1"'.getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_VALID
}
def "Upstream is not valid - specified optimism but got ethereum"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateCallLimit = false
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
4 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_chainId", new ListParams())) >> Mono.just(new ChainResponse('"0x1"'.getBytes(), null))
1 * read(new ChainRequest("net_version", new ListParams())) >> Mono.just(new ChainResponse('"1"'.getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(OPTIMISM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_FATAL_SETTINGS_ERROR
}
def "Upstream is valid if all setting are valid"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap{
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
5 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_chainId", new ListParams())) >> Mono.just(new ChainResponse('"0x1"'.getBytes(), null))
1 * read(new ChainRequest("net_version", new ListParams())) >> Mono.just(new ChainResponse('"1"'.getBytes(), null))
1 * read(new ChainRequest("eth_call", new ListParams([new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240")
), "latest"]))) >> Mono.just(new ChainResponse("0x00000000000000000000".getBytes(), null))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_VALID
}
def "Upstream is not valid if there are errors"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateGasPrice = false
}.buildOptions()
def up = Mock(Upstream) {
5 * getIngressReader() >> Mock(Reader) {
1 * read(new ChainRequest("eth_chainId", new ListParams())) >> Mono.just(new ChainResponse(null, new ChainCallError(1, "Too long")))
1 * read(new ChainRequest("net_version", new ListParams())) >> Mono.just(new ChainResponse(null, new ChainCallError(1, "Too long")))
1 * read(new ChainRequest("eth_call", new ListParams([new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a00000000000000000000000000000000000000000000000000000000000f4240")
), "latest"]))) >> Mono.just(new ChainResponse(null, new ChainCallError(1, "Too long")))
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >> Mono.just(new ChainResponse('"0x10ff9be"'.getBytes(), null))
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x10fd2ae", false]))) >>
Mono.just(new ChainResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
act == UPSTREAM_SETTINGS_ERROR
}
}

View File

@@ -14,6 +14,7 @@ import io.grpc.inprocess.InProcessChannelBuilder
import io.grpc.inprocess.InProcessServerBuilder
import org.assertj.core.api.Assertions
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
@@ -29,6 +30,7 @@ import java.net.URI
@SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
@Import(Config::class)
@ActiveProfiles("integration-test")
@Disabled
class IntegrationTest {
@Autowired

View File

@@ -0,0 +1,45 @@
package io.emeraldpay.dshackle.config.hot
import io.emeraldpay.dshackle.Global
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class CompatibleVersionsRulesTest {
@Test
fun `test parsing`() {
val raw = """
rules:
- client: "client1"
blacklist:
- 1.0.0
- 1.0.1
whitelist:
- 1.0.2
- 1.0.3
- client: "client2"
blacklist:
- 1.0.0
- 1.0.1
whitelist:
- 1.0.2
- 1.0.3
""".trimIndent()
val rules = Global.yamlMapper.readValue(raw, CompatibleVersionsRules::class.java)!!.rules
assertEquals(2, rules.size)
assertEquals("client1", rules[0].client)
assertEquals(2, rules[0].blacklist!!.size)
assertEquals("1.0.0", rules[0].blacklist!![0])
assertEquals("1.0.1", rules[0].blacklist!![1])
assertEquals(2, rules[0].whitelist!!.size)
assertEquals("1.0.2", rules[0].whitelist!![0])
assertEquals("1.0.3", rules[0].whitelist!![1])
assertEquals("client2", rules[1].client)
assertEquals(2, rules[1].blacklist!!.size)
assertEquals("1.0.0", rules[1].blacklist!![0])
assertEquals("1.0.1", rules[1].blacklist!![1])
assertEquals(2, rules[1].whitelist!!.size)
assertEquals("1.0.2", rules[1].whitelist!![0])
assertEquals("1.0.3", rules[1].whitelist!![1])
}
}

View File

@@ -47,7 +47,7 @@ class SubscribeChainStatusTest {
on { getFlux() } doReturn Flux.error(IllegalStateException())
}
val ms = mock<Multistream> {
on { chain } doReturn Chain.ETHEREUM__MAINNET
on { getChain() } doReturn Chain.ETHEREUM__MAINNET
on { getHead() } doReturn head
on { stateEvents() } doReturn Flux.empty()
}

View File

@@ -1,5 +1,7 @@
package io.emeraldpay.dshackle.upstream.beaconchain
import io.emeraldpay.dshackle.Chain.ETH_BEACON_CHAIN__MAINNET
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
import io.emeraldpay.dshackle.foundation.ChainOptions
import io.emeraldpay.dshackle.reader.ChainReader
import io.emeraldpay.dshackle.upstream.ChainCallError
@@ -39,7 +41,13 @@ class BeaconChainValidatorTest {
on { getIngressReader() } doReturn reader
on { getHead() } doReturn mock<Head>()
}
val validator = BeaconChainValidator(upstream, ChainOptions.PartialOptions.getDefaults().buildOptions())
val validator = BeaconChainSpecific.validator(
ETH_BEACON_CHAIN__MAINNET,
upstream,
ChainOptions.PartialOptions.getDefaults().buildOptions(),
ChainConfig.default(),
{ null },
)
val result = validator.validate().block()