Check gas price (#494)
* add gas validation option * gas price config for chains * check gas price * add tests * fix txt * make gas price dls * fix config
This commit is contained in:
@@ -94,8 +94,9 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
||||
validateChain(),
|
||||
validateOldBlocks(),
|
||||
validateCallLimit(),
|
||||
validateGasPrice(),
|
||||
).map {
|
||||
listOf(it.t1, it.t2, it.t3).maxOf { it }
|
||||
listOf(it.t1, it.t2, it.t3, it.t4).maxOf { it }
|
||||
}.block() ?: ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR
|
||||
}
|
||||
|
||||
@@ -195,6 +196,31 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun validateGasPrice(): Mono<ValidateUpstreamSettingsResult> {
|
||||
if (!options.validateGasPrice || config.gasPriceCondition == null) {
|
||||
return Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||
}
|
||||
return upstream.getIngressReader()
|
||||
.read(ChainRequest("eth_gasPrice", ListParams()))
|
||||
.flatMap(ChainResponse::requireStringResult)
|
||||
.map { result ->
|
||||
val actualGasPrice = result.substring(2).toLong(16)
|
||||
if (!config.gasPriceCondition!!.check(actualGasPrice)) {
|
||||
log.warn(
|
||||
"Node ${upstream.getId()} has gasPrice $actualGasPrice, " +
|
||||
"but it is not equal to the required ${config.gasPriceCondition!!.rules()}",
|
||||
)
|
||||
ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
|
||||
} else {
|
||||
ValidateUpstreamSettingsResult.UPSTREAM_VALID
|
||||
}
|
||||
}
|
||||
.onErrorResume { err ->
|
||||
log.warn("Error during gasPrice validation", err)
|
||||
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR)
|
||||
}
|
||||
}
|
||||
|
||||
private fun chainId(): Mono<String> {
|
||||
return upstream.getIngressReader()
|
||||
.read(ChainRequest("eth_chainId", ListParams()))
|
||||
|
||||
@@ -636,7 +636,7 @@ class UpstreamsConfigReaderSpec extends Specification {
|
||||
def options = partialOptions.buildOptions()
|
||||
then:
|
||||
options == new ChainOptions.Options(
|
||||
false, false, 30, Duration.ofSeconds(60), null, true, 1, true, true, true, 1_000_000
|
||||
false, false, 30, Duration.ofSeconds(60), null, true, 1, true, true, true, true, 1_000_000
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ 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.*
|
||||
@@ -55,16 +56,16 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
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
|
||||
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"() {
|
||||
@@ -112,7 +113,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
Mono.just(new ChainResponse('false'.getBytes(), null))
|
||||
]
|
||||
}
|
||||
2 * getHead() >> Mock(Head) {head ->
|
||||
2 * getHead() >> Mock(Head) { head ->
|
||||
1 * head.onSyncingNode(true)
|
||||
1 * head.onSyncingNode(false)
|
||||
}
|
||||
@@ -276,6 +277,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||
it.validateCallLimit = false
|
||||
it.validateChain = false
|
||||
it.validateGasPrice = false
|
||||
}.buildOptions()
|
||||
def up = Mock(Upstream) {
|
||||
2 * getIngressReader() >>
|
||||
@@ -297,6 +299,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
setup:
|
||||
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||
it.validateChain = false
|
||||
it.validateGasPrice = false
|
||||
}.buildOptions()
|
||||
def up = Mock(Upstream) {
|
||||
3 * getIngressReader() >> Mock(Reader) {
|
||||
@@ -321,6 +324,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
setup:
|
||||
def options = ChainOptions.PartialOptions.getDefaults().tap {
|
||||
it.validateChain = false
|
||||
it.validateGasPrice = false
|
||||
}.buildOptions()
|
||||
def up = Mock(Upstream) {
|
||||
3 * getIngressReader() >> Mock(Reader) {
|
||||
@@ -341,6 +345,52 @@ class EthereumUpstreamValidatorSpec extends Specification {
|
||||
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")
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user