solution: use reactor-based rpc client

This commit is contained in:
Igor Artamonov
2019-10-11 22:54:12 -04:00
parent fd95aa4868
commit 77685c314a
16 changed files with 131 additions and 121 deletions

View File

@@ -24,8 +24,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumWs import io.emeraldpay.dshackle.upstream.ethereum.EthereumWs
import io.emeraldpay.dshackle.upstream.grpc.GrpcUpstreams import io.emeraldpay.dshackle.upstream.grpc.GrpcUpstreams
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.DefaultRpcClient import io.infinitape.etherjar.rpc.http.ReactorHttpRpcClient
import io.infinitape.etherjar.rpc.transport.DefaultRpcTransport
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
@@ -132,18 +131,18 @@ open class ConfiguredUpstreams(
currentUpstreams.getDefaultMethods(chain) currentUpstreams.getDefaultMethods(chain)
} }
conn.rpc?.let { endpoint -> conn.rpc?.let { endpoint ->
val rpcTransport = DefaultRpcTransport(endpoint.url) val rpcClient = ReactorHttpRpcClient.newBuilder()
.setTarget(endpoint.url)
conn.rpc?.basicAuth?.let { auth -> conn.rpc?.basicAuth?.let { auth ->
rpcTransport.setBasicAuth(auth.username, auth.password) rpcClient.setBasicAuth(auth.username, auth.password)
} }
conn.rpc?.tls?.let { tls -> conn.rpc?.tls?.let { tls ->
tls.ca?.let { ca -> tls.ca?.let { ca ->
fileResolver.resolve(ca).inputStream().use { cert -> rpcTransport.setTrustedCertificate(cert) } fileResolver.resolve(ca).inputStream().use { cert -> rpcClient.setTrustedCertificate(cert) }
} }
} }
val rpcClient = DefaultRpcClient(rpcTransport)
rpcApi = DirectEthereumApi( rpcApi = DirectEthereumApi(
rpcClient, rpcClient.build(),
objectMapper, objectMapper,
methods methods
).apply { ).apply {

View File

@@ -20,33 +20,50 @@ import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.infinitape.etherjar.rpc.Batch import io.infinitape.etherjar.rpc.Batch
import io.infinitape.etherjar.rpc.Commands import io.infinitape.etherjar.rpc.Commands
import io.infinitape.etherjar.rpc.ReactorBatch
import org.slf4j.LoggerFactory
import org.springframework.scheduling.concurrent.CustomizableThreadFactory
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import java.time.Duration import java.time.Duration
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class UpstreamValidator( class UpstreamValidator(
private val ethereumUpstream: EthereumUpstream, private val ethereumUpstream: EthereumUpstream,
private val options: UpstreamsConfig.Options private val options: UpstreamsConfig.Options
) { ) {
companion object {
private val log = LoggerFactory.getLogger(UpstreamValidator::class.java)
val scheduler = Schedulers.fromExecutor(Executors.newCachedThreadPool(CustomizableThreadFactory("validator")))
}
fun validate(): Mono<UpstreamAvailability> { fun validate(): Mono<UpstreamAvailability> {
val batch = Batch() val batch = ReactorBatch()
val peerCount = batch.add(Commands.net().peerCount()) val peerCount = batch.add(Commands.net().peerCount()).result
val syncing = batch.add(Commands.eth().syncing()) val syncing = batch.add(Commands.eth().syncing()).result
return ethereumUpstream.getApi(Selector.empty) return ethereumUpstream.getApi(Selector.empty)
.map { api -> api.rpcClient.execute(batch) } .subscribeOn(scheduler)
.flatMap { Mono.fromCompletionStage(it) } .flatMapMany { api -> api.rpcClient.execute(batch) }
.timeout(Defaults.timeout) .timeout(Defaults.timeout, Mono.error(Exception("Validation timeout")))
.map { .then(syncing)
if (syncing.get().isSyncing) { .flatMap { value ->
UpstreamAvailability.SYNCING if (value.isSyncing) {
} else if (options.minPeers != null && peerCount.get() < options.minPeers!!) { Mono.just(UpstreamAvailability.SYNCING)
UpstreamAvailability.IMMATURE
} else { } else {
UpstreamAvailability.OK peerCount.map { count ->
val minPeers = options.minPeers ?: 1
if (count < minPeers) {
UpstreamAvailability.IMMATURE
} else {
UpstreamAvailability.OK
}
}
} }
}.onErrorContinue { _, _ -> UpstreamAvailability.UNAVAILABLE } }
.doOnError { err -> log.warn("Failed to validate upstream", err)}
.onErrorReturn(UpstreamAvailability.UNAVAILABLE)
} }
fun start(): Flux<UpstreamAvailability> { fun start(): Flux<UpstreamAvailability> {

View File

@@ -18,8 +18,9 @@ package io.emeraldpay.dshackle.upstream.ethereum
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.Defaults import io.emeraldpay.dshackle.Defaults
import io.emeraldpay.dshackle.upstream.CallMethods import io.emeraldpay.dshackle.upstream.CallMethods
import io.infinitape.etherjar.rpc.ReactorBatch
import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.RpcCall import io.infinitape.etherjar.rpc.RpcCall
import io.infinitape.etherjar.rpc.RpcClient
import io.infinitape.etherjar.rpc.RpcException import io.infinitape.etherjar.rpc.RpcException
import io.infinitape.etherjar.rpc.json.ResponseJson import io.infinitape.etherjar.rpc.json.ResponseJson
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@@ -27,7 +28,7 @@ import reactor.core.publisher.Mono
import java.time.Duration import java.time.Duration
open class DirectEthereumApi( open class DirectEthereumApi(
val rpcClient: RpcClient, val rpcClient: ReactorRpcClient,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
val targets: CallMethods val targets: CallMethods
): EthereumApi(objectMapper) { ): EthereumApi(objectMapper) {
@@ -68,8 +69,7 @@ open class DirectEthereumApi(
} }
private fun callUpstream(method: String, params: List<Any>): Mono<out Any> { private fun callUpstream(method: String, params: List<Any>): Mono<out Any> {
return Mono.fromCompletionStage( return rpcClient.execute(RpcCall.create(method, Any::class.java, params))
rpcClient.execute(RpcCall.create(method, Any::class.java, params)) .timeout(timeout, Mono.error(RpcException(-32603, "Upstream timeout")))
).timeout(timeout, Mono.error(RpcException(-32603, "Upstream timeout")))
} }
} }

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.upstream.Upstream import io.emeraldpay.dshackle.upstream.Upstream
import io.infinitape.etherjar.rpc.* import io.infinitape.etherjar.rpc.*
import org.slf4j.LoggerFactory
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import java.io.InputStream import java.io.InputStream
@@ -25,6 +26,10 @@ abstract class EthereumApi(
objectMapper: ObjectMapper objectMapper: ObjectMapper
) { ) {
companion object {
private val log = LoggerFactory.getLogger(EthereumApi::class.java)
}
private val jacksonRpcConverter = JacksonRpcConverter(objectMapper) private val jacksonRpcConverter = JacksonRpcConverter(objectMapper)
var upstream: Upstream? = null var upstream: Upstream? = null
@@ -40,5 +45,6 @@ abstract class EthereumApi(
return execute(0, rpcCall.method, rpcCall.params as List<Any>) return execute(0, rpcCall.method, rpcCall.params as List<Any>)
.flatMap(convertToJS) .flatMap(convertToJS)
.map(rpcCall.converter::apply) .map(rpcCall.converter::apply)
.doOnError { err -> log.debug("Failed to read from upstream", err) }
} }
} }

View File

@@ -18,35 +18,44 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Defaults import io.emeraldpay.dshackle.Defaults
import io.infinitape.etherjar.rpc.Batch import io.infinitape.etherjar.rpc.Batch
import io.infinitape.etherjar.rpc.Commands import io.infinitape.etherjar.rpc.Commands
import io.infinitape.etherjar.rpc.ReactorBatch
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.context.Lifecycle import org.springframework.context.Lifecycle
import org.springframework.scheduling.concurrent.CustomizableThreadFactory
import reactor.core.Disposable import reactor.core.Disposable
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import java.time.Duration import java.time.Duration
import java.util.concurrent.Executors
class EthereumRpcHead( class EthereumRpcHead(
private val api: DirectEthereumApi, private val api: DirectEthereumApi,
private val interval: Duration = Duration.ofSeconds(10) private val interval: Duration = Duration.ofSeconds(10)
): DefaultEthereumHead(), Lifecycle { ): DefaultEthereumHead(), Lifecycle {
companion object {
val scheduler = Schedulers.fromExecutor(Executors.newCachedThreadPool(CustomizableThreadFactory("ethereum-rpc-head")))
}
private val log = LoggerFactory.getLogger(EthereumRpcHead::class.java) private val log = LoggerFactory.getLogger(EthereumRpcHead::class.java)
private var refreshSubscription: Disposable? = null private var refreshSubscription: Disposable? = null
override fun start() { override fun start() {
val base = Flux.interval(interval) val base = Flux.interval(interval)
.publishOn(scheduler)
.flatMap { .flatMap {
val batch = Batch() api.rpcClient
val f = batch.add(Commands.eth().blockNumber) .execute(Commands.eth().blockNumber)
api.rpcClient.execute(batch) .subscribeOn(scheduler)
Mono.fromCompletionStage(f).timeout(Defaults.timeout, Mono.empty()) .timeout(Defaults.timeout, Mono.error(Exception("Block number not received")))
} }
.flatMap { .flatMap {
val batch = Batch() api.rpcClient
val f = batch.add(Commands.eth().getBlock(it)) .execute(Commands.eth().getBlock(it))
api.rpcClient.execute(batch) .subscribeOn(scheduler)
Mono.fromCompletionStage(f).timeout(Defaults.timeout, Mono.empty()) .timeout(Defaults.timeout, Mono.error(Exception("Block data not received")))
} }
.onErrorContinue { err, _ -> .onErrorContinue { err, _ ->
log.debug("RPC error ${err.message}") log.debug("RPC error ${err.message}")

View File

@@ -30,7 +30,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.emerald.ReactorEmeraldClient
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
@@ -38,7 +38,6 @@ import reactor.core.Disposable
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.publisher.toMono import reactor.core.publisher.toMono
import java.lang.Exception
import java.math.BigInteger import java.math.BigInteger
import java.time.Duration import java.time.Duration
import java.util.* import java.util.*
@@ -50,9 +49,9 @@ import kotlin.collections.ArrayList
open class GrpcUpstream( 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 blockchainStub: ReactorBlockchainGrpc.ReactorBlockchainStub,
private val objectMapper: ObjectMapper, private val objectMapper: ObjectMapper,
private val grpcTransport: EmeraldGrpcTransport private val rpcClient: ReactorEmeraldClient
): DefaultUpstream(), Lifecycle { ): DefaultUpstream(), Lifecycle {
private var allLabels: Collection<UpstreamsConfig.Labels> = ArrayList<UpstreamsConfig.Labels>() private var allLabels: Collection<UpstreamsConfig.Labels> = ArrayList<UpstreamsConfig.Labels>()
@@ -68,11 +67,10 @@ open class GrpcUpstream(
open fun createApi(matcher: Selector.Matcher): DirectEthereumApi { open fun createApi(matcher: Selector.Matcher): DirectEthereumApi {
val targets = this.getMethods() val targets = this.getMethods()
val transport = Selector.extractLabels(matcher)?.let { selector -> val client = Selector.extractLabels(matcher)?.let { selector ->
grpcTransport.copyWithSelector(selector.asProto()) rpcClient.copyWithSelector(selector.asProto())
} ?: grpcTransport } ?: rpcClient
val rpcClient = DefaultRpcClient(transport) return DirectEthereumApi(client, objectMapper, targets).let {
return DirectEthereumApi(rpcClient, objectMapper, targets).let {
it.upstream = this it.upstream = this
it it
} }
@@ -91,10 +89,10 @@ open class GrpcUpstream(
val retry: Function<Flux<BlockchainOuterClass.ChainHead>, Flux<BlockchainOuterClass.ChainHead>> = Function { val retry: Function<Flux<BlockchainOuterClass.ChainHead>, Flux<BlockchainOuterClass.ChainHead>> = Function {
setStatus(UpstreamAvailability.UNAVAILABLE) setStatus(UpstreamAvailability.UNAVAILABLE)
client.subscribeHead(chainRef) blockchainStub.subscribeHead(chainRef)
} }
val flux = client.subscribeHead(chainRef) val flux = blockchainStub.subscribeHead(chainRef)
.compose(GrpcRetry.ManyToMany.retryAfter(retry, Duration.ofSeconds(5))) .compose(GrpcRetry.ManyToMany.retryAfter(retry, Duration.ofSeconds(5)))
observeHead(flux) observeHead(flux)
} }
@@ -136,7 +134,7 @@ open class GrpcUpstream(
} }
} }
}.onErrorContinue { err, _ -> }.onErrorContinue { err, _ ->
log.error("Head subscription error: ${err.message}") log.error("Head subscription error. ${err.javaClass.name}:${err.message}", err)
}.doOnNext { }.doOnNext {
setStatus(UpstreamAvailability.OK) setStatus(UpstreamAvailability.OK)
} }

View File

@@ -26,7 +26,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.infinitape.etherjar.rpc.emerald.ReactorEmeraldClient
import io.netty.handler.ssl.* import io.netty.handler.ssl.*
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.commons.lang3.exception.ExceptionUtils
@@ -56,7 +56,7 @@ class GrpcUpstreams(
private var client: ReactorBlockchainGrpc.ReactorBlockchainStub? = null private var client: ReactorBlockchainGrpc.ReactorBlockchainStub? = null
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 private var grpcTransport: ReactorEmeraldClient? = 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)) {
@@ -73,12 +73,9 @@ class GrpcUpstreams(
val client = ReactorBlockchainGrpc.newReactorStub(channel.build()) val client = ReactorBlockchainGrpc.newReactorStub(channel.build())
this.client = client this.client = client
var i = 0 this.grpcTransport = ReactorEmeraldClient.newBuilder()
val grpcExecutor = Executors.newCachedThreadPool { r -> Thread(r, "grpc-up-$id-${i++}") };
this.grpcTransport = EmeraldGrpcTransport.newBuilder()
.forChannel(client.channel) .forChannel(client.channel)
.setObjectMapper(objectMapper) .setObjectMapper(objectMapper)
.setExecutorService(grpcExecutor)
.build() .build()
val statusSubscription = AtomicReference<Disposable>() val statusSubscription = AtomicReference<Disposable>()
@@ -109,8 +106,6 @@ class GrpcUpstreams(
prev?.dispose() prev?.dispose()
subscription subscription
} }
}.doFinally {
grpcExecutor.shutdown()
} }
return updates return updates

View File

@@ -26,7 +26,7 @@ import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.Upstream import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.Upstreams import io.emeraldpay.dshackle.upstream.Upstreams
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -42,7 +42,7 @@ class NativeCallSpec extends Specification {
setup: setup:
def quorum = Spy(new AlwaysQuorum()) def quorum = Spy(new AlwaysQuorum())
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)
@@ -67,7 +67,7 @@ class NativeCallSpec extends Specification {
def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3)) def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3))
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)
@@ -95,7 +95,7 @@ class NativeCallSpec extends Specification {
def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3)) def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3))
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)
@@ -121,7 +121,7 @@ class NativeCallSpec extends Specification {
def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3)) def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3))
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)
@@ -146,7 +146,7 @@ class NativeCallSpec extends Specification {
def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3)) def quorum = Spy(new NonEmptyQuorum(TestingCommons.rpcConverter(), 3))
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)
@@ -307,7 +307,7 @@ class NativeCallSpec extends Specification {
def quorum = Spy(new AlwaysQuorum()) def quorum = Spy(new AlwaysQuorum())
def upstreams = Stub(Upstreams) def upstreams = Stub(Upstreams)
RpcClient rpcClient = Stub(RpcClient) ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient) def apiMock = TestingCommons.api(rpcClient)
apiMock.upstream = Stub(Upstream) apiMock.upstream = Stub(Upstream)

