Checking eth_call return limit during node validation, currently 200k (#246)

Added eth_call return limit check to node validation. Currently limit is 200k
This commit is contained in:
Vyacheslav
2023-07-12 13:43:54 +03:00
committed by GitHub
parent c29b5037a1
commit b57153826c
15 changed files with 167 additions and 32 deletions

View File

@@ -38,6 +38,7 @@ class ChainsConfigReaderSpec extends Specification {
then:
eth.laggingLagSize == 1
eth.syncingLagSize == 6
eth.callLimitContract == "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
pol.laggingLagSize == 10
pol.syncingLagSize == 20
@@ -48,8 +49,5 @@ class ChainsConfigReaderSpec extends Specification {
sep.laggingLagSize == 1
sep.syncingLagSize == 10
eth.laggingLagSize == 1
eth.syncingLagSize == 6
}
}

View File

@@ -450,11 +450,13 @@ class UpstreamsConfigReaderSpec extends Specification {
disableValidation == false
validateSyncing == true
validatePeers == false
validateCalllimit == true
}
with(act.upstreams.get(1).options) {
disableValidation == false
validateSyncing == false
validatePeers == false
validateCalllimit == false
}
with(act.upstreams.get(2).options) {
disableValidation == true
@@ -633,7 +635,7 @@ class UpstreamsConfigReaderSpec extends Specification {
def options = partialOptions.buildOptions()
then:
options == new UpstreamsConfig.Options(
false, 30, Duration.ofSeconds(60), null, true, 1, true
false, 30, Duration.ofSeconds(60), null, true, 1, true, true
)
}
}

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.test
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig.Options
import io.emeraldpay.dshackle.data.BlockContainer
@@ -70,7 +71,7 @@ class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
methods,
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(labels)),
new ConnectorFactoryMock(api, new EthereumHeadMock()),
ChainsConfig.ChainConfig.default(),
ChainConfig.default(),
true
)
this.ethereumHeadMock = this.getHead() as EthereumHeadMock

View File

@@ -21,6 +21,7 @@ import io.emeraldpay.dshackle.FileResolver
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.CachesFactory
import io.emeraldpay.dshackle.config.CacheConfig
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.reader.EmptyReader

View File

@@ -15,13 +15,17 @@
*/
package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig
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.rpcclient.JsonRpcResponse
import io.emeraldpay.etherjar.hex.HexData
import io.emeraldpay.etherjar.rpc.RpcResponseError
import io.emeraldpay.etherjar.rpc.json.TransactionCallJson
import io.emeraldpay.etherjar.domain.Address
import reactor.core.publisher.Mono
import reactor.util.function.Tuples
import spock.lang.Specification
@@ -36,18 +40,18 @@ class EthereumUpstreamValidatorSpec extends Specification {
setup:
def validator = new EthereumUpstreamValidator(Stub(EthereumLikeUpstream), UpstreamsConfig.PartialOptions.getDefaults().buildOptions())
expect:
validator.resolve(Tuples.of(sync, peers)) == exp
validator.resolve(Tuples.of(sync, peers, call)) == 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
exp | sync | peers | call
OK | OK | OK | OK
IMMATURE | OK | IMMATURE | OK
UNAVAILABLE | OK | UNAVAILABLE | OK
SYNCING | SYNCING | OK | OK
SYNCING | SYNCING | IMMATURE | OK
UNAVAILABLE | SYNCING | UNAVAILABLE | OK
UNAVAILABLE | UNAVAILABLE | OK | OK
UNAVAILABLE | UNAVAILABLE | IMMATURE | OK
UNAVAILABLE | UNAVAILABLE | UNAVAILABLE | OK
}
def "Doesnt check eth_syncing when disabled"() {
@@ -62,7 +66,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getApi()
0 * up.getIngressReader()
}
def "Syncing is OK when false returned from upstream"() {
@@ -175,7 +179,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def act = validator.validatePeers().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getApi()
0 * up.getIngressReader()
}
def "Peers is IMMATURE when state returned too few peers"() {
@@ -253,4 +257,66 @@ class EthereumUpstreamValidatorSpec extends Specification {
then:
act == UNAVAILABLE
}
def "Doesnt check call limit when disabled"() {
setup:
def options = UpstreamsConfig.PartialOptions.getDefaults().tap {
it.validateCalllimit = false
}.buildOptions()
def up = Mock(EthereumLikeUpstream)
def validator = new EthereumUpstreamValidator(up, options)
when:
def act = validator.validateCallLimit().block(Duration.ofSeconds(1))
then:
act == OK
0 * up.getIngressReader()
}
def "Upstream available if not error from call limit check"() {
setup:
def options = UpstreamsConfig.PartialOptions.getDefaults().buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answerOnce("eth_call", [new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a0000000000000000000000000000000000000000000000000000000000030d40")
)], "0x00000000000000000000")
}
)
def validator = new EthereumUpstreamValidator(up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
when:
def act = validator.validateCallLimit().block(Duration.ofSeconds(1))
then:
act == OK
when:
def act2 = validator.validateCallLimit().block(Duration.ofSeconds(1))
then:
act2 == OK
}
def "Upstream not available if error returned on call limit check"() {
setup:
def options = UpstreamsConfig.PartialOptions.getDefaults().buildOptions()
def up = TestingCommons.upstream(
new ApiReaderMock().tap {
answer("eth_call", [new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
HexData.from("0xd8a26e3a0000000000000000000000000000000000000000000000000000000000030d40")
)], new RpcResponseError(RpcResponseError.CODE_INVALID_REQUEST, "Too long"))
}
)
def validator = new EthereumUpstreamValidator(up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
when:
def act = validator.validateCallLimit().block(Duration.ofSeconds(1))
then:
act == UNAVAILABLE
when:
def act2 = validator.validateCallLimit().block(Duration.ofSeconds(1))
then:
act2 == UNAVAILABLE
}
}

View File

@@ -7,6 +7,7 @@ chain-settings:
lagging: 1
chains:
- id: eth
call-validate-contract: 0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
lags:
syncing: 6
lagging: 1

View File

@@ -8,6 +8,7 @@ upstreams:
disable-validation: false
validate-syncing: true
validate-peers: false
validate-call-limit: true
connection:
ethereum:
rpc:
@@ -19,6 +20,7 @@ upstreams:
disable-validation: false
validate-syncing: false
validate-peers: false
validate-call-limit: false
connection:
ethereum:
rpc: