Return upstreamId in response for errors too (#254)
This commit is contained in:
@@ -21,7 +21,6 @@ import io.emeraldpay.dshackle.upstream.Upstream
|
|||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
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.signature.ResponseSigner
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
|
||||||
|
|
||||||
open class AlwaysQuorum : CallQuorum {
|
open class AlwaysQuorum : CallQuorum {
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@ open class AlwaysQuorum : CallQuorum {
|
|||||||
private var result: ByteArray? = null
|
private var result: ByteArray? = null
|
||||||
private var rpcError: JsonRpcError? = null
|
private var rpcError: JsonRpcError? = null
|
||||||
private var sig: ResponseSigner.Signature? = null
|
private var sig: ResponseSigner.Signature? = null
|
||||||
private var providedUpstreamId: String? = null
|
private val resolvers = ArrayList<Upstream>()
|
||||||
private val resolvers: MutableCollection<Upstream> = ConcurrentLinkedQueue()
|
|
||||||
|
|
||||||
override fun init(head: Head) {
|
override fun init(head: Head) {
|
||||||
}
|
}
|
||||||
@@ -47,21 +45,15 @@ open class AlwaysQuorum : CallQuorum {
|
|||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProvidedUpstreamId(): String? {
|
|
||||||
return providedUpstreamId
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun record(
|
override fun record(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
result = response
|
result = response
|
||||||
resolved = true
|
resolved = true
|
||||||
sig = signature
|
sig = signature
|
||||||
resolvers.add(upstream)
|
resolvers.add(upstream)
|
||||||
this.providedUpstreamId = providedUpstreamId
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,6 +64,7 @@ open class AlwaysQuorum : CallQuorum {
|
|||||||
) {
|
) {
|
||||||
this.rpcError = error.error
|
this.rpcError = error.error
|
||||||
sig = signature
|
sig = signature
|
||||||
|
resolvers.add(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResult(): ByteArray? {
|
override fun getResult(): ByteArray? {
|
||||||
@@ -82,8 +75,7 @@ open class AlwaysQuorum : CallQuorum {
|
|||||||
return rpcError
|
return rpcError
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResolvedBy(): List<Upstream> =
|
override fun getResolvedBy(): List<Upstream> = resolvers
|
||||||
resolvers.toList()
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Quorum: Accept Any"
|
return "Quorum: Accept Any"
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ open class BroadcastQuorum(
|
|||||||
private var txid: String? = null
|
private var txid: String? = null
|
||||||
private var calls = 0
|
private var calls = 0
|
||||||
private var sig: ResponseSigner.Signature? = null
|
private var sig: ResponseSigner.Signature? = null
|
||||||
private var providedUpstreamId: String? = null
|
|
||||||
|
|
||||||
override fun init(head: Head) {
|
override fun init(head: Head) {
|
||||||
}
|
}
|
||||||
@@ -49,23 +48,17 @@ open class BroadcastQuorum(
|
|||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProvidedUpstreamId(): String? {
|
|
||||||
return providedUpstreamId
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun recordValue(
|
override fun recordValue(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
responseValue: String?,
|
responseValue: String?,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
) {
|
) {
|
||||||
calls++
|
calls++
|
||||||
if (txid == null && responseValue != null) {
|
if (txid == null && responseValue != null) {
|
||||||
txid = responseValue
|
txid = responseValue
|
||||||
sig = signature
|
sig = signature
|
||||||
result = response
|
result = response
|
||||||
this.providedUpstreamId = providedUpstreamId
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,16 +66,15 @@ open class BroadcastQuorum(
|
|||||||
response: ByteArray?,
|
response: ByteArray?,
|
||||||
errorMessage: String?,
|
errorMessage: String?,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
) {
|
) {
|
||||||
// can be "message: known transaction: TXID", "Transaction with the same hash was already imported" or "message: Nonce too low"
|
// can be "message: known transaction: TXID", "Transaction with the same hash was already imported" or "message: Nonce too low"
|
||||||
calls++
|
calls++
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = response
|
result = response
|
||||||
sig = signature
|
sig = signature
|
||||||
this.providedUpstreamId = providedUpstreamId
|
|
||||||
}
|
}
|
||||||
|
resolvers.add(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ interface CallQuorum {
|
|||||||
fun record(
|
fun record(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
): Boolean
|
): Boolean
|
||||||
|
|
||||||
fun record(
|
fun record(
|
||||||
error: JsonRpcException,
|
error: JsonRpcException,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
@@ -42,7 +42,6 @@ interface CallQuorum {
|
|||||||
)
|
)
|
||||||
fun getSignature(): ResponseSigner.Signature?
|
fun getSignature(): ResponseSigner.Signature?
|
||||||
|
|
||||||
fun getProvidedUpstreamId(): String?
|
|
||||||
fun getResult(): ByteArray?
|
fun getResult(): ByteArray?
|
||||||
fun getError(): JsonRpcError?
|
fun getError(): JsonRpcError?
|
||||||
fun getResolvedBy(): Collection<Upstream>
|
fun getResolvedBy(): Collection<Upstream>
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import io.emeraldpay.dshackle.upstream.Upstream
|
|||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
|
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.signature.ResponseSigner
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,8 +34,7 @@ class NotLaggingQuorum(val maxLag: Long = 0) : CallQuorum {
|
|||||||
private val failed = AtomicReference(false)
|
private val failed = AtomicReference(false)
|
||||||
private var rpcError: JsonRpcError? = null
|
private var rpcError: JsonRpcError? = null
|
||||||
private var sig: ResponseSigner.Signature? = null
|
private var sig: ResponseSigner.Signature? = null
|
||||||
private val resolvers: MutableCollection<Upstream> = ConcurrentLinkedQueue()
|
private val resolvers = ArrayList<Upstream>()
|
||||||
private var providedUpstreamId: String? = null
|
|
||||||
|
|
||||||
override fun init(head: Head) {
|
override fun init(head: Head) {
|
||||||
}
|
}
|
||||||
@@ -52,14 +50,12 @@ class NotLaggingQuorum(val maxLag: Long = 0) : CallQuorum {
|
|||||||
override fun record(
|
override fun record(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val lagging = upstream.getLag()?.run { this > maxLag } ?: true
|
val lagging = upstream.getLag()?.run { this > maxLag } ?: true
|
||||||
if (!lagging) {
|
if (!lagging) {
|
||||||
result.set(response)
|
result.set(response)
|
||||||
sig = signature
|
sig = signature
|
||||||
this.providedUpstreamId = providedUpstreamId
|
|
||||||
resolvers.add(upstream)
|
resolvers.add(upstream)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -76,16 +72,13 @@ class NotLaggingQuorum(val maxLag: Long = 0) : CallQuorum {
|
|||||||
if (!lagging && result.get() == null) {
|
if (!lagging && result.get() == null) {
|
||||||
failed.set(true)
|
failed.set(true)
|
||||||
}
|
}
|
||||||
|
resolvers.add(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSignature(): ResponseSigner.Signature? {
|
override fun getSignature(): ResponseSigner.Signature? {
|
||||||
return sig
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProvidedUpstreamId(): String? {
|
|
||||||
return providedUpstreamId
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getResult(): ByteArray {
|
override fun getResult(): ByteArray {
|
||||||
return result.get()
|
return result.get()
|
||||||
}
|
}
|
||||||
@@ -94,8 +87,8 @@ class NotLaggingQuorum(val maxLag: Long = 0) : CallQuorum {
|
|||||||
return rpcError
|
return rpcError
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResolvedBy(): Collection<Upstream> =
|
override fun getResolvedBy(): Collection<Upstream> = resolvers
|
||||||
resolvers.toList()
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Quorum: late <= $maxLag blocks"
|
return "Quorum: late <= $maxLag blocks"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
|||||||
|
|
||||||
class NotNullQuorum : CallQuorum {
|
class NotNullQuorum : CallQuorum {
|
||||||
private var sig: ResponseSigner.Signature? = null
|
private var sig: ResponseSigner.Signature? = null
|
||||||
private var providedUpstreamId: String? = null
|
|
||||||
private var result: ByteArray? = null
|
private var result: ByteArray? = null
|
||||||
private var rpcError: JsonRpcError? = null
|
private var rpcError: JsonRpcError? = null
|
||||||
private val resolvers = ArrayList<Upstream>()
|
private val resolvers = ArrayList<Upstream>()
|
||||||
@@ -26,8 +25,7 @@ class NotNullQuorum : CallQuorum {
|
|||||||
override fun record(
|
override fun record(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
allFailed = false
|
allFailed = false
|
||||||
val receivedNull = response.isEmpty() || Global.nullValue.contentEquals(response)
|
val receivedNull = response.isEmpty() || Global.nullValue.contentEquals(response)
|
||||||
@@ -35,7 +33,6 @@ class NotNullQuorum : CallQuorum {
|
|||||||
if (seenUpstreams.contains(upId) || !receivedNull) {
|
if (seenUpstreams.contains(upId) || !receivedNull) {
|
||||||
sig = signature
|
sig = signature
|
||||||
result = response
|
result = response
|
||||||
this.providedUpstreamId = providedUpstreamId
|
|
||||||
resolvers.add(upstream)
|
resolvers.add(upstream)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -50,22 +47,20 @@ class NotNullQuorum : CallQuorum {
|
|||||||
rpcError = error.error
|
rpcError = error.error
|
||||||
} else {
|
} else {
|
||||||
result = Global.nullValue
|
result = Global.nullValue
|
||||||
resolvers.add(upstream)
|
|
||||||
}
|
}
|
||||||
sig = signature
|
sig = signature
|
||||||
}
|
}
|
||||||
|
resolvers.add(upstream)
|
||||||
seenUpstreams.add(upId)
|
seenUpstreams.add(upId)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSignature(): ResponseSigner.Signature? = sig
|
override fun getSignature(): ResponseSigner.Signature? = sig
|
||||||
|
|
||||||
override fun getProvidedUpstreamId(): String? = providedUpstreamId
|
|
||||||
|
|
||||||
override fun getResult(): ByteArray? = result
|
override fun getResult(): ByteArray? = result
|
||||||
|
|
||||||
override fun getError(): JsonRpcError? = rpcError
|
override fun getError(): JsonRpcError? = rpcError
|
||||||
|
|
||||||
override fun getResolvedBy(): Collection<Upstream> = resolvers.toList()
|
override fun getResolvedBy(): Collection<Upstream> = resolvers
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Quorum: Not null"
|
return "Quorum: Not null"
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ import org.slf4j.LoggerFactory
|
|||||||
import org.springframework.cloud.sleuth.Tracer
|
import org.springframework.cloud.sleuth.Tracer
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.util.function.Tuple2
|
||||||
import reactor.util.function.Tuple3
|
import reactor.util.function.Tuple3
|
||||||
import reactor.util.function.Tuple4
|
|
||||||
import reactor.util.function.Tuples
|
import reactor.util.function.Tuples
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
@@ -99,8 +99,8 @@ class QuorumRpcReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun execute(key: JsonRpcRequest, retrySpec: reactor.util.retry.Retry): Function<Flux<Upstream>, Mono<CallQuorum>> {
|
private fun execute(key: JsonRpcRequest, retrySpec: reactor.util.retry.Retry): Function<Flux<Upstream>, Mono<CallQuorum>> {
|
||||||
val quorumReduce = BiFunction<CallQuorum, Tuple4<ByteArray, Optional<ResponseSigner.Signature>, Upstream, Optional<String>>, CallQuorum> { res, a ->
|
val quorumReduce = BiFunction<CallQuorum, Tuple3<ByteArray, Optional<ResponseSigner.Signature>, Upstream>, CallQuorum> { res, a ->
|
||||||
if (res.record(a.t1, a.t2.orElse(null), a.t3, a.t4.orElse(null))) {
|
if (res.record(a.t1, a.t2.orElse(null), a.t3)) {
|
||||||
log.trace("Quorum is resolved for method ${key.method}")
|
log.trace("Quorum is resolved for method ${key.method}")
|
||||||
apiControl.resolve()
|
apiControl.resolve()
|
||||||
} else {
|
} else {
|
||||||
@@ -131,13 +131,13 @@ class QuorumRpcReader(
|
|||||||
.filter { it.isResolved() } // return nothing if not resolved
|
.filter { it.isResolved() } // return nothing if not resolved
|
||||||
.map { quorum ->
|
.map { quorum ->
|
||||||
// TODO find actual quorum number
|
// TODO find actual quorum number
|
||||||
Result(quorum.getResult()!!, quorum.getSignature(), 1, quorum.getResolvedBy(), quorum.getProvidedUpstreamId())
|
Result(quorum.getResult()!!, quorum.getSignature(), 1, resolvedBy())
|
||||||
}
|
}
|
||||||
.switchIfEmpty(defaultResult)
|
.switchIfEmpty(defaultResult)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun callApi(api: Upstream, key: JsonRpcRequest): Mono<Tuple4<ByteArray, Optional<ResponseSigner.Signature>, Upstream, Optional<String>>> {
|
private fun callApi(api: Upstream, key: JsonRpcRequest): Mono<Tuple3<ByteArray, Optional<ResponseSigner.Signature>, Upstream>> {
|
||||||
val apiReader = api.getIngressReader()
|
val apiReader = api.getIngressReader()
|
||||||
val spanParams = mapOf(
|
val spanParams = mapOf(
|
||||||
SPAN_REQUEST_API_TYPE to apiReader.javaClass.name,
|
SPAN_REQUEST_API_TYPE to apiReader.javaClass.name,
|
||||||
@@ -152,10 +152,10 @@ class QuorumRpcReader(
|
|||||||
}
|
}
|
||||||
// must catch not only the processing of a response but also errors thrown from the .read() call
|
// must catch not only the processing of a response but also errors thrown from the .read() call
|
||||||
.transform(withErrorResume(api, key))
|
.transform(withErrorResume(api, key))
|
||||||
.map { Tuples.of(it.t1, it.t2, api, it.t3) }
|
.map { Tuples.of(it.t1, it.t2, api) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withSignatureAndUpstream(api: Upstream, key: JsonRpcRequest, response: JsonRpcResponse): Function<Mono<ByteArray>, Mono<Tuple3<ByteArray, Optional<ResponseSigner.Signature>, Optional<String>>>> {
|
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 = response.providedSignature
|
||||||
@@ -164,7 +164,7 @@ class QuorumRpcReader(
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
Tuples.of(it, Optional.ofNullable(signature), Optional.ofNullable(response.providedUpstreamId))
|
Tuples.of(it, Optional.ofNullable(signature))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,8 +201,9 @@ class QuorumRpcReader(
|
|||||||
private fun setupDefaultResult(key: JsonRpcRequest): Mono<Result> {
|
private fun setupDefaultResult(key: JsonRpcRequest): Mono<Result> {
|
||||||
return Mono.just(quorum).flatMap { q ->
|
return Mono.just(quorum).flatMap { q ->
|
||||||
if (q.isFailed()) {
|
if (q.isFailed()) {
|
||||||
val err = q.getError()?.asException(JsonRpcResponse.NumberId(key.id))
|
val resolvedBy = resolvedBy()?.getId()
|
||||||
?: JsonRpcException(JsonRpcResponse.NumberId(key.id), JsonRpcError(-32603, "Unhandled Upstream error"))
|
val err = q.getError()?.asException(JsonRpcResponse.NumberId(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 {
|
||||||
@@ -212,13 +213,16 @@ class QuorumRpcReader(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolvedBy() =
|
||||||
|
if (quorum.getResolvedBy().isEmpty()) null else quorum.getResolvedBy().last()
|
||||||
|
|
||||||
private fun noResponse(method: String, q: CallQuorum): Mono<Result> {
|
private fun noResponse(method: String, q: CallQuorum): Mono<Result> {
|
||||||
return apiControl.upstreamsMatchesResponse()?.run {
|
return apiControl.upstreamsMatchesResponse()?.run {
|
||||||
tracer.currentSpan()?.tag(SPAN_NO_RESPONSE_MESSAGE, getFullCause())
|
tracer.currentSpan()?.tag(SPAN_NO_RESPONSE_MESSAGE, getFullCause())
|
||||||
val cause = getCause(method) ?: return Mono.empty()
|
val cause = getCause(method) ?: return Mono.empty()
|
||||||
if (cause.shouldReturnNull) {
|
if (cause.shouldReturnNull) {
|
||||||
Mono.just(
|
Mono.just(
|
||||||
Result(Global.nullValue, null, 1, emptyList(), null)
|
Result(Global.nullValue, null, 1, null)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Mono.error(RpcException(1, "No response for method $method. Cause - ${cause.cause}"))
|
Mono.error(RpcException(1, "No response for method $method. Cause - ${cause.cause}"))
|
||||||
@@ -230,7 +234,6 @@ class QuorumRpcReader(
|
|||||||
val value: ByteArray,
|
val value: ByteArray,
|
||||||
val signature: ResponseSigner.Signature?,
|
val signature: ResponseSigner.Signature?,
|
||||||
val quorum: Int,
|
val quorum: Int,
|
||||||
val resolvers: Collection<Upstream>,
|
val resolvedBy: Upstream?
|
||||||
val providedUpstreamId: String?
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcException
|
|||||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
import io.emeraldpay.etherjar.rpc.RpcException
|
import io.emeraldpay.etherjar.rpc.RpcException
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
|
||||||
|
|
||||||
abstract class ValueAwareQuorum<T>(
|
abstract class ValueAwareQuorum<T>(
|
||||||
val clazz: Class<T>
|
val clazz: Class<T>
|
||||||
@@ -31,7 +30,7 @@ abstract class ValueAwareQuorum<T>(
|
|||||||
|
|
||||||
private val log = LoggerFactory.getLogger(ValueAwareQuorum::class.java)
|
private val log = LoggerFactory.getLogger(ValueAwareQuorum::class.java)
|
||||||
private var rpcError: JsonRpcError? = null
|
private var rpcError: JsonRpcError? = null
|
||||||
private val resolvers: MutableCollection<Upstream> = ConcurrentLinkedQueue()
|
protected val resolvers = ArrayList<Upstream>()
|
||||||
|
|
||||||
fun extractValue(response: ByteArray, clazz: Class<T>): T? {
|
fun extractValue(response: ByteArray, clazz: Class<T>): T? {
|
||||||
return Global.objectMapper.readValue(response.inputStream(), clazz)
|
return Global.objectMapper.readValue(response.inputStream(), clazz)
|
||||||
@@ -40,17 +39,16 @@ abstract class ValueAwareQuorum<T>(
|
|||||||
override fun record(
|
override fun record(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
): Boolean {
|
): Boolean {
|
||||||
try {
|
try {
|
||||||
val value = extractValue(response, clazz)
|
val value = extractValue(response, clazz)
|
||||||
recordValue(response, value, signature, upstream, providedUpstreamId)
|
recordValue(response, value, signature, upstream)
|
||||||
resolvers.add(upstream)
|
resolvers.add(upstream)
|
||||||
} catch (e: RpcException) {
|
} catch (e: RpcException) {
|
||||||
recordError(response, e.rpcMessage, signature, upstream, providedUpstreamId)
|
recordError(response, e.rpcMessage, signature, upstream)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
recordError(response, e.message, signature, upstream, providedUpstreamId)
|
recordError(response, e.message, signature, upstream)
|
||||||
}
|
}
|
||||||
return isResolved()
|
return isResolved()
|
||||||
}
|
}
|
||||||
@@ -61,29 +59,26 @@ abstract class ValueAwareQuorum<T>(
|
|||||||
upstream: Upstream
|
upstream: Upstream
|
||||||
) {
|
) {
|
||||||
this.rpcError = error.error
|
this.rpcError = error.error
|
||||||
recordError(null, error.error.message, signature, upstream, null)
|
recordError(null, error.error.message, signature, upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun recordValue(
|
abstract fun recordValue(
|
||||||
response: ByteArray,
|
response: ByteArray,
|
||||||
responseValue: T?,
|
responseValue: T?,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
)
|
)
|
||||||
|
|
||||||
abstract fun recordError(
|
abstract fun recordError(
|
||||||
response: ByteArray?,
|
response: ByteArray?,
|
||||||
errorMessage: String?,
|
errorMessage: String?,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
upstream: Upstream,
|
upstream: Upstream
|
||||||
providedUpstreamId: String?
|
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun getError(): JsonRpcError? {
|
override fun getError(): JsonRpcError? {
|
||||||
return rpcError
|
return rpcError
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getResolvedBy(): Collection<Upstream> =
|
override fun getResolvedBy(): Collection<Upstream> = resolvers
|
||||||
resolvers.toList()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import io.emeraldpay.dshackle.commons.LOCAL_READER
|
|||||||
import io.emeraldpay.dshackle.commons.REMOTE_QUORUM_RPC_READER
|
import io.emeraldpay.dshackle.commons.REMOTE_QUORUM_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_RESPONSE_UPSTREAM_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
|
||||||
@@ -131,9 +130,6 @@ open class NativeCall(
|
|||||||
|
|
||||||
private fun completeSpan(callResult: CallResult, requestCount: Int) {
|
private fun completeSpan(callResult: CallResult, requestCount: Int) {
|
||||||
val span = tracer.currentSpan()
|
val span = tracer.currentSpan()
|
||||||
callResult.upstreamId?.let {
|
|
||||||
span?.tag(SPAN_RESPONSE_UPSTREAM_ID, it)
|
|
||||||
}
|
|
||||||
if (callResult.isError()) {
|
if (callResult.isError()) {
|
||||||
errorSpan(span, callResult.error?.message ?: "Internal error")
|
errorSpan(span, callResult.error?.message ?: "Internal error")
|
||||||
}
|
}
|
||||||
@@ -403,9 +399,7 @@ open class NativeCall(
|
|||||||
.map {
|
.map {
|
||||||
val bytes = ctx.resultDecorator.processResult(it)
|
val bytes = ctx.resultDecorator.processResult(it)
|
||||||
validateResult(bytes, "remote", ctx)
|
validateResult(bytes, "remote", ctx)
|
||||||
val upId = it.providedUpstreamId
|
CallResult.ok(ctx.id, ctx.nonce, bytes, it.signature, it.resolvedBy?.getId(), ctx)
|
||||||
?: if (it.resolvers.isEmpty()) ctx.upstream.getId() else it.resolvers.first().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))
|
||||||
@@ -490,10 +484,8 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
override fun processResult(result: QuorumRpcReader.Result): ByteArray {
|
override fun processResult(result: QuorumRpcReader.Result): ByteArray {
|
||||||
val bytes = result.value
|
val bytes = result.value
|
||||||
if (bytes.last() == quoteCode) {
|
if (bytes.last() == quoteCode && result.resolvedBy != null) {
|
||||||
val suffix = result.resolvers
|
val suffix = result.resolvedBy.nodeId()
|
||||||
.map { it.nodeId() }
|
|
||||||
.first()
|
|
||||||
.toUByte()
|
.toUByte()
|
||||||
.toString(16).padStart(2, padChar = '0').toByteArray()
|
.toString(16).padStart(2, padChar = '0').toByteArray()
|
||||||
bytes[bytes.lastIndex] = suffix.first()
|
bytes[bytes.lastIndex] = suffix.first()
|
||||||
@@ -598,7 +590,13 @@ open class NativeCall(
|
|||||||
|
|
||||||
open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}")
|
open class CallFailure(val id: Int, val reason: Throwable) : Exception("Failed to call $id: ${reason.message}")
|
||||||
|
|
||||||
open class CallError(val id: Int, val message: String, val upstreamError: JsonRpcError?, val data: String?) {
|
open class CallError(
|
||||||
|
val id: Int,
|
||||||
|
val message: String,
|
||||||
|
val upstreamError: JsonRpcError?,
|
||||||
|
val data: String?,
|
||||||
|
val upstreamId: String? = null
|
||||||
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
@@ -615,7 +613,7 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
fun from(t: Throwable): CallError {
|
fun from(t: Throwable): CallError {
|
||||||
return when (t) {
|
return when (t) {
|
||||||
is JsonRpcException -> CallError(t.error.code, t.error.message, t.error, getDataAsSting(t.error.details))
|
is JsonRpcException -> CallError(t.error.code, t.error.message, t.error, getDataAsSting(t.error.details), t.upstreamId)
|
||||||
is RpcException -> CallError(t.code, t.rpcMessage, null, getDataAsSting(t.details))
|
is RpcException -> CallError(t.code, t.rpcMessage, null, getDataAsSting(t.details))
|
||||||
is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null, null)
|
is CallFailure -> CallError(t.id, t.reason.message ?: "Upstream Error", null, null)
|
||||||
else -> {
|
else -> {
|
||||||
@@ -641,6 +639,16 @@ open class NativeCall(
|
|||||||
val upstreamId: String?,
|
val upstreamId: String?,
|
||||||
val ctx: ValidCallContext<ParsedCallDetails>?
|
val ctx: ValidCallContext<ParsedCallDetails>?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
id: Int,
|
||||||
|
nonce: Long?,
|
||||||
|
result: ByteArray?,
|
||||||
|
callError: CallError?,
|
||||||
|
signature: ResponseSigner.Signature?,
|
||||||
|
ctx: ValidCallContext<ParsedCallDetails>?
|
||||||
|
) : this(id, nonce, result, callError, signature, callError?.upstreamId, ctx)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun ok(id: Int, nonce: Long?, result: ByteArray, signature: ResponseSigner.Signature?, upstreamId: String?, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
fun ok(id: Int, nonce: Long?, result: ByteArray, signature: ResponseSigner.Signature?, upstreamId: String?, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
||||||
return CallResult(id, nonce, result, null, signature, upstreamId, ctx)
|
return CallResult(id, nonce, result, null, signature, upstreamId, ctx)
|
||||||
@@ -651,7 +659,7 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun fail(id: Int, nonce: Long?, error: Throwable, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
fun fail(id: Int, nonce: Long?, error: Throwable, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
||||||
return CallResult(id, nonce, null, CallError.from(error), null, null, ctx)
|
return CallResult(id, nonce, null, CallError.from(error), null, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ open class WsConnectionImpl(
|
|||||||
RpcResponseError.CODE_INTERNAL_ERROR,
|
RpcResponseError.CODE_INTERNAL_ERROR,
|
||||||
"Response not received from WebSocket"
|
"Response not received from WebSocket"
|
||||||
),
|
),
|
||||||
|
null,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ data class JsonRpcError(val code: Int, val message: String, val details: Any?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun asException(id: JsonRpcResponse.Id?): JsonRpcException {
|
fun asException(id: JsonRpcResponse.Id?): JsonRpcException {
|
||||||
return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this, false)
|
return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this, null, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun asException(id: JsonRpcResponse.Id?, upstreamId: String?): JsonRpcException {
|
||||||
|
return JsonRpcException(id ?: JsonRpcResponse.NumberId(-1), this, upstreamId, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import io.emeraldpay.etherjar.rpc.RpcException
|
|||||||
class JsonRpcException(
|
class JsonRpcException(
|
||||||
val id: JsonRpcResponse.Id,
|
val id: JsonRpcResponse.Id,
|
||||||
val error: JsonRpcError,
|
val error: JsonRpcError,
|
||||||
|
val upstreamId: String? = null,
|
||||||
writableStackTrace: Boolean = true
|
writableStackTrace: Boolean = true
|
||||||
) : Exception(error.message, null, true, writableStackTrace) {
|
) : Exception(error.message, null, true, writableStackTrace) {
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class JsonRpcHttpClient(
|
|||||||
return Function { resp ->
|
return Function { resp ->
|
||||||
resp.flatMap {
|
resp.flatMap {
|
||||||
if (it.hasError()) {
|
if (it.hasError()) {
|
||||||
Mono.error(JsonRpcException(it.id, it.error!!, false))
|
Mono.error(JsonRpcException(it.id, it.error!!, null, false))
|
||||||
} else {
|
} else {
|
||||||
Mono.just(it)
|
Mono.just(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ class WriteRpcJsonSpec extends Specification {
|
|||||||
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
def call = new ProxyCall(ProxyCall.RpcType.SINGLE)
|
||||||
call.ids[1] = 1
|
call.ids[1] = 1
|
||||||
def data = [
|
def data = [
|
||||||
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null, null), null, null, null)
|
new NativeCall.CallResult(1, null, null, new NativeCall.CallError(1, "Internal Error", null, null, null), null, null, null)
|
||||||
]
|
]
|
||||||
when:
|
when:
|
||||||
def act = writer.toJson(call, data[0])
|
def act = writer.toJson(call, data[0])
|
||||||
@@ -127,7 +127,7 @@ class WriteRpcJsonSpec extends Specification {
|
|||||||
call.ids[3] = 15
|
call.ids[3] = 15
|
||||||
def data = [
|
def data = [
|
||||||
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null),
|
new NativeCall.CallResult(1, null, '"0x98dbb1"'.bytes, null, null, null, null),
|
||||||
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null, null), null, null, null),
|
new NativeCall.CallResult(2, null, null, new NativeCall.CallError(2, "oops", null, null, null), null, null, null),
|
||||||
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null),
|
new NativeCall.CallResult(3, null, '{"hash": "0x2484f459dc"}'.bytes, null, null, null, null),
|
||||||
]
|
]
|
||||||
when:
|
when:
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class AlwaysQuorumSpec extends Specification {
|
|||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
def up = Stub(Upstream)
|
def up = Stub(Upstream)
|
||||||
when:
|
when:
|
||||||
quorum.record("123".bytes, new ResponseSigner.Signature("sig1".bytes, "test", 100), up, null)
|
quorum.record("123".bytes, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
then:
|
then:
|
||||||
quorum.isResolved()
|
quorum.isResolved()
|
||||||
quorum.getResult() == "123".bytes
|
quorum.getResult() == "123".bytes
|
||||||
|
|||||||
@@ -40,21 +40,21 @@ class BroadcastQuorumSpec extends Specification {
|
|||||||
!q.isResolved()
|
!q.isResolved()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream1, null)
|
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream1)
|
||||||
then:
|
then:
|
||||||
!q.isResolved()
|
!q.isResolved()
|
||||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _, _)
|
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2, null)
|
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||||
then:
|
then:
|
||||||
!q.isResolved()
|
!q.isResolved()
|
||||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _, _)
|
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||||
then:
|
then:
|
||||||
1 * q.recordError(_, _, _, _, _)
|
1 * q.recordError(_, _, _, _)
|
||||||
q.isResolved()
|
q.isResolved()
|
||||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||||
}
|
}
|
||||||
@@ -75,18 +75,18 @@ class BroadcastQuorumSpec extends Specification {
|
|||||||
q.record(new JsonRpcException(1, "Internal error"), null, upstream1)
|
q.record(new JsonRpcException(1, "Internal error"), null, upstream1)
|
||||||
then:
|
then:
|
||||||
!q.isResolved()
|
!q.isResolved()
|
||||||
1 * q.recordError(_, _, _, _, _)
|
1 * q.recordError(_, _, _, _)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2, null)
|
q.record('"0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"'.bytes, null, upstream2)
|
||||||
then:
|
then:
|
||||||
!q.isResolved()
|
!q.isResolved()
|
||||||
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _, _)
|
1 * q.recordValue(_, "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c", _, _)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
q.record(new JsonRpcException(1, "Nonce too low"), null, upstream3)
|
||||||
then:
|
then:
|
||||||
1 * q.recordError(_, _, _, _, _)
|
1 * q.recordError(_, _, _, _)
|
||||||
q.isResolved()
|
q.isResolved()
|
||||||
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
objectMapper.readValue(q.result, Object) == "0xeaa972c0d8d1ecd3e34fbbef6d34e06670e745c788bdba31c4234a1762f0378c"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class NotLaggingQuorumSpec extends Specification {
|
|||||||
def quorum = new NotLaggingQuorum(1)
|
def quorum = new NotLaggingQuorum(1)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
quorum.record(value, null, up, null)
|
quorum.record(value, null, up)
|
||||||
then:
|
then:
|
||||||
1 * up.getLag() >> 0
|
1 * up.getLag() >> 0
|
||||||
quorum.isResolved()
|
quorum.isResolved()
|
||||||
@@ -45,14 +45,13 @@ class NotLaggingQuorumSpec extends Specification {
|
|||||||
def quorum = new NotLaggingQuorum(1)
|
def quorum = new NotLaggingQuorum(1)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up, "test")
|
quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
then:
|
then:
|
||||||
1 * up.getLag() >> 0
|
1 * up.getLag() >> 0
|
||||||
quorum.isResolved()
|
quorum.isResolved()
|
||||||
!quorum.isFailed()
|
!quorum.isFailed()
|
||||||
quorum.result == value
|
quorum.result == value
|
||||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||||
quorum.providedUpstreamId == "test"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def "Resolves if ok lag"() {
|
def "Resolves if ok lag"() {
|
||||||
@@ -62,7 +61,7 @@ class NotLaggingQuorumSpec extends Specification {
|
|||||||
def quorum = new NotLaggingQuorum(1)
|
def quorum = new NotLaggingQuorum(1)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
quorum.record(value, null, up, null)
|
quorum.record(value, null, up)
|
||||||
then:
|
then:
|
||||||
1 * up.getLag() >> 1
|
1 * up.getLag() >> 1
|
||||||
quorum.isResolved()
|
quorum.isResolved()
|
||||||
@@ -77,7 +76,7 @@ class NotLaggingQuorumSpec extends Specification {
|
|||||||
def quorum = new NotLaggingQuorum(1)
|
def quorum = new NotLaggingQuorum(1)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
quorum.record(value, null, up, null)
|
quorum.record(value, null, up)
|
||||||
then:
|
then:
|
||||||
1 * up.getLag() >> 2
|
1 * up.getLag() >> 2
|
||||||
!quorum.isResolved()
|
!quorum.isResolved()
|
||||||
|
|||||||
@@ -22,10 +22,10 @@ class NotNullQuorumSpec extends Specification {
|
|||||||
def quorum = new NotNullQuorum()
|
def quorum = new NotNullQuorum()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up, "id")
|
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
def res1 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up1, "id1")
|
def res1 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
||||||
def res2 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up2, "id2")
|
def res2 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
||||||
def res3 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up, "id")
|
def res3 = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
then:
|
then:
|
||||||
!res
|
!res
|
||||||
!res1
|
!res1
|
||||||
@@ -35,7 +35,6 @@ class NotNullQuorumSpec extends Specification {
|
|||||||
!quorum.isFailed()
|
!quorum.isFailed()
|
||||||
quorum.isResolved()
|
quorum.isResolved()
|
||||||
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
quorum.signature == new ResponseSigner.Signature("sig1".bytes, "test", 100)
|
||||||
quorum.providedUpstreamId == "id"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def "Failed if all upstreams respond with error"() {
|
def "Failed if all upstreams respond with error"() {
|
||||||
@@ -78,7 +77,7 @@ class NotNullQuorumSpec extends Specification {
|
|||||||
def quorum = new NotNullQuorum()
|
def quorum = new NotNullQuorum()
|
||||||
|
|
||||||
when:
|
when:
|
||||||
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up, "id")
|
def res = quorum.record(value, new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up1)
|
||||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up2)
|
||||||
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
quorum.record(new JsonRpcException(10, "error"), new ResponseSigner.Signature("sig1".bytes, "test", 100), up)
|
||||||
|
|||||||
@@ -66,14 +66,14 @@ class ValueAwareQuorumSpec extends Specification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void recordValue(@NotNull byte[] response, @Nullable Object responseValue, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream, @Nullable String providedUpstreamId) {
|
void recordValue(@NotNull byte[] response, @Nullable Object responseValue, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void recordError(@Nullable byte[] response, @Nullable String errorMessage, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream, @Nullable String providedUpstreamId) {
|
void recordError(@Nullable byte[] response, @Nullable String errorMessage, @Nullable ResponseSigner.Signature signature, @NotNull Upstream upstream) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,10 +101,5 @@ class ValueAwareQuorumSpec extends Specification {
|
|||||||
boolean isFailed() {
|
boolean isFailed() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
String getProvidedUpstreamId() {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall()
|
def nativeCall = nativeCall()
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"foo\"".bytes, null, 1, Collections.singletonList(ups), null))
|
1 * read(_) >> Mono.just(new QuorumRpcReader.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,
|
||||||
@@ -181,7 +181,7 @@ class NativeCallSpec extends Specification {
|
|||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
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"), true)
|
new JsonRpcException(JsonRpcResponse.Id.from(12), new JsonRpcError(-32123, "Foo Bar", "Foo Bar Baz"), null, true)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -617,7 +617,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"0xab\"".bytes, null, 1, Collections.singletonList(ups), null))
|
1 * read(_) >> Mono.just(new QuorumRpcReader.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,
|
||||||
@@ -652,7 +652,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
nativeCall.quorumReaderFactory = Mock(QuorumReaderFactory) {
|
||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(_) >> Mono.just(new QuorumRpcReader.Result("\"0xab\"".bytes, null, 1, Collections.singletonList(ups), null))
|
1 * read(_) >> Mono.just(new QuorumRpcReader.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,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import io.emeraldpay.dshackle.upstream.ApiSource
|
|||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
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.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||||
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
|
||||||
@@ -34,7 +35,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
|
|
||||||
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
|
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
|
||||||
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
|
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
|
||||||
List<Byte> resolvers = Collections.singletonList((byte)1)
|
Upstream resolver = TestingCommons.upstream()
|
||||||
|
|
||||||
def "Reads block by hash"() {
|
def "Reads block by hash"() {
|
||||||
setup:
|
setup:
|
||||||
@@ -59,7 +60,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _,) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _,) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolvers, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,7 +90,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -127,7 +128,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -163,7 +164,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -199,7 +200,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, new ArrayList<Byte>(), null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -236,7 +237,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionReceipt", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, new ArrayList<Byte>(), null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -264,7 +265,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -295,7 +296,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -327,7 +328,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
1 * create(_, _, _, _) >> Mock(QuorumReader) {
|
||||||
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolvers, null
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -359,7 +360,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolvers, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
@@ -402,7 +403,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new QuorumRpcReader.Result(
|
new QuorumRpcReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolvers, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
|
|||||||
Reference in New Issue
Block a user