View File

@@ -23,7 +23,7 @@ import io.emeraldpay.dshackle.upstream.Upstreams
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.domain.Address import io.infinitape.etherjar.domain.Address
import io.infinitape.etherjar.domain.BlockHash import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.json.BlockJson import io.infinitape.etherjar.rpc.json.BlockJson
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.publisher.TopicProcessor import reactor.core.publisher.TopicProcessor
@@ -56,7 +56,7 @@ class TrackAddressSpec extends Specification {
.setBalance("1234567890") .setBalance("1234567890")
.build() .build()
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackAddress trackAddress = new TrackAddress(upstreams, Schedulers.immediate()) TrackAddress trackAddress = new TrackAddress(upstreams, Schedulers.immediate())
@@ -98,7 +98,7 @@ class TrackAddressSpec extends Specification {
} }
def blocksBus = TopicProcessor.create() def blocksBus = TopicProcessor.create()
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackAddress trackAddress = new TrackAddress(upstreams, Schedulers.immediate()) TrackAddress trackAddress = new TrackAddress(upstreams, Schedulers.immediate())

View File

@@ -24,7 +24,7 @@ import io.emeraldpay.dshackle.upstream.Upstreams
import io.emeraldpay.grpc.Chain 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.RpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.json.BlockJson import io.infinitape.etherjar.rpc.json.BlockJson
import io.infinitape.etherjar.rpc.json.TransactionJson import io.infinitape.etherjar.rpc.json.TransactionJson
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@@ -87,7 +87,7 @@ class TrackTxSpec extends Specification {
.setTimestamp(blockJson.timestamp.getTime()) .setTimestamp(blockJson.timestamp.getTime())
).build() ).build()
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())
@@ -119,7 +119,7 @@ class TrackTxSpec extends Specification {
.setMined(false) .setMined(false)
.build() .build()
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())
@@ -176,7 +176,7 @@ class TrackTxSpec extends Specification {
it it
} }
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())
@@ -275,7 +275,7 @@ class TrackTxSpec extends Specification {
) )
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())
@@ -316,7 +316,7 @@ class TrackTxSpec extends Specification {
def "Tracked after first load"() { def "Tracked after first load"() {
setup: setup:
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())
@@ -337,7 +337,7 @@ class TrackTxSpec extends Specification {
def "Update of last notified keeps everything else"() { def "Update of last notified keeps everything else"() {
setup: setup:
def apiMock = TestingCommons.api(Stub(RpcClient)) def apiMock = TestingCommons.api(Stub(ReactorRpcClient))
def upstreamMock = TestingCommons.upstream(apiMock) def upstreamMock = TestingCommons.upstream(apiMock)
Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock) Upstreams upstreams = new UpstreamsMock(Chain.ETHEREUM, upstreamMock)
TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate()) TrackTx trackTx = new TrackTx(upstreams, Schedulers.immediate())

View File

@@ -22,6 +22,7 @@ import io.emeraldpay.dshackle.upstream.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.grpc.stub.StreamObserver import io.grpc.stub.StreamObserver
import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.RpcClient
import io.infinitape.etherjar.rpc.RpcResponseError import io.infinitape.etherjar.rpc.RpcResponseError
import io.infinitape.etherjar.rpc.json.ResponseJson import io.infinitape.etherjar.rpc.json.ResponseJson
@@ -36,7 +37,7 @@ class EthereumApiMock extends DirectEthereumApi {
List<PredefinedResponse> predefined = [] List<PredefinedResponse> predefined = []
private ObjectMapper objectMapper private ObjectMapper objectMapper
EthereumApiMock(@NotNull RpcClient rpcClient, @NotNull ObjectMapper objectMapper, @NotNull Chain chain) { EthereumApiMock(@NotNull ReactorRpcClient rpcClient, @NotNull ObjectMapper objectMapper, @NotNull Chain chain) {
super(rpcClient, objectMapper, new DirectCallMethods()) super(rpcClient, objectMapper, new DirectCallMethods())
this.objectMapper = objectMapper this.objectMapper = objectMapper
} }

View File

@@ -16,14 +16,14 @@
package io.emeraldpay.dshackle.test package io.emeraldpay.dshackle.test
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.upstream.CallMethods
import io.emeraldpay.dshackle.upstream.DirectCallMethods import io.emeraldpay.dshackle.upstream.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
import io.infinitape.etherjar.rpc.Batch import io.infinitape.etherjar.rpc.ReactorBatch
import io.infinitape.etherjar.rpc.ExecutableBatch import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.RpcCall import io.infinitape.etherjar.rpc.RpcCall
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.RpcCallResponse
import io.infinitape.etherjar.rpc.transport.BatchStatus import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@@ -31,7 +31,7 @@ class EthereumApiStub extends DirectEthereumApi {
private String id private String id
private static ObjectMapper objectMapper = TestingCommons.objectMapper() private static ObjectMapper objectMapper = TestingCommons.objectMapper()
private static RpcClient rpcClient = new RpcClientMock(); private static ReactorRpcClient rpcClient = new RpcClientMock();
EthereumApiStub(Integer id) { EthereumApiStub(Integer id) {
this(id.toString()) this(id.toString())
@@ -47,30 +47,16 @@ class EthereumApiStub extends DirectEthereumApi {
return "API Stub $id" return "API Stub $id"
} }
static class RpcClientMock implements RpcClient { static class RpcClientMock implements ReactorRpcClient {
@Override @Override
CompletableFuture<BatchStatus> execute(Batch batch) { Flux<RpcCallResponse> execute(ReactorBatch batch) {
return null return Flux.error(new Exception("Not implemented in mock"))
} }
@Override @Override
def <RES> CompletableFuture<RES> execute(RpcCall<?, RES> call) { def <JS, RES> Mono<RES> execute(RpcCall<JS, RES> call) {
return null return Mono.error(new Exception("Not implemented in mock"))
}
@Override
ExecutableBatch batch() {
return null
}
@Override
EthCommands eth() {
return null
}
@Override
TraceCommands trace() {
return null
} }
} }
} }

View File

@@ -28,6 +28,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.JacksonRpcConverter import io.infinitape.etherjar.rpc.JacksonRpcConverter
import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.RpcClient
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@@ -47,7 +48,7 @@ class TestingCommons {
return objectMapper return objectMapper
} }
static EthereumApiMock api(RpcClient rpcClient) { static EthereumApiMock api(ReactorRpcClient rpcClient) {
return new EthereumApiMock(rpcClient, objectMapper(), Chain.ETHEREUM) return new EthereumApiMock(rpcClient, objectMapper(), Chain.ETHEREUM)
} }

View File

@@ -2,9 +2,8 @@ package io.emeraldpay.dshackle.upstream
import io.emeraldpay.dshackle.test.EthereumUpstreamMock import io.emeraldpay.dshackle.test.EthereumUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import spock.lang.Specification import spock.lang.Specification
class CurrentUpstreamsSpec extends Specification { class CurrentUpstreamsSpec extends Specification {
@@ -12,7 +11,7 @@ class CurrentUpstreamsSpec extends Specification {
def "add upstream"() { def "add upstream"() {
setup: setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper()) def current = new CurrentUpstreams(TestingCommons.objectMapper())
def up = new EthereumUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up = new EthereumUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
when: when:
current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED))
then: then:
@@ -23,9 +22,9 @@ class CurrentUpstreamsSpec extends Specification {
def "add multiple upstreams"() { def "add multiple upstreams"() {
setup: setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper()) def current = new CurrentUpstreams(TestingCommons.objectMapper())
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(RpcClient))) def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(ReactorRpcClient)))
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
when: when:
current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED))
current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED))
@@ -39,10 +38,10 @@ class CurrentUpstreamsSpec extends Specification {
def "remove upstream"() { def "remove upstream"() {
setup: setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper()) def current = new CurrentUpstreams(TestingCommons.objectMapper())
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(RpcClient))) def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(ReactorRpcClient)))
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
def up1_del = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient))) def up1_del = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
when: when:
current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED))
current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED))

