Detect and send node version in api (#445)
This commit is contained in:
Submodule emerald-grpc updated: 398c3dd5b4...ca4852a4a4
@@ -208,7 +208,7 @@ class QuorumRequestReader(
|
|||||||
private fun setupDefaultResult(key: ChainRequest): Mono<Result> {
|
private fun setupDefaultResult(key: ChainRequest): Mono<Result> {
|
||||||
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()
|
||||||
val err = handleError(q.getError(), key.id, resolvedBy)
|
val err = handleError(q.getError(), key.id, resolvedBy)
|
||||||
log.debug("Quorum is failed. Method ${key.method}, message ${err.message}")
|
log.debug("Quorum is failed. Method ${key.method}, message ${err.message}")
|
||||||
Mono.error(err)
|
Mono.error(err)
|
||||||
@@ -220,7 +220,7 @@ class QuorumRequestReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun resolvedBy() =
|
private fun resolvedBy() =
|
||||||
if (quorum.getResolvedBy().isEmpty()) null else quorum.getResolvedBy().last()
|
if (quorum.getResolvedBy().isEmpty()) null else quorum.getResolvedBy().last().getUpstreamSettingsData()
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class BroadcastReader(
|
|||||||
val sig = getSignature(key, it.jsonRpcResponse, it.upstream.getId())
|
val sig = getSignature(key, it.jsonRpcResponse, it.upstream.getId())
|
||||||
quorum.record(it.jsonRpcResponse, sig, it.upstream)
|
quorum.record(it.jsonRpcResponse, sig, it.upstream)
|
||||||
} else {
|
} else {
|
||||||
val err = ChainException(ChainResponse.NumberId(key.id), it.jsonRpcResponse.error!!, it.upstream.getId())
|
val err = ChainException(ChainResponse.NumberId(key.id), it.jsonRpcResponse.error!!, it.upstream.getUpstreamSettingsData())
|
||||||
quorum.record(err, null, it.upstream)
|
quorum.record(err, null, it.upstream)
|
||||||
}
|
}
|
||||||
quorum
|
quorum
|
||||||
@@ -58,7 +58,7 @@ class BroadcastReader(
|
|||||||
quorum.getResponse()!!.getResult(),
|
quorum.getResponse()!!.getResult(),
|
||||||
quorum.getSignature(),
|
quorum.getSignature(),
|
||||||
upstreams.size,
|
upstreams.size,
|
||||||
quorum.getResolvedBy().first(),
|
quorum.getResolvedBy().first().getUpstreamSettingsData(),
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
Mono.just(res)
|
Mono.just(res)
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ abstract class RequestReader(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun handleError(error: ChainCallError?, id: Int, resolvedBy: String?) =
|
protected fun handleError(error: ChainCallError?, id: Int, upstreamSettingsData: Upstream.UpstreamSettingsData?) =
|
||||||
error?.asException(ChainResponse.NumberId(id), resolvedBy)
|
error?.asException(ChainResponse.NumberId(id), upstreamSettingsData)
|
||||||
?: ChainException(ChainResponse.NumberId(id), ChainCallError(-32603, "Unhandled Upstream error"), resolvedBy)
|
?: ChainException(ChainResponse.NumberId(id), ChainCallError(-32603, "Unhandled Upstream error"), upstreamSettingsData)
|
||||||
|
|
||||||
protected fun getSignature(key: ChainRequest, response: ChainResponse, upstreamId: String) =
|
protected fun getSignature(key: ChainRequest, response: ChainResponse, upstreamId: String) =
|
||||||
response.providedSignature
|
response.providedSignature
|
||||||
@@ -50,7 +50,7 @@ abstract class RequestReader(
|
|||||||
val value: ByteArray,
|
val value: ByteArray,
|
||||||
val signature: ResponseSigner.Signature?,
|
val signature: ResponseSigner.Signature?,
|
||||||
val quorum: Int,
|
val quorum: Int,
|
||||||
val resolvedBy: Upstream?,
|
val resolvedUpstreamData: Upstream.UpstreamSettingsData?,
|
||||||
val stream: Flux<Chunk>?,
|
val stream: Flux<Chunk>?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
||||||
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.ethereum.rpc.RpcException
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcResponseError
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcResponseError
|
||||||
@@ -122,7 +123,8 @@ open class NativeCall(
|
|||||||
Mono.just(firstChunk)
|
Mono.just(firstChunk)
|
||||||
.map {
|
.map {
|
||||||
buildStreamResult(it, callResult.id)
|
buildStreamResult(it, callResult.id)
|
||||||
.setUpstreamId(callResult.upstreamId)
|
.setUpstreamId(callResult.upstreamSettingsData?.id)
|
||||||
|
.setUpstreamNodeVersion(callResult.upstreamSettingsData?.nodeVersion)
|
||||||
.build()
|
.build()
|
||||||
},
|
},
|
||||||
stream.skip(1).map { buildStreamResult(it, callResult.id).build() },
|
stream.skip(1).map { buildStreamResult(it, callResult.id).build() },
|
||||||
@@ -224,7 +226,10 @@ open class NativeCall(
|
|||||||
if (it.nonce != null && it.signature != null) {
|
if (it.nonce != null && it.signature != null) {
|
||||||
result.signature = buildSignature(it.nonce, it.signature)
|
result.signature = buildSignature(it.nonce, it.signature)
|
||||||
}
|
}
|
||||||
it.upstreamId ?.let { result.upstreamId = it }
|
it.upstreamSettingsData?.let {
|
||||||
|
result.upstreamId = it.id
|
||||||
|
result.upstreamNodeVersion = it.nodeVersion
|
||||||
|
}
|
||||||
return result.build()
|
return result.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,12 +411,12 @@ open class NativeCall(
|
|||||||
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, false))
|
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, false))
|
||||||
.map {
|
.map {
|
||||||
val result = it.getResult()
|
val result = it.getResult()
|
||||||
val upstreamId = it.providedUpstreamId ?: ctx.upstream.getId()
|
val resolvedUpstreamData = it.resolvedUpstreamData ?: ctx.upstream.getUpstreamSettingsData()
|
||||||
validateResult(result, "local", ctx)
|
validateResult(result, "local", ctx)
|
||||||
if (ctx.nonce != null) {
|
if (ctx.nonce != null) {
|
||||||
CallResult.ok(ctx.id, ctx.nonce, result, signer.sign(ctx.nonce, result, upstreamId), upstreamId, ctx)
|
CallResult.ok(ctx.id, ctx.nonce, result, signer.sign(ctx.nonce, result, resolvedUpstreamData?.id ?: ctx.upstream.getId()), resolvedUpstreamData, ctx)
|
||||||
} else {
|
} else {
|
||||||
CallResult.ok(ctx.id, null, result, null, upstreamId, ctx)
|
CallResult.ok(ctx.id, null, result, null, resolvedUpstreamData, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.switchIfEmpty(
|
}.switchIfEmpty(
|
||||||
@@ -435,13 +440,13 @@ open class NativeCall(
|
|||||||
return SpannedReader(reader, tracer, RPC_READER)
|
return SpannedReader(reader, tracer, RPC_READER)
|
||||||
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, ctx.streamRequest))
|
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, ctx.streamRequest))
|
||||||
.map {
|
.map {
|
||||||
val upId = it.resolvedBy?.getId() ?: ctx.upstream.getId()
|
val resolvedUpstreamData = it.resolvedUpstreamData ?: ctx.upstream.getUpstreamSettingsData()
|
||||||
if (it.stream == null) {
|
if (it.stream == null) {
|
||||||
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, upId, ctx)
|
CallResult.ok(ctx.id, ctx.nonce, bytes, it.signature, resolvedUpstreamData, ctx)
|
||||||
} else {
|
} else {
|
||||||
CallResult.ok(ctx.id, ctx.nonce, ByteArray(0), it.signature, upId, ctx, it.stream)
|
CallResult.ok(ctx.id, ctx.nonce, ByteArray(0), it.signature, resolvedUpstreamData, ctx, it.stream)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onErrorResume { t ->
|
.onErrorResume { t ->
|
||||||
@@ -533,8 +538,8 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
override fun processResult(result: RequestReader.Result): ByteArray {
|
override fun processResult(result: RequestReader.Result): ByteArray {
|
||||||
val bytes = result.value
|
val bytes = result.value
|
||||||
if (bytes.last() == quoteCode && result.resolvedBy != null) {
|
if (bytes.last() == quoteCode && result.resolvedUpstreamData != null) {
|
||||||
val suffix = result.resolvedBy.nodeId()
|
val suffix = result.resolvedUpstreamData.nodeId
|
||||||
.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()
|
||||||
@@ -648,7 +653,7 @@ open class NativeCall(
|
|||||||
val message: String,
|
val message: String,
|
||||||
val upstreamError: ChainCallError?,
|
val upstreamError: ChainCallError?,
|
||||||
val data: String?,
|
val data: String?,
|
||||||
val upstreamId: String? = null,
|
val upstreamSettingsData: Upstream.UpstreamSettingsData? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -666,7 +671,7 @@ open class NativeCall(
|
|||||||
}
|
}
|
||||||
fun from(t: Throwable): CallError {
|
fun from(t: Throwable): CallError {
|
||||||
return when (t) {
|
return when (t) {
|
||||||
is ChainException -> CallError(t.error.code, t.error.message, t.error, getDataAsSting(t.error.details), t.upstreamId)
|
is ChainException -> CallError(t.error.code, t.error.message, t.error, getDataAsSting(t.error.details), t.upstreamSettingsData)
|
||||||
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 -> {
|
||||||
@@ -689,7 +694,7 @@ open class NativeCall(
|
|||||||
val result: ByteArray?,
|
val result: ByteArray?,
|
||||||
val error: CallError?,
|
val error: CallError?,
|
||||||
val signature: ResponseSigner.Signature?,
|
val signature: ResponseSigner.Signature?,
|
||||||
val upstreamId: String?,
|
val upstreamSettingsData: Upstream.UpstreamSettingsData?,
|
||||||
val ctx: ValidCallContext<ParsedCallDetails>?,
|
val ctx: ValidCallContext<ParsedCallDetails>?,
|
||||||
val stream: Flux<Chunk>? = null,
|
val stream: Flux<Chunk>? = null,
|
||||||
) {
|
) {
|
||||||
@@ -701,15 +706,15 @@ open class NativeCall(
|
|||||||
callError: CallError?,
|
callError: CallError?,
|
||||||
signature: ResponseSigner.Signature?,
|
signature: ResponseSigner.Signature?,
|
||||||
ctx: ValidCallContext<ParsedCallDetails>?,
|
ctx: ValidCallContext<ParsedCallDetails>?,
|
||||||
) : this(id, nonce, result, callError, signature, callError?.upstreamId, ctx)
|
) : this(id, nonce, result, callError, signature, callError?.upstreamSettingsData, 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?, upstreamSettingsData: Upstream.UpstreamSettingsData?, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
||||||
return CallResult(id, nonce, result, null, signature, upstreamId, ctx)
|
return CallResult(id, nonce, result, null, signature, upstreamSettingsData, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ok(id: Int, nonce: Long?, result: ByteArray, signature: ResponseSigner.Signature?, upstreamId: String?, ctx: ValidCallContext<ParsedCallDetails>?, stream: Flux<Chunk>?): CallResult {
|
fun ok(id: Int, nonce: Long?, result: ByteArray, signature: ResponseSigner.Signature?, upstreamSettingsData: Upstream.UpstreamSettingsData?, ctx: ValidCallContext<ParsedCallDetails>?, stream: Flux<Chunk>?): CallResult {
|
||||||
return CallResult(id, nonce, result, null, signature, upstreamId, ctx, stream)
|
return CallResult(id, nonce, result, null, signature, upstreamSettingsData, ctx, stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fail(id: Int, nonce: Long?, error: CallError, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
fun fail(id: Int, nonce: Long?, error: CallError, ctx: ValidCallContext<ParsedCallDetails>?): CallResult {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ open class GenericUpstreamCreator(
|
|||||||
chainConfig,
|
chainConfig,
|
||||||
connectorFactory,
|
connectorFactory,
|
||||||
cs::validator,
|
cs::validator,
|
||||||
cs::labelDetector,
|
cs::upstreamSettingsDetector,
|
||||||
cs::lowerBoundBlockDetector,
|
cs::lowerBoundBlockDetector,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ data class ChainCallError(val code: Int, val message: String, val details: Any?)
|
|||||||
return ChainCallUpstreamException(id ?: ChainResponse.NumberId(-1), this)
|
return ChainCallUpstreamException(id ?: ChainResponse.NumberId(-1), this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asException(id: ChainResponse.Id?, upstreamId: String?): ChainException {
|
fun asException(id: ChainResponse.Id?, upstreamSettingsData: Upstream.UpstreamSettingsData?): ChainException {
|
||||||
return ChainException(id ?: ChainResponse.NumberId(-1), this, upstreamId, false)
|
return ChainException(id ?: ChainResponse.NumberId(-1), this, upstreamSettingsData, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
|||||||
open class ChainException(
|
open class ChainException(
|
||||||
val id: ChainResponse.Id,
|
val id: ChainResponse.Id,
|
||||||
val error: ChainCallError,
|
val error: ChainCallError,
|
||||||
val upstreamId: String? = null,
|
val upstreamSettingsData: Upstream.UpstreamSettingsData? = null,
|
||||||
writableStackTrace: Boolean = true,
|
writableStackTrace: Boolean = true,
|
||||||
cause: Throwable? = null,
|
cause: Throwable? = null,
|
||||||
) : Exception(error.message, cause, true, writableStackTrace) {
|
) : Exception(error.message, cause, true, writableStackTrace) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class ChainResponse(
|
|||||||
* When making a request through Dshackle protocol a remote may provide its signature with the response, which we keep here
|
* When making a request through Dshackle protocol a remote may provide its signature with the response, which we keep here
|
||||||
*/
|
*/
|
||||||
val providedSignature: ResponseSigner.Signature? = null,
|
val providedSignature: ResponseSigner.Signature? = null,
|
||||||
val providedUpstreamId: String? = null,
|
val resolvedUpstreamData: Upstream.UpstreamSettingsData? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
constructor(stream: Flux<Chunk>, id: Int) :
|
constructor(stream: Flux<Chunk>, id: Int) :
|
||||||
@@ -40,8 +40,8 @@ class ChainResponse(
|
|||||||
|
|
||||||
constructor(result: ByteArray?, error: ChainCallError?) : this(result, error, NumberId(0), null)
|
constructor(result: ByteArray?, error: ChainCallError?) : this(result, error, NumberId(0), null)
|
||||||
|
|
||||||
constructor(result: ByteArray?, error: ChainCallError?, resolvedBy: String?) :
|
constructor(result: ByteArray?, error: ChainCallError?, resolvedUpstreamData: Upstream.UpstreamSettingsData?) :
|
||||||
this(result, error, NumberId(0), null, null, resolvedBy)
|
this(result, error, NumberId(0), null, null, resolvedUpstreamData)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val NULL_VALUE = "null".toByteArray()
|
private val NULL_VALUE = "null".toByteArray()
|
||||||
@@ -135,7 +135,7 @@ class ChainResponse(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun copyWithId(id: Id): ChainResponse {
|
fun copyWithId(id: Id): ChainResponse {
|
||||||
return ChainResponse(result, error, id, stream, providedSignature, providedUpstreamId)
|
return ChainResponse(result, error, id, stream, providedSignature, resolvedUpstreamData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
|
|||||||
@@ -334,6 +334,14 @@ abstract class Multistream(
|
|||||||
|
|
||||||
override fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData = lowerBlock
|
override fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData = lowerBlock
|
||||||
|
|
||||||
|
override fun getUpstreamSettingsData(): Upstream.UpstreamSettingsData? {
|
||||||
|
return Upstream.UpstreamSettingsData(
|
||||||
|
nodeId(),
|
||||||
|
getId(),
|
||||||
|
UNKNOWN_CLIENT_VERSION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeUpstreamsStatuses() {
|
private fun observeUpstreamsStatuses() {
|
||||||
subscribeAddedUpstreams()
|
subscribeAddedUpstreams()
|
||||||
.flatMap { upstream ->
|
.flatMap { upstream ->
|
||||||
|
|||||||
@@ -45,8 +45,17 @@ interface Upstream : Lifecycle {
|
|||||||
fun getCapabilities(): Set<Capability>
|
fun getCapabilities(): Set<Capability>
|
||||||
fun isGrpc(): Boolean
|
fun isGrpc(): Boolean
|
||||||
fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData
|
fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData
|
||||||
|
fun getUpstreamSettingsData(): UpstreamSettingsData?
|
||||||
|
|
||||||
fun <T : Upstream> cast(selfType: Class<T>): T
|
fun <T : Upstream> cast(selfType: Class<T>): T
|
||||||
|
|
||||||
fun nodeId(): Byte
|
fun nodeId(): Byte
|
||||||
|
|
||||||
|
data class UpstreamSettingsData(
|
||||||
|
val nodeId: Byte,
|
||||||
|
val id: String,
|
||||||
|
val nodeVersion: String,
|
||||||
|
) {
|
||||||
|
constructor(id: String) : this(0, id, UNKNOWN_CLIENT_VERSION)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,25 +4,45 @@ import com.fasterxml.jackson.databind.JsonNode
|
|||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.reader.ChainReader
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
typealias LabelsDetectorBuilder = (Chain, ChainReader) -> LabelsDetector?
|
const val UNKNOWN_CLIENT_VERSION = "unknown"
|
||||||
interface LabelsDetector {
|
|
||||||
fun detectLabels(): Flux<Pair<String, String>>
|
typealias UpstreamSettingsDetectorBuilder = (Chain, Upstream) -> UpstreamSettingsDetector?
|
||||||
|
abstract class UpstreamSettingsDetector(
|
||||||
|
private val upstream: Upstream,
|
||||||
|
) {
|
||||||
|
protected val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
|
abstract fun detectLabels(): Flux<Pair<String, String>>
|
||||||
|
|
||||||
|
fun detectClientVersion(): Mono<String> {
|
||||||
|
return upstream.getIngressReader()
|
||||||
|
.read(clientVersionRequest())
|
||||||
|
.flatMap(ChainResponse::requireResult)
|
||||||
|
.map(::parseClientVersion)
|
||||||
|
.onErrorResume {
|
||||||
|
log.warn("Can't detect the client version of upstream ${upstream.getId()}, reason - {}", it.message)
|
||||||
|
Mono.just(UNKNOWN_CLIENT_VERSION)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun clientVersionRequest(): ChainRequest
|
||||||
|
|
||||||
|
protected abstract fun parseClientVersion(data: ByteArray): String
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BasicEthLabelsDetector(
|
abstract class BasicEthUpstreamSettingsDetector(
|
||||||
private val reader: ChainReader,
|
private val upstream: Upstream,
|
||||||
) : LabelsDetector {
|
) : UpstreamSettingsDetector(upstream) {
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
|
||||||
|
|
||||||
protected abstract fun nodeTypeRequest(): NodeTypeRequest
|
protected abstract fun nodeTypeRequest(): NodeTypeRequest
|
||||||
|
|
||||||
protected fun detectNodeType(): Flux<Pair<String, String>?> {
|
protected fun detectNodeType(): Flux<Pair<String, String>?> {
|
||||||
val nodeTypeRequest = nodeTypeRequest()
|
val nodeTypeRequest = nodeTypeRequest()
|
||||||
return reader
|
return upstream
|
||||||
|
.getIngressReader()
|
||||||
.read(nodeTypeRequest.request)
|
.read(nodeTypeRequest.request)
|
||||||
.flatMap(ChainResponse::requireResult)
|
.flatMap(ChainResponse::requireResult)
|
||||||
.map { Global.objectMapper.readValue<JsonNode>(it) }
|
.map { Global.objectMapper.readValue<JsonNode>(it) }
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.beaconchain
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.node.NullNode
|
|
||||||
import io.emeraldpay.dshackle.reader.ChainReader
|
|
||||||
import io.emeraldpay.dshackle.upstream.BasicEthLabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.RestParams
|
|
||||||
import reactor.core.publisher.Flux
|
|
||||||
|
|
||||||
class BeaconChainLabelsDetector(
|
|
||||||
reader: ChainReader,
|
|
||||||
) : BasicEthLabelsDetector(reader) {
|
|
||||||
|
|
||||||
override fun nodeTypeRequest(): NodeTypeRequest {
|
|
||||||
return NodeTypeRequest(
|
|
||||||
ChainRequest("GET#/eth/v1/node/version", RestParams.emptyParams()),
|
|
||||||
) { node ->
|
|
||||||
node.get("data")?.get("version") ?: NullNode.instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
|
||||||
return Flux.merge(
|
|
||||||
detectNodeType(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,11 +11,10 @@ import io.emeraldpay.dshackle.config.ChainsConfig
|
|||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
import io.emeraldpay.dshackle.foundation.ChainOptions
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.reader.ChainReader
|
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.RestParams
|
import io.emeraldpay.dshackle.upstream.rpcclient.RestParams
|
||||||
@@ -56,8 +55,8 @@ object BeaconChainSpecific : AbstractPollChainSpecific() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun labelDetector(chain: Chain, reader: ChainReader): LabelsDetector {
|
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector {
|
||||||
return BeaconChainLabelsDetector(reader)
|
return BeaconChainUpstreamSettingsDetector(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validator(
|
override fun validator(
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.beaconchain
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
|
import com.fasterxml.jackson.databind.node.NullNode
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.upstream.BasicEthUpstreamSettingsDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.UNKNOWN_CLIENT_VERSION
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.RestParams
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
|
|
||||||
|
class BeaconChainUpstreamSettingsDetector(
|
||||||
|
upstream: Upstream,
|
||||||
|
) : BasicEthUpstreamSettingsDetector(upstream) {
|
||||||
|
|
||||||
|
override fun nodeTypeRequest(): NodeTypeRequest {
|
||||||
|
return NodeTypeRequest(
|
||||||
|
clientVersionRequest(),
|
||||||
|
) { node ->
|
||||||
|
node.get("data")?.get("version") ?: NullNode.instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun detectLabels(): Flux<Pair<String, String>> {
|
||||||
|
return Flux.merge(
|
||||||
|
detectNodeType(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clientVersionRequest(): ChainRequest {
|
||||||
|
return ChainRequest("GET#/eth/v1/node/version", RestParams.emptyParams())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseClientVersion(data: ByteArray): String {
|
||||||
|
val node = Global.objectMapper.readValue<JsonNode>(data)
|
||||||
|
|
||||||
|
return node.get("data")?.get("version")?.textValue() ?: UNKNOWN_CLIENT_VERSION
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,10 @@ open class BitcoinRpcUpstream(
|
|||||||
return LowerBoundBlockDetector.LowerBlockData.default()
|
return LowerBoundBlockDetector.LowerBlockData.default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUpstreamSettingsData(): Upstream.UpstreamSettingsData? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
||||||
if (!selfType.isAssignableFrom(this.javaClass)) {
|
if (!selfType.isAssignableFrom(this.javaClass)) {
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.LogsOracle
|
import io.emeraldpay.dshackle.upstream.LogsOracle
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
||||||
@@ -95,8 +95,8 @@ object EthereumChainSpecific : AbstractPollChainSpecific() {
|
|||||||
return EthereumLowerBoundBlockDetector(chain, upstream)
|
return EthereumLowerBoundBlockDetector(chain, upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun labelDetector(chain: Chain, reader: ChainReader): LabelsDetector {
|
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector {
|
||||||
return EthereumLabelsDetector(reader, chain)
|
return EthereumUpstreamSettingsDetector(upstream, chain)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
|
override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import io.emeraldpay.dshackle.upstream.ChainException
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
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.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.EthereumCallSelector
|
import io.emeraldpay.dshackle.upstream.calls.EthereumCallSelector
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.domain.Address
|
import io.emeraldpay.dshackle.upstream.ethereum.domain.Address
|
||||||
@@ -91,7 +92,7 @@ class EthereumDirectReader(
|
|||||||
Mono.empty()
|
Mono.empty()
|
||||||
} else {
|
} else {
|
||||||
Mono.just(
|
Mono.just(
|
||||||
Result(TxContainer.from(tx, result.data), result.upstreamId),
|
Result(TxContainer.from(tx, result.data), result.resolvedUpstreamData),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,7 +117,7 @@ class EthereumDirectReader(
|
|||||||
if (str.startsWith("\"") && str.endsWith("\"")) {
|
if (str.startsWith("\"") && str.endsWith("\"")) {
|
||||||
Result(
|
Result(
|
||||||
Wei.from(str.substring(1, str.length - 1)),
|
Wei.from(str.substring(1, str.length - 1)),
|
||||||
it.upstreamId,
|
it.resolvedUpstreamData,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
throw RpcException(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "Not Wei value")
|
throw RpcException(RpcResponseError.CODE_UPSTREAM_INVALID_RESPONSE, "Not Wei value")
|
||||||
@@ -180,7 +181,7 @@ class EthereumDirectReader(
|
|||||||
log.debug("Empty logs for block $key")
|
log.debug("Empty logs for block $key")
|
||||||
Mono.empty()
|
Mono.empty()
|
||||||
} else {
|
} else {
|
||||||
Mono.just(Result(logs, it.upstreamId))
|
Mono.just(Result(logs, it.resolvedUpstreamData))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +209,7 @@ class EthereumDirectReader(
|
|||||||
Mono.just(
|
Mono.just(
|
||||||
Result(
|
Result(
|
||||||
BlockContainer.from(block, result.data, "unknown"),
|
BlockContainer.from(block, result.data, "unknown"),
|
||||||
result.upstreamId,
|
result.resolvedUpstreamData,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -245,12 +246,12 @@ class EthereumDirectReader(
|
|||||||
}.flatMap {
|
}.flatMap {
|
||||||
it.read(request)
|
it.read(request)
|
||||||
}.map {
|
}.map {
|
||||||
Result(it.value, it.resolvedBy?.getId())
|
Result(it.value, it.resolvedUpstreamData)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Result<T>(
|
data class Result<T>(
|
||||||
val data: T,
|
val data: T,
|
||||||
val upstreamId: String?,
|
val resolvedUpstreamData: Upstream.UpstreamSettingsData?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.LogsOracle
|
import io.emeraldpay.dshackle.upstream.LogsOracle
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.hex.HexQuantity
|
import io.emeraldpay.dshackle.upstream.ethereum.hex.HexQuantity
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
||||||
@@ -71,7 +72,7 @@ class EthereumLocalReader(
|
|||||||
* parses JSON into Map. But the purpose of further processing and caching for some of the requests we want
|
* parses JSON into Map. But the purpose of further processing and caching for some of the requests we want
|
||||||
* to have actual data types.
|
* to have actual data types.
|
||||||
*/
|
*/
|
||||||
fun commonRequests(key: ChainRequest): Mono<Pair<ByteArray, String?>>? {
|
fun commonRequests(key: ChainRequest): Mono<Pair<ByteArray, Upstream.UpstreamSettingsData?>>? {
|
||||||
val method = key.method
|
val method = key.method
|
||||||
val params = key.params
|
val params = key.params
|
||||||
if (params is ListParams) {
|
if (params is ListParams) {
|
||||||
@@ -88,7 +89,7 @@ class EthereumLocalReader(
|
|||||||
}
|
}
|
||||||
reader.txByHashAsCont()
|
reader.txByHashAsCont()
|
||||||
.read(hash)
|
.read(hash)
|
||||||
.map { it.data.json!! to it.upstreamId }
|
.map { it.data.json!! to it.resolvedUpstreamData }
|
||||||
}
|
}
|
||||||
|
|
||||||
method == "eth_getBlockByHash" -> {
|
method == "eth_getBlockByHash" -> {
|
||||||
@@ -105,7 +106,7 @@ class EthereumLocalReader(
|
|||||||
if (withTx) {
|
if (withTx) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
reader.blocksByIdAsCont().read(hash).map { it.data.json!! to it.upstreamId }
|
reader.blocksByIdAsCont().read(hash).map { it.data.json!! to it.resolvedUpstreamData }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ class EthereumLocalReader(
|
|||||||
}
|
}
|
||||||
reader.receipts()
|
reader.receipts()
|
||||||
.read(hash)
|
.read(hash)
|
||||||
.map { it.data to it.upstreamId }
|
.map { it.data to it.resolvedUpstreamData }
|
||||||
}
|
}
|
||||||
|
|
||||||
method == "drpc_getLogsEstimate" -> {
|
method == "drpc_getLogsEstimate" -> {
|
||||||
@@ -138,7 +139,7 @@ class EthereumLocalReader(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getBlockByNumber(params: List<Any?>): Mono<Pair<ByteArray, String?>>? {
|
fun getBlockByNumber(params: List<Any?>): Mono<Pair<ByteArray, Upstream.UpstreamSettingsData?>>? {
|
||||||
if (params.size != 2 || params[0] == null || params[1] == null) {
|
if (params.size != 2 || params[0] == null || params[1] == null) {
|
||||||
throw RpcException(RpcResponseError.CODE_INVALID_METHOD_PARAMS, "Must provide 2 parameters")
|
throw RpcException(RpcResponseError.CODE_INVALID_METHOD_PARAMS, "Must provide 2 parameters")
|
||||||
}
|
}
|
||||||
@@ -179,10 +180,10 @@ class EthereumLocalReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return reader.blocksByHeightAsCont()
|
return reader.blocksByHeightAsCont()
|
||||||
.read(number).map { it.data.json!! to it.upstreamId }
|
.read(number).map { it.data.json!! to it.resolvedUpstreamData }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLogsEstimate(params: List<Any?>): Mono<Pair<ByteArray, String?>>? {
|
fun getLogsEstimate(params: List<Any?>): Mono<Pair<ByteArray, Upstream.UpstreamSettingsData?>>? {
|
||||||
if (logsOracle == null) {
|
if (logsOracle == null) {
|
||||||
throw NotImplementedError()
|
throw NotImplementedError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import io.emeraldpay.dshackle.reader.ChainReader
|
import io.emeraldpay.dshackle.upstream.BasicEthUpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.BasicEthLabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
|
||||||
const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
||||||
|
|
||||||
class EthereumLabelsDetector(
|
class EthereumUpstreamSettingsDetector(
|
||||||
private val reader: ChainReader,
|
private val upstream: Upstream,
|
||||||
private val chain: Chain,
|
private val chain: Chain,
|
||||||
) : BasicEthLabelsDetector(reader) {
|
) : BasicEthUpstreamSettingsDetector(upstream) {
|
||||||
private val blockNumberReader = EthereumArchiveBlockNumberReader(reader)
|
private val blockNumberReader = EthereumArchiveBlockNumberReader(upstream.getIngressReader())
|
||||||
|
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun detectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
@@ -24,6 +24,18 @@ class EthereumLabelsDetector(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun clientVersionRequest(): ChainRequest {
|
||||||
|
return ChainRequest("web3_clientVersion", ListParams())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseClientVersion(data: ByteArray): String {
|
||||||
|
val version = String(data)
|
||||||
|
if (version.startsWith("\"") && version.endsWith("\"")) {
|
||||||
|
return version.substring(1, version.length - 1)
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
private fun detectArchiveNode(): Mono<Pair<String, String>> {
|
private fun detectArchiveNode(): Mono<Pair<String, String>> {
|
||||||
return Mono.zip(
|
return Mono.zip(
|
||||||
blockNumberReader.readEarliestBlock(chain).flatMap { haveBalance(it) },
|
blockNumberReader.readEarliestBlock(chain).flatMap { haveBalance(it) },
|
||||||
@@ -34,7 +46,7 @@ class EthereumLabelsDetector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun haveBalance(blockNumber: String): Mono<ByteArray> {
|
private fun haveBalance(blockNumber: String): Mono<ByteArray> {
|
||||||
return reader.read(
|
return upstream.getIngressReader().read(
|
||||||
ChainRequest(
|
ChainRequest(
|
||||||
"eth_getBalance",
|
"eth_getBalance",
|
||||||
ListParams(ZERO_ADDRESS, blockNumber),
|
ListParams(ZERO_ADDRESS, blockNumber),
|
||||||
@@ -44,7 +56,7 @@ class EthereumLabelsDetector(
|
|||||||
|
|
||||||
override fun nodeTypeRequest(): NodeTypeRequest {
|
override fun nodeTypeRequest(): NodeTypeRequest {
|
||||||
return NodeTypeRequest(
|
return NodeTypeRequest(
|
||||||
ChainRequest("web3_clientVersion", ListParams()),
|
clientVersionRequest(),
|
||||||
) { node -> node }
|
) { node -> node }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,11 +10,12 @@ import io.emeraldpay.dshackle.upstream.EgressSubscription
|
|||||||
import io.emeraldpay.dshackle.upstream.EmptyEgressSubscription
|
import io.emeraldpay.dshackle.upstream.EmptyEgressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.LogsOracle
|
import io.emeraldpay.dshackle.upstream.LogsOracle
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.NoIngressSubscription
|
import io.emeraldpay.dshackle.upstream.NoIngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.NoopCachingReader
|
import io.emeraldpay.dshackle.upstream.NoopCachingReader
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
import io.emeraldpay.dshackle.upstream.calls.CallSelector
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
||||||
@@ -37,7 +38,7 @@ abstract class AbstractChainSpecific : ChainSpecific {
|
|||||||
return { _, _, _ -> NoopCachingReader }
|
return { _, _, _ -> NoopCachingReader }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun labelDetector(chain: Chain, reader: ChainReader): LabelsDetector? {
|
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector? {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,11 +19,11 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.LogsOracle
|
import io.emeraldpay.dshackle.upstream.LogsOracle
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.beaconchain.BeaconChainSpecific
|
import io.emeraldpay.dshackle.upstream.beaconchain.BeaconChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||||
@@ -60,7 +60,7 @@ interface ChainSpecific {
|
|||||||
|
|
||||||
fun validator(chain: Chain, upstream: Upstream, options: ChainOptions.Options, config: ChainConfig): UpstreamValidator
|
fun validator(chain: Chain, upstream: Upstream, options: ChainOptions.Options, config: ChainConfig): UpstreamValidator
|
||||||
|
|
||||||
fun labelDetector(chain: Chain, reader: ChainReader): LabelsDetector?
|
fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector?
|
||||||
|
|
||||||
fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription
|
fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import io.emeraldpay.dshackle.upstream.Capability
|
|||||||
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
import io.emeraldpay.dshackle.upstream.DefaultUpstream
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetectorBuilder
|
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetectorBuilder
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetectorBuilder
|
||||||
|
import io.emeraldpay.dshackle.upstream.UNKNOWN_CLIENT_VERSION
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetectorBuilder
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidatorBuilder
|
import io.emeraldpay.dshackle.upstream.UpstreamValidatorBuilder
|
||||||
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
|
import io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult
|
||||||
@@ -30,6 +31,7 @@ import reactor.core.publisher.Flux
|
|||||||
import reactor.core.publisher.Sinks
|
import reactor.core.publisher.Sinks
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
open class GenericUpstream(
|
open class GenericUpstream(
|
||||||
id: String,
|
id: String,
|
||||||
@@ -42,7 +44,7 @@ open class GenericUpstream(
|
|||||||
chainConfig: ChainsConfig.ChainConfig,
|
chainConfig: ChainsConfig.ChainConfig,
|
||||||
connectorFactory: ConnectorFactory,
|
connectorFactory: ConnectorFactory,
|
||||||
validatorBuilder: UpstreamValidatorBuilder,
|
validatorBuilder: UpstreamValidatorBuilder,
|
||||||
labelsDetectorBuilder: LabelsDetectorBuilder,
|
upstreamSettingsDetectorBuilder: UpstreamSettingsDetectorBuilder,
|
||||||
lowerBoundBlockDetectorBuilder: LowerBoundBlockDetectorBuilder,
|
lowerBoundBlockDetectorBuilder: LowerBoundBlockDetectorBuilder,
|
||||||
) : DefaultUpstream(id, hash, null, UpstreamAvailability.OK, options, role, targets, node, chainConfig), Lifecycle {
|
) : DefaultUpstream(id, hash, null, UpstreamAvailability.OK, options, role, targets, node, chainConfig), Lifecycle {
|
||||||
|
|
||||||
@@ -54,12 +56,13 @@ open class GenericUpstream(
|
|||||||
private val hasLiveSubscriptionHead: AtomicBoolean = AtomicBoolean(false)
|
private val hasLiveSubscriptionHead: AtomicBoolean = AtomicBoolean(false)
|
||||||
protected val connector: GenericConnector = connectorFactory.create(this, chain)
|
protected val connector: GenericConnector = connectorFactory.create(this, chain)
|
||||||
private var livenessSubscription: Disposable? = null
|
private var livenessSubscription: Disposable? = null
|
||||||
private val labelsDetector = labelsDetectorBuilder(chain, this.getIngressReader())
|
private val settingsDetector = upstreamSettingsDetectorBuilder(chain, this)
|
||||||
|
|
||||||
private val lowerBoundBlockDetector = lowerBoundBlockDetectorBuilder(chain, this)
|
private val lowerBoundBlockDetector = lowerBoundBlockDetectorBuilder(chain, this)
|
||||||
|
|
||||||
private val started = AtomicBoolean(false)
|
private val started = AtomicBoolean(false)
|
||||||
private val isUpstreamValid = AtomicBoolean(false)
|
private val isUpstreamValid = AtomicBoolean(false)
|
||||||
|
private val clientVersion = AtomicReference(UNKNOWN_CLIENT_VERSION)
|
||||||
|
|
||||||
override fun getHead(): Head {
|
override fun getHead(): Head {
|
||||||
return connector.getHead()
|
return connector.getHead()
|
||||||
@@ -91,6 +94,14 @@ open class GenericUpstream(
|
|||||||
return lowerBoundBlockDetector.getCurrentLowerBlock()
|
return lowerBoundBlockDetector.getCurrentLowerBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUpstreamSettingsData(): Upstream.UpstreamSettingsData? {
|
||||||
|
return Upstream.UpstreamSettingsData(
|
||||||
|
nodeId(),
|
||||||
|
getId(),
|
||||||
|
clientVersion.get(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
||||||
if (!selfType.isAssignableFrom(this.javaClass)) {
|
if (!selfType.isAssignableFrom(this.javaClass)) {
|
||||||
@@ -165,8 +176,14 @@ open class GenericUpstream(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detectLabels() {
|
private fun detectSettings() {
|
||||||
labelsDetector?.detectLabels()?.subscribe { label -> updateLabels(label) }
|
settingsDetector?.detectLabels()?.subscribe { label -> updateLabels(label) }
|
||||||
|
|
||||||
|
settingsDetector?.detectClientVersion()
|
||||||
|
?.subscribe {
|
||||||
|
log.info("Detected node version $it for upstream ${getId()}")
|
||||||
|
clientVersion.set(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upstreamStart() {
|
private fun upstreamStart() {
|
||||||
@@ -185,7 +202,7 @@ open class GenericUpstream(
|
|||||||
}, {
|
}, {
|
||||||
log.debug("Error while checking live subscription for ${getId()}", it)
|
log.debug("Error while checking live subscription for ${getId()}", it)
|
||||||
},)
|
},)
|
||||||
detectLabels()
|
detectSettings()
|
||||||
|
|
||||||
detectLowerBlock()
|
detectLowerBlock()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ class BitcoinGrpcUpstream(
|
|||||||
return LowerBoundBlockDetector.LowerBlockData.default()
|
return LowerBoundBlockDetector.LowerBlockData.default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUpstreamSettingsData(): Upstream.UpstreamSettingsData? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
override fun <T : Upstream> cast(selfType: Class<T>): T {
|
||||||
if (!selfType.isAssignableFrom(this.javaClass)) {
|
if (!selfType.isAssignableFrom(this.javaClass)) {
|
||||||
|
|||||||
@@ -182,4 +182,8 @@ open class GenericGrpcUpstream(
|
|||||||
override fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData {
|
override fun getLowerBlock(): LowerBoundBlockDetector.LowerBlockData {
|
||||||
return LowerBoundBlockDetector.LowerBlockData.default()
|
return LowerBoundBlockDetector.LowerBlockData.default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getUpstreamSettingsData(): Upstream.UpstreamSettingsData? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
|||||||
import io.emeraldpay.dshackle.upstream.SingleCallValidator
|
import io.emeraldpay.dshackle.upstream.SingleCallValidator
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
import io.emeraldpay.dshackle.upstream.generic.AbstractPollChainSpecific
|
||||||
import io.emeraldpay.dshackle.upstream.generic.GenericUpstreamValidator
|
import io.emeraldpay.dshackle.upstream.generic.GenericUpstreamValidator
|
||||||
@@ -82,6 +83,10 @@ object NearChainSpecific : AbstractPollChainSpecific() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector {
|
||||||
|
return NearUpstreamSettingsDetector(upstream)
|
||||||
|
}
|
||||||
|
|
||||||
override fun latestBlockRequest(): ChainRequest = // {...}
|
override fun latestBlockRequest(): ChainRequest = // {...}
|
||||||
ChainRequest("block", ObjectParams("finality" to "optimistic"))
|
ChainRequest("block", ObjectParams("finality" to "optimistic"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.near
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
|
|
||||||
|
class NearUpstreamSettingsDetector(
|
||||||
|
upstream: Upstream,
|
||||||
|
) : UpstreamSettingsDetector(upstream) {
|
||||||
|
override fun detectLabels(): Flux<Pair<String, String>> {
|
||||||
|
return Flux.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clientVersionRequest(): ChainRequest {
|
||||||
|
return ChainRequest("status", ListParams())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseClientVersion(data: ByteArray): String {
|
||||||
|
return Global.objectMapper.readValue<NearVersionResponse>(data).nearVersion.version
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private data class NearVersionResponse(
|
||||||
|
@JsonProperty("version")
|
||||||
|
val nearVersion: NearVersion,
|
||||||
|
)
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private data class NearVersion(
|
||||||
|
@JsonProperty("version")
|
||||||
|
val version: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ import io.emeraldpay.dshackle.reader.ChainReader
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.RequestMetrics
|
import io.emeraldpay.dshackle.upstream.RequestMetrics
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcResponseError
|
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcResponseError
|
||||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
@@ -109,7 +110,7 @@ class JsonRpcGrpcClient(
|
|||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
Mono.just(ChainResponse(bytes, null, ChainResponse.NumberId(0), null, signature, resp.upstreamId))
|
Mono.just(ChainResponse(bytes, null, ChainResponse.NumberId(0), null, signature, Upstream.UpstreamSettingsData(0, resp.upstreamId, resp.upstreamNodeVersion)))
|
||||||
} else {
|
} else {
|
||||||
metrics?.fails?.increment()
|
metrics?.fails?.increment()
|
||||||
Mono.error(
|
Mono.error(
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.DefaultSolanaMethods
|
import io.emeraldpay.dshackle.upstream.DefaultSolanaMethods
|
||||||
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
import io.emeraldpay.dshackle.upstream.IngressSubscription
|
||||||
import io.emeraldpay.dshackle.upstream.LabelsDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
import io.emeraldpay.dshackle.upstream.LowerBoundBlockDetector
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
import io.emeraldpay.dshackle.upstream.SingleCallValidator
|
import io.emeraldpay.dshackle.upstream.SingleCallValidator
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
import io.emeraldpay.dshackle.upstream.UpstreamValidator
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
|
||||||
import io.emeraldpay.dshackle.upstream.generic.AbstractChainSpecific
|
import io.emeraldpay.dshackle.upstream.generic.AbstractChainSpecific
|
||||||
@@ -142,8 +142,8 @@ object SolanaChainSpecific : AbstractChainSpecific() {
|
|||||||
return SolanaLowerBoundBlockDetector(chain, upstream)
|
return SolanaLowerBoundBlockDetector(chain, upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun labelDetector(chain: Chain, reader: ChainReader): LabelsDetector? {
|
override fun upstreamSettingsDetector(chain: Chain, upstream: Upstream): UpstreamSettingsDetector {
|
||||||
return null
|
return SolanaUpstreamSettingsDetector(upstream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
|
override fun makeIngressSubscription(ws: WsSubscriptions): IngressSubscription {
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.solana
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.UpstreamSettingsDetector
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
|
import reactor.core.publisher.Flux
|
||||||
|
|
||||||
|
class SolanaUpstreamSettingsDetector(
|
||||||
|
upstream: Upstream,
|
||||||
|
) : UpstreamSettingsDetector(upstream) {
|
||||||
|
override fun detectLabels(): Flux<Pair<String, String>> {
|
||||||
|
return Flux.empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun clientVersionRequest(): ChainRequest {
|
||||||
|
return ChainRequest("getVersion", ListParams())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun parseClientVersion(data: ByteArray): String {
|
||||||
|
return Global.objectMapper.readValue<SolanaVersion>(data).version
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
private data class SolanaVersion(
|
||||||
|
@JsonProperty("solana-core")
|
||||||
|
val version: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ import io.emeraldpay.dshackle.monitoring.accesslog.AccessHandlerHttp
|
|||||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||||
import io.emeraldpay.dshackle.rpc.NativeSubscribe
|
import io.emeraldpay.dshackle.rpc.NativeSubscribe
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.ethereum.json.RequestJson
|
import io.emeraldpay.dshackle.upstream.ethereum.json.RequestJson
|
||||||
import io.micrometer.core.instrument.Counter
|
import io.micrometer.core.instrument.Counter
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
@@ -85,7 +86,7 @@ class WebsocketHandlerSpec extends Specification {
|
|||||||
|
|
||||||
def "Respond to a single call"() {
|
def "Respond to a single call"() {
|
||||||
setup:
|
setup:
|
||||||
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, "test", null, null)
|
def response = new NativeCall.CallResult(0, null, '{"foo": 1}'.bytes, null, null, new Upstream.UpstreamSettingsData("test"), null, null)
|
||||||
|
|
||||||
def nativeCall = Mock(NativeCall) {
|
def nativeCall = Mock(NativeCall) {
|
||||||
1 * it.nativeCallResult(_) >> Flux.fromIterable([response])
|
1 * it.nativeCallResult(_) >> Flux.fromIterable([response])
|
||||||
|
|||||||
@@ -120,14 +120,10 @@ class NativeCallSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
|
|
||||||
def ups = Mock(Upstream) {
|
|
||||||
_ * nodeId() >> (byte) 1
|
|
||||||
}
|
|
||||||
|
|
||||||
def nativeCall = nativeCall()
|
def nativeCall = nativeCall()
|
||||||
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(_) >> Mono.just(new RequestReader.Result("\"foo\"".bytes, null, 1, ups, null))
|
1 * read(_) >> Mono.just(new RequestReader.Result("\"foo\"".bytes, null, 1, new Upstream.UpstreamSettingsData((byte)1, "test", "v"), null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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,
|
||||||
@@ -250,7 +246,7 @@ class NativeCallSpec extends Specification {
|
|||||||
|
|
||||||
when:
|
when:
|
||||||
def resp = nativeCall.buildResponse(
|
def resp = nativeCall.buildResponse(
|
||||||
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, new ResponseSigner.Signature("sig1".bytes, "test", 100), "test", null, null)
|
new NativeCall.CallResult(1561, 10, objectMapper.writeValueAsBytes(json), null, new ResponseSigner.Signature("sig1".bytes, "test", 100), new Upstream.UpstreamSettingsData("test"), null, null)
|
||||||
)
|
)
|
||||||
then:
|
then:
|
||||||
resp.id == 1561
|
resp.id == 1561
|
||||||
@@ -597,9 +593,6 @@ class NativeCallSpec extends Specification {
|
|||||||
|
|
||||||
def "Decorate eth_newFilter result"() {
|
def "Decorate eth_newFilter result"() {
|
||||||
setup:
|
setup:
|
||||||
def ups = Mock(Upstream) {
|
|
||||||
_ * nodeId() >> (byte)255
|
|
||||||
}
|
|
||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
def methods = new ManagedCallMethods(
|
def methods = new ManagedCallMethods(
|
||||||
new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false),
|
new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false),
|
||||||
@@ -616,7 +609,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(_) >> Mono.just(new RequestReader.Result("\"0xab\"".bytes, null, 1, ups, null))
|
1 * read(_) >> Mono.just(new RequestReader.Result("\"0xab\"".bytes, null, 1, new Upstream.UpstreamSettingsData((byte) 255, "", ""), null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||||
@@ -633,9 +626,6 @@ class NativeCallSpec extends Specification {
|
|||||||
|
|
||||||
def "Decorate eth_newFilter result with short nodeId"() {
|
def "Decorate eth_newFilter result with short nodeId"() {
|
||||||
setup:
|
setup:
|
||||||
def ups = Mock(Upstream) {
|
|
||||||
_ * nodeId() >> (byte)1
|
|
||||||
}
|
|
||||||
def quorum = new AlwaysQuorum()
|
def quorum = new AlwaysQuorum()
|
||||||
def methods = new ManagedCallMethods(
|
def methods = new ManagedCallMethods(
|
||||||
new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false),
|
new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET, false),
|
||||||
@@ -652,7 +642,7 @@ class NativeCallSpec extends Specification {
|
|||||||
def nativeCall = nativeCall(multistreamHolder)
|
def nativeCall = nativeCall(multistreamHolder)
|
||||||
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
nativeCall.requestReaderFactory = Mock(RequestReaderFactory) {
|
||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(_) >> Mono.just(new RequestReader.Result("\"0xab\"".bytes, null, 1, ups, null))
|
1 * read(_) >> Mono.just(new RequestReader.Result("\"0xab\"".bytes, null, 1, new Upstream.UpstreamSettingsData((byte) 1, "", ""), null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
def call = new NativeCall.ValidCallContext(1, 10, multistream, Selector.empty, quorum,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class GenericUpstreamMock extends GenericUpstream {
|
|||||||
ChainConfig.default(),
|
ChainConfig.default(),
|
||||||
new ConnectorFactoryMock(api, new EthereumHeadMock()),
|
new ConnectorFactoryMock(api, new EthereumHeadMock()),
|
||||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&validator,
|
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&validator,
|
||||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&labelDetector,
|
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&upstreamSettingsDetector,
|
||||||
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&lowerBoundBlockDetector,
|
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&lowerBoundBlockDetector,
|
||||||
)
|
)
|
||||||
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class FilteredApisSpec extends Specification {
|
|||||||
ChainsConfig.ChainConfig.default(),
|
ChainsConfig.ChainConfig.default(),
|
||||||
connectorFactory,
|
connectorFactory,
|
||||||
cs.&validator,
|
cs.&validator,
|
||||||
cs.&labelDetector,
|
cs.&upstreamSettingsDetector,
|
||||||
cs.&lowerBoundBlockDetector
|
cs.&lowerBoundBlockDetector
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
|
|
||||||
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
|
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
|
||||||
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
|
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
|
||||||
Upstream resolver = TestingCommons.upstream()
|
Upstream.UpstreamSettingsData data = new Upstream.UpstreamSettingsData("test")
|
||||||
|
|
||||||
def "Reads block by hash"() {
|
def "Reads block by hash"() {
|
||||||
setup:
|
setup:
|
||||||
@@ -54,7 +54,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getBlockByHash", new ListParams([hash1, false]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getBlockByHash", new ListParams([hash1, false]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getBlockByHash", new ListParams([hash1, false]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getBlockByHash", new ListParams([hash1, false]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(null), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -114,7 +114,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x64", false]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getBlockByNumber", new ListParams(["0x64", false]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getLogs", new ListParams([Map.of("blockHash", hash1)]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getLogs", new ListParams([Map.of("blockHash", hash1)]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes([json]), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes([json]), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -179,7 +179,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getTransactionByHash", new ListParams([hash1]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getTransactionByHash", new ListParams([hash1]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -212,7 +212,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getTransactionReceipt", new ListParams([hash1]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getTransactionReceipt", new ListParams([hash1]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getTransactionReceipt", new ListParams([hash1]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getTransactionReceipt", new ListParams([hash1]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -271,7 +271,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getTransactionByHash", new ListParams([hash1]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getTransactionByHash", new ListParams([hash1]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(null), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes(null), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -301,7 +301,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getBalance", new ListParams([address1, "latest"]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getBalance", new ListParams([address1, "latest"]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -332,7 +332,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
1 * create(_) >> Mock(RequestReader) {
|
1 * create(_) >> Mock(RequestReader) {
|
||||||
1 * read(new ChainRequest("eth_getBalance", new ListParams([address1, "0xa8c9bb"]))) >> Mono.just(
|
1 * read(new ChainRequest("eth_getBalance", new ListParams([address1, "0xa8c9bb"]))) >> Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, resolver, null
|
Global.objectMapper.writeValueAsBytes("0x100"), null, 1, data, null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -361,7 +361,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
@@ -401,7 +401,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def result = Mono.just(
|
def result = Mono.just(
|
||||||
new RequestReader.Result(
|
new RequestReader.Result(
|
||||||
Global.objectMapper.writeValueAsBytes(json), null, 1, resolver, null)
|
Global.objectMapper.writeValueAsBytes(json), null, 1, data, null)
|
||||||
)
|
)
|
||||||
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
EthereumDirectReader ethereumDirectReader = new EthereumDirectReader(
|
||||||
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import spock.lang.Specification
|
|||||||
|
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
|
||||||
class EthereumLabelsDetectorSpec extends Specification {
|
class EthereumUpstreamSettingsDetectorSpec extends Specification {
|
||||||
|
|
||||||
def "Detect labels"() {
|
def "Detect labels"() {
|
||||||
setup:
|
setup:
|
||||||
@@ -27,7 +27,7 @@ class EthereumLabelsDetectorSpec extends Specification {
|
|||||||
answer("eth_getBalance", ["0x0000000000000000000000000000000000000000", "0x2710"], "")
|
answer("eth_getBalance", ["0x0000000000000000000000000000000000000000", "0x2710"], "")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
def detector = new EthereumLabelsDetector(up.getIngressReader(), Chain.ETHEREUM__MAINNET)
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
def act = detector.detectLabels()
|
def act = detector.detectLabels()
|
||||||
@@ -51,7 +51,7 @@ class EthereumLabelsDetectorSpec extends Specification {
|
|||||||
def "No any label"() {
|
def "No any label"() {
|
||||||
setup:
|
setup:
|
||||||
def up = Mock(DefaultUpstream) {
|
def up = Mock(DefaultUpstream) {
|
||||||
1 * getIngressReader() >> Mock(Reader) {
|
4 * getIngressReader() >> Mock(Reader) {
|
||||||
1 * read(new ChainRequest("web3_clientVersion", new ListParams())) >>
|
1 * read(new ChainRequest("web3_clientVersion", new ListParams())) >>
|
||||||
Mono.just(new ChainResponse('no/v1.19.3+e8ac1da4/linux-x64/dotnet7.0.8'.getBytes(), null))
|
Mono.just(new ChainResponse('no/v1.19.3+e8ac1da4/linux-x64/dotnet7.0.8'.getBytes(), null))
|
||||||
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >>
|
1 * read(new ChainRequest("eth_blockNumber", new ListParams())) >>
|
||||||
@@ -62,7 +62,7 @@ class EthereumLabelsDetectorSpec extends Specification {
|
|||||||
Mono.just(new ChainResponse("".getBytes(), null))
|
Mono.just(new ChainResponse("".getBytes(), null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def detector = new EthereumLabelsDetector(up.getIngressReader(), Chain.ETHEREUM__MAINNET)
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
when:
|
when:
|
||||||
def act = detector.detectLabels()
|
def act = detector.detectLabels()
|
||||||
then:
|
then:
|
||||||
@@ -70,4 +70,22 @@ class EthereumLabelsDetectorSpec extends Specification {
|
|||||||
.expectComplete()
|
.expectComplete()
|
||||||
.verify(Duration.ofSeconds(1))
|
.verify(Duration.ofSeconds(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Detect client version"() {
|
||||||
|
setup:
|
||||||
|
def up = Mock(DefaultUpstream) {
|
||||||
|
2 * getIngressReader() >> Mock(Reader) {
|
||||||
|
1 * read(new ChainRequest("web3_clientVersion", new ListParams())) >>
|
||||||
|
Mono.just(new ChainResponse('"Erigon/v1.12.0-stable-e501b3b0/linux-amd64/go1.20.3"'.getBytes(), null))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
|
when:
|
||||||
|
def act = detector.detectClientVersion()
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext("Erigon/v1.12.0-stable-e501b3b0/linux-amd64/go1.20.3")
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,8 @@ import io.emeraldpay.api.proto.BlockchainOuterClass.NativeCallReplyItem
|
|||||||
import io.emeraldpay.api.proto.BlockchainOuterClass.NativeCallRequest
|
import io.emeraldpay.api.proto.BlockchainOuterClass.NativeCallRequest
|
||||||
import io.emeraldpay.dshackle.config.MainConfig
|
import io.emeraldpay.dshackle.config.MainConfig
|
||||||
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
import io.emeraldpay.dshackle.upstream.MultistreamHolder
|
||||||
|
import io.emeraldpay.dshackle.upstream.UNKNOWN_CLIENT_VERSION
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
|
||||||
import io.emeraldpay.dshackle.upstream.stream.Chunk
|
import io.emeraldpay.dshackle.upstream.stream.Chunk
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -31,7 +33,7 @@ class NativeCallTest {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
on { nativeCallResult(request) } doReturn Flux.just(
|
on { nativeCallResult(request) } doReturn Flux.just(
|
||||||
NativeCall.CallResult.ok(1, null, "0x1".toByteArray(), null, "id", null),
|
NativeCall.CallResult.ok(1, null, "0x1".toByteArray(), null, Upstream.UpstreamSettingsData("id"), null),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ class NativeCallTest {
|
|||||||
.expectNext(
|
.expectNext(
|
||||||
NativeCallReplyItem.newBuilder()
|
NativeCallReplyItem.newBuilder()
|
||||||
.setUpstreamId("id")
|
.setUpstreamId("id")
|
||||||
|
.setUpstreamNodeVersion(UNKNOWN_CLIENT_VERSION)
|
||||||
.setId(1)
|
.setId(1)
|
||||||
.setSucceed(true)
|
.setSucceed(true)
|
||||||
.setPayload(ByteString.copyFrom("0x1".toByteArray()))
|
.setPayload(ByteString.copyFrom("0x1".toByteArray()))
|
||||||
@@ -60,7 +63,7 @@ class NativeCallTest {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
on { nativeCallResult(request) } doReturn Flux.just(
|
on { nativeCallResult(request) } doReturn Flux.just(
|
||||||
NativeCall.CallResult(1, null, null, NativeCall.CallError(50001, "message", null, null, "upId"), null, null),
|
NativeCall.CallResult(1, null, null, NativeCall.CallError(50001, "message", null, null, Upstream.UpstreamSettingsData("upId")), null, null),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +72,7 @@ class NativeCallTest {
|
|||||||
NativeCallReplyItem.newBuilder()
|
NativeCallReplyItem.newBuilder()
|
||||||
.setUpstreamId("upId")
|
.setUpstreamId("upId")
|
||||||
.setId(1)
|
.setId(1)
|
||||||
|
.setUpstreamNodeVersion(UNKNOWN_CLIENT_VERSION)
|
||||||
.setSucceed(false)
|
.setSucceed(false)
|
||||||
.setErrorMessage("message")
|
.setErrorMessage("message")
|
||||||
.setItemErrorCode(50001)
|
.setItemErrorCode(50001)
|
||||||
@@ -95,7 +99,7 @@ class NativeCallTest {
|
|||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
on { nativeCallResult(request) } doReturn Flux.just(
|
on { nativeCallResult(request) } doReturn Flux.just(
|
||||||
NativeCall.CallResult.ok(1, null, "".toByteArray(), null, "upId", null, chunks),
|
NativeCall.CallResult.ok(1, null, "".toByteArray(), null, Upstream.UpstreamSettingsData("upId"), null, chunks),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +108,7 @@ class NativeCallTest {
|
|||||||
NativeCallReplyItem.newBuilder()
|
NativeCallReplyItem.newBuilder()
|
||||||
.setUpstreamId("upId")
|
.setUpstreamId("upId")
|
||||||
.setId(1)
|
.setId(1)
|
||||||
|
.setUpstreamNodeVersion(UNKNOWN_CLIENT_VERSION)
|
||||||
.setChunked(true)
|
.setChunked(true)
|
||||||
.setSucceed(true)
|
.setSucceed(true)
|
||||||
.setPayload(ByteString.copyFrom("0x1".toByteArray()))
|
.setPayload(ByteString.copyFrom("0x1".toByteArray()))
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.near
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.reader.ChainReader
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.test.StepVerifier
|
||||||
|
|
||||||
|
class NearUpstreamSettingsDetectorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `detect client version`() {
|
||||||
|
val reader = mock<ChainReader> {
|
||||||
|
on { read(ChainRequest("status", ListParams())) } doReturn
|
||||||
|
Mono.just(
|
||||||
|
ChainResponse(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"version": {
|
||||||
|
"build": "1.38.1",
|
||||||
|
"rustc_version": "1.75.0",
|
||||||
|
"version": "1.38.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".trimIndent().toByteArray(),
|
||||||
|
null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val up = mock<Upstream> {
|
||||||
|
on { getIngressReader() } doReturn reader
|
||||||
|
}
|
||||||
|
|
||||||
|
val detector = NearUpstreamSettingsDetector(up)
|
||||||
|
|
||||||
|
StepVerifier.create(detector.detectClientVersion())
|
||||||
|
.expectNext("1.38.1")
|
||||||
|
.expectComplete()
|
||||||
|
.verify()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.solana
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.reader.ChainReader
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainCallError
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
|
import io.emeraldpay.dshackle.upstream.UNKNOWN_CLIENT_VERSION
|
||||||
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.test.StepVerifier
|
||||||
|
|
||||||
|
class SolanaUpstreamSettingsDetectorTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `detect client version`() {
|
||||||
|
val reader = mock<ChainReader> {
|
||||||
|
on { read(ChainRequest("getVersion", ListParams())) } doReturn
|
||||||
|
Mono.just(
|
||||||
|
ChainResponse(
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"feature-set": 2891131721,
|
||||||
|
"solana-core": "1.16.7"
|
||||||
|
}
|
||||||
|
""".trimIndent().toByteArray(),
|
||||||
|
null,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val up = mock<Upstream> {
|
||||||
|
on { getIngressReader() } doReturn reader
|
||||||
|
}
|
||||||
|
|
||||||
|
val detector = SolanaUpstreamSettingsDetector(up)
|
||||||
|
|
||||||
|
StepVerifier.create(detector.detectClientVersion())
|
||||||
|
.expectNext("1.16.7")
|
||||||
|
.expectComplete()
|
||||||
|
.verify()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unknown client if there is an error`() {
|
||||||
|
val reader = mock<ChainReader> {
|
||||||
|
on { read(ChainRequest("getVersion", ListParams())) } doReturn
|
||||||
|
Mono.just(
|
||||||
|
ChainResponse(
|
||||||
|
null,
|
||||||
|
ChainCallError(
|
||||||
|
1,
|
||||||
|
"message",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val up = mock<Upstream> {
|
||||||
|
on { getIngressReader() } doReturn reader
|
||||||
|
}
|
||||||
|
|
||||||
|
val detector = SolanaUpstreamSettingsDetector(up)
|
||||||
|
|
||||||
|
StepVerifier.create(detector.detectClientVersion())
|
||||||
|
.expectNext(UNKNOWN_CLIENT_VERSION)
|
||||||
|
.expectComplete()
|
||||||
|
.verify()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user