diff --git a/docs/04-upstream-config.adoc b/docs/04-upstream-config.adoc index 7f0b2b73..54850a68 100644 --- a/docs/04-upstream-config.adoc +++ b/docs/04-upstream-config.adoc @@ -151,6 +151,12 @@ Currently, dshackle supports next chains (should be used as chain names in confi - base-goerli - linea - linea-goerli +- fantom +- fantom-testnet +- gnosis +- gnosis-chiado +- avalanche +- avalanche-fuji === Roles and Fallback upstream diff --git a/emerald-grpc b/emerald-grpc index 1afdf11b..3a5a63d6 160000 --- a/emerald-grpc +++ b/emerald-grpc @@ -1 +1 @@ -Subproject commit 1afdf11bbfb439eb4cd30a85cec3bc5996b34d10 +Subproject commit 3a5a63d618f5c4e920d51d3400297a62838ceb96 diff --git a/src/main/kotlin/io/emeraldpay/dshackle/BlockchainType.kt b/src/main/kotlin/io/emeraldpay/dshackle/BlockchainType.kt index 5efd0955..26a6a468 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/BlockchainType.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/BlockchainType.kt @@ -4,46 +4,21 @@ enum class BlockchainType { BITCOIN, EVM_POW, EVM_POS; companion object { + + val pow = setOf( + Chain.ETHEREUM_CLASSIC__MAINNET, Chain.RSK__MAINNET, Chain.ETHEREUM__KOVAN, + Chain.ETHEREUM__MORDEN, Chain.ETHEREUM__RINKEBY + ) + val bitcoin = setOf(Chain.BITCOIN__MAINNET, Chain.BITCOIN__TESTNET) @JvmStatic fun from(chain: Chain): BlockchainType { - if (chain == Chain.ETHEREUM__ROPSTEN || - chain == Chain.POLYGON_POS__MAINNET || - chain == Chain.OPTIMISM__MAINNET || - chain == Chain.ARBITRUM__MAINNET || - chain == Chain.BSC__MAINNET || - chain == Chain.ETHEREUM__GOERLI || - chain == Chain.ETHEREUM__MAINNET || - chain == Chain.ETHEREUM__SEPOLIA || - chain == Chain.ARBITRUM__GOERLI || - chain == Chain.OPTIMISM__GOERLI || - chain == Chain.ARBITRUM_NOVA__MAINNET || - chain == Chain.POLYGON_ZKEVM__MAINNET || - chain == Chain.POLYGON_ZKEVM__TESTNET || - chain == Chain.ZKSYNC__MAINNET || - chain == Chain.ZKSYNC__TESTNET || - chain == Chain.POLYGON_POS__MUMBAI || - chain == Chain.BASE__MAINNET || - chain == Chain.BASE__GOERLI || - chain == Chain.LINEA__MAINNET || - chain == Chain.LINEA__GOERLI - ) { - return EVM_POS + return if (pow.contains(chain)) { + EVM_POW + } else if (bitcoin.contains(chain)) { + BITCOIN + } else { + EVM_POS } - if (chain == Chain.ETHEREUM_CLASSIC__MAINNET || - chain == Chain.FANTOM__MAINNET || - chain == Chain.RSK__MAINNET || - chain == Chain.ETHEREUM__KOVAN || - chain == Chain.ETHEREUM__MORDEN || - chain == Chain.ETHEREUM__RINKEBY - ) { - return EVM_POW - } - if (chain == Chain.BITCOIN__MAINNET || - chain == Chain.BITCOIN__TESTNET - ) { - return BITCOIN - } - throw IllegalArgumentException("Unknown type of blockchain: $chain") } } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/Chain.kt b/src/main/kotlin/io/emeraldpay/dshackle/Chain.kt index 6d4baa1d..bb0e5489 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/Chain.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/Chain.kt @@ -49,6 +49,8 @@ enum class Chain(val id: Int, val chainCode: String, val chainName: String) { 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"), // Testnets ETHEREUM__MORDEN(10001, "MORDEN", "Morden Testnet"), @@ -88,7 +90,10 @@ enum class Chain(val id: Int, val chainCode: String, val chainName: String) { "Polygon POS Mumbai Testnet" ), BASE__GOERLI(10014, "BASE_GOERLI", "Base Goerli Testnet"), - LINEA__GOERLI(10015, "LINEA_GOERLI", "Linea 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"); companion object { fun byId(id: Int): Chain { diff --git a/src/main/kotlin/io/emeraldpay/dshackle/Global.kt b/src/main/kotlin/io/emeraldpay/dshackle/Global.kt index f05ed86e..f501d052 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/Global.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/Global.kt @@ -81,6 +81,12 @@ class Global { "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, ) fun chainById(id: String?): Chain { diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt index 20b5f02f..1c54280f 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/calls/DefaultEthereumMethods.kt @@ -104,6 +104,15 @@ class DefaultEthereumMethods( Chain.LINEA__MAINNET to HardcodedData("\"59144\"", "\"0xe708\""), Chain.LINEA__GOERLI to HardcodedData("\"59140\"", "\"0xe704\""), + + Chain.FANTOM__MAINNET to HardcodedData("\"250\"", "\"0xfa\""), + Chain.FANTOM__TESTNET to HardcodedData("\"4002\"", "\"0xfa2\""), + + Chain.GNOSIS__MAINNET to HardcodedData("\"100\"", "\"0x64\""), + Chain.GNOSIS__CHIADO to HardcodedData("\"10200\"", "\"0x27d8\""), + + Chain.AVALANCHE__MAINNET to HardcodedData("\"43114\"", "\"0xa86a\""), + Chain.AVALANCHE__FUJI to HardcodedData("\"43113\"", "\"0xa869\""), ) fun getChainByData(data: HardcodedData) = CHAIN_DATA.entries.find { it.value == data }?.key 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 997cda8f..07cb3fc4 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumUpstreamValidator.kt @@ -37,6 +37,7 @@ import org.springframework.scheduling.concurrent.CustomizableThreadFactory import reactor.core.publisher.Flux import reactor.core.publisher.Mono import reactor.core.scheduler.Schedulers +import reactor.kotlin.extra.retry.retryRandomBackoff import reactor.util.function.Tuple2 import java.time.Duration import java.util.concurrent.Executors @@ -93,6 +94,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor( UpstreamAvailability.OK } } + .doOnError { err -> log.error("Error during syncing validation for ${upstream.getId()}", err) } .onErrorReturn(UpstreamAvailability.UNAVAILABLE) } @@ -118,6 +120,7 @@ open class EthereumUpstreamValidator @JvmOverloads constructor( UpstreamAvailability.OK } } + .doOnError { err -> log.error("Error during peer count validation for ${upstream.getId()}", err) } .onErrorReturn(UpstreamAvailability.UNAVAILABLE) } @@ -203,13 +206,25 @@ open class EthereumUpstreamValidator @JvmOverloads constructor( Mono.fromCallable { log.error("No response for eth_call limit check from ${upstream.getId()}") } .then(Mono.error(TimeoutException("Validation timeout for call limit"))) ) + .retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx -> + log.warn( + "error during validateCallLimit for ${upstream.getId()}, iteration ${ctx.iteration()}", + ctx.exception() + ) + } .onErrorReturn(false) } private fun chainId(): Mono { return upstream.getIngressReader() .read(JsonRpcRequest("eth_chainId", emptyList())) - .doOnError { log.error("Error during execution 'eth_chainId' - ${it.message}") } + .retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx -> + log.warn( + "error during chainId retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}", + ctx.exception() + ) + } + .doOnError { log.error("Error during execution 'eth_chainId' - ${it.message} for ${upstream.getId()}") } .flatMap(JsonRpcResponse::requireResult) .map { String(it) } } @@ -217,7 +232,13 @@ open class EthereumUpstreamValidator @JvmOverloads constructor( private fun netVersion(): Mono { return upstream.getIngressReader() .read(JsonRpcRequest("net_version", emptyList())) - .doOnError { log.error("Error during execution 'net_version' - ${it.message}") } + .retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx -> + log.warn( + "error during netVersion retrieving for ${upstream.getId()}, iteration ${ctx.iteration()}", + ctx.exception() + ) + } + .doOnError { log.error("Error during execution 'net_version' - ${it.message} for ${upstream.getId()}") } .flatMap(JsonRpcResponse::requireResult) .map { String(it) } } diff --git a/src/main/resources/chains.yaml b/src/main/resources/chains.yaml index 6b7fab26..fd2dcacc 100644 --- a/src/main/resources/chains.yaml +++ b/src/main/resources/chains.yaml @@ -90,3 +90,41 @@ chain-settings: lags: syncing: 40 lagging: 20 + - id: avalanche + options: + validate-peers: false + validate-syncing: false + lags: + syncing: 10 + lagging: 5 + - id: avalanche-fuji + options: + validate-peers: false + validate-syncing: false + lags: + syncing: 10 + lagging: 5 + - id: gnosis + options: + validate-peers: false + lags: + syncing: 10 + lagging: 5 + - id: gnosis-chiado + options: + validate-peers: false + lags: + syncing: 10 + lagging: 5 + - id: fantom + options: + validate-peers: false + lags: + syncing: 10 + lagging: 5 + - id: fantom-testnet + options: + validate-peers: false + lags: + syncing: 10 + lagging: 5