View File

@@ -22,8 +22,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumWs import io.emeraldpay.dshackle.upstream.ethereum.EthereumWs
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.DefaultRpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.RpcClient
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Retry import spock.lang.Retry
import spock.lang.Specification import spock.lang.Specification
@@ -32,7 +31,7 @@ import java.time.Duration
class FilteredApisSpec extends Specification { class FilteredApisSpec extends Specification {
def rpcClient = new DefaultRpcClient(null) def rpcClient = Stub(ReactorRpcClient)
def objectMapper = TestingCommons.objectMapper() def objectMapper = TestingCommons.objectMapper()
def ethereumTargets = new QuorumBasedMethods(objectMapper, Chain.ETHEREUM) def ethereumTargets = new QuorumBasedMethods(objectMapper, Chain.ETHEREUM)
@@ -141,8 +140,8 @@ class FilteredApisSpec extends Specification {
def "Makes pause between batches"() { def "Makes pause between batches"() {
when: when:
def api1 = TestingCommons.api(Stub(RpcClient)) def api1 = TestingCommons.api(Stub(ReactorRpcClient))
def api2 = TestingCommons.api(Stub(RpcClient)) def api2 = TestingCommons.api(Stub(ReactorRpcClient))
def up1 = TestingCommons.upstream(api1) def up1 = TestingCommons.upstream(api1)
def up2 = TestingCommons.upstream(api2) def up2 = TestingCommons.upstream(api2)
then: then:

View File

@@ -29,8 +29,8 @@ 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.JacksonRpcConverter
import io.infinitape.etherjar.rpc.RpcClient import io.infinitape.etherjar.rpc.ReactorRpcClient
import io.infinitape.etherjar.rpc.emerald.EmeraldGrpcTransport import io.infinitape.etherjar.rpc.emerald.ReactorEmeraldClient
import io.infinitape.etherjar.rpc.json.BlockJson import io.infinitape.etherjar.rpc.json.BlockJson
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -48,7 +48,7 @@ class GrpcUpstreamSpec extends Specification {
setup: setup:
def callData = [:] def callData = [:]
def chain = Chain.ETHEREUM def chain = Chain.ETHEREUM
def api = TestingCommons.api(Stub(RpcClient)) def api = TestingCommons.api(Stub(ReactorRpcClient))
def block1 = new BlockJson().with { def block1 = new BlockJson().with {
it.number = 650246 it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
@@ -74,7 +74,7 @@ class GrpcUpstreamSpec extends Specification {
) )
} }
}) })
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build() def transport = ReactorEmeraldClient.newBuilder().forChannel(client.channel).build()
def upstream = new GrpcUpstream("test", chain, client, objectMapper, transport) 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()
@@ -91,7 +91,7 @@ class GrpcUpstreamSpec extends Specification {
def "Follows difficulty, ignores less difficult"() { def "Follows difficulty, ignores less difficult"() {
setup: setup:
def api = TestingCommons.api(Stub(RpcClient)) def api = TestingCommons.api(Stub(ReactorRpcClient))
def block1 = new BlockJson().with { def block1 = new BlockJson().with {
it.number = 650246 it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
@@ -130,7 +130,7 @@ class GrpcUpstreamSpec extends Specification {
) )
} }
}) })
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build() def transport = ReactorEmeraldClient.newBuilder().forChannel(client.channel).build()
def upstream = new GrpcUpstream("test", Chain.ETHEREUM, client, objectMapper, transport) def upstream = new GrpcUpstream("test", Chain.ETHEREUM, client, objectMapper, transport)
upstream.setLag(0) upstream.setLag(0)
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder() upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
@@ -150,7 +150,7 @@ class GrpcUpstreamSpec extends Specification {
def callData = [:] def callData = [:]
def finished = new CompletableFuture<Boolean>() def finished = new CompletableFuture<Boolean>()
def chain = Chain.ETHEREUM def chain = Chain.ETHEREUM
def api = TestingCommons.api(Stub(RpcClient)) def api = TestingCommons.api(Stub(ReactorRpcClient))
def block1 = new BlockJson().with { def block1 = new BlockJson().with {
it.number = 650246 it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
@@ -190,7 +190,7 @@ class GrpcUpstreamSpec extends Specification {
finished.complete(true) finished.complete(true)
} }
}) })
def transport = EmeraldGrpcTransport.newBuilder().forChannel(client.channel).build() def transport = ReactorEmeraldClient.newBuilder().forChannel(client.channel).build()
def upstream = new GrpcUpstream("test", chain, client, objectMapper, transport) 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()