Add BroadcastReader instead of BroadcastQuorum (#263)
This commit is contained in:
@@ -7,16 +7,15 @@ const val SPAN_STATUS_MESSAGE = "status.message"
|
|||||||
const val SPAN_READER_RESULT = "reader.result"
|
const val SPAN_READER_RESULT = "reader.result"
|
||||||
const val SPAN_REQUEST_API_TYPE = "request.api.type"
|
const val SPAN_REQUEST_API_TYPE = "request.api.type"
|
||||||
const val SPAN_REQUEST_UPSTREAM_ID = "request.upstreamId"
|
const val SPAN_REQUEST_UPSTREAM_ID = "request.upstreamId"
|
||||||
const val SPAN_RESPONSE_UPSTREAM_ID = "response.upstreamId"
|
|
||||||
const val SPAN_REQUEST_ID = "request.id"
|
const val SPAN_REQUEST_ID = "request.id"
|
||||||
const val SPAN_NO_RESPONSE_MESSAGE = "no-response.message"
|
const val SPAN_NO_RESPONSE_MESSAGE = "no-response.message"
|
||||||
|
|
||||||
const val LOCAL_READER = "localReader"
|
const val LOCAL_READER = "localReader"
|
||||||
const val REMOTE_QUORUM_RPC_READER = "remoteQuorumRpcReader"
|
const val RPC_READER = "rpcReader"
|
||||||
const val API_READER = "apiReader"
|
const val API_READER = "apiReader"
|
||||||
const val CACHE_BLOCK_BY_HASH_READER = "cacheBlockByHashReader"
|
const val CACHE_BLOCK_BY_HASH_READER = "cacheBlockByHashReader"
|
||||||
const val DIRECT_QUORUM_RPC_READER = "directQuorumRpcReader"
|
const val DIRECT_QUORUM_RPC_READER = "directQuorumRpcReader"
|
||||||
const val CACHE_HEIGHT_BY_HASH_READER = "cacheHeightByHashReader"
|
|
||||||
const val CACHE_BLOCK_BY_HEIGHT_READER = "cacheBlockByHeightReader"
|
const val CACHE_BLOCK_BY_HEIGHT_READER = "cacheBlockByHeightReader"
|
||||||
const val CACHE_TX_BY_HASH_READER = "cacheTxByHashReader"
|
const val CACHE_TX_BY_HASH_READER = "cacheTxByHashReader"
|
||||||
const val CACHE_RECEIPTS_READER = "cacheReceiptsReader"
|
const val CACHE_RECEIPTS_READER = "cacheReceiptsReader"
|
||||||
|
const val BROADCAST_READER = "broadcastReader"
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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.quorum
|
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
|
||||||
import io.emeraldpay.dshackle.upstream.ApiSource
|
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
|
||||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
|
||||||
import org.springframework.cloud.sleuth.Tracer
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
|
|
||||||
interface QuorumReader : Reader<JsonRpcRequest, QuorumRpcReader.Result> {
|
|
||||||
fun attempts(): AtomicInteger
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates instance of a Quorum based reader
|
|
||||||
interface QuorumReaderFactory {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun default(): QuorumReaderFactory {
|
|
||||||
return Default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun create(apis: ApiSource, quorum: CallQuorum, signer: ResponseSigner?, tracer: Tracer): QuorumReader
|
|
||||||
|
|
||||||
class Default : QuorumReaderFactory {
|
|
||||||
override fun create(apis: ApiSource, quorum: CallQuorum, signer: ResponseSigner?, tracer: Tracer): QuorumReader {
|
|
||||||
return QuorumRpcReader(apis, quorum, signer, tracer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,10 +20,10 @@ import io.emeraldpay.dshackle.commons.API_READER
|
|||||||
import io.emeraldpay.dshackle.commons.SPAN_NO_RESPONSE_MESSAGE
|
import io.emeraldpay.dshackle.commons.SPAN_NO_RESPONSE_MESSAGE
|
||||||
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_API_TYPE
|
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_API_TYPE
|
||||||
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID
|
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReader
|
||||||
import io.emeraldpay.dshackle.reader.SpannedReader
|
import io.emeraldpay.dshackle.reader.SpannedReader
|
||||||
import io.emeraldpay.dshackle.upstream.ApiSource
|
import io.emeraldpay.dshackle.upstream.ApiSource
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
@@ -47,9 +47,9 @@ import java.util.function.Function
|
|||||||
class QuorumRpcReader(
|
class QuorumRpcReader(
|
||||||
private val apiControl: ApiSource,
|
private val apiControl: ApiSource,
|
||||||
private val quorum: CallQuorum,
|
private val quorum: CallQuorum,
|
||||||
private val signer: ResponseSigner?,
|
signer: ResponseSigner?,
|
||||||
private val tracer: Tracer
|
private val tracer: Tracer
|
||||||
) : QuorumReader {
|
) : RpcReader(signer) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(QuorumRpcReader::class.java)
|
private val log = LoggerFactory.getLogger(QuorumRpcReader::class.java)
|
||||||
@@ -158,12 +158,7 @@ class QuorumRpcReader(
|
|||||||
private fun withSignatureAndUpstream(api: Upstream, key: JsonRpcRequest, response: JsonRpcResponse): Function<Mono<ByteArray>, Mono<Tuple2<ByteArray, Optional<ResponseSigner.Signature>>>> {
|
private fun withSignatureAndUpstream(api: Upstream, key: JsonRpcRequest, response: JsonRpcResponse): Function<Mono<ByteArray>, Mono<Tuple2<ByteArray, Optional<ResponseSigner.Signature>>>> {
|
||||||
return Function { src ->
|
return Function { src ->
|
||||||
src.map {
|
src.map {
|
||||||
val signature = response.providedSignature
|
val signature = getSignature(key, response, api.getId())
|
||||||
?: if (key.nonce != null) {
|
|
||||||
signer?.sign(key.nonce, response.getResult(), api.getId())
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
Tuples.of(it, Optional.ofNullable(signature))
|
Tuples.of(it, Optional.ofNullable(signature))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,14 +171,7 @@ class QuorumRpcReader(
|
|||||||
// when the call failed with an error we want to notify the quorum because
|
// when the call failed with an error we want to notify the quorum because
|
||||||
// it may use the error message or other details
|
// it may use the error message or other details
|
||||||
//
|
//
|
||||||
val cleanErr: JsonRpcException = when (err) {
|
val cleanErr: JsonRpcException = getError(key, err)
|
||||||
is RpcException -> JsonRpcException.from(err)
|
|
||||||
is JsonRpcException -> err
|
|
||||||
else -> JsonRpcException(
|
|
||||||
JsonRpcResponse.NumberId(key.id),
|
|
||||||
JsonRpcError(-32603, "Unhandled internal error: ${err.javaClass}: ${err.message}")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
quorum.record(cleanErr, null, api,)
|
quorum.record(cleanErr, null, api,)
|
||||||
// if it's failed after that, then we don't need more calls, stop api source
|
// if it's failed after that, then we don't need more calls, stop api source
|
||||||
if (quorum.isFailed()) {
|
if (quorum.isFailed()) {
|
||||||
@@ -202,8 +190,7 @@ class QuorumRpcReader(
|
|||||||
return Mono.just(quorum).flatMap { q ->
|
return Mono.just(quorum).flatMap { q ->
|
||||||
if (q.isFailed()) {
|
if (q.isFailed()) {
|
||||||
val resolvedBy = resolvedBy()?.getId()
|
val resolvedBy = resolvedBy()?.getId()
|
||||||
val err = q.getError()?.asException(JsonRpcResponse.NumberId(key.id), resolvedBy)
|
val err = handleError(q.getError(), key.id, resolvedBy)
|
||||||
?: JsonRpcException(JsonRpcResponse.NumberId(key.id), JsonRpcError(-32603, "Unhandled Upstream error"), resolvedBy)
|
|
||||||
log.warn("Quorum is failed. Method ${key.method}, message ${err.message}")
|
log.warn("Quorum is failed. Method ${key.method}, message ${err.message}")
|
||||||
Mono.error(err)
|
Mono.error(err)
|
||||||
} else {
|
} else {
|
||||||
@@ -229,11 +216,4 @@ class QuorumRpcReader(
|
|||||||
}
|
}
|
||||||
} ?: Mono.error(RpcException(1, "Quorum [$q] is not resolved [isResolved - ${q.isResolved()}]"))
|
} ?: Mono.error(RpcException(1, "Quorum [$q] is not resolved [isResolved - ${q.isResolved()}]"))
|
||||||
}
|
}
|
||||||
|
|
||||||
class Result(
|
|
||||||
val value: ByteArray,
|
|
||||||
val signature: ResponseSigner.Signature?,
|
|
||||||
val quorum: Int,
|
|
||||||
val resolvedBy: Upstream?
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
118
src/main/kotlin/io/emeraldpay/dshackle/reader/BroadcastReader.kt
Normal file
118
src/main/kotlin/io/emeraldpay/dshackle/reader/BroadcastReader.kt
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
package io.emeraldpay.dshackle.reader
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.commons.BROADCAST_READER
|
||||||
|
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID
|
||||||
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.cloud.sleuth.Tracer
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
class BroadcastReader(
|
||||||
|
private val upstreams: List<Upstream>,
|
||||||
|
matcher: Selector.Matcher,
|
||||||
|
signer: ResponseSigner?,
|
||||||
|
private val tracer: Tracer
|
||||||
|
) : RpcReader(signer) {
|
||||||
|
private val internalMatcher = Selector.MultiMatcher(
|
||||||
|
listOf(Selector.AvailabilityMatcher(), matcher)
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = LoggerFactory.getLogger(BroadcastReader::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun attempts(): AtomicInteger {
|
||||||
|
return AtomicInteger(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(key: JsonRpcRequest): Mono<Result> {
|
||||||
|
return Mono.just(upstreams)
|
||||||
|
.map { ups ->
|
||||||
|
ups.filter { internalMatcher.matches(it) }.map { execute(key, it) }
|
||||||
|
}.flatMap {
|
||||||
|
Mono.zip(it) { responses ->
|
||||||
|
analyzeResponses(
|
||||||
|
key,
|
||||||
|
getJsonRpcResponses(key.method, responses)
|
||||||
|
)
|
||||||
|
}.onErrorResume { err ->
|
||||||
|
log.error("Broadcast error: ${err.message}")
|
||||||
|
Mono.error(handleError(null, 0, null))
|
||||||
|
}.flatMap { broadcastResult ->
|
||||||
|
if (broadcastResult.result != null) {
|
||||||
|
Mono.just(
|
||||||
|
Result(broadcastResult.result, broadcastResult.signature, 0, null)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val err = handleError(broadcastResult.error, key.id, null)
|
||||||
|
Mono.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun analyzeResponses(key: JsonRpcRequest, jsonRpcResponses: List<BroadcastResponse>): BroadcastResult {
|
||||||
|
val errors = mutableListOf<JsonRpcError>()
|
||||||
|
jsonRpcResponses.forEach {
|
||||||
|
val response = it.jsonRpcResponse
|
||||||
|
if (response.hasResult()) {
|
||||||
|
val signature = getSignature(key, response, it.upstreamId)
|
||||||
|
return BroadcastResult(response.getResult(), null, signature)
|
||||||
|
} else if (response.hasError()) {
|
||||||
|
errors.add(response.error!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val error = errors.takeIf { it.isNotEmpty() }?.get(0)
|
||||||
|
|
||||||
|
return BroadcastResult(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getJsonRpcResponses(method: String, responses: Array<Any>) =
|
||||||
|
responses
|
||||||
|
.map { response ->
|
||||||
|
(response as BroadcastResponse)
|
||||||
|
.also { r ->
|
||||||
|
if (r.jsonRpcResponse.hasResult()) {
|
||||||
|
log.info(
|
||||||
|
"Response for $method from upstream ${r.upstreamId}: ${String(r.jsonRpcResponse.getResult())}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun execute(
|
||||||
|
key: JsonRpcRequest,
|
||||||
|
upstream: Upstream
|
||||||
|
): Mono<BroadcastResponse> =
|
||||||
|
SpannedReader(
|
||||||
|
upstream.getIngressReader(), tracer, BROADCAST_READER, mapOf(SPAN_REQUEST_UPSTREAM_ID to upstream.getId())
|
||||||
|
)
|
||||||
|
.read(key)
|
||||||
|
.map { BroadcastResponse(it, upstream.getId()) }
|
||||||
|
.onErrorResume {
|
||||||
|
log.warn("Error during execution ${key.method} from upstream ${upstream.getId()} with message - ${it.message}")
|
||||||
|
Mono.just(
|
||||||
|
BroadcastResponse(JsonRpcResponse(null, getError(key, it).error), upstream.getId())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BroadcastResponse(
|
||||||
|
val jsonRpcResponse: JsonRpcResponse,
|
||||||
|
val upstreamId: String
|
||||||
|
)
|
||||||
|
|
||||||
|
private class BroadcastResult(
|
||||||
|
val result: ByteArray?,
|
||||||
|
val error: JsonRpcError?,
|
||||||
|
val signature: ResponseSigner.Signature?
|
||||||
|
) {
|
||||||
|
constructor(error: JsonRpcError?) : this(null, error, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package io.emeraldpay.dshackle.reader
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
|
import io.emeraldpay.dshackle.quorum.QuorumRpcReader
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReader.Result
|
||||||
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
|
import io.emeraldpay.etherjar.rpc.RpcException
|
||||||
|
import org.springframework.cloud.sleuth.Tracer
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
abstract class RpcReader(
|
||||||
|
private val signer: ResponseSigner?,
|
||||||
|
) : Reader<JsonRpcRequest, Result> {
|
||||||
|
abstract fun attempts(): AtomicInteger
|
||||||
|
|
||||||
|
protected fun getError(key: JsonRpcRequest, err: Throwable) =
|
||||||
|
when (err) {
|
||||||
|
is RpcException -> JsonRpcException.from(err)
|
||||||
|
is JsonRpcException -> err
|
||||||
|
else -> JsonRpcException(
|
||||||
|
JsonRpcResponse.NumberId(key.id),
|
||||||
|
JsonRpcError(-32603, "Unhandled internal error: ${err.javaClass}: ${err.message}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun handleError(error: JsonRpcError?, id: Int, resolvedBy: String?) =
|
||||||
|
error?.asException(JsonRpcResponse.NumberId(id), resolvedBy)
|
||||||
|
?: JsonRpcException(JsonRpcResponse.NumberId(id), JsonRpcError(-32603, "Unhandled Upstream error"), resolvedBy)
|
||||||
|
|
||||||
|
protected fun getSignature(key: JsonRpcRequest, response: JsonRpcResponse, upstreamId: String) =
|
||||||
|
response.providedSignature
|
||||||
|
?: if (key.nonce != null) {
|
||||||
|
signer?.sign(key.nonce, response.getResult(), upstreamId)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
class Result(
|
||||||
|
val value: ByteArray,
|
||||||
|
val signature: ResponseSigner.Signature?,
|
||||||
|
val quorum: Int,
|
||||||
|
val resolvedBy: Upstream?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RpcReaderFactory {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun default(): RpcReaderFactory {
|
||||||
|
return Default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(data: RpcReaderData): RpcReader
|
||||||
|
|
||||||
|
class Default : RpcReaderFactory {
|
||||||
|
override fun create(data: RpcReaderData): RpcReader {
|
||||||
|
if (data.method == "eth_sendRawTransaction") {
|
||||||
|
return BroadcastReader(data.multistream.getAll(), data.matcher, data.signer, data.tracer)
|
||||||
|
}
|
||||||
|
val apis = data.multistream.getApiSource(data.matcher)
|
||||||
|
return QuorumRpcReader(apis, data.quorum, data.signer, data.tracer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class RpcReaderData(
|
||||||
|
val multistream: Multistream,
|
||||||
|
val method: String,
|
||||||
|
val matcher: Selector.Matcher,
|
||||||
|
val quorum: CallQuorum,
|
||||||
|
val signer: ResponseSigner?,
|
||||||
|
val tracer: Tracer
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -25,15 +25,16 @@ import io.emeraldpay.dshackle.Global
|
|||||||
import io.emeraldpay.dshackle.Global.Companion.nullValue
|
import io.emeraldpay.dshackle.Global.Companion.nullValue
|
||||||
import io.emeraldpay.dshackle.SilentException
|
import io.emeraldpay.dshackle.SilentException
|
||||||
import io.emeraldpay.dshackle.commons.LOCAL_READER
|
import io.emeraldpay.dshackle.commons.LOCAL_READER
|
||||||
import io.emeraldpay.dshackle.commons.REMOTE_QUORUM_RPC_READER
|
import io.emeraldpay.dshackle.commons.RPC_READER
|
||||||
import io.emeraldpay.dshackle.commons.SPAN_ERROR
|
import io.emeraldpay.dshackle.commons.SPAN_ERROR
|
||||||
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_ID
|
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_ID
|
||||||
import io.emeraldpay.dshackle.commons.SPAN_STATUS_MESSAGE
|
import io.emeraldpay.dshackle.commons.SPAN_STATUS_MESSAGE
|
||||||
import io.emeraldpay.dshackle.config.MainConfig
|
import io.emeraldpay.dshackle.config.MainConfig
|
||||||
import io.emeraldpay.dshackle.quorum.CallQuorum
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
|
import io.emeraldpay.dshackle.reader.RpcReader
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumRpcReader
|
import io.emeraldpay.dshackle.reader.RpcReaderFactory
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReaderFactory.RpcReaderData
|
||||||
import io.emeraldpay.dshackle.reader.SpannedReader
|
import io.emeraldpay.dshackle.reader.SpannedReader
|
||||||
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
|
||||||
import io.emeraldpay.dshackle.upstream.ApiSource
|
import io.emeraldpay.dshackle.upstream.ApiSource
|
||||||
@@ -79,7 +80,7 @@ open class NativeCall(
|
|||||||
private val localRouterEnabled = config.cache?.requestsCacheEnabled ?: true
|
private val localRouterEnabled = config.cache?.requestsCacheEnabled ?: true
|
||||||
private val passthrough = config.passthrough
|
private val passthrough = config.passthrough
|
||||||
|
|
||||||
var quorumReaderFactory: QuorumReaderFactory = QuorumReaderFactory.default()
|
var rpcReaderFactory: RpcReaderFactory = RpcReaderFactory.default()
|
||||||
private val ethereumCallSelectors = EnumMap<Chain, EthereumCallSelector>(Chain::class.java)
|
private val ethereumCallSelectors = EnumMap<Chain, EthereumCallSelector>(Chain::class.java)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -391,15 +392,18 @@ open class NativeCall(
|
|||||||
if (!ctx.upstream.getMethods().isCallable(ctx.payload.method)) {
|
if (!ctx.upstream.getMethods().isCallable(ctx.payload.method)) {
|
||||||
return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method"))
|
return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method"))
|
||||||
}
|
}
|
||||||
val reader = quorumReaderFactory.create(ctx.getApis(), ctx.callQuorum, signer, tracer)
|
val reader = rpcReaderFactory.create(
|
||||||
|
RpcReaderData(ctx.upstream, ctx.payload.method, ctx.matcher, ctx.callQuorum, signer, tracer)
|
||||||
|
)
|
||||||
val counter = reader.attempts()
|
val counter = reader.attempts()
|
||||||
|
|
||||||
return SpannedReader(reader, tracer, REMOTE_QUORUM_RPC_READER)
|
return SpannedReader(reader, tracer, RPC_READER)
|
||||||
.read(JsonRpcRequest(ctx.payload.method, ctx.payload.params, ctx.nonce, ctx.forwardedSelector))
|
.read(JsonRpcRequest(ctx.payload.method, ctx.payload.params, ctx.nonce, ctx.forwardedSelector))
|
||||||
.map {
|
.map {
|
||||||
val bytes = ctx.resultDecorator.processResult(it)
|
val bytes = ctx.resultDecorator.processResult(it)
|
||||||
validateResult(bytes, "remote", ctx)
|
validateResult(bytes, "remote", ctx)
|
||||||
CallResult.ok(ctx.id, ctx.nonce, bytes, it.signature, it.resolvedBy?.getId(), ctx)
|
val upId = it.resolvedBy?.getId() ?: ctx.upstream.getId()
|
||||||
|
CallResult.ok(ctx.id, ctx.nonce, bytes, it.signature, upId, ctx)
|
||||||
}
|
}
|
||||||
.onErrorResume { t ->
|
.onErrorResume { t ->
|
||||||
Mono.just(CallResult.fail(ctx.id, ctx.nonce, t, ctx))
|
Mono.just(CallResult.fail(ctx.id, ctx.nonce, t, ctx))
|
||||||
@@ -470,11 +474,11 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface ResultDecorator {
|
interface ResultDecorator {
|
||||||
fun processResult(result: QuorumRpcReader.Result): ByteArray
|
fun processResult(result: RpcReader.Result): ByteArray
|
||||||
}
|
}
|
||||||
|
|
||||||
open class NoneResultDecorator : ResultDecorator {
|
open class NoneResultDecorator : ResultDecorator {
|
||||||
override fun processResult(result: QuorumRpcReader.Result): ByteArray = result.value
|
override fun processResult(result: RpcReader.Result): ByteArray = result.value
|
||||||
}
|
}
|
||||||
|
|
||||||
open class CreateFilterDecorator : ResultDecorator {
|
open class CreateFilterDecorator : ResultDecorator {
|
||||||
@@ -482,7 +486,7 @@ open class NativeCall(
|
|||||||
companion object {
|
companion object {
|
||||||
const val quoteCode = '"'.code.toByte()
|
const val quoteCode = '"'.code.toByte()
|
||||||
}
|
}
|
||||||
override fun processResult(result: QuorumRpcReader.Result): ByteArray {
|
override fun processResult(result: RpcReader.Result): ByteArray {
|
||||||
val bytes = result.value
|
val bytes = result.value
|
||||||
if (bytes.last() == quoteCode && result.resolvedBy != null) {
|
if (bytes.last() == quoteCode && result.resolvedBy != null) {
|
||||||
val suffix = result.resolvedBy.nodeId()
|
val suffix = result.resolvedBy.nodeId()
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package io.emeraldpay.dshackle.upstream.calls
|
|||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.BroadcastQuorum
|
|
||||||
import io.emeraldpay.dshackle.quorum.CallQuorum
|
import io.emeraldpay.dshackle.quorum.CallQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.NotNullQuorum
|
import io.emeraldpay.dshackle.quorum.NotNullQuorum
|
||||||
@@ -198,7 +197,6 @@ class DefaultEthereumMethods(
|
|||||||
when (method) {
|
when (method) {
|
||||||
"eth_getTransactionCount" -> AlwaysQuorum()
|
"eth_getTransactionCount" -> AlwaysQuorum()
|
||||||
"eth_getBalance" -> AlwaysQuorum()
|
"eth_getBalance" -> AlwaysQuorum()
|
||||||
"eth_sendRawTransaction" -> BroadcastQuorum()
|
|
||||||
"eth_blockNumber" -> NotLaggingQuorum(0)
|
"eth_blockNumber" -> NotLaggingQuorum(0)
|
||||||
else -> AlwaysQuorum()
|
else -> AlwaysQuorum()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import io.emeraldpay.dshackle.data.BlockId
|
|||||||
import io.emeraldpay.dshackle.data.DefaultContainer
|
import io.emeraldpay.dshackle.data.DefaultContainer
|
||||||
import io.emeraldpay.dshackle.data.TxContainer
|
import io.emeraldpay.dshackle.data.TxContainer
|
||||||
import io.emeraldpay.dshackle.data.TxId
|
import io.emeraldpay.dshackle.data.TxId
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
|
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReaderFactory
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
@@ -51,7 +51,7 @@ class EthereumDirectReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private val objectMapper: ObjectMapper = Global.objectMapper
|
private val objectMapper: ObjectMapper = Global.objectMapper
|
||||||
var quorumReaderFactory: QuorumReaderFactory = QuorumReaderFactory.default()
|
var rpcReaderFactory: RpcReaderFactory = RpcReaderFactory.default()
|
||||||
|
|
||||||
val blockReader: Reader<BlockHash, Result<BlockContainer>>
|
val blockReader: Reader<BlockHash, Result<BlockContainer>>
|
||||||
val blockByHeightReader: Reader<Long, Result<BlockContainer>>
|
val blockByHeightReader: Reader<Long, Result<BlockContainer>>
|
||||||
@@ -183,19 +183,17 @@ class EthereumDirectReader(
|
|||||||
request: JsonRpcRequest,
|
request: JsonRpcRequest,
|
||||||
matcher: Selector.Matcher = Selector.empty
|
matcher: Selector.Matcher = Selector.empty
|
||||||
): Mono<Result<ByteArray>> {
|
): Mono<Result<ByteArray>> {
|
||||||
return Mono.just(quorumReaderFactory)
|
return Mono.just(rpcReaderFactory)
|
||||||
.map {
|
.map {
|
||||||
|
val requestMatcher = Selector.Builder()
|
||||||
|
.withMatcher(matcher)
|
||||||
|
.forMethod(request.method)
|
||||||
|
.build()
|
||||||
it.create(
|
it.create(
|
||||||
up.getApiSource(
|
RpcReaderFactory.RpcReaderData(
|
||||||
Selector.Builder()
|
up, request.method, requestMatcher,
|
||||||
.withMatcher(matcher)
|
callMethodsFactory.create().createQuorumFor(request.method), null, tracer
|
||||||
.forMethod(request.method)
|
)
|
||||||
.build()
|
|
||||||
),
|
|
||||||
callMethodsFactory.create().createQuorumFor(request.method),
|
|
||||||
// we do not use Signer for internal requests because it doesn't make much sense
|
|
||||||
null,
|
|
||||||
tracer
|
|
||||||
)
|
)
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
it.read(request)
|
it.read(request)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
1 * getIngressReader() >> Mock(Reader) {
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.just(JsonRpcResponse.ok("1"))
|
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.just(JsonRpcResponse.ok("1"))
|
||||||
@@ -72,6 +73,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> api
|
_ * getIngressReader() >> api
|
||||||
}
|
}
|
||||||
@@ -109,6 +111,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> api
|
_ * getIngressReader() >> api
|
||||||
}
|
}
|
||||||
@@ -136,6 +139,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> Mock(Reader) {
|
_ * getIngressReader() >> Mock(Reader) {
|
||||||
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||||
@@ -168,6 +172,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> Mock(Reader) {
|
_ * getIngressReader() >> Mock(Reader) {
|
||||||
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
2 * read(new JsonRpcRequest("eth_test", [])) >>> [
|
||||||
@@ -236,6 +241,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> api
|
_ * getIngressReader() >> api
|
||||||
}
|
}
|
||||||
@@ -264,6 +270,7 @@ class QuorumRpcReaderSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def up = Mock(Upstream) {
|
def up = Mock(Upstream) {
|
||||||
_ * getLag() >> 0
|
_ * getLag() >> 0
|
||||||
|
_ * getId() >> "id"
|
||||||
_ * isAvailable() >> true
|
_ * isAvailable() >> true
|
||||||
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
_ * getRole() >> UpstreamsConfig.UpstreamRole.PRIMARY
|
||||||
_ * getIngressReader() >> Mock(Reader) {
|
_ * getIngressReader() >> Mock(Reader) {
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package io.emeraldpay.dshackle.reader
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||||
|
import org.springframework.cloud.sleuth.Tracer
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.test.StepVerifier
|
||||||
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
class BroadcastReaderSpec extends Specification {
|
||||||
|
|
||||||
|
def "Return responses from all upstreams"() {
|
||||||
|
setup:
|
||||||
|
def result = "123".getBytes()
|
||||||
|
def up = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.just(new JsonRpcResponse(result, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up1 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.just(new JsonRpcResponse(result, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up2 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.just(new JsonRpcResponse(result, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, Stub(Tracer))
|
||||||
|
when:
|
||||||
|
def act = reader.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNextMatches {
|
||||||
|
it.value == result
|
||||||
|
}
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Return response if at least one upstream responds"() {
|
||||||
|
setup:
|
||||||
|
def result = "123".getBytes()
|
||||||
|
def up = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.just(new JsonRpcResponse(result, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up1 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.error(new JsonRpcException(1, "too low"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up2 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.error(new JsonRpcException(1, "too low")) }
|
||||||
|
}
|
||||||
|
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, Stub(Tracer))
|
||||||
|
when:
|
||||||
|
def act = reader.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNextMatches {
|
||||||
|
it.value == result
|
||||||
|
}
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Return response from matched upstream"() {
|
||||||
|
setup:
|
||||||
|
def result = "123".getBytes()
|
||||||
|
def up = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.just(new JsonRpcResponse(result, null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up1 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> false
|
||||||
|
0 * getId() >> "id"
|
||||||
|
0 * getIngressReader() >> Mock(Reader)
|
||||||
|
}
|
||||||
|
def up2 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> false
|
||||||
|
0 * getId() >> "id"
|
||||||
|
0 * getIngressReader() >> Mock(Reader)
|
||||||
|
}
|
||||||
|
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, Stub(Tracer))
|
||||||
|
when:
|
||||||
|
def act = reader.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNextMatches {
|
||||||
|
it.value == result
|
||||||
|
}
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Return error if all upstreams return error"() {
|
||||||
|
setup:
|
||||||
|
def up = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.error(new JsonRpcException(1, "too low"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up1 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.error(new JsonRpcException(1, "too low"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def up2 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> true
|
||||||
|
_ * getId() >> "id"
|
||||||
|
1 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"])) >>
|
||||||
|
Mono.error(new JsonRpcException(1, "too low"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, Stub(Tracer))
|
||||||
|
when:
|
||||||
|
def act = reader.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectError(JsonRpcException.class)
|
||||||
|
.verify(Duration.ofSeconds(3))
|
||||||
|
}
|
||||||
|
|
||||||
|
def "No response if no available upstreams"() {
|
||||||
|
setup:
|
||||||
|
def up = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> false
|
||||||
|
0 * getId() >> "id"
|
||||||
|
0 * getIngressReader() >> Mock(Reader)
|
||||||
|
}
|
||||||
|
def up1 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> false
|
||||||
|
0 * getId() >> "id"
|
||||||
|
0 * getIngressReader() >> Mock(Reader)
|
||||||
|
}
|
||||||
|
def up2 = Mock(Upstream) {
|
||||||
|
1 * isAvailable() >> false
|
||||||
|
0 * getId() >> "id"
|
||||||
|
0 * getIngressReader() >> Mock(Reader)
|
||||||
|
}
|
||||||
|
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, Stub(Tracer))
|
||||||
|
when:
|
||||||
|
def act = reader
|
||||||
|
.read(new JsonRpcRequest("eth_sendRawTransaction", ["0x1"]))
|
||||||
|
.switchIfEmpty(Mono.just(new RpcReader.Result(new byte[0], null, 0, null)))
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNextMatches {
|
||||||
|
it.value == new byte[0]
|
||||||
|
}
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(3))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,17 +25,12 @@ import io.emeraldpay.dshackle.Global
|
|||||||
import io.emeraldpay.dshackle.config.CacheConfig
|
import io.emeraldpay.dshackle.config.CacheConfig
|
||||||
import io.emeraldpay.dshackle.config.MainConfig
|
import io.emeraldpay.dshackle.config.MainConfig
|
||||||
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReader
|
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
|
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumRpcReader
|
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReader
|
||||||
|
import io.emeraldpay.dshackle.reader.RpcReaderFactory
|
||||||
import io.emeraldpay.dshackle.test.MultistreamHolderMock
|
import io.emeraldpay.dshackle.test.MultistreamHolderMock
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.*
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
|
||||||
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.ManagedCallMethods
|
import io.emeraldpay.dshackle.upstream.calls.ManagedCallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
||||||
@@ -132,9 +127,9 @@ class NativeCallSpec extends Specification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def nativeCall = nativeCall()
|
def nativeCall = nativeCall()
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"foo\"".bytes, null, 1, ups))
|
1 * read(_) >> Mono.just(new RpcReader.Result("\"foo\"".bytes, null, 1, ups))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def call = new NativeCall.ValidCallContext(1, 10, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
def call = new NativeCall.ValidCallContext(1, 10, TestingCommons.multistream(TestingCommons.api()), Selector.empty, quorum,
|
||||||
@@ -153,8 +148,8 @@ class NativeCallSpec extends Specification {
|
|||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
|
|
||||||
def nativeCall = nativeCall()
|
def nativeCall = nativeCall()
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * attempts() >> new AtomicInteger(1)
|
1 * attempts() >> new AtomicInteger(1)
|
||||||
1 * read(new JsonRpcRequest("eth_test", [], 10)) >> Mono.empty()
|
1 * read(new JsonRpcRequest("eth_test", [], 10)) >> Mono.empty()
|
||||||
}
|
}
|
||||||
@@ -178,8 +173,8 @@ class NativeCallSpec extends Specification {
|
|||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
|
|
||||||
def nativeCall = nativeCall()
|
def nativeCall = nativeCall()
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_test", [], 10)) >> Mono.error(
|
1 * read(new JsonRpcRequest("eth_test", [], 10)) >> Mono.error(
|
||||||
new JsonRpcException(JsonRpcResponse.Id.from(12), new JsonRpcError(-32123, "Foo Bar", "Foo Bar Baz"), null, true)
|
new JsonRpcException(JsonRpcResponse.Id.from(12), new JsonRpcError(-32123, "Foo Bar", "Foo Bar Baz"), null, true)
|
||||||
)
|
)
|
||||||
@@ -615,9 +610,9 @@ class NativeCallSpec extends Specification {
|
|||||||
_ * it.observeChains() >> Flux.empty()
|
_ * it.observeChains() >> Flux.empty()
|
||||||
}
|
}
|
||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||||
@@ -650,9 +645,9 @@ class NativeCallSpec extends Specification {
|
|||||||
_ * it.observeChains() >> Flux.empty()
|
_ * it.observeChains() >> Flux.empty()
|
||||||
}
|
}
|
||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
1 * read(_) >> Mono.just(new RpcReader.Result("\"0xab\"".bytes, null, 1, ups))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||||
|
|||||||
@@ -5,22 +5,17 @@ import io.emeraldpay.dshackle.Global
|
|||||||
import io.emeraldpay.dshackle.cache.Caches
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
import io.emeraldpay.dshackle.cache.CurrentBlockCache
|
import io.emeraldpay.dshackle.cache.CurrentBlockCache
|
||||||
import io.emeraldpay.dshackle.data.DefaultContainer
|
import io.emeraldpay.dshackle.data.DefaultContainer
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReader
|
import io.emeraldpay.dshackle.reader.RpcReader
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
|
import io.emeraldpay.dshackle.reader.RpcReaderFactory
|
||||||
import io.emeraldpay.dshackle.quorum.QuorumRpcReader
|
|
||||||
import io.emeraldpay.dshackle.test.TestingCommons
|
import io.emeraldpay.dshackle.test.TestingCommons
|
||||||
import io.emeraldpay.dshackle.upstream.ApiSource
|
import io.emeraldpay.dshackle.upstream.*
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||||
|
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.etherjar.domain.Address
|
import io.emeraldpay.etherjar.domain.Address
|
||||||
import io.emeraldpay.etherjar.domain.BlockHash
|
import io.emeraldpay.etherjar.domain.BlockHash
|
||||||
import io.emeraldpay.etherjar.domain.TransactionId
|
import io.emeraldpay.etherjar.domain.TransactionId
|
||||||
import io.emeraldpay.etherjar.domain.Wei
|
import io.emeraldpay.etherjar.domain.Wei
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
|
|
||||||
import io.emeraldpay.etherjar.rpc.json.TransactionJson
|
import io.emeraldpay.etherjar.rpc.json.TransactionJson
|
||||||
import io.emeraldpay.etherjar.rpc.json.TransactionReceiptJson
|
import io.emeraldpay.etherjar.rpc.json.TransactionReceiptJson
|
||||||
import org.apache.commons.collections4.Factory
|
import org.apache.commons.collections4.Factory
|
||||||
@@ -47,19 +42,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
parentHash = BlockHash.from(hash1)
|
parentHash = BlockHash.from(hash1)
|
||||||
transactions = []
|
transactions = []
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _,) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -77,19 +69,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Produce empty result on non-existing block"() {
|
def "Produce empty result on non-existing block"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -113,21 +102,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
parentHash = BlockHash.from(hash1)
|
parentHash = BlockHash.from(hash1)
|
||||||
transactions = []
|
transactions = []
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(
|
|
||||||
new Selector.Builder().withMatcher(new Selector.HeightMatcher(100)).forMethod("eth_getBlockByNumber").build()
|
|
||||||
) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -151,19 +135,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
blockNumber = 100
|
blockNumber = 100
|
||||||
blockHash = BlockHash.from(hash1)
|
blockHash = BlockHash.from(hash1)
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -187,19 +168,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
blockNumber = 100
|
blockNumber = 100
|
||||||
blockHash = BlockHash.from(hash1)
|
blockHash = BlockHash.from(hash1)
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -220,9 +198,6 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
blockNumber = 100
|
blockNumber = 100
|
||||||
blockHash = BlockHash.from(hash1)
|
blockHash = BlockHash.from(hash1)
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
@@ -231,12 +206,12 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * cacheReceipt(Caches.Tag.REQUESTED, { DefaultContainer data -> data.txId.toHex() == hash1.substring(2) && data.height == 100 })
|
1 * cacheReceipt(Caches.Tag.REQUESTED, { DefaultContainer data -> data.txId.toHex() == hash1.substring(2) && data.height == 100 })
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, caches, new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), caches, new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -252,19 +227,16 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
|
|
||||||
def "Produce empty on non-existing tx"() {
|
def "Produce empty on non-existing tx"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(Multistream) {
|
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -281,7 +253,6 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
def "Reads balance - height is unknown"() {
|
def "Reads balance - height is unknown"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(Multistream) {
|
def up = Mock(Multistream) {
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
1 * getHead() >> Mock(Head) {
|
1 * getHead() >> Mock(Head) {
|
||||||
1 * getCurrentHeight() >> null
|
1 * getCurrentHeight() >> null
|
||||||
}
|
}
|
||||||
@@ -292,10 +263,10 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -313,7 +284,6 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
def "Reads balance - height is known"() {
|
def "Reads balance - height is known"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(Multistream) {
|
def up = Mock(Multistream) {
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
1 * getHead() >> Mock(Head) {
|
1 * getHead() >> Mock(Head) {
|
||||||
1 * getCurrentHeight() >> 11_061_691
|
1 * getCurrentHeight() >> 11_061_691
|
||||||
}
|
}
|
||||||
@@ -324,10 +294,10 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -352,25 +322,22 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
totalDifficulty = BigInteger.ONE
|
totalDifficulty = BigInteger.ONE
|
||||||
transactions = []
|
transactions = []
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
3 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
3 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
3 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
ethereumDirectReader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
ethereumDirectReader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
2 * create(_, _, _, _) >> Mock(QuorumReader) {
|
2 * create(_) >> Mock(RpcReader) {
|
||||||
2 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >>>
|
2 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >>>
|
||||||
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
||||||
}
|
}
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> result
|
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -395,25 +362,22 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
parentHash = BlockHash.from(hash1)
|
parentHash = BlockHash.from(hash1)
|
||||||
transactions = []
|
transactions = []
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
|
||||||
3 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
}
|
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
3 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
3 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
|
||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new RpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
ethereumDirectReader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
ethereumDirectReader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
2 * create(_, _, _, _) >> Mock(QuorumReader) {
|
2 * create(_) >> Mock(RpcReader) {
|
||||||
2 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >>>
|
2 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >>>
|
||||||
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
||||||
}
|
}
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_) >> Mock(RpcReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> result
|
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,7 +395,6 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
def "Reads balance with retries - expects an error within 1 sec"() {
|
def "Reads balance with retries - expects an error within 1 sec"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(Multistream) {
|
def up = Mock(Multistream) {
|
||||||
4 * getApiSource(_) >> Stub(ApiSource)
|
|
||||||
1 * getHead() >> Mock(Head) {
|
1 * getHead() >> Mock(Head) {
|
||||||
1 * getCurrentHeight() >> null
|
1 * getCurrentHeight() >> null
|
||||||
}
|
}
|
||||||
@@ -442,8 +405,8 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
EthereumDirectReader reader = new EthereumDirectReader(
|
EthereumDirectReader reader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
)
|
)
|
||||||
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
reader.rpcReaderFactory = Mock(RpcReaderFactory) {
|
||||||
4 * create(_, _, _, _) >> Mock(QuorumReader) {
|
4 * create(_) >> Mock(RpcReader) {
|
||||||
4 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >>>
|
4 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >>>
|
||||||
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException()),
|
[Mono.error(new RuntimeException()), Mono.error(new RuntimeException()),
|
||||||
Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
Mono.error(new RuntimeException()), Mono.error(new RuntimeException())]
|
||||||
|
|||||||
Reference in New Issue
Block a user