Track upstream finalization data (#499)
This commit is contained in:
@@ -108,7 +108,7 @@ class ApiReaderMock implements Reader<ChainRequest, ChainResponse> {
|
||||
}
|
||||
error = new ChainCallError(-32601, "Method ${request.method} with ${request.params} is not mocked")
|
||||
}
|
||||
return new ChainResponse(result, error, ChainResponse.Id.from(request.id), null, null, null)
|
||||
return new ChainResponse(result, error, ChainResponse.Id.from(request.id), null, null, null, null)
|
||||
} as Callable<ChainResponse>
|
||||
return Mono.fromCallable(call)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ class GenericUpstreamMock extends GenericUpstream {
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&validator,
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&upstreamSettingsDetector,
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&lowerBoundService,
|
||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&finalizationDetectorBuilder,
|
||||
)
|
||||
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
||||
setLag(0)
|
||||
|
||||
@@ -77,7 +77,8 @@ class FilteredApisSpec extends Specification {
|
||||
connectorFactory,
|
||||
cs.&validator,
|
||||
cs.&upstreamSettingsDetector,
|
||||
cs.&lowerBoundService
|
||||
cs.&lowerBoundService,
|
||||
cs.&finalizationDetectorBuilder
|
||||
)
|
||||
}
|
||||
def matcher = new Selector.LabelMatcher("test", ["foo"])
|
||||
|
||||
@@ -12,6 +12,7 @@ import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.*
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.domain.Address
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.domain.BlockHash
|
||||
@@ -33,6 +34,40 @@ class EthereumDirectReaderSpec extends Specification {
|
||||
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
|
||||
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
|
||||
Upstream.UpstreamSettingsData data = new Upstream.UpstreamSettingsData("test")
|
||||
def "Reads block by finalization"() {
|
||||
setup:
|
||||
def json = new BlockJson().tap {
|
||||
number = 100
|
||||
hash = BlockHash.from(hash1)
|
||||
timestamp = Instant.now()
|
||||
totalDifficulty = BigInteger.ONE
|
||||
parentHash = BlockHash.from(hash1)
|
||||
transactions = []
|
||||
}
|
||||
def calls = Mock(Factory) {
|
||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false)
|
||||
}
|
||||
EthereumDirectReader reader = new EthereumDirectReader(
|
||||
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||
)
|
||||
reader.requestReaderFactory = Mock(RequestReaderFactory) {
|
||||
1 * create({ it.upstreamFilter.sort == Selector.Sort.safe }) >> Mock(RequestReader) {
|
||||
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["safe", false]))) >> Mono.just(
|
||||
new RequestReader.Result(
|
||||
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null)
|
||||
)
|
||||
}
|
||||
}
|
||||
when:
|
||||
def act = reader.blockByFinalizationReader.read(FinalizationType.SAFE_BLOCK)
|
||||
then:
|
||||
StepVerifier.create(act)
|
||||
.expectNextMatches { block ->
|
||||
block.data.hash.toHexWithPrefix() == hash1
|
||||
}
|
||||
.expectComplete()
|
||||
.verify(Duration.ofSeconds(1))
|
||||
}
|
||||
|
||||
def "Reads block by hash"() {
|
||||
setup:
|
||||
|
||||
@@ -10,6 +10,8 @@ import io.emeraldpay.dshackle.upstream.EmptyHead
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationData
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
||||
import org.apache.commons.collections4.functors.ConstantFactory
|
||||
@@ -84,8 +86,8 @@ class EthereumLocalReaderSpec extends Specification {
|
||||
then:
|
||||
act != null
|
||||
with(act.block()) {
|
||||
it.first.length > 0
|
||||
with(Global.objectMapper.readValue(it.first, BlockJson)) {
|
||||
it.result.length > 0
|
||||
with(Global.objectMapper.readValue(it.result, BlockJson)) {
|
||||
number == 101
|
||||
}
|
||||
}
|
||||
@@ -112,8 +114,8 @@ class EthereumLocalReaderSpec extends Specification {
|
||||
then:
|
||||
act != null
|
||||
with(act.block()) {
|
||||
it.first.length > 0
|
||||
with(Global.objectMapper.readValue(it.first, BlockJson)) {
|
||||
it.result.length > 0
|
||||
with(Global.objectMapper.readValue(it.result, BlockJson)) {
|
||||
number == 0
|
||||
}
|
||||
}
|
||||
@@ -140,13 +142,43 @@ class EthereumLocalReaderSpec extends Specification {
|
||||
then:
|
||||
act != null
|
||||
with(act.block()) {
|
||||
it.first.length > 0
|
||||
with(Global.objectMapper.readValue(it.first, BlockJson)) {
|
||||
it.result.length > 0
|
||||
with(Global.objectMapper.readValue(it.result, BlockJson)) {
|
||||
number == 74735
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "getBlockByNumber fetches the block by tag"() {
|
||||
setup:
|
||||
def head = Stub(Head) {}
|
||||
def reader = Mock(EthereumCachingReader) {
|
||||
1 * blockByFinalization() >> Mock(Reader) {
|
||||
1 * read(FinalizationType.SAFE_BLOCK) >> Mono.just(
|
||||
new EthereumDirectReader.Result<>(TestingCommons.blockForEthereum(74735L), null)
|
||||
)
|
||||
}
|
||||
}
|
||||
def methods = new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false)
|
||||
def router = new EthereumLocalReader(reader, methods, head, null)
|
||||
|
||||
when:
|
||||
def act = router.read(
|
||||
new ChainRequest("eth_getBlockByNumber",
|
||||
new ListParams("safe", false))
|
||||
)
|
||||
|
||||
then:
|
||||
act != null
|
||||
with(act.block()) {
|
||||
it.result.length > 0
|
||||
with(Global.objectMapper.readValue(it.result, BlockJson)) {
|
||||
number == 74735
|
||||
}
|
||||
it.finalization == new FinalizationData(74735, FinalizationType.SAFE_BLOCK)
|
||||
}
|
||||
}
|
||||
|
||||
def "getBlockByNumber skips requests with tx bodies"() {
|
||||
setup:
|
||||
def head = Mock(Head)
|
||||
|
||||
@@ -60,7 +60,7 @@ class GrpcHeadSpec extends Specification {
|
||||
}
|
||||
})
|
||||
def convert = { BlockchainOuterClass.ChainHead head ->
|
||||
new GrpcHead.GrpcHeadData(TestingCommons.blockForBitcoin(head.height), List.of())
|
||||
new GrpcHead.GrpcHeadData(TestingCommons.blockForBitcoin(head.height), List.of(), List.of())
|
||||
}
|
||||
def head = new GrpcHead(
|
||||
"test",
|
||||
@@ -127,7 +127,7 @@ class GrpcHeadSpec extends Specification {
|
||||
}
|
||||
})
|
||||
def convert = { BlockchainOuterClass.ChainHead head ->
|
||||
new GrpcHead.GrpcHeadData(TestingCommons.blockForBitcoin(head.height), List.of())
|
||||
new GrpcHead.GrpcHeadData(TestingCommons.blockForBitcoin(head.height), List.of(), List.of())
|
||||
}
|
||||
def head = new GrpcHead(
|
||||
"test",
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package io.emeraldpay.dshackle.upstream
|
||||
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass.BlockTag
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass.HeightSelector
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationData
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
|
||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
@@ -53,6 +57,94 @@ class SelectorTest {
|
||||
)
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("finalData")
|
||||
fun `sort with finalization`(
|
||||
finalizationType: FinalizationType,
|
||||
finalizationProto: BlockchainOuterClass.BlockTag,
|
||||
) {
|
||||
val up1 = mock<Upstream> {
|
||||
on { getFinalizations() } doReturn listOf(FinalizationData(1L, finalizationType))
|
||||
}
|
||||
val up2 = mock<Upstream> {
|
||||
on { getFinalizations() } doReturn listOf(FinalizationData(10L, finalizationType))
|
||||
}
|
||||
val up3 = mock<Upstream> {
|
||||
on { getFinalizations() } doReturn listOf(FinalizationData(100L, finalizationType))
|
||||
}
|
||||
val up4 = mock<Upstream> {
|
||||
on { getFinalizations() } doReturn listOf()
|
||||
}
|
||||
val ups = listOf(up4, up3, up2, up1)
|
||||
val requestSelectors = listOf(
|
||||
BlockchainOuterClass.Selector.newBuilder()
|
||||
.setHeightSelector(
|
||||
HeightSelector.newBuilder()
|
||||
.setTag(finalizationProto),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val upstreamFilter = Selector.convertToUpstreamFilter(requestSelectors)
|
||||
|
||||
val actual = ups.sortedWith(upstreamFilter.sort.comparator)
|
||||
|
||||
assertEquals(
|
||||
listOf(up3, up2, up1, up4),
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `sort with latest`() {
|
||||
val mockHead1 = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn 1L
|
||||
}
|
||||
val up1 = mock<Upstream> {
|
||||
on { getHead() } doReturn mockHead1
|
||||
}
|
||||
|
||||
val mockHead2 = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn 2L
|
||||
}
|
||||
val up2 = mock<Upstream> {
|
||||
on { getHead() } doReturn mockHead2
|
||||
}
|
||||
|
||||
val mockHead3 = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn 3L
|
||||
}
|
||||
val up3 = mock<Upstream> {
|
||||
on { getHead() } doReturn mockHead3
|
||||
}
|
||||
|
||||
val mockHead4 = mock<Head> {
|
||||
on { getCurrentHeight() } doReturn null
|
||||
}
|
||||
val up4 = mock<Upstream> {
|
||||
on { getHead() } doReturn mockHead4
|
||||
}
|
||||
|
||||
val ups = listOf(up2, up1, up4, up3)
|
||||
val requestSelectors = listOf(
|
||||
BlockchainOuterClass.Selector.newBuilder()
|
||||
.setHeightSelector(
|
||||
HeightSelector.newBuilder()
|
||||
.setTag(BlockTag.LATEST),
|
||||
)
|
||||
.build(),
|
||||
)
|
||||
|
||||
val upstreamFilter = Selector.convertToUpstreamFilter(requestSelectors)
|
||||
|
||||
val actual = ups.sortedWith(upstreamFilter.sort.comparator)
|
||||
|
||||
assertEquals(
|
||||
listOf(up3, up2, up1, up4),
|
||||
actual,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `preserve the same order if no lower bound type`() {
|
||||
val up1 = mock<Upstream> {
|
||||
@@ -127,5 +219,12 @@ class SelectorTest {
|
||||
of(LowerBoundType.BLOCK, BlockchainOuterClass.LowerBoundType.LOWER_BOUND_BLOCK),
|
||||
of(LowerBoundType.SLOT, BlockchainOuterClass.LowerBoundType.LOWER_BOUND_SLOT),
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
fun finalData(): List<Arguments> =
|
||||
listOf(
|
||||
of(FinalizationType.SAFE_BLOCK, BlockTag.SAFE),
|
||||
of(FinalizationType.FINALIZED_BLOCK, BlockTag.FINALIZED),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package io.emeraldpay.dshackle.upstream.ethereum
|
||||
|
||||
import io.emeraldpay.dshackle.Global
|
||||
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.ethereum.json.BlockJson
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionRefJson
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationData
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.`when`
|
||||
import reactor.core.publisher.Mono
|
||||
import java.time.Duration
|
||||
import java.time.Instant
|
||||
|
||||
class EthereumFinalizationDetectorTest {
|
||||
|
||||
private lateinit var upstream: Upstream
|
||||
private lateinit var chainReader: ChainReader
|
||||
private lateinit var detector: EthereumFinalizationDetector
|
||||
|
||||
@BeforeEach
|
||||
fun setUp() {
|
||||
upstream = mock()
|
||||
chainReader = mock()
|
||||
`when`(upstream.getIngressReader()).thenReturn(chainReader)
|
||||
detector = EthereumFinalizationDetector()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testDetectFinalization() {
|
||||
`when`(chainReader.read(ChainRequest("eth_getBlockByNumber", ListParams("safe", false), 1)))
|
||||
.thenReturn(
|
||||
Mono.just(
|
||||
ChainResponse(
|
||||
Global.objectMapper.writeValueAsString(
|
||||
BlockJson<TransactionRefJson>().apply {
|
||||
number = 1
|
||||
timestamp = Instant.now()
|
||||
},
|
||||
).toByteArray(),
|
||||
null,
|
||||
),
|
||||
),
|
||||
)
|
||||
`when`(chainReader.read(ChainRequest("eth_getBlockByNumber", ListParams("finalized", false), 2)))
|
||||
.thenReturn(
|
||||
Mono.just(
|
||||
ChainResponse(
|
||||
Global.objectMapper.writeValueAsString(
|
||||
BlockJson<TransactionRefJson>().apply {
|
||||
number = 2
|
||||
timestamp = Instant.now()
|
||||
},
|
||||
).toByteArray(),
|
||||
null,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
val flux = detector.detectFinalization(upstream, Duration.ofMillis(200))
|
||||
flux.take(2).collectList().block()
|
||||
val result = detector.getFinalizations().toList()
|
||||
Assertions.assertEquals(2, result.size)
|
||||
org.assertj.core.api.Assertions.assertThat(result)
|
||||
.contains(FinalizationData(2L, FinalizationType.FINALIZED_BLOCK))
|
||||
.contains(FinalizationData(1L, FinalizationType.SAFE_BLOCK))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package io.emeraldpay.dshackle.upstream.grpc
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import io.emeraldpay.api.proto.BlockchainGrpc
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.Common
|
||||
import io.emeraldpay.dshackle.Chain
|
||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.test.MockGrpcServerKt
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationData
|
||||
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
|
||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcGrpcClient
|
||||
import io.grpc.stub.StreamObserver
|
||||
import org.junit.jupiter.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
import reactor.core.publisher.Sinks
|
||||
import reactor.core.scheduler.Schedulers
|
||||
|
||||
class GenericGrpcUpstreamTest {
|
||||
private val parentId = "testParent"
|
||||
private val hash: Byte = 0x01
|
||||
private val role = UpstreamsConfig.UpstreamRole.PRIMARY
|
||||
private val headSink = Sinks.many().multicast().directBestEffort<BlockchainOuterClass.ChainHead>()
|
||||
private val remote =
|
||||
MockGrpcServerKt().clientForServer(
|
||||
object : BlockchainGrpc.BlockchainImplBase() {
|
||||
override fun subscribeHead(
|
||||
request: Common.Chain,
|
||||
responseObserver: StreamObserver<BlockchainOuterClass.ChainHead>,
|
||||
) {
|
||||
Thread {
|
||||
headSink.asFlux().subscribe { data ->
|
||||
responseObserver.onNext(data)
|
||||
Thread.sleep(500)
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
},
|
||||
)
|
||||
private val client = Mockito.mock(JsonRpcGrpcClient::class.java)
|
||||
private val nodeRating = 5
|
||||
private val overrideLabels = Mockito.mock(UpstreamsConfig.Labels::class.java)
|
||||
private val headScheduler = Schedulers.single()
|
||||
|
||||
private fun getUpstream(): GrpcUpstream {
|
||||
return GenericGrpcUpstream(
|
||||
parentId,
|
||||
hash,
|
||||
role,
|
||||
Chain.LINEA__MAINNET,
|
||||
remote,
|
||||
client,
|
||||
nodeRating,
|
||||
overrideLabels,
|
||||
ChainsConfig.ChainConfig.default(),
|
||||
headScheduler,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start() {
|
||||
val up = getUpstream()
|
||||
up.getHead().start()
|
||||
up.start()
|
||||
headSink.emitNext(
|
||||
BlockchainOuterClass.ChainHead.newBuilder()
|
||||
.setChain(Common.ChainRef.CHAIN_LINEA__MAINNET)
|
||||
.setHeight(10L)
|
||||
.setWeight(ByteString.EMPTY)
|
||||
.setBlockId("a2622ec25e883dd13c1091c18ba717a1a794713baa77b8e68ec6a993045cb50f")
|
||||
.setTimestamp(0)
|
||||
.addAllFinalizationData(
|
||||
mutableListOf(
|
||||
Common
|
||||
.FinalizationData
|
||||
.newBuilder()
|
||||
.setType(Common.FinalizationType.FINALIZATION_FINALIZED_BLOCK)
|
||||
.setHeight(8L)
|
||||
.build(),
|
||||
),
|
||||
)
|
||||
.addAllLowerBounds(
|
||||
mutableListOf(
|
||||
BlockchainOuterClass.LowerBound
|
||||
.newBuilder()
|
||||
.setLowerBoundType(BlockchainOuterClass.LowerBoundType.LOWER_BOUND_TX)
|
||||
.setLowerBoundTimestamp(0)
|
||||
.setLowerBoundValue(1L).build(),
|
||||
),
|
||||
)
|
||||
.build(),
|
||||
) { _, _ ->
|
||||
true
|
||||
}
|
||||
Thread.sleep(100)
|
||||
Assertions.assertEquals(1, up.getFinalizations().size)
|
||||
Assertions.assertTrue(
|
||||
up.getFinalizations()
|
||||
.contains(FinalizationData(8L, FinalizationType.FINALIZED_BLOCK)),
|
||||
)
|
||||
Assertions.assertTrue(
|
||||
up.getLowerBounds()
|
||||
.contains(LowerBoundData(1L, 0L, LowerBoundType.TX)),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getFinalizations() {
|
||||
val up = getUpstream()
|
||||
val finalizationData1 = FinalizationData(100L, FinalizationType.FINALIZED_BLOCK)
|
||||
|
||||
val finalizationData2 = FinalizationData(200L, FinalizationType.FINALIZED_BLOCK)
|
||||
|
||||
up.addFinalization(finalizationData1, "upstream1")
|
||||
up.addFinalization(finalizationData2, "upstream2")
|
||||
|
||||
val finalizations = up.getFinalizations()
|
||||
Assertions.assertEquals(1, finalizations.size)
|
||||
Assertions.assertTrue(finalizations.contains(finalizationData2))
|
||||
}
|
||||
}
|
||||
40
src/test/kotlin/test/MockGrpcServerKt.kt
Normal file
40
src/test/kotlin/test/MockGrpcServerKt.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright (c) 2019 ETCDEV GmbH
|
||||
* Copyright (c) 2020 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.test
|
||||
|
||||
import io.emeraldpay.api.proto.BlockchainGrpc
|
||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
||||
import io.grpc.inprocess.InProcessChannelBuilder
|
||||
import io.grpc.inprocess.InProcessServerBuilder
|
||||
import io.grpc.testing.GrpcCleanupRule
|
||||
import org.junit.Rule
|
||||
|
||||
class MockGrpcServerKt {
|
||||
|
||||
@get:Rule
|
||||
val grpcCleanup = GrpcCleanupRule()
|
||||
|
||||
fun clientForServer(impl: BlockchainGrpc.BlockchainImplBase): ReactorBlockchainGrpc.ReactorBlockchainStub {
|
||||
val serverName = InProcessServerBuilder.generateName()
|
||||
grpcCleanup.register(
|
||||
InProcessServerBuilder
|
||||
.forName(serverName).directExecutor().addService(impl).build().start(),
|
||||
)
|
||||
val channel = grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build())
|
||||
return ReactorBlockchainGrpc.newReactorStub(channel)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user