Add determination of the lower block (#372)
This commit is contained in:
@@ -20,8 +20,10 @@ import io.emeraldpay.dshackle.Chain
|
||||
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.data.BlockContainer
|
||||
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||
import io.emeraldpay.dshackle.upstream.calls.*
|
||||
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
|
||||
@@ -29,7 +31,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import org.jetbrains.annotations.NotNull
|
||||
import org.reactivestreams.Publisher
|
||||
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||
|
||||
class GenericUpstreamMock extends GenericUpstream {
|
||||
EthereumHeadMock ethereumHeadMock
|
||||
@@ -74,6 +75,7 @@ class GenericUpstreamMock extends GenericUpstream {
|
||||
new ConnectorFactoryMock(api, new EthereumHeadMock()),
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&validator,
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&labelDetector,
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&lowerBoundBlockDetector,
|
||||
)
|
||||
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
||||
setLag(0)
|
||||
@@ -104,4 +106,9 @@ class GenericUpstreamMock extends GenericUpstream {
|
||||
String toString() {
|
||||
return "Upstream mock ${getId()}"
|
||||
}
|
||||
|
||||
@Override
|
||||
LowerBoundBlockDetector.LowerBlockData getLowerBlock() {
|
||||
return new LowerBoundBlockDetector.LowerBlockData(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class FilteredApisSpec extends Specification {
|
||||
connectorFactory,
|
||||
cs.&validator,
|
||||
cs.&labelDetector,
|
||||
cs.&lowerBoundBlockDetector
|
||||
)
|
||||
}
|
||||
def matcher = new Selector.LabelMatcher("test", ["foo"])
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package io.emeraldpay.dshackle.upstream
|
||||
|
||||
import io.emeraldpay.dshackle.Chain
|
||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLowerBoundBlockDetector
|
||||
import io.emeraldpay.dshackle.upstream.polkadot.PolkadotLowerBoundBlockDetector
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.params.ParameterizedTest
|
||||
import org.junit.jupiter.params.provider.Arguments
|
||||
import org.junit.jupiter.params.provider.MethodSource
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
import java.time.Duration
|
||||
|
||||
class RecursiveLowerBoundBlockDetectorTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("detectors")
|
||||
fun `find lower block closer to the height`(
|
||||
reader: JsonRpcReader,
|
||||
detectorClass: Class<LowerBoundBlockDetector>,
|
||||
) {
|
||||
val head = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn 18000000
|
||||
}
|
||||
val upstream = mock<Upstream> {
|
||||
on { getHead() } doReturn head
|
||||
on { getIngressReader() } doReturn reader
|
||||
}
|
||||
|
||||
val detector = detectorClass.getConstructor(Chain::class.java, Upstream::class.java).newInstance(Chain.UNSPECIFIED, upstream)
|
||||
|
||||
StepVerifier.withVirtualTime { detector.lowerBlock() }
|
||||
.expectSubscription()
|
||||
.expectNoEvent(Duration.ofSeconds(15))
|
||||
.expectNext(LowerBoundBlockDetector.LowerBlockData(17964844L))
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
|
||||
Assertions.assertEquals(LowerBoundBlockDetector.LowerBlockData(17964844L), detector.getCurrentLowerBlock())
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("detectorsFirstBlock")
|
||||
fun `lower block is 0x1`(
|
||||
reader: JsonRpcReader,
|
||||
detectorClass: Class<LowerBoundBlockDetector>,
|
||||
) {
|
||||
val head = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn 18000000
|
||||
}
|
||||
val upstream = mock<Upstream> {
|
||||
on { getHead() } doReturn head
|
||||
on { getIngressReader() } doReturn reader
|
||||
}
|
||||
|
||||
val detector = detectorClass.getConstructor(Chain::class.java, Upstream::class.java).newInstance(Chain.UNSPECIFIED, upstream)
|
||||
|
||||
StepVerifier.withVirtualTime { detector.lowerBlock() }
|
||||
.expectSubscription()
|
||||
.expectNoEvent(Duration.ofSeconds(15))
|
||||
.expectNext(LowerBoundBlockDetector.LowerBlockData(1))
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
|
||||
Assertions.assertEquals(LowerBoundBlockDetector.LowerBlockData(1), detector.getCurrentLowerBlock())
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val blocks = listOf(
|
||||
9000000L, 13500000L, 15750000L, 16875000L, 17437500L, 17718750L, 17859375L, 17929688L, 17964844L, 17947266L,
|
||||
17956055L, 17960449L, 17962646L, 17963745L, 17964294L, 17964569L, 17964706L, 17964775L, 17964809L, 17964826L,
|
||||
17964835L, 17964839L, 17964841L, 17964842L, 17964843L,
|
||||
)
|
||||
private const val hash1 = "0x1b1a5dd69e12aa12e2b9197be0d0cceef3dde6368ea6376ad7c8b06488c9cf6a"
|
||||
private const val hash2 = "0x1b1a5dd69e12aa12e2b9197be0d0cceef3dde6368ea6376ad7c8b06488c9cf7a"
|
||||
|
||||
@JvmStatic
|
||||
fun detectors(): List<Arguments> = listOf(
|
||||
Arguments.of(
|
||||
mock<JsonRpcReader> {
|
||||
blocks.forEach {
|
||||
if (it == 17964844L) {
|
||||
on {
|
||||
read(JsonRpcRequest("eth_getBalance", listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", it.toHex())))
|
||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
||||
} else {
|
||||
on {
|
||||
read(JsonRpcRequest("eth_getBalance", listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", it.toHex())))
|
||||
} doReturn Mono.error(RuntimeException())
|
||||
}
|
||||
}
|
||||
},
|
||||
EthereumLowerBoundBlockDetector::class.java,
|
||||
),
|
||||
Arguments.of(
|
||||
mock<JsonRpcReader> {
|
||||
blocks.forEach {
|
||||
if (it == 17964844L) {
|
||||
on {
|
||||
read(JsonRpcRequest("chain_getBlockHash", listOf(it.toHex())))
|
||||
} doReturn Mono.just(JsonRpcResponse("\"$hash1\"".toByteArray(), null))
|
||||
on {
|
||||
read(JsonRpcRequest("state_getMetadata", listOf(hash1)))
|
||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
||||
} else {
|
||||
on {
|
||||
read(JsonRpcRequest("chain_getBlockHash", listOf(it.toHex())))
|
||||
} doReturn Mono.just(JsonRpcResponse("\"$hash2\"".toByteArray(), null))
|
||||
on {
|
||||
read(JsonRpcRequest("state_getMetadata", listOf(hash2)))
|
||||
} doReturn Mono.error(RuntimeException())
|
||||
}
|
||||
}
|
||||
},
|
||||
PolkadotLowerBoundBlockDetector::class.java,
|
||||
),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun detectorsFirstBlock(): List<Arguments> = listOf(
|
||||
Arguments.of(
|
||||
mock<JsonRpcReader> {
|
||||
on {
|
||||
read(JsonRpcRequest("chain_getBlockHash", listOf(any())))
|
||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
||||
on {
|
||||
read(JsonRpcRequest("state_getMetadata", listOf(any())))
|
||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
||||
},
|
||||
PolkadotLowerBoundBlockDetector::class.java,
|
||||
),
|
||||
Arguments.of(
|
||||
mock<JsonRpcReader> {
|
||||
on { read(any()) } doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
||||
},
|
||||
EthereumLowerBoundBlockDetector::class.java,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.emeraldpay.dshackle.upstream.solana
|
||||
|
||||
import io.emeraldpay.dshackle.Chain
|
||||
import io.emeraldpay.dshackle.Global
|
||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import reactor.core.publisher.Mono
|
||||
import reactor.test.StepVerifier
|
||||
import java.time.Duration
|
||||
|
||||
class SolanaLowerBoundBlockDetectorTest {
|
||||
|
||||
@Test
|
||||
fun `get solana lower block and slot`() {
|
||||
val reader = mock<JsonRpcReader> {
|
||||
on { read(JsonRpcRequest("getFirstAvailableBlock", listOf())) } doReturn
|
||||
Mono.just(JsonRpcResponse("25000000".toByteArray(), null))
|
||||
on { read(JsonRpcRequest("getBlocks", listOf(24999990L, 25000000L))) } doReturn
|
||||
Mono.just(JsonRpcResponse("[23000000, 23000005, 23000010]".toByteArray(), null))
|
||||
on {
|
||||
read(
|
||||
JsonRpcRequest(
|
||||
"getBlock",
|
||||
listOf(
|
||||
23000010L,
|
||||
mapOf(
|
||||
"showRewards" to false,
|
||||
"transactionDetails" to "none",
|
||||
"maxSupportedTransactionVersion" to 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
} doReturn Mono.just(
|
||||
JsonRpcResponse(
|
||||
Global.objectMapper.writeValueAsBytes(
|
||||
mapOf(
|
||||
"blockHeight" to 21000000,
|
||||
"blockTime" to 111,
|
||||
"blockhash" to "22",
|
||||
"previousBlockhash" to "33",
|
||||
),
|
||||
),
|
||||
null,
|
||||
),
|
||||
)
|
||||
}
|
||||
val upstream = mock<Upstream> {
|
||||
on { getIngressReader() } doReturn reader
|
||||
}
|
||||
|
||||
val detector = SolanaLowerBoundBlockDetector(Chain.UNSPECIFIED, upstream)
|
||||
|
||||
StepVerifier.withVirtualTime { detector.lowerBlock() }
|
||||
.expectSubscription()
|
||||
.expectNoEvent(Duration.ofSeconds(15))
|
||||
.expectNext(LowerBoundBlockDetector.LowerBlockData(21000000, 23000010))
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
|
||||
Assertions.assertEquals(LowerBoundBlockDetector.LowerBlockData(21000000, 23000010), detector.getCurrentLowerBlock())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.emeraldpay.dshackle.upstream.starknet
|
||||
|
||||
import io.emeraldpay.dshackle.Chain
|
||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.kotlin.mock
|
||||
import reactor.test.StepVerifier
|
||||
import java.time.Duration
|
||||
|
||||
class StarknetLowerBoundBlockDetectorTest {
|
||||
|
||||
@Test
|
||||
fun `starknet lower block is 1`() {
|
||||
val detector = StarknetLowerBoundBlockDetector(Chain.UNSPECIFIED, mock<Upstream>())
|
||||
|
||||
StepVerifier.withVirtualTime { detector.lowerBlock() }
|
||||
.expectSubscription()
|
||||
.expectNoEvent(Duration.ofSeconds(15))
|
||||
.expectNext(LowerBoundBlockDetector.LowerBlockData(1))
|
||||
.thenCancel()
|
||||
.verify(Duration.ofSeconds(3))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user