diff --git a/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfig.kt b/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfig.kt index 7ffa3c7b..4d174a6f 100644 --- a/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfig.kt +++ b/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfig.kt @@ -44,6 +44,25 @@ data class ChainsConfig(private val chains: List) : Iterable "$op $limit" } } + enum class LowerBoundType { + UNKNOWN, STATE, SLOT, BLOCK, TX, LOGS, TRACE, PROOF, BLOB, EPOCH, RECEIPTS; + + companion object { + fun byName(name: String): LowerBoundType { + return LowerBoundType.entries.firstOrNull { it.name.equals(name, ignoreCase = true) } ?: UNKNOWN + } + } + } + + open class GoldLowerBound( + val block: Long, + ) + + class GoldLowerBoundWithHash( + block: Long, + val hash: String, + ) : GoldLowerBound(block) + data class ChainConfig( val expectedBlockTime: Duration, val syncingLagSize: Int, @@ -59,6 +78,7 @@ data class ChainsConfig(private val chains: List) : Iterable ) { companion object { @JvmStatic @@ -80,6 +100,7 @@ data class ChainsConfig(private val chains: List) : Iterable) : Iterable { + return chainMap.values + } } diff --git a/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfigReader.kt b/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfigReader.kt index 19c0666b..e7934004 100644 --- a/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfigReader.kt +++ b/foundation/src/main/kotlin/org/drpc/chainsconfig/ChainsConfigReader.kt @@ -137,9 +137,47 @@ class ChainsConfigReader( blockchain = blockchain, type = type, gasPriceCondition = ChainsConfig.GasPriceCondition(gasPriceConditions), + goldLowerBounds = readGoldLowerBounds(node), ) } + private fun readGoldLowerBounds(node: MappingNode): Map { + val goldLowerBounds = getMapping(node, "lower-bounds") ?: return emptyMap() + + return goldLowerBounds.value.asSequence() + .filter { + (it.keyNode.valueAsString()?.isNotBlank() ?: false) && it.valueNode.tag == Tag.MAP + }.map { + val type = ChainsConfig.LowerBoundType.byName(it.keyNode.valueAsString()!!) + val bound = if (type == ChainsConfig.LowerBoundType.TX || type == ChainsConfig.LowerBoundType.RECEIPTS) { + readHashGoldLowerBound(it.valueNode as MappingNode) + } else { + readGoldLowerBound(it.valueNode as MappingNode) + } + + if (bound != null) { + type to bound + } else { + null + } + } + .filterNotNull() + .associate { it } + } + + private fun readGoldLowerBound(node: MappingNode): ChainsConfig.GoldLowerBound? { + val block = getValueAsLong(node, "block") ?: return null + + return ChainsConfig.GoldLowerBound(block) + } + + private fun readHashGoldLowerBound(node: MappingNode): ChainsConfig.GoldLowerBound? { + val hash = getValueAsString(node, "hash") ?: return null + val block = getValueAsLong(node, "block") ?: return null + + return ChainsConfig.GoldLowerBoundWithHash(block, hash) + } + override fun read(input: MappingNode?): ChainsConfig { val default = readChains(readNode(defaultConfig)) val current = readChains(input) diff --git a/foundation/src/main/resources/public b/foundation/src/main/resources/public index 5ad28fd8..b381391a 160000 --- a/foundation/src/main/resources/public +++ b/foundation/src/main/resources/public @@ -1 +1 @@ -Subproject commit 5ad28fd817b3615ce3534f9df63b14735f5f7f01 +Subproject commit b381391a23f75edc1c86dddf3a9aa91cc7a852e1 diff --git a/foundation/src/test/kotlin/org/drpc/chainsconfig/ChainsConfigReaderTest.kt b/foundation/src/test/kotlin/org/drpc/chainsconfig/ChainsConfigReaderTest.kt index abc1f6a1..c1a529d3 100644 --- a/foundation/src/test/kotlin/org/drpc/chainsconfig/ChainsConfigReaderTest.kt +++ b/foundation/src/test/kotlin/org/drpc/chainsconfig/ChainsConfigReaderTest.kt @@ -1,12 +1,13 @@ package org.drpc.chainsconfig +import io.emeraldpay.dshackle.config.ChainsConfig import io.emeraldpay.dshackle.config.ChainsConfigReader import io.emeraldpay.dshackle.foundation.ChainOptionsReader import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import java.math.BigInteger -internal class ChainsConfigReaderTest { +class ChainsConfigReaderTest { @Test fun `read standard config without custom`() { @@ -41,5 +42,17 @@ internal class ChainsConfigReaderTest { assertEquals(config.code, "FTA") assertEquals(config.grpcId, 102) assertEquals(config.netVersion, BigInteger.valueOf(251)) + assertEquals(15L, config.goldLowerBounds[ChainsConfig.LowerBoundType.STATE]!!.block) + assertEquals(150L, config.goldLowerBounds[ChainsConfig.LowerBoundType.BLOCK]!!.block) + assertEquals(1L, config.goldLowerBounds[ChainsConfig.LowerBoundType.TX]!!.block) + assertEquals( + "0x5e77a04531c7c107af1882d76cbff9486d0a9aa53701c30888509d4f5f2b003a", + (config.goldLowerBounds[ChainsConfig.LowerBoundType.TX] as ChainsConfig.GoldLowerBoundWithHash).hash, + ) + assertEquals(1000L, config.goldLowerBounds[ChainsConfig.LowerBoundType.RECEIPTS]!!.block) + assertEquals( + "0x4e72a04531c7c107af1882d76cbff9486d0a9aa53701c30888509d4f5f2b003a", + (config.goldLowerBounds[ChainsConfig.LowerBoundType.RECEIPTS] as ChainsConfig.GoldLowerBoundWithHash).hash, + ) } } diff --git a/foundation/src/test/resources/configs/chains-basic.yaml b/foundation/src/test/resources/configs/chains-basic.yaml index 8ba3e3af..cae9937f 100644 --- a/foundation/src/test/resources/configs/chains-basic.yaml +++ b/foundation/src/test/resources/configs/chains-basic.yaml @@ -16,4 +16,15 @@ chain-settings: short-names: [fantom] code: FTA grpcId: 102 - chain-id: 0xfb \ No newline at end of file + chain-id: 0xfb + lower-bounds: + tx: + block: 1 + hash: "0x5e77a04531c7c107af1882d76cbff9486d0a9aa53701c30888509d4f5f2b003a" + receipts: + block: 1000 + hash: "0x4e72a04531c7c107af1882d76cbff9486d0a9aa53701c30888509d4f5f2b003a" + state: + block: 15 + block: + block: 150 diff --git a/src/main/kotlin/io/emeraldpay/dshackle/startup/configure/UpstreamCreator.kt b/src/main/kotlin/io/emeraldpay/dshackle/startup/configure/UpstreamCreator.kt index 5a7e7f27..ec36a2d7 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/startup/configure/UpstreamCreator.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/startup/configure/UpstreamCreator.kt @@ -9,6 +9,8 @@ import io.emeraldpay.dshackle.foundation.ChainOptions.Options import io.emeraldpay.dshackle.upstream.CallTargetsHolder import io.emeraldpay.dshackle.upstream.calls.CallMethods import io.emeraldpay.dshackle.upstream.calls.ManagedCallMethods +import io.emeraldpay.dshackle.upstream.lowerbound.GoldLowerBounds +import jakarta.annotation.PostConstruct import org.slf4j.Logger import org.slf4j.LoggerFactory import java.util.function.Function @@ -20,6 +22,11 @@ abstract class UpstreamCreator( ) { protected val log: Logger = LoggerFactory.getLogger(this::class.java) + @PostConstruct + fun init() { + GoldLowerBounds.init(chainsConfig.getChainConfigs()) + } + companion object { fun getHash(nodeId: Int?, obj: Any, hashes: MutableSet): Short { val hash = nodeId?.toShort() diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetector.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetector.kt index f29a6d7a..872df450 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetector.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetector.kt @@ -1,15 +1,19 @@ package io.emeraldpay.dshackle.upstream.ethereum import io.emeraldpay.dshackle.Defaults +import io.emeraldpay.dshackle.config.ChainsConfig import io.emeraldpay.dshackle.data.BlockContainer import io.emeraldpay.dshackle.upstream.ChainRequest +import io.emeraldpay.dshackle.upstream.ChainResponse import io.emeraldpay.dshackle.upstream.Upstream +import io.emeraldpay.dshackle.upstream.lowerbound.GoldLowerBounds import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound import io.emeraldpay.dshackle.upstream.lowerbound.toHex import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import reactor.core.publisher.Flux +import reactor.core.publisher.Mono class EthereumLowerBoundReceiptsDetector( private val upstream: Upstream, @@ -29,6 +33,7 @@ class EthereumLowerBoundReceiptsDetector( "invalid block height", // hyperliquid "header not found", "pruned history unavailable", // xlayer + "transaction indexing is in progress", ).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS) } @@ -39,6 +44,34 @@ class EthereumLowerBoundReceiptsDetector( } override fun internalDetectLowerBound(): Flux { + val receiptsGoldBound = GoldLowerBounds.getBound(upstream.getChain(), LowerBoundType.RECEIPTS) + if (receiptsGoldBound == null || receiptsGoldBound !is ChainsConfig.GoldLowerBoundWithHash) { + return recursiveDetectReceiptsLowerBound() + } + return Mono.just(receiptsGoldBound) + .flatMapMany { bound -> + upstream.getIngressReader() + .read(ChainRequest("eth_getTransactionReceipt", ListParams(bound.hash))) + .timeout(Defaults.internalCallsTimeout) + .flatMap(ChainResponse::requireResult) + .flatMapMany { + if (it.contentEquals("null".toByteArray())) { + throw IllegalStateException("no gold bound") + } else { + Flux.just(LowerBoundData(1, LowerBoundType.RECEIPTS)) + } + } + .onErrorResume { + recursiveDetectReceiptsLowerBound() + } + } + } + + override fun types(): Set { + return setOf(LowerBoundType.RECEIPTS) + } + + private fun recursiveDetectReceiptsLowerBound(): Flux { return recursiveLowerBound.recursiveDetectLowerBoundWithOffset(MAX_OFFSET) { block -> upstream.getIngressReader() .read( @@ -72,8 +105,4 @@ class EthereumLowerBoundReceiptsDetector( } } } - - override fun types(): Set { - return setOf(LowerBoundType.RECEIPTS) - } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetector.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetector.kt index 48f39833..5a005136 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetector.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetector.kt @@ -1,15 +1,19 @@ package io.emeraldpay.dshackle.upstream.ethereum import io.emeraldpay.dshackle.Defaults +import io.emeraldpay.dshackle.config.ChainsConfig import io.emeraldpay.dshackle.data.BlockContainer import io.emeraldpay.dshackle.upstream.ChainRequest +import io.emeraldpay.dshackle.upstream.ChainResponse import io.emeraldpay.dshackle.upstream.Upstream +import io.emeraldpay.dshackle.upstream.lowerbound.GoldLowerBounds import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound import io.emeraldpay.dshackle.upstream.lowerbound.toHex import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import reactor.core.publisher.Flux +import reactor.core.publisher.Mono class EthereumLowerBoundTxDetector( private val upstream: Upstream, @@ -24,6 +28,7 @@ class EthereumLowerBoundTxDetector( "Unexpected error", // hyperliquid "invalid block height", // hyperliquids "pruned history unavailable", // xlayer blocks + "transaction indexing is in progress", ).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS) } @@ -34,6 +39,34 @@ class EthereumLowerBoundTxDetector( } override fun internalDetectLowerBound(): Flux { + val txGoldBound = GoldLowerBounds.getBound(upstream.getChain(), LowerBoundType.TX) + if (txGoldBound == null || txGoldBound !is ChainsConfig.GoldLowerBoundWithHash) { + return recursiveDetectTxLowerBound() + } + return Mono.just(txGoldBound) + .flatMapMany { bound -> + upstream.getIngressReader() + .read(ChainRequest("eth_getTransactionByHash", ListParams(bound.hash))) + .timeout(Defaults.internalCallsTimeout) + .flatMap(ChainResponse::requireResult) + .flatMapMany { + if (it.contentEquals("null".toByteArray())) { + throw IllegalStateException("no gold bound") + } else { + Flux.just(LowerBoundData(1, LowerBoundType.TX)) + } + } + .onErrorResume { + recursiveDetectTxLowerBound() + } + } + } + + override fun types(): Set { + return setOf(LowerBoundType.TX) + } + + private fun recursiveDetectTxLowerBound(): Flux { return recursiveLowerBound.recursiveDetectLowerBoundWithOffset(MAX_OFFSET) { block -> upstream.getIngressReader() .read( @@ -67,8 +100,4 @@ class EthereumLowerBoundTxDetector( } } } - - override fun types(): Set { - return setOf(LowerBoundType.TX) - } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBounds.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBounds.kt new file mode 100644 index 00000000..82a8ab78 --- /dev/null +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBounds.kt @@ -0,0 +1,28 @@ +package io.emeraldpay.dshackle.upstream.lowerbound + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.Global +import io.emeraldpay.dshackle.config.ChainsConfig + +object GoldLowerBounds { + + private lateinit var bounds: Map> + + fun init(chainConfigs: Collection) { + bounds = chainConfigs.filter { + it.shortNames.isNotEmpty() && + Global.chainById(it.shortNames[0]) != Chain.UNSPECIFIED && + it.goldLowerBounds.isNotEmpty() + }.associate { cfg -> + Global.chainById(cfg.shortNames[0]) to cfg.goldLowerBounds.mapKeys { toLowerBoundType(it.key) } + } + } + + fun getBound(chain: Chain, boundType: LowerBoundType): ChainsConfig.GoldLowerBound? { + return bounds[chain]?.get(boundType) + } + + private fun toLowerBoundType(type: ChainsConfig.LowerBoundType): LowerBoundType { + return LowerBoundType.byName(type.name) + } +} diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/LowerBoundDetector.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/LowerBoundDetector.kt index 2dbd5002..e56226bf 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/LowerBoundDetector.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/LowerBoundDetector.kt @@ -47,7 +47,7 @@ abstract class LowerBoundDetector( }, ) .filter { - it.lowerBound >= (lowerBounds.getLastBound(it.type)?.lowerBound ?: 0) + (it.lowerBound >= (lowerBounds.getLastBound(it.type)?.lowerBound ?: 0)) || it.lowerBound == 1L } .map { lowerBounds.updateBound(it) diff --git a/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetectorTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetectorTest.kt new file mode 100644 index 00000000..f3bbdeca --- /dev/null +++ b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundReceiptsDetectorTest.kt @@ -0,0 +1,56 @@ +package io.emeraldpay.dshackle.upstream.ethereum + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.config.ChainsConfig +import io.emeraldpay.dshackle.reader.ChainReader +import io.emeraldpay.dshackle.upstream.ChainRequest +import io.emeraldpay.dshackle.upstream.ChainResponse +import io.emeraldpay.dshackle.upstream.Upstream +import io.emeraldpay.dshackle.upstream.lowerbound.GoldLowerBounds +import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType +import io.emeraldpay.dshackle.upstream.lowerbound.NoopManualLowerBoundService +import io.emeraldpay.dshackle.upstream.rpcclient.ListParams +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import reactor.core.publisher.Mono +import reactor.test.StepVerifier +import java.time.Duration + +class EthereumLowerBoundReceiptsDetectorTest { + + @Test + fun `archival bound if there is a gold bound`() { + val reader = mock { + on { read(ChainRequest("eth_getTransactionReceipt", ListParams("goldHash"))) } doReturn + Mono.just(ChainResponse("result".toByteArray(), null)) + } + val upstream = mock { + on { getChain() } doReturn Chain.POLYGON__MAINNET + on { getIngressReader() } doReturn reader + } + GoldLowerBounds.init( + listOf( + ChainsConfig.ChainConfig + .default() + .copy( + shortNames = listOf("polygon"), + goldLowerBounds = mapOf(ChainsConfig.LowerBoundType.RECEIPTS to ChainsConfig.GoldLowerBoundWithHash(10L, "goldHash")), + ), + ), + ) + + val txDetector = EthereumLowerBoundReceiptsDetector(upstream) + + StepVerifier.withVirtualTime { txDetector.detectLowerBound(NoopManualLowerBoundService()) } + .expectSubscription() + .expectNoEvent(Duration.ofSeconds(15)) + .expectNextMatches { it.lowerBound == 1L && it.type == LowerBoundType.RECEIPTS } + .thenCancel() + .verify(Duration.ofSeconds(1)) + + verify(reader).read(ChainRequest("eth_getTransactionReceipt", ListParams("goldHash"))) + verify(upstream).getIngressReader() + } +} diff --git a/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetectorTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetectorTest.kt new file mode 100644 index 00000000..61e38d57 --- /dev/null +++ b/src/test/kotlin/io/emeraldpay/dshackle/upstream/ethereum/EthereumLowerBoundTxDetectorTest.kt @@ -0,0 +1,56 @@ +package io.emeraldpay.dshackle.upstream.ethereum + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.config.ChainsConfig +import io.emeraldpay.dshackle.reader.ChainReader +import io.emeraldpay.dshackle.upstream.ChainRequest +import io.emeraldpay.dshackle.upstream.ChainResponse +import io.emeraldpay.dshackle.upstream.Upstream +import io.emeraldpay.dshackle.upstream.lowerbound.GoldLowerBounds +import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType +import io.emeraldpay.dshackle.upstream.lowerbound.NoopManualLowerBoundService +import io.emeraldpay.dshackle.upstream.rpcclient.ListParams +import org.junit.jupiter.api.Test +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import reactor.core.publisher.Mono +import reactor.test.StepVerifier +import java.time.Duration + +class EthereumLowerBoundTxDetectorTest { + + @Test + fun `archival bound if there is a gold bound`() { + val reader = mock { + on { read(ChainRequest("eth_getTransactionByHash", ListParams("goldHash"))) } doReturn + Mono.just(ChainResponse("result".toByteArray(), null)) + } + val upstream = mock { + on { getChain() } doReturn Chain.POLYGON__MAINNET + on { getIngressReader() } doReturn reader + } + GoldLowerBounds.init( + listOf( + ChainsConfig.ChainConfig + .default() + .copy( + shortNames = listOf("polygon"), + goldLowerBounds = mapOf(ChainsConfig.LowerBoundType.TX to ChainsConfig.GoldLowerBoundWithHash(10L, "goldHash")), + ), + ), + ) + + val txDetector = EthereumLowerBoundTxDetector(upstream) + + StepVerifier.withVirtualTime { txDetector.detectLowerBound(NoopManualLowerBoundService()) } + .expectSubscription() + .expectNoEvent(Duration.ofSeconds(15)) + .expectNextMatches { it.lowerBound == 1L && it.type == LowerBoundType.TX } + .thenCancel() + .verify(Duration.ofSeconds(1)) + + verify(reader).read(ChainRequest("eth_getTransactionByHash", ListParams("goldHash"))) + verify(upstream).getIngressReader() + } +} diff --git a/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBoundsTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBoundsTest.kt new file mode 100644 index 00000000..12d5faa8 --- /dev/null +++ b/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/GoldLowerBoundsTest.kt @@ -0,0 +1,63 @@ +package io.emeraldpay.dshackle.upstream.lowerbound + +import io.emeraldpay.dshackle.Chain +import io.emeraldpay.dshackle.config.ChainsConfig +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +class GoldLowerBoundsTest { + + @Test + fun `init with all bounds`() { + val cfg = ChainsConfig.ChainConfig.default() + .copy( + shortNames = listOf("polygon"), + goldLowerBounds = ChainsConfig.LowerBoundType.entries + .associateWith { + if (it == ChainsConfig.LowerBoundType.TX || it == ChainsConfig.LowerBoundType.RECEIPTS) { + ChainsConfig.GoldLowerBoundWithHash(5L, "hash_$it") + } else { + ChainsConfig.GoldLowerBound(10L) + } + }, + ) + + GoldLowerBounds.init(listOf(cfg)) + + LowerBoundType.entries + .filter { it != LowerBoundType.UNKNOWN } + .forEach { + val bound = GoldLowerBounds.getBound(Chain.POLYGON__MAINNET, it) + + assertThat(bound).isNotNull + + if (it == LowerBoundType.TX || it == LowerBoundType.RECEIPTS) { + assertThat(bound) + .usingRecursiveComparison() + .isEqualTo(ChainsConfig.GoldLowerBoundWithHash(5L, "hash_$it")) + } else { + assertThat(bound) + .usingRecursiveComparison() + .isEqualTo(ChainsConfig.GoldLowerBound(10L)) + } + } + } + + @Test + fun `no gold bound`() { + val cfg = ChainsConfig.ChainConfig.default() + .copy( + shortNames = listOf("polygon"), + ) + + GoldLowerBounds.init(listOf(cfg)) + + LowerBoundType.entries + .filter { it != LowerBoundType.UNKNOWN } + .forEach { + val bound = GoldLowerBounds.getBound(Chain.POLYGON__MAINNET, it) + + assertThat(bound).isNull() + } + } +} diff --git a/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/RecursiveLowerBoundServiceTest.kt b/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/RecursiveLowerBoundServiceTest.kt index a5fd2bc4..bbe29ad2 100644 --- a/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/RecursiveLowerBoundServiceTest.kt +++ b/src/test/kotlin/io/emeraldpay/dshackle/upstream/lowerbound/RecursiveLowerBoundServiceTest.kt @@ -13,6 +13,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.ZERO_ADDRESS import io.emeraldpay.dshackle.upstream.polkadot.PolkadotLowerBoundService import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -247,6 +248,12 @@ class RecursiveLowerBoundServiceTest { private const val STATE_CHECKER_CALL_DATA = "0x1eaf190c" private const val STATE_CHECKER_BYTECODE = "0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c80631eaf190c14602a575b5f5ffd5b60306044565b604051603b91906078565b60405180910390f35b5f5f73ffffffffffffffffffffffffffffffffffffffff1631905090565b5f819050919050565b6072816062565b82525050565b5f60208201905060895f830184606b565b9291505056fea2646970667358221220251f5b4d2ed1abe77f66fde198a57ada08562dc3b0afbc6bac0261d1bf516b5d64736f6c634300081e0033" + @BeforeAll + @JvmStatic + fun init() { + GoldLowerBounds.init(emptyList()) + } + @JvmStatic fun detectorsFirstBlock(): List = listOf( Arguments.of(