solution: use grpc transport from etherjar
This commit is contained in:
@@ -76,6 +76,7 @@ dependencies {
|
|||||||
compile "io.infinitape:etherjar-hex:$etherjarVersion"
|
compile "io.infinitape:etherjar-hex:$etherjarVersion"
|
||||||
compile "io.infinitape:etherjar-rpc-http:$etherjarVersion"
|
compile "io.infinitape:etherjar-rpc-http:$etherjarVersion"
|
||||||
compile "io.infinitape:etherjar-rpc-ws:$etherjarVersion"
|
compile "io.infinitape:etherjar-rpc-ws:$etherjarVersion"
|
||||||
|
compile "io.infinitape:etherjar-rpc-emerald:$etherjarVersion"
|
||||||
compile "io.infinitape:etherjar-tx:$etherjarVersion"
|
compile "io.infinitape:etherjar-tx:$etherjarVersion"
|
||||||
|
|
||||||
compile 'org.apache.httpcomponents:httpmime:4.5.8'
|
compile 'org.apache.httpcomponents:httpmime:4.5.8'
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2019 ETCDEV GmbH
|
|
||||||
*
|
|
||||||
* 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.upstream.grpc
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import com.google.protobuf.ByteString
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
|
||||||
import io.emeraldpay.api.proto.Common
|
|
||||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
|
||||||
import io.emeraldpay.grpc.Chain
|
|
||||||
import io.infinitape.etherjar.rpc.Batch
|
|
||||||
import io.infinitape.etherjar.rpc.JacksonRpcConverter
|
|
||||||
import io.infinitape.etherjar.rpc.RpcException
|
|
||||||
import io.infinitape.etherjar.rpc.RpcResponseError
|
|
||||||
import io.infinitape.etherjar.rpc.transport.BatchStatus
|
|
||||||
import io.infinitape.etherjar.rpc.transport.RpcTransport
|
|
||||||
import reactor.util.function.Tuple3
|
|
||||||
import reactor.util.function.Tuples
|
|
||||||
import java.time.Duration
|
|
||||||
import java.util.concurrent.CompletableFuture
|
|
||||||
import java.util.function.Function
|
|
||||||
|
|
||||||
class EthereumGrpcTransport(
|
|
||||||
private val chainRef: Common.ChainRef,
|
|
||||||
private val labelSelector: BlockchainOuterClass.Selector?,
|
|
||||||
private val client: ReactorBlockchainGrpc.ReactorBlockchainStub,
|
|
||||||
private val objectMapper: ObjectMapper
|
|
||||||
): RpcTransport {
|
|
||||||
|
|
||||||
private val jacksonRpcConverter = JacksonRpcConverter(objectMapper)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
chain: Chain,
|
|
||||||
client: ReactorBlockchainGrpc.ReactorBlockchainStub,
|
|
||||||
objectMapper: ObjectMapper
|
|
||||||
) : this(Common.ChainRef.forNumber(chain.id), null, client, objectMapper)
|
|
||||||
|
|
||||||
fun withLabels(matcher: Selector.LabelSelectorMatcher?): EthereumGrpcTransport {
|
|
||||||
if ((matcher == null || matcher is Selector.AnyLabelMatcher) && labelSelector == null) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
return EthereumGrpcTransport(chainRef, matcher?.asProto(), client, objectMapper)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun replyProcessor(mapping: HashMap<Int, Batch.BatchItem<Any, Any>>): Function<BlockchainOuterClass.NativeCallReplyItem, Boolean> {
|
|
||||||
return Function { resp ->
|
|
||||||
val id = resp.id
|
|
||||||
val bi = mapping.remove(id)
|
|
||||||
if (bi != null) {
|
|
||||||
if (resp.succeed) {
|
|
||||||
try {
|
|
||||||
val rpcResp = jacksonRpcConverter.fromJson(resp.payload.toByteArray().inputStream(), bi.call.jsonType, Int::class.java)
|
|
||||||
bi.onComplete(rpcResp)
|
|
||||||
return@Function true
|
|
||||||
} catch (e: RpcException) {
|
|
||||||
bi.onError(e)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bi.onError(RpcException(RpcResponseError.CODE_INTERNAL_ERROR, resp.errorMessage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sumStatus = { t: Tuple3<Int, Int, Int>, ok: Boolean ->
|
|
||||||
if (ok) Tuples.of(t.t1 + 1, t.t2, t.t3 + 1)
|
|
||||||
else Tuples.of(t.t1, t.t2 + 1, t.t3 + 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val asStatus = Function<Tuple3<Int, Int, Int>, BatchStatus> {
|
|
||||||
BatchStatus.newBuilder()
|
|
||||||
.withSucceed(it.t1)
|
|
||||||
.withFailed(it.t2)
|
|
||||||
.withTotal(it.t3)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun prepareMapping(items: List<Batch.BatchItem<out Any, out Any>>, req: BlockchainOuterClass.NativeCallRequest.Builder): HashMap<Int, Batch.BatchItem<Any, Any>> {
|
|
||||||
val mapping = HashMap<Int, Batch.BatchItem<Any, Any>>()
|
|
||||||
var seq: Int = 0
|
|
||||||
items.forEach { bi ->
|
|
||||||
val id = seq++
|
|
||||||
mapping[id] = bi as Batch.BatchItem<Any, Any>
|
|
||||||
val call = bi.call
|
|
||||||
val params = objectMapper.writeValueAsBytes(call.params)
|
|
||||||
val nativeCallItem = BlockchainOuterClass.NativeCallItem.newBuilder()
|
|
||||||
.setId(id)
|
|
||||||
.setMethod(call.method)
|
|
||||||
.setPayload(ByteString.copyFrom(params))
|
|
||||||
.build()
|
|
||||||
req.addItems(nativeCallItem)
|
|
||||||
}
|
|
||||||
return mapping
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun execute(items: List<Batch.BatchItem<out Any, out Any>>): CompletableFuture<BatchStatus> {
|
|
||||||
val req = BlockchainOuterClass.NativeCallRequest.newBuilder()
|
|
||||||
.setChain(chainRef)
|
|
||||||
if (labelSelector != null) {
|
|
||||||
req.setSelector(labelSelector)
|
|
||||||
}
|
|
||||||
val mapping = prepareMapping(items, req)
|
|
||||||
return client.nativeCall(req.build())
|
|
||||||
.map(replyProcessor(mapping))
|
|
||||||
.reduce(Tuples.of(0, 0, 0), sumStatus)
|
|
||||||
.map(asStatus)
|
|
||||||
.doFinally {
|
|
||||||
mapping.values.forEach { bi ->
|
|
||||||
bi.onError(RpcException(-32603, "RPC response not received for ${bi.call.method}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.timeout(Duration.ofSeconds(15))
|
|
||||||
.toFuture()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,6 +28,7 @@ import io.emeraldpay.grpc.Chain
|
|||||||
import io.infinitape.etherjar.domain.BlockHash
|
import io.infinitape.etherjar.domain.BlockHash
|
||||||
import io.infinitape.etherjar.domain.TransactionId
|
import io.infinitape.etherjar.domain.TransactionId
|
||||||
import io.infinitape.etherjar.rpc.*
|
import io.infinitape.etherjar.rpc.*
|
||||||
|
import io.infinitape.etherjar.rpc.emerald.EmeraldGrpcTransport
|
||||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.context.Lifecycle
|
import org.springframework.context.Lifecycle
|
||||||
@@ -48,7 +49,8 @@ open class GrpcUpstream(
|
|||||||
private val parentId: String,
|
private val parentId: String,
|
||||||
private val chain: Chain,
|
private val chain: Chain,
|
||||||
private val client: ReactorBlockchainGrpc.ReactorBlockchainStub,
|
private val client: ReactorBlockchainGrpc.ReactorBlockchainStub,
|
||||||
private val objectMapper: ObjectMapper
|
private val objectMapper: ObjectMapper,
|
||||||
|
private val grpcTransport: EmeraldGrpcTransport
|
||||||
): DefaultUpstream(), Lifecycle {
|
): DefaultUpstream(), Lifecycle {
|
||||||
|
|
||||||
private var allLabels: Collection<UpstreamsConfig.Labels> = ArrayList<UpstreamsConfig.Labels>()
|
private var allLabels: Collection<UpstreamsConfig.Labels> = ArrayList<UpstreamsConfig.Labels>()
|
||||||
@@ -60,13 +62,15 @@ open class GrpcUpstream(
|
|||||||
private val nodes = AtomicReference<NodeDetailsList>(NodeDetailsList())
|
private val nodes = AtomicReference<NodeDetailsList>(NodeDetailsList())
|
||||||
private val head = Head(this)
|
private val head = Head(this)
|
||||||
private var targets: CallMethods? = null
|
private var targets: CallMethods? = null
|
||||||
private val grpcTransport = EthereumGrpcTransport(chain, client, objectMapper)
|
|
||||||
|
|
||||||
private var headSubscription: Disposable? = null
|
private var headSubscription: Disposable? = null
|
||||||
|
|
||||||
open fun createApi(matcher: Selector.Matcher): DirectEthereumApi {
|
open fun createApi(matcher: Selector.Matcher): DirectEthereumApi {
|
||||||
val targets = this.getMethods()
|
val targets = this.getMethods()
|
||||||
val rpcClient = DefaultRpcClient(grpcTransport.withLabels(Selector.extractLabels(matcher)))
|
val transport = Selector.extractLabels(matcher)?.let { selector ->
|
||||||
|
grpcTransport.copyWithSelector(selector.asProto())
|
||||||
|
} ?: grpcTransport
|
||||||
|
val rpcClient = DefaultRpcClient(transport)
|
||||||
return DirectEthereumApi(rpcClient, objectMapper, targets).let {
|
return DirectEthereumApi(rpcClient, objectMapper, targets).let {
|
||||||
it.upstream = this
|
it.upstream = this
|
||||||
it
|
it
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import io.emeraldpay.dshackle.upstream.UpstreamChange
|
|||||||
import io.emeraldpay.grpc.Chain
|
import io.emeraldpay.grpc.Chain
|
||||||
import io.grpc.ManagedChannelBuilder
|
import io.grpc.ManagedChannelBuilder
|
||||||
import io.grpc.netty.NettyChannelBuilder
|
import io.grpc.netty.NettyChannelBuilder
|
||||||
|
import io.infinitape.etherjar.rpc.emerald.EmeraldGrpcTransport
|
||||||
import io.netty.handler.ssl.*
|
import io.netty.handler.ssl.*
|
||||||
import org.apache.commons.lang3.StringUtils
|
import org.apache.commons.lang3.StringUtils
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -30,6 +31,7 @@ import reactor.core.publisher.Flux
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import kotlin.concurrent.withLock
|
import kotlin.concurrent.withLock
|
||||||
|
|
||||||
@@ -46,6 +48,8 @@ class GrpcUpstreams(
|
|||||||
private val known = HashMap<Chain, GrpcUpstream>()
|
private val known = HashMap<Chain, GrpcUpstream>()
|
||||||
private val lock = ReentrantLock()
|
private val lock = ReentrantLock()
|
||||||
|
|
||||||
|
private var grpcTransport: EmeraldGrpcTransport? = null
|
||||||
|
|
||||||
fun start(): Flux<UpstreamChange> {
|
fun start(): Flux<UpstreamChange> {
|
||||||
val channel: ManagedChannelBuilder<*> = if (auth != null && StringUtils.isNotEmpty(auth.ca)) {
|
val channel: ManagedChannelBuilder<*> = if (auth != null && StringUtils.isNotEmpty(auth.ca)) {
|
||||||
NettyChannelBuilder.forAddress(host, port)
|
NettyChannelBuilder.forAddress(host, port)
|
||||||
@@ -59,6 +63,13 @@ class GrpcUpstreams(
|
|||||||
|
|
||||||
val client = ReactorBlockchainGrpc.newReactorStub(channel.build())
|
val client = ReactorBlockchainGrpc.newReactorStub(channel.build())
|
||||||
this.client = client
|
this.client = client
|
||||||
|
var i = 0
|
||||||
|
val grpcExecutor = Executors.newFixedThreadPool(8) { r -> Thread(r, "grpc-up-$id-${i++}") };
|
||||||
|
this.grpcTransport = EmeraldGrpcTransport.newBuilder()
|
||||||
|
.forChannel(client.channel)
|
||||||
|
.setObjectMapper(objectMapper)
|
||||||
|
.setExecutorService(grpcExecutor)
|
||||||
|
.build()
|
||||||
|
|
||||||
val updates = Flux.interval(Duration.ZERO, Duration.ofMinutes(1))
|
val updates = Flux.interval(Duration.ZERO, Duration.ofMinutes(1))
|
||||||
.flatMap {
|
.flatMap {
|
||||||
@@ -67,6 +78,8 @@ class GrpcUpstreams(
|
|||||||
processDescription(value)
|
processDescription(value)
|
||||||
}.doOnError { t ->
|
}.doOnError { t ->
|
||||||
log.error("Failed to get description from $host:$port", t)
|
log.error("Failed to get description from $host:$port", t)
|
||||||
|
}.doFinally {
|
||||||
|
grpcExecutor.shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO subscribe only after receiving details
|
//TODO subscribe only after receiving details
|
||||||
@@ -125,7 +138,7 @@ class GrpcUpstreams(
|
|||||||
lock.withLock {
|
lock.withLock {
|
||||||
val current = known[chain]
|
val current = known[chain]
|
||||||
return if (current == null) {
|
return if (current == null) {
|
||||||
val created = GrpcUpstream(id, chain, client!!, objectMapper)
|
val created = GrpcUpstream(id, chain, client!!, objectMapper, grpcTransport!!.copyForChain(chain))
|
||||||
known[chain] = created
|
known[chain] = created
|
||||||
created.start()
|
created.start()
|
||||||
UpstreamChange(chain, created, UpstreamChange.ChangeType.ADDED)
|
UpstreamChange(chain, created, UpstreamChange.ChangeType.ADDED)
|
||||||
|
|||||||
@@ -1,142 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2019 ETCDEV GmbH
|
|
||||||
*
|
|
||||||
* 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.upstream.grpc
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
|
||||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
|
||||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
|
||||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
|
||||||
import io.emeraldpay.dshackle.test.EthereumApiMock
|
|
||||||
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
|
|
||||||
import io.emeraldpay.dshackle.test.MockServer
|
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
|
||||||
import io.emeraldpay.dshackle.upstream.DirectCallMethods
|
|
||||||
import io.emeraldpay.dshackle.upstream.QuorumBasedMethods
|
|
||||||
import io.emeraldpay.dshackle.upstream.Upstreams
|
|
||||||
import io.emeraldpay.dshackle.upstream.grpc.EthereumGrpcTransport
|
|
||||||
import io.emeraldpay.grpc.Chain
|
|
||||||
import io.infinitape.etherjar.rpc.Batch
|
|
||||||
import io.infinitape.etherjar.rpc.RpcCall
|
|
||||||
import io.infinitape.etherjar.rpc.RpcClient
|
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
class EthereumGrpcTransportSpec extends Specification {
|
|
||||||
|
|
||||||
MockServer mockServer = new MockServer()
|
|
||||||
ObjectMapper objectMapper = TestingCommons.objectMapper()
|
|
||||||
def ethereumTargets = new QuorumBasedMethods(objectMapper, Chain.ETHEREUM)
|
|
||||||
|
|
||||||
def "Make simple call"() {
|
|
||||||
setup:
|
|
||||||
def otherSideApi = new EthereumApiMock(Mock(RpcClient), objectMapper, Chain.ETHEREUM)
|
|
||||||
|
|
||||||
def callData = [:]
|
|
||||||
def otherSideUpstreams = Mock(Upstreams)
|
|
||||||
def otherSideAggr = TestingCommons.aggregatedUpstream(
|
|
||||||
new EthereumUpstreamMock(Chain.ETHEREUM, otherSideApi, new DirectCallMethods(["eth_test"]))
|
|
||||||
)
|
|
||||||
|
|
||||||
def otherSideNativeCall = new NativeCall(otherSideUpstreams, objectMapper)
|
|
||||||
otherSideApi.upstream = otherSideAggr
|
|
||||||
|
|
||||||
def client = mockServer.clientForServer(new ReactorBlockchainGrpc.BlockchainImplBase() {
|
|
||||||
@Override
|
|
||||||
Flux<BlockchainOuterClass.NativeCallReplyItem> nativeCall(Mono<BlockchainOuterClass.NativeCallRequest> request) {
|
|
||||||
callData["request"] = request.block()
|
|
||||||
return otherSideNativeCall.nativeCall(request)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
EthereumGrpcTransport transport = new EthereumGrpcTransport(Chain.ETHEREUM, client, objectMapper)
|
|
||||||
when:
|
|
||||||
otherSideApi.answer("eth_test", [1], "bar")
|
|
||||||
def batch = new Batch()
|
|
||||||
def f = batch.add(RpcCall.create("eth_test", [1]))
|
|
||||||
def status = transport.execute(batch.items).get()
|
|
||||||
|
|
||||||
then:
|
|
||||||
1 * otherSideUpstreams.getUpstream(Chain.ETHEREUM) >> otherSideAggr
|
|
||||||
status.failed == 0
|
|
||||||
status.succeed == 1
|
|
||||||
status.total == 1
|
|
||||||
callData.request != null
|
|
||||||
with((BlockchainOuterClass.NativeCallRequest)callData.request) {
|
|
||||||
chain.number == Chain.ETHEREUM.id
|
|
||||||
itemsCount == 1
|
|
||||||
with(getItems(0)) {
|
|
||||||
method == "eth_test"
|
|
||||||
payload.toStringUtf8() == "[1]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f.get() == "bar"
|
|
||||||
}
|
|
||||||
|
|
||||||
def "Make few calls"() {
|
|
||||||
setup:
|
|
||||||
def otherSideApi = new EthereumApiMock(Mock(RpcClient), objectMapper, Chain.ETHEREUM)
|
|
||||||
|
|
||||||
def callData = [:]
|
|
||||||
def otherSideUpstreams = Mock(Upstreams)
|
|
||||||
def otherSideAggr = TestingCommons.aggregatedUpstream(
|
|
||||||
new EthereumUpstreamMock(Chain.ETHEREUM, otherSideApi, new DirectCallMethods(["eth_test", "eth_test2"]))
|
|
||||||
)
|
|
||||||
def otherSideNativeCall = new NativeCall(otherSideUpstreams, objectMapper)
|
|
||||||
otherSideApi.upstream = otherSideAggr
|
|
||||||
|
|
||||||
def client = mockServer.clientForServer(new ReactorBlockchainGrpc.BlockchainImplBase() {
|
|
||||||
@Override
|
|
||||||
Flux<BlockchainOuterClass.NativeCallReplyItem> nativeCall(Mono<BlockchainOuterClass.NativeCallRequest> request) {
|
|
||||||
callData["request"] = request.block()
|
|
||||||
return otherSideNativeCall.nativeCall(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
EthereumGrpcTransport transport = new EthereumGrpcTransport(Chain.ETHEREUM, client, objectMapper)
|
|
||||||
when:
|
|
||||||
otherSideApi.answer("eth_test", [1], "bar")
|
|
||||||
otherSideApi.answer("eth_test2", [2, "3"], "baz")
|
|
||||||
|
|
||||||
def batch = new Batch()
|
|
||||||
def f1 = batch.add(RpcCall.create("eth_test", [1]))
|
|
||||||
def f2 = batch.add(RpcCall.create("eth_test2", [2, "3"]))
|
|
||||||
def status = transport.execute(batch.items).get()
|
|
||||||
|
|
||||||
then:
|
|
||||||
1 * otherSideUpstreams.getUpstream(Chain.ETHEREUM) >> otherSideAggr
|
|
||||||
status.failed == 0
|
|
||||||
status.succeed == 2
|
|
||||||
status.total == 2
|
|
||||||
callData.request != null
|
|
||||||
with((BlockchainOuterClass.NativeCallRequest)callData.request) {
|
|
||||||
chain.number == Chain.ETHEREUM.id
|
|
||||||
itemsCount == 2
|
|
||||||
with(getItems(0)) {
|
|
||||||
method == "eth_test"
|
|
||||||
payload.toStringUtf8() == "[1]"
|
|
||||||
}
|
|
||||||
with(getItems(1)) {
|
|
||||||
method == "eth_test2"
|
|
||||||
payload.toStringUtf8() == "[2,\"3\"]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
f1.get() == "bar"
|
|
||||||
f2.get() == "baz"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -28,12 +28,15 @@ import io.emeraldpay.dshackle.upstream.grpc.GrpcUpstream
|
|||||||
import io.emeraldpay.grpc.Chain
|
import io.emeraldpay.grpc.Chain
|
||||||
import io.grpc.stub.StreamObserver
|
import io.grpc.stub.StreamObserver
|
||||||
import io.infinitape.etherjar.domain.BlockHash
|
import io.infinitape.etherjar.domain.BlockHash
|
||||||
|
import io.infinitape.etherjar.rpc.JacksonRpcConverter
|
||||||
import io.infinitape.etherjar.rpc.RpcClient
|
import io.infinitape.etherjar.rpc.RpcClient
|
||||||
|
import io.infinitape.etherjar.rpc.emerald.EmeraldGrpcTransport
|
||||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.concurrent.CompletableFuture
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
class GrpcUpstreamSpec extends Specification {
|
class GrpcUpstreamSpec extends Specification {
|
||||||
|
|
||||||
@@ -70,7 +73,8 @@ class GrpcUpstreamSpec extends Specification {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build()
|
||||||
|
def upstream = new GrpcUpstream("test", chain, client, objectMapper, transport)
|
||||||
upstream.setLag(0)
|
upstream.setLag(0)
|
||||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||||
@@ -129,7 +133,8 @@ class GrpcUpstreamSpec extends Specification {
|
|||||||
finished.complete(true)
|
finished.complete(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build()
|
||||||
|
def upstream = new GrpcUpstream("test", chain, client, objectMapper, transport)
|
||||||
upstream.setLag(0)
|
upstream.setLag(0)
|
||||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||||
@@ -189,7 +194,8 @@ class GrpcUpstreamSpec extends Specification {
|
|||||||
finished.complete(true)
|
finished.complete(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build()
|
||||||
|
def upstream = new GrpcUpstream("test", chain, client, objectMapper, transport)
|
||||||
upstream.setLag(0)
|
upstream.setLag(0)
|
||||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||||
|
|||||||
Reference in New Issue
Block a user