Update dependencies (#767)

This commit is contained in:
KirillPamPam
2026-01-06 16:32:03 +04:00
committed by GitHub
parent c5e1436462
commit 536c663b2a
96 changed files with 434 additions and 912 deletions

View File

@@ -72,19 +72,16 @@ dependencies {
implementation(libs.grpc.proto.util) implementation(libs.grpc.proto.util)
implementation libs.micrometer.registry.prometheus implementation libs.micrometer.registry.prometheus
implementation libs.micrometer.ctx.prop
implementation libs.lettuce.core implementation libs.lettuce.core
implementation(libs.spring.cloud.starter.sleuth) {
exclude module: 'spring-security-rsa'
}
implementation libs.brave.instrumentation.grpc implementation libs.brave.instrumentation.grpc
implementation libs.brave.ctx.slf4j
implementation libs.logstash.encoder implementation libs.logstash.encoder
implementation libs.janino implementation libs.janino
implementation libs.spring.cloud.sleuth.zipkin
implementation libs.bitcoinj implementation libs.bitcoinj
implementation libs.snake.yaml implementation libs.snake.yaml

View File

@@ -1,5 +1,5 @@
plugins { plugins {
id 'org.jetbrains.kotlin.jvm' version '1.9.25' id 'org.jetbrains.kotlin.jvm' version '2.3.0'
id 'maven-publish' id 'maven-publish'
} }
@@ -12,7 +12,8 @@ group = 'dshackle'
dependencies { dependencies {
implementation 'org.yaml:snakeyaml:1.24' implementation 'org.yaml:snakeyaml:1.24'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' testImplementation 'org.junit.jupiter:junit-jupiter:6.0.1'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
} }
test { test {

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -1,22 +1,20 @@
[versions] [versions]
detekt = "1.23.1" groovy = "5.0.3"
groovy = "4.0.15"
protoc = "4.33.2" protoc = "4.33.2"
jackson = "2.11.0" jackson = "2.20.1"
grpc = "1.57.0" grpc = "1.78.0"
reactive-grpc = "1.2.0" spring-boot = "4.0.1"
spring-boot = "2.6.15" reactor = "3.8.1"
spring-security = "5.5.3" netty = "4.2.9.Final"
reactor = "3.4.32" netty-tcnative = "2.0.74.Final"
netty = "4.1.110.Final" kotlin = "2.3.0"
netty-tcnative = "2.0.66.Final" httpcomponents = "5.5.1"
kotlin = "1.9.25"
httpcomponents = "4.5.8"
[libraries] [libraries]
apache-commons-lang3 = "org.apache.commons:commons-lang3:3.9" apache-commons-lang3 = "org.apache.commons:commons-lang3:3.9"
apache-commons-collections4 = "org.apache.commons:commons-collections4:4.3" apache-commons-collections4 = "org.apache.commons:commons-collections4:4.3"
apache-commons-math3 = "org.apache.commons:commons-math3:3.6.1" apache-commons-math3 = "org.apache.commons:commons-math3:3.6.1"
commons-codec = "commons-codec:commons-codec:1.20.0"
bitcoinj = "org.bitcoinj:bitcoinj-core:0.15.8" bitcoinj = "org.bitcoinj:bitcoinj-core:0.15.8"
@@ -33,21 +31,19 @@ commons-io = "commons-io:commons-io:2.6"
cglib-nodep = "cglib:cglib-nodep:3.3.0" cglib-nodep = "cglib:cglib-nodep:3.3.0"
detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
equals-verifier = "nl.jqno.equalsverifier:equalsverifier:3.10.1" equals-verifier = "nl.jqno.equalsverifier:equalsverifier:3.10.1"
groovy = { module = "org.apache.groovy:groovy", version.ref = "groovy" } groovy = { module = "org.apache.groovy:groovy", version.ref = "groovy" }
grpc-proto-util = { module = "com.google.protobuf:protobuf-java-util", version.ref = "protoc" } grpc-proto-util = { module = "com.google.protobuf:protobuf-java-util", version.ref = "protoc" }
grpc-protobuf = { module = "io.grpc:grpc-protobuf", version.ref = "grpc" } grpc-protobuf = { module = "io.grpc:grpc-protobuf", version.ref = "grpc" }
grpc-inprocess = { module = "io.grpc:grpc-inprocess", version.ref = "grpc" }
grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" } grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" }
grpc-netty = { module = "io.grpc:grpc-netty", version.ref = "grpc" } grpc-netty = { module = "io.grpc:grpc-netty", version.ref = "grpc" }
grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" } grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" }
grpc-services = { module = "io.grpc:grpc-services", version.ref = "grpc" } grpc-services = { module = "io.grpc:grpc-services", version.ref = "grpc" }
httpcomponents-httpmime = { module = "org.apache.httpcomponents:httpmime", version.ref = "httpcomponents" } httpcomponents-httpclient = { module = "org.apache.httpcomponents.client5:httpclient5", version.ref = "httpcomponents" }
httpcomponents-httpclient = { module = "org.apache.httpcomponents:httpclient", version.ref = "httpcomponents" }
jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" } jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson" }
jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" } jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
@@ -65,11 +61,12 @@ kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref =
lettuce-core = "io.lettuce:lettuce-core:5.2.2.RELEASE" lettuce-core = "io.lettuce:lettuce-core:5.2.2.RELEASE"
micrometer-registry-prometheus = "io.micrometer:micrometer-registry-prometheus:1.10.0" micrometer-registry-prometheus = "io.micrometer:micrometer-registry-prometheus:1.16.0"
micrometer-ctx-prop = "io.micrometer:context-propagation:1.2.0"
mockserver-netty = "org.mock-server:mockserver-netty:5.11.2" mockserver-netty = "org.mock-server:mockserver-netty:5.15.0"
mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:5.1.0" mockito-kotlin = "org.mockito.kotlin:mockito-kotlin:6.1.0"
netty-common = { module = "io.netty:netty-common", version.ref = "netty" } netty-common = { module = "io.netty:netty-common", version.ref = "netty" }
netty-transport = { module = "io.netty:netty-transport", version.ref = "netty" } netty-transport = { module = "io.netty:netty-transport", version.ref = "netty" }
@@ -90,8 +87,9 @@ zeromq = "org.zeromq:jeromq:0.5.2"
objgenesis = "org.objenesis:objenesis:3.1" objgenesis = "org.objenesis:objenesis:3.1"
reactor-core = { module = "io.projectreactor:reactor-core", version.ref = "reactor" } reactor-core = { module = "io.projectreactor:reactor-core", version.ref = "reactor" }
reactor-netty = { module = "io.projectreactor.netty:reactor-netty", version = "1.1.10" } reactor-micrometer = { module = "io.projectreactor:reactor-core-micrometer", version.ref = "reactor" }
reactor-extra = { module = "io.projectreactor.addons:reactor-extra", version = "3.5.1" } reactor-netty = { module = "io.projectreactor.netty:reactor-netty", version = "1.3.1" }
reactor-extra = { module = "io.projectreactor.addons:reactor-extra", version = "3.6.0" }
reactor-kotlin = { module = "io.projectreactor.kotlin:reactor-kotlin-extensions", version = "1.1.7" } reactor-kotlin = { module = "io.projectreactor.kotlin:reactor-kotlin-extensions", version = "1.1.7" }
reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "reactor" } reactor-test = { module = "io.projectreactor:reactor-test", version.ref = "reactor" }
@@ -99,24 +97,20 @@ reactor-grpc-stub = "com.salesforce.servicelibs:reactor-grpc-stub:1.2.0"
snake-yaml = "org.yaml:snakeyaml:1.24" snake-yaml = "org.yaml:snakeyaml:1.24"
spockframework-core = "org.spockframework:spock-core:2.3-groovy-4.0" spockframework-core = "org.spockframework:spock-core:2.4-groovy-5.0"
spring-boot-starter = { module = "org.springframework.boot:spring-boot-starter", version.ref = "spring-boot" } spring-boot-starter = { module = "org.springframework.boot:spring-boot-starter", version.ref = "spring-boot" }
spring-security-core = { module = "org.springframework.security:spring-security-core", version.ref = "spring-security" } spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "spring-boot" }
spring-security-web = { module = "org.springframework.security:spring-security-web", version.ref = "spring-security" }
spring-security-config = { module = "org.springframework.security:spring-security-config", version.ref = "spring-security" }
spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" } spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "spring-boot" }
testcontainers = "org.testcontainers:testcontainers:1.21.3" testcontainers = "org.testcontainers:testcontainers:1.21.3"
testcontainers-ganache = "io.github.ganchix:testcontainers-java-module-ganache:0.0.4" testcontainers-ganache = "io.github.ganchix:testcontainers-java-module-ganache:0.0.4"
junit-jupiter = "org.junit.jupiter:junit-jupiter:5.9.1"
assertj = "org.assertj:assertj-core:3.23.1" assertj = "org.assertj:assertj-core:3.23.1"
spring-cloud-starter-sleuth = "org.springframework.cloud:spring-cloud-starter-sleuth:3.1.6" brave-instrumentation-grpc = "io.zipkin.brave:brave-instrumentation-grpc:6.3.0"
spring-cloud-sleuth-zipkin = "org.springframework.cloud:spring-cloud-sleuth-zipkin:3.1.6" brave-ctx-slf4j = "io.zipkin.brave:brave-context-slf4j:6.3.0"
brave-instrumentation-grpc = "io.zipkin.brave:brave-instrumentation-grpc:5.15.0"
auth0-jwt = "com.auth0:java-jwt:4.4.0" auth0-jwt = "com.auth0:java-jwt:4.4.0"
@@ -125,24 +119,23 @@ mockito-inline = "org.mockito:mockito-inline:4.0.0"
semver4j = "com.vdurmont:semver4j:3.1.0" semver4j = "com.vdurmont:semver4j:3.1.0"
[bundles] [bundles]
apache-commons = ["commons-io", "apache-commons-lang3", "apache-commons-collections4", "apache-commons-math3"] apache-commons = ["commons-io", "apache-commons-lang3", "apache-commons-collections4", "apache-commons-math3", "commons-codec"]
grpc = ["grpc-protobuf", "grpc-stub", "grpc-netty", "grpc-proto-util", "grpc-services"] grpc = ["grpc-protobuf", "grpc-stub", "grpc-netty", "grpc-proto-util", "grpc-services", "grpc-inprocess"]
httpcomponents = ["httpcomponents-httpmime", "httpcomponents-httpclient"] httpcomponents = ["httpcomponents-httpclient"]
jackson = ["jackson-core", "jackson-databind", "jackson-datatype-jdk8", "jackson-datatype-jsr310", "jackson-module-kotlin", "jackson-yaml"] jackson = ["jackson-core", "jackson-databind", "jackson-datatype-jdk8", "jackson-datatype-jsr310", "jackson-module-kotlin", "jackson-yaml"]
kotlin = ["kotlin-stdlib-jdk8", "kotlin-reflect"] kotlin = ["kotlin-stdlib-jdk8", "kotlin-reflect"]
netty = ["netty-common", "netty-transport", "netty-handler-core", "netty-handler-proxy", "netty-resolver-core", "netty-resolver-dns", "netty-codec-core", "netty-codec-http", "netty-codec-http2", "netty-buffer", "netty-tcnative-core"] netty = ["netty-common", "netty-transport", "netty-handler-core", "netty-handler-proxy", "netty-resolver-core", "netty-resolver-dns", "netty-codec-core", "netty-codec-http", "netty-codec-http2", "netty-buffer", "netty-tcnative-core"]
reactor = ["reactor-core", "reactor-netty", "reactor-extra", "reactor-kotlin"] reactor = ["reactor-core", "reactor-netty", "reactor-extra", "reactor-kotlin", "reactor-micrometer"]
spring-framework = ["spring-boot-starter", "spring-security-core", "spring-security-web", "spring-security-config"] spring-framework = ["spring-boot-starter", "spring-boot-starter-actuator"]
testcontainers = ["testcontainers", "testcontainers-ganache"] testcontainers = ["testcontainers", "testcontainers-ganache"]
junit = ["junit-jupiter", "assertj"] junit = ["assertj"]
bouncycastle = ["bouncycastle-pkix", "bouncycastle-prov"] bouncycastle = ["bouncycastle-pkix", "bouncycastle-prov"]
[plugins] [plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
jib = { id = "com.google.cloud.tools.jib", version = "3.4.3" } jib = { id = "com.google.cloud.tools.jib", version = "3.5.2" }
spring = { id = "org.springframework.boot", version = "2.6.0" } spring = { id = "org.springframework.boot", version = "4.0.1" }
git = { id = "com.palantir.git-version", version = "0.12.3" } git = { id = "com.palantir.git-version", version = "0.12.3" }
protobuf = { id = "com.google.protobuf", version = "0.9.1" } protobuf = { id = "com.google.protobuf", version = "0.9.6" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.6.0" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version = "11.6.0" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

View File

@@ -25,10 +25,11 @@ import io.grpc.ServerCall
import io.grpc.ServerCallHandler import io.grpc.ServerCallHandler
import io.grpc.ServerInterceptor import io.grpc.ServerInterceptor
import io.grpc.netty.NettyServerBuilder import io.grpc.netty.NettyServerBuilder
import io.grpc.protobuf.services.ProtoReflectionService import io.grpc.protobuf.services.ProtoReflectionServiceV1
import io.micrometer.core.instrument.Metrics import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.scheduling.concurrent.CustomizableThreadFactory import org.springframework.scheduling.concurrent.CustomizableThreadFactory
@@ -36,8 +37,6 @@ import org.springframework.stereotype.Service
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
@Service @Service
open class GrpcServer( open class GrpcServer(
@@ -103,21 +102,16 @@ open class GrpcServer(
serverBuilder.addService(it) serverBuilder.addService(it)
} }
serverBuilder.addService(ProtoReflectionService.newInstance()) serverBuilder.addService(ProtoReflectionServiceV1.newInstance())
val pool = Executors.newFixedThreadPool(20, CustomizableThreadFactory("fixed-grpc-")) val pool = Executors.newFixedThreadPool(20, CustomizableThreadFactory("fixed-grpc-"))
serverBuilder.executor( serverBuilder.executor(
if (mainConfig.monitoring.enableExtended) { ExecutorServiceMetrics.monitor(
ExecutorServiceMetrics.monitor( Metrics.globalRegistry,
Metrics.globalRegistry, pool,
pool, "fixed-grpc-executor",
"fixed-grpc-executor", ),
Tag.of("reactor_scheduler_id", "_"),
)
} else {
pool
},
) )
val server = serverBuilder.build() val server = serverBuilder.build()

View File

@@ -17,32 +17,28 @@
package io.emeraldpay.dshackle package io.emeraldpay.dshackle
import io.emeraldpay.dshackle.config.MainConfig import io.emeraldpay.dshackle.config.MainConfig
import io.emeraldpay.dshackle.monitoring.MonitoringSetup
import io.emeraldpay.dshackle.monitoring.accesslog.AccessHandlerHttp import io.emeraldpay.dshackle.monitoring.accesslog.AccessHandlerHttp
import io.emeraldpay.dshackle.proxy.ProxyServer import io.emeraldpay.dshackle.proxy.ProxyServer
import io.emeraldpay.dshackle.proxy.ReadRpcJson import io.emeraldpay.dshackle.proxy.ReadRpcJson
import io.emeraldpay.dshackle.proxy.WriteRpcJson import io.emeraldpay.dshackle.proxy.WriteRpcJson
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 jakarta.annotation.PostConstruct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import javax.annotation.PostConstruct
/** /**
* Starts HTTP proxy endpoint, if configured * Starts HTTP proxy endpoint, if configured
*/ */
@Service @Service
class ProxyStarter( class ProxyStarter(
@Autowired private val mainConfig: MainConfig, private val mainConfig: MainConfig,
@Autowired private val readRpcJson: ReadRpcJson, private val readRpcJson: ReadRpcJson,
@Autowired private val writeRpcJson: WriteRpcJson, private val writeRpcJson: WriteRpcJson,
@Autowired private val nativeCall: NativeCall, private val nativeCall: NativeCall,
@Autowired private val nativeSubscribe: NativeSubscribe, private val nativeSubscribe: NativeSubscribe,
@Autowired private val tlsSetup: TlsSetup, private val tlsSetup: TlsSetup,
@Autowired private val accessHandlerHttp: AccessHandlerHttp, private val accessHandlerHttp: AccessHandlerHttp,
// depend on Monitoring, declared here just to ensure it's properly initialized before the Proxy
@Autowired private val monitoringSetup: MonitoringSetup,
) { ) {
companion object { companion object {

View File

@@ -16,6 +16,10 @@
*/ */
package io.emeraldpay.dshackle package io.emeraldpay.dshackle
import io.micrometer.context.ContextRegistry
import io.micrometer.context.integration.Slf4jThreadLocalAccessor
import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics
import io.netty.handler.ssl.OpenSsl import io.netty.handler.ssl.OpenSsl
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.boot.ResourceBanner import org.springframework.boot.ResourceBanner
@@ -23,6 +27,9 @@ import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.core.io.ClassPathResource import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.support.ResourcePropertySource import org.springframework.core.io.support.ResourcePropertySource
import reactor.core.Scannable
import reactor.core.Scannable.Attr
import reactor.core.publisher.Hooks
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
@SpringBootApplication(scanBasePackages = ["io.emeraldpay.dshackle"]) @SpringBootApplication(scanBasePackages = ["io.emeraldpay.dshackle"])
@@ -31,9 +38,22 @@ open class Starter
private val log = LoggerFactory.getLogger(Starter::class.java) private val log = LoggerFactory.getLogger(Starter::class.java)
fun main(args: Array<String>) { fun main(args: Array<String>) {
ContextRegistry.getInstance().registerThreadLocalAccessor(Slf4jThreadLocalAccessor())
Hooks.enableAutomaticContextPropagation()
OpenSsl.ensureAvailability() OpenSsl.ensureAvailability()
Schedulers.enableMetrics() // add metrics for internal reactor schedulers
Schedulers.addExecutorServiceDecorator("key") { scheduler, execService ->
val schedulerName = Scannable.from(scheduler).scanOrDefault(Attr.NAME, scheduler.javaClass.name)
if (schedulerName.contains("single") || schedulerName.contains("parallel")) {
ExecutorServiceMetrics.monitor(
Metrics.globalRegistry,
execService,
schedulerName,
)
}
execService
}
HeapDumpCreator.init() HeapDumpCreator.init()

View File

@@ -24,13 +24,12 @@ import io.netty.handler.ssl.SslContextBuilder
import io.netty.handler.ssl.SslProvider import io.netty.handler.ssl.SslProvider
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.security.cert.CertificateFactory import java.security.cert.CertificateFactory
@Service @Service
open class TlsSetup( open class TlsSetup(
@Autowired val fileResolver: FileResolver, val fileResolver: FileResolver,
) { ) {
companion object { companion object {

View File

@@ -24,16 +24,15 @@ import io.lettuce.core.api.StatefulRedisConnection
import io.lettuce.core.codec.ByteArrayCodec import io.lettuce.core.codec.ByteArrayCodec
import io.lettuce.core.codec.RedisCodec import io.lettuce.core.codec.RedisCodec
import io.lettuce.core.codec.StringCodec import io.lettuce.core.codec.StringCodec
import jakarta.annotation.PostConstruct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
import java.util.EnumMap import java.util.EnumMap
import javax.annotation.PostConstruct
import kotlin.system.exitProcess import kotlin.system.exitProcess
@Repository @Repository
open class CachesFactory( open class CachesFactory(
@Autowired private val cacheConfig: CacheConfig, private val cacheConfig: CacheConfig,
) { ) {
companion object { companion object {

View File

@@ -5,7 +5,7 @@ import reactor.core.publisher.Mono
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
class CurrentBlockCache<K, D> : Reader<K, D> { class CurrentBlockCache<K, D : Any> : Reader<K, D> {
private val cache = AtomicReference(ConcurrentHashMap<K, D>()) private val cache = AtomicReference(ConcurrentHashMap<K, D>())

View File

@@ -29,7 +29,7 @@ import java.time.Instant
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.min import kotlin.math.min
abstract class OnBlockRedisCache<T>( abstract class OnBlockRedisCache<T : Any>(
private val redis: RedisReactiveCommands<String, ByteArray>, private val redis: RedisReactiveCommands<String, ByteArray>,
private val chain: Chain, private val chain: Chain,
private val valueType: ValueContainer.ValueType, private val valueType: ValueContainer.ValueType,

View File

@@ -29,7 +29,7 @@ import java.time.Instant
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import kotlin.math.min import kotlin.math.min
abstract class OnTxRedisCache<T>( abstract class OnTxRedisCache<T : Any>(
private val redis: RedisReactiveCommands<String, ByteArray>, private val redis: RedisReactiveCommands<String, ByteArray>,
private val chain: Chain, private val chain: Chain,
private val valueType: CachesProto.ValueContainer.ValueType, private val valueType: CachesProto.ValueContainer.ValueType,

View File

@@ -13,7 +13,7 @@ import java.time.Duration
/** /**
* A flux holder that reconnects to it on failure taking into account a back off strategy * A flux holder that reconnects to it on failure taking into account a back off strategy
*/ */
class DurableFlux<T>( class DurableFlux<T : Any>(
private val provider: () -> Flux<T>, private val provider: () -> Flux<T>,
private val errorBackOff: BackOff, private val errorBackOff: BackOff,
private val log: Logger, private val log: Logger,
@@ -55,7 +55,7 @@ class DurableFlux<T>(
} }
} }
class Builder<T> { class Builder<T : Any> {
private var provider: (() -> Flux<T>)? = null private var provider: (() -> Flux<T>)? = null
@@ -63,7 +63,7 @@ class DurableFlux<T>(
protected var log: Logger = DurableFlux.defaultLog protected var log: Logger = DurableFlux.defaultLog
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <X> using(provider: () -> Flux<X>): Builder<X> { fun <X : Any> using(provider: () -> Flux<X>): Builder<X> {
this.provider = provider as () -> Flux<T> this.provider = provider as () -> Flux<T>
return this as Builder<X> return this as Builder<X>
} }

View File

@@ -6,7 +6,7 @@ import reactor.core.publisher.Sinks
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class DynamicMergeFlux<K : Any, T>(private val scheduler: Scheduler) { class DynamicMergeFlux<K : Any, T : Any>(private val scheduler: Scheduler) {
private val merge = Sinks.many().multicast().directBestEffort<T>() private val merge = Sinks.many().multicast().directBestEffort<T>()
private val sources = ConcurrentHashMap<K, Disposable>() private val sources = ConcurrentHashMap<K, Disposable>()

View File

@@ -11,7 +11,7 @@ import kotlin.concurrent.write
* A flux holder that that creates it only if requested. Keeps it for the following calls, so all the following calls will * A flux holder that that creates it only if requested. Keeps it for the following calls, so all the following calls will
* reuse it. Forgets as soon as it completes/cancelled, so it will be recreated again if needed. * reuse it. Forgets as soon as it completes/cancelled, so it will be recreated again if needed.
*/ */
class SharedFluxHolder<T>( class SharedFluxHolder<T : Any>(
/** /**
* Provider for the flux. Note that it can be called multiple times but only one is used at the same time. * Provider for the flux. Note that it can be called multiple times but only one is used at the same time.
* I.e., if there is a few calls because of a thread-race only one is kept. * I.e., if there is a few calls because of a thread-race only one is kept.
@@ -66,7 +66,7 @@ class SharedFluxHolder<T>(
} }
} }
data class Holder<T>( data class Holder<T : Any>(
val flux: Flux<T>, val flux: Flux<T>,
val id: Long, val id: Long,
) )

View File

@@ -91,7 +91,6 @@ class AuthConfigReader : YamlConfigReader<AuthConfig>() {
auth.clientCAs.add(it) auth.clientCAs.add(it)
} }
getList<ScalarNode>(clientNode, "cas")?.let { getList<ScalarNode>(clientNode, "cas")?.let {
println(it)
it.value?.let { crt -> it.value?.let { crt ->
auth.clientCAs.addAll(crt.map { v -> v.value }) auth.clientCAs.addAll(crt.map { v -> v.value })
} }

View File

@@ -38,7 +38,7 @@ class TokensConfig(
id.isNullOrBlank() -> "id" id.isNullOrBlank() -> "id"
blockchain == null -> "blockchain" blockchain == null -> "blockchain"
name.isNullOrBlank() -> "name" name.isNullOrBlank() -> "name"
type == null -> type type == null -> "type"
address.isNullOrBlank() -> "address" address.isNullOrBlank() -> "address"
blockchain != null && blockchain != null &&
(blockchain!!.type == BlockchainType.ETHEREUM) && (blockchain!!.type == BlockchainType.ETHEREUM) &&

View File

@@ -10,7 +10,6 @@ import io.emeraldpay.dshackle.upstream.generic.ChainSpecificRegistry
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.cloud.sleuth.Tracer
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
@@ -26,7 +25,6 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
headScheduler: Scheduler, headScheduler: Scheduler,
@Qualifier("subScheduler") @Qualifier("subScheduler")
subScheduler: Scheduler, subScheduler: Scheduler,
tracer: Tracer,
multistreamEventsScheduler: Scheduler, multistreamEventsScheduler: Scheduler,
): List<Multistream> { ): List<Multistream> {
return Chain.entries return Chain.entries
@@ -40,7 +38,6 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
cachesFactory, cachesFactory,
headScheduler, headScheduler,
subScheduler, subScheduler,
tracer,
multistreamEventsScheduler, multistreamEventsScheduler,
) )
} }
@@ -52,7 +49,6 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
cachesFactory: CachesFactory, cachesFactory: CachesFactory,
headScheduler: Scheduler, headScheduler: Scheduler,
subScheduler: Scheduler, subScheduler: Scheduler,
tracer: Tracer,
multistreamEventsScheduler: Scheduler, multistreamEventsScheduler: Scheduler,
): Multistream { ): Multistream {
val name = "multi-$chain" val name = "multi-$chain"
@@ -65,7 +61,7 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
CopyOnWriteArrayList(), CopyOnWriteArrayList(),
caches, caches,
headScheduler, headScheduler,
cs.makeCachingReaderBuilder(tracer), cs.makeCachingReaderBuilder(),
cs::localReaderBuilder, cs::localReaderBuilder,
cs.subscriptionBuilder(subScheduler), cs.subscriptionBuilder(subScheduler),
).also { register(it, name) } ).also { register(it, name) }

View File

@@ -1,12 +1,11 @@
package io.emeraldpay.dshackle.config.context package io.emeraldpay.dshackle.config.context
import io.emeraldpay.dshackle.config.MonitoringConfig
import io.micrometer.core.instrument.Metrics import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics import io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.DependsOn
import org.springframework.scheduling.concurrent.CustomizableThreadFactory import org.springframework.scheduling.concurrent.CustomizableThreadFactory
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
@@ -17,6 +16,7 @@ import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@Configuration @Configuration
@DependsOn("monitoringSetup")
open class SchedulersConfig { open class SchedulersConfig {
companion object { companion object {
@@ -32,65 +32,65 @@ open class SchedulersConfig {
} }
@Bean @Bean
open fun rpcScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun rpcScheduler(): Scheduler {
return makeScheduler("blockchain-rpc-scheduler", 20, monitoringConfig) return makeScheduler("blockchain-rpc-scheduler", 20)
} }
@Bean @Bean
open fun headScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun headScheduler(): Scheduler {
return makeScheduler("head-scheduler", 4, monitoringConfig) return makeScheduler("head-scheduler", 4)
} }
@Bean @Bean
open fun subScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun subScheduler(): Scheduler {
return makeScheduler("sub-scheduler", 4, monitoringConfig) return makeScheduler("sub-scheduler", 4)
} }
@Bean @Bean
open fun multistreamEventsScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun multistreamEventsScheduler(): Scheduler {
return makeScheduler("events-scheduler", 4, monitoringConfig) return makeScheduler("events-scheduler", 4)
} }
@Bean @Bean
open fun wsConnectionResubscribeScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun wsConnectionResubscribeScheduler(): Scheduler {
return makeScheduler("ws-connection-resubscribe-scheduler", 2, monitoringConfig) return makeScheduler("ws-connection-resubscribe-scheduler", 2)
} }
@Bean @Bean
open fun wsScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun wsScheduler(): Scheduler {
return makeScheduler("ws-scheduler", 4, monitoringConfig) return makeScheduler("ws-scheduler", 4)
} }
@Bean @Bean
open fun headLivenessScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun headLivenessScheduler(): Scheduler {
return makeScheduler("head-liveness-scheduler", 4, monitoringConfig) return makeScheduler("head-liveness-scheduler", 4)
} }
@Bean @Bean
open fun grpcChannelExecutor(monitoringConfig: MonitoringConfig): Executor { open fun grpcChannelExecutor(): Executor {
return makePool("grpc-client-channel", 10, monitoringConfig) return makePool("grpc-client-channel", 10)
} }
@Bean @Bean
open fun authScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun authScheduler(): Scheduler {
return makeScheduler("auth-scheduler", 4, monitoringConfig) return makeScheduler("auth-scheduler", 4)
} }
@Bean @Bean
open fun httpScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun httpScheduler(): Scheduler {
return makeScheduler("http-scheduler", 30, monitoringConfig) return makeScheduler("http-scheduler", 30)
} }
@Bean @Bean
open fun eventsScheduler(monitoringConfig: MonitoringConfig): Scheduler { open fun eventsScheduler(): Scheduler {
return makeScheduler("ws-events-scheduler", 30, monitoringConfig) return makeScheduler("ws-events-scheduler", 30)
} }
private fun makeScheduler(name: String, size: Int, monitoringConfig: MonitoringConfig): Scheduler { private fun makeScheduler(name: String, size: Int): Scheduler {
return Schedulers.fromExecutorService(makePool(name, size, monitoringConfig)) return Schedulers.fromExecutorService(makePool(name, size))
} }
private fun makePool(name: String, size: Int, monitoringConfig: MonitoringConfig): ExecutorService { private fun makePool(name: String, size: Int): ExecutorService {
val cachedPool = ThreadPoolExecutor( val cachedPool = ThreadPoolExecutor(
size, size,
size * threadsMultiplier, size * threadsMultiplier,
@@ -100,15 +100,10 @@ open class SchedulersConfig {
CustomizableThreadFactory("$name-"), CustomizableThreadFactory("$name-"),
) )
return if (monitoringConfig.enableExtended) { return ExecutorServiceMetrics.monitor(
ExecutorServiceMetrics.monitor( Metrics.globalRegistry,
Metrics.globalRegistry, cachedPool,
cachedPool, name,
name, )
Tag.of("reactor_scheduler_id", "_"),
)
} else {
cachedPool
}
} }
} }

View File

@@ -1,6 +1,9 @@
package io.emeraldpay.dshackle.config.context package io.emeraldpay.dshackle.config.context
import brave.Tracing
import brave.context.slf4j.MDCScopeDecorator
import brave.grpc.GrpcTracing import brave.grpc.GrpcTracing
import brave.propagation.CurrentTraceContext
import brave.rpc.RpcTracing import brave.rpc.RpcTracing
import brave.sampler.Sampler import brave.sampler.Sampler
import io.grpc.ServerInterceptor import io.grpc.ServerInterceptor
@@ -9,14 +12,28 @@ import org.springframework.context.annotation.Configuration
@Configuration @Configuration
open class TraceConfiguration { open class TraceConfiguration {
@Bean
open fun defaultSampler(): Sampler = Sampler.ALWAYS_SAMPLE
@Bean
open fun braveTracing(defaultSampler: Sampler): Tracing {
val currentTraceContext =
CurrentTraceContext.Default.newBuilder()
.addScopeDecorator(MDCScopeDecorator.newBuilder().build())
.build()
return Tracing.newBuilder()
.localServiceName("grpc-server")
.currentTraceContext(currentTraceContext)
.build()
}
@Bean
open fun rpcTracing(tracing: Tracing): RpcTracing = RpcTracing.create(tracing)
@Bean @Bean
open fun grpcTracing(rpcTracing: RpcTracing): GrpcTracing = GrpcTracing.create(rpcTracing) open fun grpcTracing(rpcTracing: RpcTracing): GrpcTracing = GrpcTracing.create(rpcTracing)
@Bean @Bean
open fun grpcServerBraveInterceptor(grpcTracing: GrpcTracing): ServerInterceptor = open fun grpcServerBraveInterceptor(grpcTracing: GrpcTracing): ServerInterceptor = grpcTracing.newServerInterceptor()
grpcTracing.newServerInterceptor()
@Bean
open fun defaultSampler(): Sampler = Sampler.ALWAYS_SAMPLE
} }

View File

@@ -1,8 +1,10 @@
package io.emeraldpay.dshackle.config.hot package io.emeraldpay.dshackle.config.hot
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import org.apache.hc.client5.http.classic.methods.HttpGet
import org.apache.hc.client5.http.impl.classic.HttpClients
import org.apache.hc.core5.http.io.entity.EntityUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.web.client.RestTemplate
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import java.time.Duration import java.time.Duration
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
@@ -17,12 +19,13 @@ class AutoReloadbleConfig<T>(
private val log = LoggerFactory.getLogger(AutoReloadbleConfig::class.java) private val log = LoggerFactory.getLogger(AutoReloadbleConfig::class.java)
} }
private val restTemplate = RestTemplate() private val httpClient = HttpClients.createDefault()
private val instance = AtomicReference<T>() private val instance = AtomicReference<T>()
fun reload() { fun reload() {
try { try {
val response = restTemplate.getForObject(configUrl, String::class.java) val getReq = HttpGet(configUrl)
val response = httpClient.execute<String>(getReq) { resp -> EntityUtils.toString(resp.entity) }
if (response != null) { if (response != null) {
instance.set(parseConfig(response)) instance.set(parseConfig(response))
} }

View File

@@ -5,16 +5,16 @@ import com.fasterxml.jackson.annotation.JsonProperty
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CompatibleVersionsRules( data class CompatibleVersionsRules(
@JsonProperty("rules") @param:JsonProperty("rules")
val rules: List<CompatibleVersionsRule>, val rules: List<CompatibleVersionsRule>,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CompatibleVersionsRule( data class CompatibleVersionsRule(
@JsonProperty("client") @param:JsonProperty("client")
val client: String, val client: String,
@JsonProperty("blacklist") @param:JsonProperty("blacklist")
val blacklist: List<String>?, val blacklist: List<String>?,
@JsonProperty("whitelist") @param:JsonProperty("whitelist")
val whitelist: List<String>?, val whitelist: List<String>?,
) )

View File

@@ -1,37 +0,0 @@
package io.emeraldpay.dshackle.config.spans
import brave.handler.MutableSpan
import io.emeraldpay.dshackle.commons.SPAN_ERROR
import io.emeraldpay.dshackle.commons.SPAN_NO_RESPONSE_MESSAGE
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.util.concurrent.TimeUnit.MICROSECONDS
import java.util.concurrent.TimeUnit.MILLISECONDS
interface SpanExportable {
fun isExportable(span: MutableSpan): Boolean
}
@Component
class ErrorSpanExportable : SpanExportable {
override fun isExportable(span: MutableSpan): Boolean = span.tags().containsKey(SPAN_ERROR)
}
@Component
class NoResponseSpanExportable : SpanExportable {
override fun isExportable(span: MutableSpan): Boolean = span.tags().containsKey(SPAN_NO_RESPONSE_MESSAGE)
}
@Component
class LongResponseSpanExportable(
@Value("\${spans.collect.long-span-threshold}")
private val longSpanThreshold: Long? = null,
) : SpanExportable {
override fun isExportable(span: MutableSpan): Boolean {
return MILLISECONDS.convert(
span.finishTimestamp() - span.startTimestamp(),
MICROSECONDS,
) >= longSpanThreshold!!
}
}

View File

@@ -1,84 +0,0 @@
package io.emeraldpay.dshackle.config.spans
import brave.handler.MutableSpan
import brave.handler.SpanHandler
import brave.propagation.TraceContext
import com.github.benmanes.caffeine.cache.Caffeine
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.stereotype.Component
import java.time.Duration
@Component
@ConditionalOnProperty(value = ["spring.zipkin.enabled"], havingValue = "true")
class ProviderSpanHandler(
private val spanExportableList: List<SpanExportable>,
private val zipkinSpanHandler: SpanHandler,
) : SpanHandler() {
private val log = LoggerFactory.getLogger(ProviderSpanHandler::class.java)
private val spans = Caffeine
.newBuilder()
.expireAfterWrite(Duration.ofMinutes(2))
.build<String, MutableList<MutableSpan>>()
override fun end(context: TraceContext, span: MutableSpan, cause: Cause): Boolean {
if (span.traceId().length > 20 && cause == Cause.FINISHED) {
val key = span.parentId() ?: span.id()
val spanList = spans.asMap().computeIfAbsent(key) { mutableListOf() }
spanList.add(span)
}
return false
}
fun sendSpans(traceContext: TraceContext) {
try {
sendSpansInternal(traceContext)
} catch (e: Exception) {
log.warn("Error while handling and sending spans - ${e.message}")
}
}
private fun sendSpansInternal(traceContext: TraceContext) {
val spansInfo = SpansInfo()
processSpans(traceContext.spanIdString(), spansInfo)
spansInfo.spans
.map { it.parentId() }
.forEach {
if (it != null) {
spans.invalidate(it)
}
}
if (spansInfo.exportable) {
spansInfo.spans.forEach {
zipkinSpanHandler.end(traceContext, it, Cause.FINISHED)
}
}
}
private fun processSpans(spanId: String, spansInfo: SpansInfo) {
val currentSpans: List<MutableSpan>? = spans.getIfPresent(spanId)
currentSpans?.forEach {
processSpanInfo(it, spansInfo)
if (spanId != it.id()) {
processSpans(it.id(), spansInfo)
}
}
}
private fun processSpanInfo(span: MutableSpan, spansInfo: SpansInfo) {
spansInfo.spans.add(span)
if (spanExportableList.any { it.isExportable(span) }) {
spansInfo.exportable = true
}
}
private data class SpansInfo(
var exportable: Boolean = false,
val spans: MutableList<MutableSpan> = mutableListOf(),
)
}

View File

@@ -1,90 +0,0 @@
package io.emeraldpay.dshackle.config.spans
import io.emeraldpay.dshackle.config.MainConfig
import org.apache.http.conn.ssl.SSLConnectionSocketFactory
import org.apache.http.impl.client.HttpClients
import org.bouncycastle.openssl.PEMParser
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer
import org.springframework.http.HttpRequest
import org.springframework.http.client.ClientHttpRequestExecution
import org.springframework.http.client.ClientHttpRequestInterceptor
import org.springframework.http.client.ClientHttpResponse
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory
import org.springframework.stereotype.Component
import org.springframework.web.client.RestTemplate
import java.io.ByteArrayOutputStream
import java.io.StringReader
import java.nio.file.Files
import java.nio.file.Paths
import java.security.KeyFactory
import java.security.KeyStore
import java.security.SecureRandom
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.security.spec.PKCS8EncodedKeySpec
import java.util.zip.GZIPOutputStream
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
@Component
class ZipkinSSLCustomizer(private val mainConfig: MainConfig) : ZipkinRestTemplateCustomizer {
override fun customizeTemplate(restTemplate: RestTemplate): RestTemplate {
return if (mainConfig.tls?.enabled == true) {
setupSSL(mainConfig.tls!!.certificate!!, mainConfig.tls!!.key!!)
} else {
restTemplate
}.apply {
interceptors.add(0, GZipInterceptor())
}
}
private fun setupSSL(certPath: String, privateKeyPath: String): RestTemplate {
// Load the certificate
val certificateReader = StringReader(Files.readString(Paths.get(certPath)))
val pemObject = PEMParser(certificateReader).readPemObject()
val certificate = CertificateFactory.getInstance("X.509").generateCertificate(pemObject.content.inputStream()) as X509Certificate
// Load the private key
val privateKeyReader = StringReader(Files.readString(Paths.get(privateKeyPath)))
val pemKeyPair = PEMParser(privateKeyReader).readPemObject()
val privKeySpec = PKCS8EncodedKeySpec(pemKeyPair.content)
val privateKey = KeyFactory.getInstance("RSA").generatePrivate(privKeySpec)
// Create the key store
val keyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null)
setCertificateEntry("certificate", certificate)
setKeyEntry("private-key", privateKey, null, arrayOf(certificate))
}
// Create the SSL context
val keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()).apply {
init(keyStore, null)
}
val sslContext = SSLContext.getInstance("TLS").apply {
init(keyManagerFactory.keyManagers, null, SecureRandom())
}
// Create the HTTP client
val socketFactory = SSLConnectionSocketFactory(sslContext)
val httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build()
// Create the request factory
val requestFactory = HttpComponentsClientHttpRequestFactory(httpClient)
// Create the RestTemplate
return RestTemplate(requestFactory)
}
private class GZipInterceptor : ClientHttpRequestInterceptor {
override fun intercept(
request: HttpRequest,
body: ByteArray,
execution: ClientHttpRequestExecution,
): ClientHttpResponse {
request.headers.add("Content-Encoding", "gzip")
val gzipped = ByteArrayOutputStream()
GZIPOutputStream(gzipped).use { compressor -> compressor.write(body) }
return execution.execute(request, gzipped.toByteArray())
}
}
}

View File

@@ -18,19 +18,18 @@ package io.emeraldpay.dshackle.monitoring
import com.sun.net.httpserver.HttpServer import com.sun.net.httpserver.HttpServer
import io.emeraldpay.dshackle.config.HealthConfig import io.emeraldpay.dshackle.config.HealthConfig
import io.emeraldpay.dshackle.upstream.MultistreamHolder import io.emeraldpay.dshackle.upstream.MultistreamHolder
import jakarta.annotation.PostConstruct
import jakarta.annotation.PreDestroy
import org.apache.hc.core5.http.HttpStatus
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.IOException import java.io.IOException
import java.net.InetSocketAddress import java.net.InetSocketAddress
import javax.annotation.PostConstruct
import javax.annotation.PreDestroy
@Service @Service
class HealthCheckSetup( class HealthCheckSetup(
@Autowired private val healthConfig: HealthConfig, private val healthConfig: HealthConfig,
@Autowired private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
) { ) {
companion object { companion object {
@@ -63,9 +62,9 @@ class HealthCheckSetup(
} }
val ok = response.ok val ok = response.ok
val data = response.details.joinToString("\n") val data = response.details.joinToString("\n")
val code = if (ok) HttpStatus.OK else HttpStatus.SERVICE_UNAVAILABLE val code = if (ok) HttpStatus.SC_OK else HttpStatus.SC_SERVICE_UNAVAILABLE
log.debug("Health check response: ${code.value()} ${code.reasonPhrase} $data") log.debug("Health check response: $code $data")
httpExchange.sendResponseHeaders(code.value(), data.toByteArray().size.toLong()) httpExchange.sendResponseHeaders(code, data.toByteArray().size.toLong())
httpExchange.responseBody.use { os -> httpExchange.responseBody.use { os ->
os.write(data.toByteArray()) os.write(data.toByteArray())
} }

View File

@@ -26,10 +26,10 @@ import io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics
import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics import io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics
import io.micrometer.core.instrument.binder.system.ProcessorMetrics import io.micrometer.core.instrument.binder.system.ProcessorMetrics
import io.micrometer.core.instrument.config.MeterFilter import io.micrometer.core.instrument.config.MeterFilter
import io.micrometer.prometheus.PrometheusConfig import io.micrometer.prometheusmetrics.PrometheusConfig
import io.micrometer.prometheus.PrometheusMeterRegistry import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
import jakarta.annotation.PostConstruct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.io.IOException import java.io.IOException
import java.net.InetAddress import java.net.InetAddress
@@ -41,11 +41,10 @@ import java.util.concurrent.ThreadFactory
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import javax.annotation.PostConstruct
@Service @Service
class MonitoringSetup( class MonitoringSetup(
@Autowired private val monitoringConfig: MonitoringConfig, private val monitoringConfig: MonitoringConfig,
) { ) {
companion object { companion object {

View File

@@ -23,13 +23,12 @@ import io.grpc.ServerCall
import io.grpc.ServerCallHandler import io.grpc.ServerCallHandler
import io.grpc.ServerInterceptor import io.grpc.ServerInterceptor
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.Instant import java.time.Instant
@Service @Service
class AccessHandlerGrpc( class AccessHandlerGrpc(
@Autowired private val accessLogWriter: AccessLogWriter, private val accessLogWriter: AccessLogWriter,
) : ServerInterceptor { ) : ServerInterceptor {
companion object { companion object {

View File

@@ -5,7 +5,6 @@ import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.config.MainConfig import io.emeraldpay.dshackle.config.MainConfig
import io.emeraldpay.dshackle.rpc.NativeCall import io.emeraldpay.dshackle.rpc.NativeCall
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.netty.http.server.HttpServerRequest import reactor.netty.http.server.HttpServerRequest
import reactor.netty.http.websocket.WebsocketInbound import reactor.netty.http.websocket.WebsocketInbound
@@ -20,8 +19,8 @@ import kotlin.concurrent.withLock
*/ */
@Service @Service
class AccessHandlerHttp( class AccessHandlerHttp(
@Autowired private val mainConfig: MainConfig, mainConfig: MainConfig,
@Autowired accessLogWriter: AccessLogWriter, accessLogWriter: AccessLogWriter,
) { ) {
companion object { companion object {

View File

@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.monitoring.accesslog
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.config.MainConfig import io.emeraldpay.dshackle.config.MainConfig
import jakarta.annotation.PostConstruct
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Repository import org.springframework.stereotype.Repository
@@ -28,7 +29,6 @@ import java.time.Instant
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.annotation.PostConstruct
@Repository @Repository
class AccessLogWriter( class AccessLogWriter(

View File

@@ -24,9 +24,9 @@ import io.emeraldpay.dshackle.upstream.ChainResponse
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled import io.netty.buffer.Unpooled
import org.apache.hc.core5.http.HttpHeaders
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.http.HttpHeaders
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.netty.http.server.HttpServerRequest import reactor.netty.http.server.HttpServerRequest

View File

@@ -28,7 +28,8 @@ import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Timer import io.micrometer.core.instrument.Timer
import io.netty.channel.ChannelHandler import io.netty.channel.ChannelHandler
import io.netty.channel.ChannelHandlerContext import io.netty.channel.ChannelHandlerContext
import io.netty.channel.nio.NioEventLoopGroup import io.netty.channel.MultiThreadIoEventLoopGroup
import io.netty.channel.nio.NioIoHandler
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import reactor.netty.http.server.HttpServer import reactor.netty.http.server.HttpServer
import reactor.netty.http.server.HttpServerRoutes import reactor.netty.http.server.HttpServerRoutes
@@ -108,7 +109,7 @@ class ProxyServer(
serverBuilder serverBuilder
.route(this::setupRoutes) .route(this::setupRoutes)
.runOn(NioEventLoopGroup()) .runOn(MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()))
.bindNow() .bindNow()
} }

View File

@@ -16,12 +16,9 @@
package io.emeraldpay.dshackle.quorum package io.emeraldpay.dshackle.quorum
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.commons.API_READER
import io.emeraldpay.dshackle.commons.SPAN_NO_RESPONSE_MESSAGE
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_API_TYPE import io.emeraldpay.dshackle.commons.SPAN_REQUEST_API_TYPE
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID
import io.emeraldpay.dshackle.reader.RequestReader import io.emeraldpay.dshackle.reader.RequestReader
import io.emeraldpay.dshackle.reader.SpannedReader
import io.emeraldpay.dshackle.upstream.ApiSource import io.emeraldpay.dshackle.upstream.ApiSource
import io.emeraldpay.dshackle.upstream.ChainCallUpstreamException import io.emeraldpay.dshackle.upstream.ChainCallUpstreamException
import io.emeraldpay.dshackle.upstream.ChainException import io.emeraldpay.dshackle.upstream.ChainException
@@ -32,7 +29,6 @@ import io.emeraldpay.dshackle.upstream.error.UpstreamErrorHandler
import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException import io.emeraldpay.dshackle.upstream.ethereum.rpc.RpcException
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
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.Tuple2
@@ -50,7 +46,6 @@ class QuorumRequestReader(
private val apiControl: ApiSource, private val apiControl: ApiSource,
private val quorum: CallQuorum, private val quorum: CallQuorum,
signer: ResponseSigner?, signer: ResponseSigner?,
private val tracer: Tracer,
) : RequestReader(signer) { ) : RequestReader(signer) {
private val errorHandler = UpstreamErrorHandler private val errorHandler = UpstreamErrorHandler
@@ -58,7 +53,7 @@ class QuorumRequestReader(
private val log = LoggerFactory.getLogger(QuorumRequestReader::class.java) private val log = LoggerFactory.getLogger(QuorumRequestReader::class.java)
} }
constructor(apiControl: ApiSource, quorum: CallQuorum, tracer: Tracer) : this(apiControl, quorum, null, tracer) constructor(apiControl: ApiSource, quorum: CallQuorum) : this(apiControl, quorum, null)
override fun attempts(): AtomicInteger = apiControl.attempts() override fun attempts(): AtomicInteger = apiControl.attempts()
@@ -147,7 +142,7 @@ class QuorumRequestReader(
SPAN_REQUEST_API_TYPE to apiReader.javaClass.name, SPAN_REQUEST_API_TYPE to apiReader.javaClass.name,
SPAN_REQUEST_UPSTREAM_ID to api.getId(), SPAN_REQUEST_UPSTREAM_ID to api.getId(),
) )
return SpannedReader(apiReader, tracer, API_READER, spanParams) return apiReader
.read(key) .read(key)
.flatMap { response -> .flatMap { response ->
log.trace("Received response from upstream ${api.getId()} for method ${key.method}") log.trace("Received response from upstream ${api.getId()} for method ${key.method}")
@@ -173,7 +168,7 @@ class QuorumRequestReader(
} }
} }
private fun <T> withErrorResume(api: Upstream, key: ChainRequest): Function<Mono<T>, Mono<T>> { private fun <T : Any> withErrorResume(api: Upstream, key: ChainRequest): Function<Mono<T>, Mono<T>> {
return Function { src -> return Function { src ->
src.onErrorResume { err -> src.onErrorResume { err ->
errorHandler.handle(api, key, err.message) errorHandler.handle(api, key, err.message)
@@ -232,7 +227,6 @@ class QuorumRequestReader(
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())
val cause = getCause(method) ?: return Mono.error(RpcException(1, "No response for method $method", getFullCause())) val cause = getCause(method) ?: return Mono.error(RpcException(1, "No response for method $method", getFullCause()))
if (cause.shouldReturnNull) { if (cause.shouldReturnNull) {
Mono.just( Mono.just(

View File

@@ -1,7 +1,5 @@
package io.emeraldpay.dshackle.reader package io.emeraldpay.dshackle.reader
import io.emeraldpay.dshackle.commons.BROADCAST_READER
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_UPSTREAM_ID
import io.emeraldpay.dshackle.quorum.CallQuorum import io.emeraldpay.dshackle.quorum.CallQuorum
import io.emeraldpay.dshackle.upstream.ChainException import io.emeraldpay.dshackle.upstream.ChainException
import io.emeraldpay.dshackle.upstream.ChainRequest import io.emeraldpay.dshackle.upstream.ChainRequest
@@ -11,7 +9,6 @@ import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.error.UpstreamErrorHandler import io.emeraldpay.dshackle.upstream.error.UpstreamErrorHandler
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
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 java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@@ -21,7 +18,6 @@ class BroadcastReader(
matcher: Selector.Matcher, matcher: Selector.Matcher,
signer: ResponseSigner?, signer: ResponseSigner?,
private val quorum: CallQuorum, private val quorum: CallQuorum,
private val tracer: Tracer,
) : RequestReader(signer) { ) : RequestReader(signer) {
private val errorHandler = UpstreamErrorHandler private val errorHandler = UpstreamErrorHandler
private val internalMatcher = Selector.MultiMatcher( private val internalMatcher = Selector.MultiMatcher(
@@ -77,12 +73,7 @@ class BroadcastReader(
key: ChainRequest, key: ChainRequest,
upstream: Upstream, upstream: Upstream,
): Mono<BroadcastResponse> = ): Mono<BroadcastResponse> =
SpannedReader( upstream.getIngressReader()
upstream.getIngressReader(),
tracer,
BROADCAST_READER,
mapOf(SPAN_REQUEST_UPSTREAM_ID to upstream.getId()),
)
.read(key) .read(key)
.map { BroadcastResponse(it, upstream) } .map { BroadcastResponse(it, upstream) }
.onErrorResume { .onErrorResume {

View File

@@ -25,7 +25,7 @@ import reactor.core.publisher.Mono
* Composition of multiple readers. * Composition of multiple readers.
* Reader returns first value returned by any of the source readers by checking one by one until one of them returns a non-empty result. * Reader returns first value returned by any of the source readers by checking one by one until one of them returns a non-empty result.
*/ */
class CompoundReader<K, D> ( class CompoundReader<K, D : Any> (
private vararg val readers: Reader<K, D>, private vararg val readers: Reader<K, D>,
) : Reader<K, D> { ) : Reader<K, D> {

View File

@@ -18,7 +18,7 @@ package io.emeraldpay.dshackle.reader
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
class EmptyReader<K, D> : Reader<K, D> { class EmptyReader<K, D : Any> : Reader<K, D> {
override fun read(key: K): Mono<D> { override fun read(key: K): Mono<D> {
return Mono.empty() return Mono.empty()

View File

@@ -18,7 +18,7 @@ package io.emeraldpay.dshackle.reader
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
interface Reader<in K, D> { interface Reader<in K, D : Any> {
fun read(key: K): Mono<D> fun read(key: K): Mono<D>
} }

View File

@@ -21,7 +21,7 @@ import java.util.function.Function
/** /**
* Reader wrapper that maps the input key from ne value to another (ex. convert from Long to String) * Reader wrapper that maps the input key from ne value to another (ex. convert from Long to String)
*/ */
class RekeyingReader<K, K1, D>( class RekeyingReader<K : Any, K1 : Any, D : Any>(
/** /**
* Mapping between original Key and Key supported by the reader * Mapping between original Key and Key supported by the reader
*/ */

View File

@@ -15,7 +15,6 @@ 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.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.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@@ -72,10 +71,10 @@ interface RequestReaderFactory {
class Default : RequestReaderFactory { class Default : RequestReaderFactory {
override fun create(data: ReaderData): RequestReader { override fun create(data: ReaderData): RequestReader {
if (data.quorum is MaximumValueQuorum || data.quorum is BroadcastQuorum) { if (data.quorum is MaximumValueQuorum || data.quorum is BroadcastQuorum) {
return BroadcastReader(data.multistream.getAll(), data.upstreamFilter.matcher, data.signer, data.quorum, data.tracer) return BroadcastReader(data.multistream.getAll(), data.upstreamFilter.matcher, data.signer, data.quorum)
} }
val apis = data.multistream.getApiSource(data.upstreamFilter) val apis = data.multistream.getApiSource(data.upstreamFilter)
return QuorumRequestReader(apis, data.quorum, data.signer, data.tracer) return QuorumRequestReader(apis, data.quorum, data.signer)
} }
} }
@@ -84,6 +83,5 @@ interface RequestReaderFactory {
val upstreamFilter: Selector.UpstreamFilter, val upstreamFilter: Selector.UpstreamFilter,
val quorum: CallQuorum, val quorum: CallQuorum,
val signer: ResponseSigner?, val signer: ResponseSigner?,
val tracer: Tracer,
) )
} }

View File

@@ -1,58 +0,0 @@
package io.emeraldpay.dshackle.reader
import io.emeraldpay.dshackle.commons.SPAN_ERROR
import io.emeraldpay.dshackle.commons.SPAN_READER_NAME
import io.emeraldpay.dshackle.commons.SPAN_READER_RESULT
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_CANCELLED
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_INFO
import io.emeraldpay.dshackle.commons.SPAN_STATUS_MESSAGE
import io.emeraldpay.dshackle.data.HashId
import io.emeraldpay.dshackle.upstream.ChainRequest
import org.springframework.cloud.sleuth.Tracer
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth
import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.switchIfEmpty
class SpannedReader<K, D>(
private val reader: Reader<K, D>,
private val tracer: Tracer,
private val name: String,
private val additionalParams: Map<String, String> = emptyMap(),
) : Reader<K, D> {
override fun read(key: K): Mono<D> {
val newSpan = tracer.nextSpan(tracer.currentSpan())
.name(reader.javaClass.name)
.tag(SPAN_READER_NAME, name)
.start()
extractInfoFromKey(key)?.let {
newSpan.tag(SPAN_REQUEST_INFO, it)
}
additionalParams.forEach { newSpan.tag(it.key, it.value) }
return reader.read(key)
.contextWrite { ReactorSleuth.putSpanInScope(tracer, it, newSpan) }
.doOnError {
newSpan.tag(SPAN_ERROR, "true")
.tag(SPAN_STATUS_MESSAGE, it.message)
.end()
}
.doOnNext { newSpan.end() }
.doOnCancel {
newSpan.tag(SPAN_STATUS_MESSAGE, SPAN_REQUEST_CANCELLED).end()
}
.switchIfEmpty {
newSpan.tag(SPAN_READER_RESULT, "empty result").end()
Mono.empty()
}
}
private fun extractInfoFromKey(key: K): String? {
return when (key) {
is ChainRequest -> "method: ${key.method}"
is HashId, Long -> "params: $key"
else -> null
}
}
}

View File

@@ -21,7 +21,7 @@ import java.util.function.Function
/** /**
* Reader wrapper that transforms output of the reader to a different format * Reader wrapper that transforms output of the reader to a different format
*/ */
class TransformingReader<K, D0, D>( class TransformingReader<K, D0 : Any, D : Any>(
/** /**
* Actual reader * Actual reader
*/ */

View File

@@ -16,19 +16,16 @@
*/ */
package io.emeraldpay.dshackle.rpc package io.emeraldpay.dshackle.rpc
import brave.Tracer
import io.emeraldpay.api.proto.BlockchainOuterClass import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common import io.emeraldpay.api.proto.Common
import io.emeraldpay.api.proto.ReactorBlockchainGrpc import io.emeraldpay.api.proto.ReactorBlockchainGrpc
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.ChainValue import io.emeraldpay.dshackle.ChainValue
import io.emeraldpay.dshackle.config.spans.ProviderSpanHandler
import io.micrometer.core.instrument.Counter import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Metrics import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Timer import io.micrometer.core.instrument.Timer
import org.apache.commons.lang3.RandomStringUtils import org.apache.commons.lang3.RandomStringUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.context.annotation.DependsOn import org.springframework.context.annotation.DependsOn
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@@ -47,11 +44,8 @@ class BlockchainRpc(
private val describe: Describe, private val describe: Describe,
private val subscribeStatus: SubscribeStatus, private val subscribeStatus: SubscribeStatus,
private val subscribeNodeStatus: SubscribeNodeStatus, private val subscribeNodeStatus: SubscribeNodeStatus,
@Qualifier("rpcScheduler") @param:Qualifier("rpcScheduler")
private val scheduler: Scheduler, private val scheduler: Scheduler,
@Autowired(required = false)
private val providerSpanHandler: ProviderSpanHandler?,
private val tracer: Tracer,
private val subscribeChainStatus: SubscribeChainStatus, private val subscribeChainStatus: SubscribeChainStatus,
) : ReactorBlockchainGrpc.BlockchainImplBase() { ) : ReactorBlockchainGrpc.BlockchainImplBase() {
@@ -100,10 +94,6 @@ class BlockchainRpc(
} }
}.doOnError { }.doOnError {
failMetric.increment() failMetric.increment()
}.doFinally {
tracer.currentSpan()?.run {
providerSpanHandler?.sendSpans(this.context())
}
} }
} }

View File

@@ -21,14 +21,13 @@ import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.upstream.Capability import io.emeraldpay.dshackle.upstream.Capability
import io.emeraldpay.dshackle.upstream.MultistreamHolder import io.emeraldpay.dshackle.upstream.MultistreamHolder
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@Service @Service
class Describe( class Describe(
@Autowired private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
@Autowired private val subscribeStatus: SubscribeStatus, private val subscribeStatus: SubscribeStatus,
) { ) {
fun describe(requestMono: Mono<BlockchainOuterClass.DescribeRequest>): Mono<BlockchainOuterClass.DescribeResponse> { fun describe(requestMono: Mono<BlockchainOuterClass.DescribeRequest>): Mono<BlockchainOuterClass.DescribeResponse> {

View File

@@ -25,19 +25,12 @@ import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.Global.Companion.nullValue import io.emeraldpay.dshackle.Global.Companion.nullValue
import io.emeraldpay.dshackle.SilentException import io.emeraldpay.dshackle.SilentException
import io.emeraldpay.dshackle.commons.LOCAL_READER
import io.emeraldpay.dshackle.commons.RPC_READER
import io.emeraldpay.dshackle.commons.SPAN_ERROR
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_CANCELLED
import io.emeraldpay.dshackle.commons.SPAN_REQUEST_ID
import io.emeraldpay.dshackle.commons.SPAN_STATUS_MESSAGE
import io.emeraldpay.dshackle.config.MainConfig import io.emeraldpay.dshackle.config.MainConfig
import io.emeraldpay.dshackle.quorum.CallQuorum import io.emeraldpay.dshackle.quorum.CallQuorum
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
import io.emeraldpay.dshackle.reader.RequestReader import io.emeraldpay.dshackle.reader.RequestReader
import io.emeraldpay.dshackle.reader.RequestReaderFactory import io.emeraldpay.dshackle.reader.RequestReaderFactory
import io.emeraldpay.dshackle.reader.RequestReaderFactory.ReaderData import io.emeraldpay.dshackle.reader.RequestReaderFactory.ReaderData
import io.emeraldpay.dshackle.reader.SpannedReader
import io.emeraldpay.dshackle.upstream.ApiSource import io.emeraldpay.dshackle.upstream.ApiSource
import io.emeraldpay.dshackle.upstream.ChainCallError import io.emeraldpay.dshackle.upstream.ChainCallError
import io.emeraldpay.dshackle.upstream.ChainException import io.emeraldpay.dshackle.upstream.ChainException
@@ -61,21 +54,16 @@ import io.micrometer.core.instrument.Metrics
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.cloud.sleuth.Span
import org.springframework.cloud.sleuth.Tracer
import org.springframework.cloud.sleuth.instrument.reactor.ReactorSleuth
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.kotlin.core.publisher.toMono import reactor.kotlin.core.publisher.toMono
import reactor.util.context.Context
@Service @Service
open class NativeCall( open class NativeCall(
private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
private val signer: ResponseSigner, private val signer: ResponseSigner,
config: MainConfig, config: MainConfig,
private val tracer: Tracer,
) { ) {
private val log = LoggerFactory.getLogger(NativeCall::class.java) private val log = LoggerFactory.getLogger(NativeCall::class.java)
@@ -89,16 +77,14 @@ open class NativeCall(
return nativeCallResult(requestMono) return nativeCallResult(requestMono)
.flatMapSequential(this::processCallResult) .flatMapSequential(this::processCallResult)
.onErrorResume(this::processException) .onErrorResume(this::processException)
.contextCapture()
} }
open fun nativeCallResult(requestMono: Mono<BlockchainOuterClass.NativeCallRequest>): Flux<CallResult> { open fun nativeCallResult(requestMono: Mono<BlockchainOuterClass.NativeCallRequest>): Flux<CallResult> {
val requestSpan = tracer.currentSpan()
return requestMono.flatMapMany(this::prepareCall) return requestMono.flatMapMany(this::prepareCall)
.flatMap { .flatMap {
val requestId = it.requestId
val requestCount = it.requestCount
val id = it.getContextId() val id = it.getContextId()
val result = processCallContext(it, requestSpan) val result = processCallContext(it)
return@flatMap result return@flatMap result
.onErrorResume { err -> .onErrorResume { err ->
@@ -122,12 +108,7 @@ open class NativeCall(
} }
} }
} }
completeSpan(callRes, requestCount)
} }
.doOnCancel {
tracer.currentSpan()?.tag(SPAN_STATUS_MESSAGE, SPAN_REQUEST_CANCELLED)?.end()
}
.contextWrite { ctx -> createTracingReactorContext(ctx, requestCount, requestId, requestSpan) }
} }
} }
@@ -178,42 +159,8 @@ open class NativeCall(
return builder return builder
} }
private fun completeSpan(callResult: CallResult, requestCount: Int) {
val span = tracer.currentSpan()
if (callResult.isError()) {
errorSpan(span, callResult.error?.message ?: "Internal error")
}
if (requestCount > 1) {
span?.end()
}
}
private fun errorSpan(span: Span?, message: String) {
span?.apply {
tag(SPAN_ERROR, "true")
tag(SPAN_STATUS_MESSAGE, message)
}
}
private fun createTracingReactorContext(
ctx: Context,
requestCount: Int,
requestId: String,
requestSpan: Span?,
): Context {
if (requestCount > 1) {
val span = tracer.nextSpan(requestSpan)
.name("emerald.blockchain/nativecall")
.tag(SPAN_REQUEST_ID, requestId)
.start()
return ReactorSleuth.putSpanInScope(tracer, ctx, span)
}
return ctx
}
private fun processCallContext( private fun processCallContext(
callContext: CallContext, callContext: CallContext,
requestSpan: Span?,
): Mono<CallResult> { ): Mono<CallResult> {
return if (callContext.isValid()) { return if (callContext.isValid()) {
run { run {
@@ -222,9 +169,6 @@ open class NativeCall(
} catch (e: Exception) { } catch (e: Exception) {
return@run Mono.error(e) return@run Mono.error(e)
} }
if (callContext.requestCount == 1 && callContext.requestId.isNotBlank()) {
requestSpan?.tag(SPAN_REQUEST_ID, callContext.requestId)
}
this.fetch(parsed) this.fetch(parsed)
.doOnError { e -> log.warn("Error during native call: ${e.message}") } .doOnError { e -> log.warn("Error during native call: ${e.message}") }
} }
@@ -306,7 +250,6 @@ open class NativeCall(
0 0
} }
val message = it?.message ?: "Internal error" val message = it?.message ?: "Internal error"
errorSpan(tracer.currentSpan(), message)
return BlockchainOuterClass.NativeCallReplyItem.newBuilder() return BlockchainOuterClass.NativeCallReplyItem.newBuilder()
.setSucceed(false) .setSucceed(false)
.setErrorMessage(message) .setErrorMessage(message)
@@ -466,7 +409,7 @@ open class NativeCall(
fun fetch(ctx: ValidCallContext<ParsedCallDetails>): Mono<CallResult> { fun fetch(ctx: ValidCallContext<ParsedCallDetails>): Mono<CallResult> {
return ctx.upstream.getLocalReader() return ctx.upstream.getLocalReader()
.flatMap { api -> .flatMap { api ->
SpannedReader(api, tracer, LOCAL_READER) api
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, false, ctx.upstreamFilter)) .read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, false, ctx.upstreamFilter))
.map { .map {
val result = it.getResult() val result = it.getResult()
@@ -503,7 +446,7 @@ open class NativeCall(
return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method")) return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method"))
} }
val reader = requestReaderFactory.create( val reader = requestReaderFactory.create(
ReaderData(ctx.upstream, ctx.upstreamFilter, ctx.callQuorum, signer, tracer), ReaderData(ctx.upstream, ctx.upstreamFilter, ctx.callQuorum, signer),
) )
val counter = reader.attempts() val counter = reader.attempts()
val isRipple = ctx.upstream.getChain() in listOf(Chain.RIPPLE__MAINNET, Chain.RIPPLE__TESTNET) val isRipple = ctx.upstream.getChain() in listOf(Chain.RIPPLE__MAINNET, Chain.RIPPLE__TESTNET)
@@ -512,7 +455,7 @@ open class NativeCall(
streamRequest = false streamRequest = false
} }
return SpannedReader(reader, tracer, RPC_READER) return reader
.read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, streamRequest)) .read(ctx.payload.toChainRequest(ctx.nonce, ctx.forwardedSelector, streamRequest))
.map { .map {
val resolvedUpstreamData = it.resolvedUpstreamData.ifEmpty { val resolvedUpstreamData = it.resolvedUpstreamData.ifEmpty {

View File

@@ -31,7 +31,6 @@ import io.grpc.Status
import io.grpc.StatusException import io.grpc.StatusException
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@@ -39,8 +38,8 @@ import java.util.concurrent.atomic.AtomicLong
@Service @Service
open class NativeSubscribe( open class NativeSubscribe(
@Autowired private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
@Autowired private val signer: ResponseSigner, private val signer: ResponseSigner,
) { ) {
companion object { companion object {

View File

@@ -26,14 +26,13 @@ import io.emeraldpay.dshackle.upstream.MultistreamHolder
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
@Service @Service
class StreamHead( class StreamHead(
@Autowired private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
) { ) {
private val log = LoggerFactory.getLogger(StreamHead::class.java) private val log = LoggerFactory.getLogger(StreamHead::class.java)

View File

@@ -17,9 +17,9 @@
package io.emeraldpay.dshackle.upstream package io.emeraldpay.dshackle.upstream
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import jakarta.annotation.PreDestroy
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
import javax.annotation.PreDestroy
@Component @Component
open class CurrentMultistreamHolder( open class CurrentMultistreamHolder(

View File

@@ -21,5 +21,5 @@ package io.emeraldpay.dshackle.upstream
interface IngressSubscription { interface IngressSubscription {
fun getAvailableTopics(): List<String> fun getAvailableTopics(): List<String>
fun <T> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>? fun <T : Any> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>?
} }

View File

@@ -25,7 +25,7 @@ open class NoIngressSubscription : IngressSubscription {
return listOf() return listOf()
} }
override fun <T> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>? { override fun <T : Any> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>? {
return null return null
} }
} }

View File

@@ -20,7 +20,7 @@ import reactor.core.publisher.Flux
/** /**
* Note that T is supposed to be serializable as JSON * Note that T is supposed to be serializable as JSON
*/ */
interface SubscriptionConnect<T> { interface SubscriptionConnect<T : Any> {
fun connect(matcher: Selector.Matcher): Flux<T> fun connect(matcher: Selector.Matcher): Flux<T>
} }

View File

@@ -54,7 +54,7 @@ abstract class BasicUpstreamSettingsDetector(
protected abstract fun clientVersion(node: JsonNode): String? protected abstract fun clientVersion(node: JsonNode): String?
protected abstract fun clientType(node: JsonNode): String? protected abstract fun clientType(node: JsonNode): String?
protected fun detectNodeType(): Flux<Pair<String, String>?> { protected fun detectNodeType(): Flux<Pair<String, String>> {
val nodeTypeRequest = nodeTypeRequest() val nodeTypeRequest = nodeTypeRequest()
return upstream return upstream
.getIngressReader() .getIngressReader()

View File

@@ -71,7 +71,7 @@ enum class ValidateUpstreamSettingsResult(val priority: Int) {
UPSTREAM_FATAL_SETTINGS_ERROR(2), UPSTREAM_FATAL_SETTINGS_ERROR(2),
} }
interface SingleValidator<T> { interface SingleValidator<T : Any> {
fun validate(onError: T): Mono<T> fun validate(onError: T): Mono<T>
} }

View File

@@ -153,23 +153,23 @@ class BeaconChainBlockHeaderDeserializer : JsonDeserializer<BeaconChainBlockHead
} }
private data class BeaconChainSyncing( private data class BeaconChainSyncing(
@JsonProperty("data") @param:JsonProperty("data")
val data: BeaconChainSyncingData, val data: BeaconChainSyncingData,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
private data class BeaconChainSyncingData( private data class BeaconChainSyncingData(
@JsonProperty("is_syncing") @param:JsonProperty("is_syncing")
val isSyncing: Boolean, val isSyncing: Boolean,
) )
private data class BeaconChainPeers( private data class BeaconChainPeers(
@JsonProperty("data") @param:JsonProperty("data")
val data: BeaconChainPeersData, val data: BeaconChainPeersData,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
private data class BeaconChainPeersData( private data class BeaconChainPeersData(
@JsonProperty("connected") @param:JsonProperty("connected")
val connected: String, val connected: String,
) )

View File

@@ -84,7 +84,7 @@ open class BitcoinReader(
mempool.stop() mempool.stop()
} }
fun <T> castedRead(req: ChainRequest, clazz: Class<T>): Mono<T> { fun <T : Any> castedRead(req: ChainRequest, clazz: Class<T>): Mono<T> {
return upstreams.getDirectApi(Selector.empty).flatMap { api -> return upstreams.getDirectApi(Selector.empty).flatMap { api ->
api.read(req) api.read(req)
.flatMap(ChainResponse::requireResult) .flatMap(ChainResponse::requireResult)

View File

@@ -121,49 +121,49 @@ object CosmosChainSpecific : AbstractPollChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosBlockResult( data class CosmosBlockResult(
@JsonProperty("block_id") var blockId: CosmosBlockId, @param:JsonProperty("block_id") var blockId: CosmosBlockId,
@JsonProperty("block") var block: CosmosBlockData, @param:JsonProperty("block") var block: CosmosBlockData,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosBlockId( data class CosmosBlockId(
@JsonProperty("hash") var hash: String, @param:JsonProperty("hash") var hash: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosHeader( data class CosmosHeader(
@JsonProperty("last_block_id") var lastBlockId: CosmosBlockId, @param:JsonProperty("last_block_id") var lastBlockId: CosmosBlockId,
@JsonProperty("height") var height: String, @param:JsonProperty("height") var height: String,
@JsonProperty("time") var time: Instant, @param:JsonProperty("time") var time: Instant,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosStatus( data class CosmosStatus(
@JsonProperty("node_info") var nodeInfo: CosmosNodeInfo, @param:JsonProperty("node_info") var nodeInfo: CosmosNodeInfo,
@JsonProperty("sync_info") var syncInfo: CosmosSyncInfo, @param:JsonProperty("sync_info") var syncInfo: CosmosSyncInfo,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosNodeInfo( data class CosmosNodeInfo(
@JsonProperty("version") var version: String, @param:JsonProperty("version") var version: String,
@JsonProperty("network") var network: String, @param:JsonProperty("network") var network: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosSyncInfo( data class CosmosSyncInfo(
@JsonProperty("earliest_block_height") var earliestBlockHeight: String, @param:JsonProperty("earliest_block_height") var earliestBlockHeight: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosBlockEvent( data class CosmosBlockEvent(
@JsonProperty("data") var data: CosmosBlockEventData, @param:JsonProperty("data") var data: CosmosBlockEventData,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class CosmosBlockEventData( data class CosmosBlockEventData(
@JsonProperty("value") var value: CosmosBlockData, @param:JsonProperty("value") var value: CosmosBlockData,
) )
data class CosmosBlockData( data class CosmosBlockData(
@JsonProperty("header") var header: CosmosHeader, @param:JsonProperty("header") var header: CosmosHeader,
) )

View File

@@ -17,11 +17,6 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.cache.Caches import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.CurrentBlockCache import io.emeraldpay.dshackle.cache.CurrentBlockCache
import io.emeraldpay.dshackle.commons.CACHE_BLOCK_BY_HASH_READER
import io.emeraldpay.dshackle.commons.CACHE_BLOCK_BY_HEIGHT_READER
import io.emeraldpay.dshackle.commons.CACHE_RECEIPTS_READER
import io.emeraldpay.dshackle.commons.CACHE_TX_BY_HASH_READER
import io.emeraldpay.dshackle.commons.DIRECT_QUORUM_RPC_READER
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.data.TxContainer import io.emeraldpay.dshackle.data.TxContainer
@@ -29,7 +24,6 @@ import io.emeraldpay.dshackle.data.TxId
import io.emeraldpay.dshackle.reader.CompoundReader import io.emeraldpay.dshackle.reader.CompoundReader
import io.emeraldpay.dshackle.reader.Reader import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.reader.RekeyingReader import io.emeraldpay.dshackle.reader.RekeyingReader
import io.emeraldpay.dshackle.reader.SpannedReader
import io.emeraldpay.dshackle.upstream.CachingReader import io.emeraldpay.dshackle.upstream.CachingReader
import io.emeraldpay.dshackle.upstream.Multistream import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.Selector import io.emeraldpay.dshackle.upstream.Selector
@@ -43,7 +37,6 @@ import io.emeraldpay.dshackle.upstream.ethereum.domain.Wei
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionLogJson import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionLogJson
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
import org.apache.commons.collections4.Factory import org.apache.commons.collections4.Factory
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import java.util.function.Function import java.util.function.Function
@@ -54,29 +47,28 @@ open class EthereumCachingReader(
private val up: Multistream, private val up: Multistream,
private val caches: Caches, private val caches: Caches,
callMethodsFactory: Factory<CallMethods>, callMethodsFactory: Factory<CallMethods>,
private val tracer: Tracer,
) : CachingReader { ) : CachingReader {
private val balanceCache = CurrentBlockCache<Address, Wei>() private val balanceCache = CurrentBlockCache<Address, Wei>()
private val directReader = EthereumDirectReader(up, caches, balanceCache, callMethodsFactory, tracer) private val directReader = EthereumDirectReader(up, caches, balanceCache, callMethodsFactory)
open fun blockByFinalization(): Reader<FinalizationType, Result<BlockContainer>> { open fun blockByFinalization(): Reader<FinalizationType, Result<BlockContainer>> {
return SpannedReader(directReader.blockByFinalizationReader, tracer, DIRECT_QUORUM_RPC_READER) return directReader.blockByFinalizationReader
} }
open fun blocksByIdAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<BlockId, Result<BlockContainer>> { open fun blocksByIdAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<BlockId, Result<BlockContainer>> {
val idToBlockHash = Function<BlockId, Request<BlockHash>> { id -> Request(BlockHash.from(id.value), upstreamFilter) } val idToBlockHash = Function<BlockId, Request<BlockHash>> { id -> Request(BlockHash.from(id.value), upstreamFilter) }
return CompoundReader( return CompoundReader(
SpannedReader(CacheWithUpstreamIdReader(caches.getBlocksByHash()), tracer, CACHE_BLOCK_BY_HASH_READER), CacheWithUpstreamIdReader(caches.getBlocksByHash()),
SpannedReader(RekeyingReader(idToBlockHash, directReader.blockReader), tracer, DIRECT_QUORUM_RPC_READER), RekeyingReader(idToBlockHash, directReader.blockReader),
) )
} }
open fun blocksByHeightAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<Long, Result<BlockContainer>> { open fun blocksByHeightAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<Long, Result<BlockContainer>> {
val numToRequest = Function<Long, Request<Long>> { num -> Request(num, upstreamFilter) } val numToRequest = Function<Long, Request<Long>> { num -> Request(num, upstreamFilter) }
return CompoundReader( return CompoundReader(
SpannedReader(CacheWithUpstreamIdReader(caches.getBlocksByHeight()), tracer, CACHE_BLOCK_BY_HEIGHT_READER), CacheWithUpstreamIdReader(caches.getBlocksByHeight()),
SpannedReader(RekeyingReader(numToRequest, directReader.blockByHeightReader), tracer, DIRECT_QUORUM_RPC_READER), RekeyingReader(numToRequest, directReader.blockByHeightReader),
) )
} }
@@ -87,8 +79,8 @@ open class EthereumCachingReader(
open fun txByHashAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<TxId, Result<TxContainer>> { open fun txByHashAsCont(upstreamFilter: Selector.UpstreamFilter): Reader<TxId, Result<TxContainer>> {
val idToTxHash = Function<TxId, Request<TransactionId>> { id -> Request(TransactionId.from(id.value), upstreamFilter) } val idToTxHash = Function<TxId, Request<TransactionId>> { id -> Request(TransactionId.from(id.value), upstreamFilter) }
return CompoundReader( return CompoundReader(
CacheWithUpstreamIdReader(SpannedReader(caches.getTxByHash(), tracer, CACHE_TX_BY_HASH_READER)), CacheWithUpstreamIdReader(caches.getTxByHash()),
SpannedReader(RekeyingReader(idToTxHash, directReader.txReader), tracer, DIRECT_QUORUM_RPC_READER), RekeyingReader(idToTxHash, directReader.txReader),
) )
} }
@@ -106,8 +98,8 @@ open class EthereumCachingReader(
directReader.receiptReader, directReader.receiptReader,
) )
return CompoundReader( return CompoundReader(
CacheWithUpstreamIdReader(SpannedReader(caches.getReceipts(), tracer, CACHE_RECEIPTS_READER)), CacheWithUpstreamIdReader(caches.getReceipts()),
SpannedReader(requested, tracer, DIRECT_QUORUM_RPC_READER), requested,
) )
} }
@@ -126,7 +118,7 @@ open class EthereumCachingReader(
override fun stop() { override fun stop() {
} }
private class CacheWithUpstreamIdReader<K, D>( private class CacheWithUpstreamIdReader<K, D : Any>(
private val reader: Reader<K, D>, private val reader: Reader<K, D>,
) : Reader<K, Result<D>> { ) : Reader<K, Result<D>> {
override fun read(key: K): Mono<Result<D>> { override fun read(key: K): Mono<Result<D>> {

View File

@@ -36,7 +36,6 @@ import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundService
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import java.math.BigInteger import java.math.BigInteger
@@ -89,8 +88,8 @@ object EthereumChainSpecific : AbstractPollChainSpecific() {
} }
} }
override fun makeCachingReaderBuilder(tracer: Tracer): CachingReaderBuilder { override fun makeCachingReaderBuilder(): CachingReaderBuilder {
return { ms, caches, methodsFactory -> EthereumCachingReader(ms, caches, methodsFactory, tracer) } return { ms, caches, methodsFactory -> EthereumCachingReader(ms, caches, methodsFactory) }
} }
override fun upstreamValidators( override fun upstreamValidators(

View File

@@ -37,7 +37,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import org.apache.commons.collections4.Factory import org.apache.commons.collections4.Factory
import org.apache.commons.lang3.exception.ExceptionUtils import org.apache.commons.lang3.exception.ExceptionUtils
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.util.retry.Retry import reactor.util.retry.Retry
import java.time.Duration import java.time.Duration
@@ -51,7 +50,6 @@ class EthereumDirectReader(
private val caches: Caches, private val caches: Caches,
private val balanceCache: CurrentBlockCache<Address, Wei>, private val balanceCache: CurrentBlockCache<Address, Wei>,
private val callMethodsFactory: Factory<CallMethods>, private val callMethodsFactory: Factory<CallMethods>,
private val tracer: Tracer,
) { ) {
companion object { companion object {
@@ -257,7 +255,6 @@ class EthereumDirectReader(
Selector.UpstreamFilter(sort, matcher), Selector.UpstreamFilter(sort, matcher),
callMethodsFactory.create().createQuorumFor(request.method), callMethodsFactory.create().createQuorumFor(request.method),
null, null,
tracer,
), ),
) )
}.flatMap { }.flatMap {

View File

@@ -35,7 +35,7 @@ class EthereumUpstreamSettingsDetector(
) )
} }
private fun detectFlashBlocks(): Mono<Pair<String, String>>? { private fun detectFlashBlocks(): Mono<Pair<String, String>> {
return upstream.getIngressReader().read( return upstream.getIngressReader().read(
ChainRequest( ChainRequest(
"eth_getBlockByNumber", "eth_getBlockByNumber",

View File

@@ -34,7 +34,7 @@ class EthereumWsIngressSubscription(
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>? { override fun <T : Any> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T>? {
if (topic == EthereumEgressSubscription.METHOD_PENDING_TXES) { if (topic == EthereumEgressSubscription.METHOD_PENDING_TXES) {
return pendingTxes as SubscriptionConnect<T> return pendingTxes as SubscriptionConnect<T>
} }

View File

@@ -30,7 +30,6 @@ import io.emeraldpay.dshackle.upstream.calls.CallSelector
import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions import io.emeraldpay.dshackle.upstream.ethereum.WsSubscriptions
import io.emeraldpay.dshackle.upstream.finalization.FinalizationDetector import io.emeraldpay.dshackle.upstream.finalization.FinalizationDetector
import io.emeraldpay.dshackle.upstream.finalization.NoopFinalizationDetector import io.emeraldpay.dshackle.upstream.finalization.NoopFinalizationDetector
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import java.util.function.Supplier import java.util.function.Supplier
@@ -48,7 +47,7 @@ abstract class AbstractChainSpecific : ChainSpecific {
return NoopFinalizationDetector() return NoopFinalizationDetector()
} }
override fun makeCachingReaderBuilder(tracer: Tracer): CachingReaderBuilder { override fun makeCachingReaderBuilder(): CachingReaderBuilder {
return { _, _, _ -> NoopCachingReader } return { _, _, _ -> NoopCachingReader }
} }

View File

@@ -48,7 +48,6 @@ import io.emeraldpay.dshackle.upstream.solana.SolanaChainSpecific
import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific
import io.emeraldpay.dshackle.upstream.ton.TonHttpSpecific import io.emeraldpay.dshackle.upstream.ton.TonHttpSpecific
import org.apache.commons.collections4.Factory import org.apache.commons.collections4.Factory
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Scheduler import reactor.core.scheduler.Scheduler
import java.util.function.Supplier import java.util.function.Supplier
@@ -77,7 +76,7 @@ interface ChainSpecific {
fun subscriptionBuilder(headScheduler: Scheduler): (Multistream) -> EgressSubscription fun subscriptionBuilder(headScheduler: Scheduler): (Multistream) -> EgressSubscription
fun makeCachingReaderBuilder(tracer: Tracer): CachingReaderBuilder fun makeCachingReaderBuilder(): CachingReaderBuilder
fun validator( fun validator(
chain: Chain, chain: Chain,

View File

@@ -26,7 +26,7 @@ class GenericIngressSubscription(
private val holders = ConcurrentHashMap<Pair<String, Any?>, SubscriptionConnect<out Any>>() private val holders = ConcurrentHashMap<Pair<String, Any?>, SubscriptionConnect<out Any>>()
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun <T> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T> { override fun <T : Any> get(topic: String, params: Any?, unsubscribeMethod: String): SubscriptionConnect<T> {
return holders.computeIfAbsent(topic to params) { key -> return holders.computeIfAbsent(topic to params) { key ->
GenericSubscriptionConnect( GenericSubscriptionConnect(
chain, chain,
@@ -58,7 +58,7 @@ class GenericSubscriptionConnect(
.flatMapMany { it.t2 } .flatMapMany { it.t2 }
.timeout( .timeout(
Duration.ofSeconds(85), Duration.ofSeconds(85),
Mono.empty<ByteArray?>().doOnEach { Mono.empty<ByteArray>().doOnEach {
log.warn("Timeout during subscription to $topic after 85 seconds") log.warn("Timeout during subscription to $topic after 85 seconds")
}, },
) )

View File

@@ -22,11 +22,11 @@ class GrpcUpstreamCreator(
private val authorizationConfig: AuthorizationConfig, private val authorizationConfig: AuthorizationConfig,
private val compressionConfig: CompressionConfig, private val compressionConfig: CompressionConfig,
private val fileResolver: FileResolver, private val fileResolver: FileResolver,
@Qualifier("grpcChannelExecutor") @param:Qualifier("grpcChannelExecutor")
private val channelExecutor: Executor, private val channelExecutor: Executor,
private val grpcTracing: GrpcTracing, @param:Qualifier("headScheduler")
@Qualifier("headScheduler")
private val headScheduler: Scheduler, private val headScheduler: Scheduler,
private val grpcTracing: GrpcTracing,
private val grpcAuthContext: GrpcAuthContext, private val grpcAuthContext: GrpcAuthContext,
) { ) {
@Value("\${spring.application.max-metadata-size}") @Value("\${spring.application.max-metadata-size}")

View File

@@ -107,8 +107,8 @@ object KadenaChainSpecific : AbstractPollChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class KadenaHeader( data class KadenaHeader(
@JsonProperty("height") var height: Long, @param:JsonProperty("height") var height: Long,
@JsonProperty("weight") var weight: String, @param:JsonProperty("weight") var weight: String,
@JsonProperty("instance") var instance: String, @param:JsonProperty("instance") var instance: String,
@JsonProperty("id") var id: String, @param:JsonProperty("id") var id: String,
) )

View File

@@ -32,7 +32,7 @@ class KadenaUpstreamSettingsDetector(
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class KadenaHeader( data class KadenaHeader(
@JsonProperty("instance") var instance: String, @param:JsonProperty("instance") var instance: String,
) )
override fun nodeTypeRequest(): NodeTypeRequest = NodeTypeRequest(clientVersionRequest()) override fun nodeTypeRequest(): NodeTypeRequest = NodeTypeRequest(clientVersionRequest())

View File

@@ -119,26 +119,26 @@ object NearChainSpecific : AbstractPollChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class NearBlock( data class NearBlock(
@JsonProperty("header") var header: NearHeader, @param:JsonProperty("header") var header: NearHeader,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class NearHeader( data class NearHeader(
@JsonProperty("height") var height: Long, @param:JsonProperty("height") var height: Long,
@JsonProperty("hash") var hash: String, @param:JsonProperty("hash") var hash: String,
@JsonProperty("prev_hash") var prevHash: String, @param:JsonProperty("prev_hash") var prevHash: String,
@JsonProperty("timestamp") var timestamp: Long, @param:JsonProperty("timestamp") var timestamp: Long,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class NearStatus( data class NearStatus(
@JsonProperty("chain_id") var chainId: String, @param:JsonProperty("chain_id") var chainId: String,
@JsonProperty("sync_info") var syncInfo: NearSync, @param:JsonProperty("sync_info") var syncInfo: NearSync,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class NearSync( data class NearSync(
@JsonProperty("syncing") var syncing: Boolean, @param:JsonProperty("syncing") var syncing: Boolean,
@JsonProperty("earliest_block_height") var earliestHeight: Long, @param:JsonProperty("earliest_block_height") var earliestHeight: Long,
@JsonProperty("earliest_block_time") var earliestBlockTime: Instant, @param:JsonProperty("earliest_block_time") var earliestBlockTime: Instant,
) )

View File

@@ -32,13 +32,13 @@ class NearUpstreamSettingsDetector(
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
private data class NearVersionResponse( private data class NearVersionResponse(
@JsonProperty("version") @param:JsonProperty("version")
val nearVersion: NearVersion, val nearVersion: NearVersion,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
private data class NearVersion( private data class NearVersion(
@JsonProperty("version") @param:JsonProperty("version")
val version: String, val version: String,
) )

View File

@@ -158,30 +158,30 @@ object PolkadotChainSpecific : AbstractPollChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class PolkadotBlockResponse( data class PolkadotBlockResponse(
@JsonProperty("block") var block: PolkadotBlock, @param:JsonProperty("block") var block: PolkadotBlock,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class PolkadotBlock( data class PolkadotBlock(
@JsonProperty("header") var header: PolkadotHeader, @param:JsonProperty("header") var header: PolkadotHeader,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class PolkadotHeader( data class PolkadotHeader(
@JsonProperty("parentHash") var parentHash: String, @param:JsonProperty("parentHash") var parentHash: String,
@JsonProperty("number") var number: String, @param:JsonProperty("number") var number: String,
@JsonProperty("stateRoot") var stateRoot: String, @param:JsonProperty("stateRoot") var stateRoot: String,
@JsonProperty("extrinsicsRoot") var extrinsicsRoot: String, @param:JsonProperty("extrinsicsRoot") var extrinsicsRoot: String,
@JsonProperty("digest") var digest: PolkadotDigest, @param:JsonProperty("digest") var digest: PolkadotDigest,
) )
data class PolkadotDigest( data class PolkadotDigest(
@JsonProperty("logs") var logs: List<String>, @param:JsonProperty("logs") var logs: List<String>,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class PolkadotHealth( data class PolkadotHealth(
@JsonProperty("peers") var peers: Long, @param:JsonProperty("peers") var peers: Long,
@JsonProperty("isSyncing") var isSyncing: Boolean, @param:JsonProperty("isSyncing") var isSyncing: Boolean,
@JsonProperty("shouldHavePeers") var shouldHavePeers: Boolean, @param:JsonProperty("shouldHavePeers") var shouldHavePeers: Boolean,
) )

View File

@@ -3,7 +3,6 @@ package io.emeraldpay.dshackle.upstream.ripple
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.JsonNode
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.config.ChainsConfig.ChainConfig import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
@@ -181,124 +180,124 @@ class RippleUpstreamSettingsDetector(val upstream: Upstream) : BasicUpstreamSett
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleInfoWrapper( data class RippleInfoWrapper(
@JsonProperty("info") var info: RippleInfo, @param:JsonProperty("info") var info: RippleInfo,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleInfo( data class RippleInfo(
@JsonProperty("clio_version") var clio: String?, @param:JsonProperty("clio_version") var clio: String?,
@JsonProperty("build_version") var buildVersion: String?, @param:JsonProperty("build_version") var buildVersion: String?,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleBlock( data class RippleBlock(
@JsonProperty("closed") var closed: RippleClosed, @param:JsonProperty("closed") var closed: RippleClosed,
@JsonProperty("open") var open: RippleOpen?, @param:JsonProperty("open") var open: RippleOpen?,
@JsonProperty("status") var status: String?, @param:JsonProperty("status") var status: String?,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleClosed( data class RippleClosed(
@JsonProperty("ledger") var ledger: RippleClosedLedger, @param:JsonProperty("ledger") var ledger: RippleClosedLedger,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleOpen( data class RippleOpen(
@JsonProperty("ledger") var ledger: RippleOpenLedger, @param:JsonProperty("ledger") var ledger: RippleOpenLedger,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleClosedLedger( data class RippleClosedLedger(
@JsonProperty("account_hash") var accountHash: String, @param:JsonProperty("account_hash") var accountHash: String,
@JsonProperty("close_flags") var closeFlags: Short, @param:JsonProperty("close_flags") var closeFlags: Short,
@JsonProperty("close_time") var closeTime: Long, @param:JsonProperty("close_time") var closeTime: Long,
@JsonProperty("close_time_human") var closeTimeHuman: String, @param:JsonProperty("close_time_human") var closeTimeHuman: String,
@JsonProperty("close_time_iso") var closeTimeIso: String, @param:JsonProperty("close_time_iso") var closeTimeIso: String,
@JsonProperty("close_time_resolution") var closeTimeResolution: Short, @param:JsonProperty("close_time_resolution") var closeTimeResolution: Short,
@JsonProperty("closed") var closed: Boolean, @param:JsonProperty("closed") var closed: Boolean,
@JsonProperty("ledger_hash") var ledgerHash: String, @param:JsonProperty("ledger_hash") var ledgerHash: String,
@JsonProperty("ledger_index") var ledgerIndex: String, @param:JsonProperty("ledger_index") var ledgerIndex: String,
@JsonProperty("parent_close_time") var parentCloseTime: Long, @param:JsonProperty("parent_close_time") var parentCloseTime: Long,
@JsonProperty("parent_hash") var parentHash: String, @param:JsonProperty("parent_hash") var parentHash: String,
@JsonProperty("total_coins") var totalCoins: String, @param:JsonProperty("total_coins") var totalCoins: String,
@JsonProperty("transaction_hash") var transactionHash: String, @param:JsonProperty("transaction_hash") var transactionHash: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleOpenLedger( data class RippleOpenLedger(
@JsonProperty("closed") var closed: Boolean, @param:JsonProperty("closed") var closed: Boolean,
@JsonProperty("ledger_index") var ledgerIndex: String, @param:JsonProperty("ledger_index") var ledgerIndex: String,
@JsonProperty("parent_hash") var parentHash: String, @param:JsonProperty("parent_hash") var parentHash: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleState( data class RippleState(
@JsonProperty("state") var state: RippleServerState, @param:JsonProperty("state") var state: RippleServerState,
@JsonProperty("status") var status: String? = null, @param:JsonProperty("status") var status: String? = null,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class RippleServerState( data class RippleServerState(
@JsonProperty("build_version") val buildVersion: String, @param:JsonProperty("build_version") val buildVersion: String,
@JsonProperty("complete_ledgers") val completeLedgers: String, @param:JsonProperty("complete_ledgers") val completeLedgers: String,
@JsonProperty("initial_sync_duration_us") val initialSyncDurationUs: String?, @param:JsonProperty("initial_sync_duration_us") val initialSyncDurationUs: String?,
@JsonProperty("io_latency_ms") val ioLatencyMs: Int, @param:JsonProperty("io_latency_ms") val ioLatencyMs: Int,
@JsonProperty("jq_trans_overflow") val jqTransOverflow: String, @param:JsonProperty("jq_trans_overflow") val jqTransOverflow: String,
@JsonProperty("last_close") val lastClose: LastClose, @param:JsonProperty("last_close") val lastClose: LastClose,
@JsonProperty("load_base") val loadBase: Int, @param:JsonProperty("load_base") val loadBase: Int,
@JsonProperty("load_factor") val loadFactor: Int, @param:JsonProperty("load_factor") val loadFactor: Int,
@JsonProperty("load_factor_fee_escalation") val loadFactorFeeEscalation: Int, @param:JsonProperty("load_factor_fee_escalation") val loadFactorFeeEscalation: Int,
@JsonProperty("load_factor_fee_queue") val loadFactorFeeQueue: Int, @param:JsonProperty("load_factor_fee_queue") val loadFactorFeeQueue: Int,
@JsonProperty("load_factor_fee_reference") val loadFactorFeeReference: Int, @param:JsonProperty("load_factor_fee_reference") val loadFactorFeeReference: Int,
@JsonProperty("load_factor_server") val loadFactorServer: Int, @param:JsonProperty("load_factor_server") val loadFactorServer: Int,
@JsonProperty("network_id") val networkId: Int, @param:JsonProperty("network_id") val networkId: Int,
@JsonProperty("peer_disconnects") val peerDisconnects: String?, @param:JsonProperty("peer_disconnects") val peerDisconnects: String?,
@JsonProperty("peer_disconnects_resources") val peerDisconnectsResources: String?, @param:JsonProperty("peer_disconnects_resources") val peerDisconnectsResources: String?,
@JsonProperty("peers") val peers: Int, @param:JsonProperty("peers") val peers: Int,
@JsonProperty("ports") val ports: List<Port>, @param:JsonProperty("ports") val ports: List<Port>,
@JsonProperty("pubkey_node") val pubkeyNode: String?, @param:JsonProperty("pubkey_node") val pubkeyNode: String?,
@JsonProperty("server_state") val serverState: String?, @param:JsonProperty("server_state") val serverState: String?,
@JsonProperty("server_state_duration_us") val serverStateDurationUs: String?, @param:JsonProperty("server_state_duration_us") val serverStateDurationUs: String?,
@JsonProperty("state_accounting") val stateAccounting: StateAccounting?, @param:JsonProperty("state_accounting") val stateAccounting: StateAccounting?,
@JsonProperty("time") val time: String, @param:JsonProperty("time") val time: String,
@JsonProperty("uptime") val uptime: Long, @param:JsonProperty("uptime") val uptime: Long,
@JsonProperty("validated_ledger") val validatedLedger: ValidatedLedger, @param:JsonProperty("validated_ledger") val validatedLedger: ValidatedLedger,
@JsonProperty("validation_quorum") val validationQuorum: Int, @param:JsonProperty("validation_quorum") val validationQuorum: Int,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class LastClose( data class LastClose(
@JsonProperty("converge_time") val convergeTime: Int, @param:JsonProperty("converge_time") val convergeTime: Int,
@JsonProperty("proposers") val proposers: Int, @param:JsonProperty("proposers") val proposers: Int,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class Port( data class Port(
@JsonProperty("port") val port: String, @param:JsonProperty("port") val port: String,
@JsonProperty("protocol") val protocol: List<String>, @param:JsonProperty("protocol") val protocol: List<String>,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class StateAccounting( data class StateAccounting(
@JsonProperty("connected") val connected: StateDuration, @param:JsonProperty("connected") val connected: StateDuration,
@JsonProperty("disconnected") val disconnected: StateDuration, @param:JsonProperty("disconnected") val disconnected: StateDuration,
@JsonProperty("full") val full: StateDuration, @param:JsonProperty("full") val full: StateDuration,
@JsonProperty("syncing") val syncing: StateDuration, @param:JsonProperty("syncing") val syncing: StateDuration,
@JsonProperty("tracking") val tracking: StateDuration, @param:JsonProperty("tracking") val tracking: StateDuration,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class StateDuration( data class StateDuration(
@JsonProperty("duration_us") val durationUs: String, @param:JsonProperty("duration_us") val durationUs: String,
@JsonProperty("transitions") val transitions: String, @param:JsonProperty("transitions") val transitions: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class ValidatedLedger( data class ValidatedLedger(
@JsonProperty("base_fee") val baseFee: Int, @param:JsonProperty("base_fee") val baseFee: Int,
@JsonProperty("close_time") val closeTime: Long, @param:JsonProperty("close_time") val closeTime: Long,
@JsonProperty("hash") val hash: String, @param:JsonProperty("hash") val hash: String,
@JsonProperty("reserve_base") val reserveBase: Long, @param:JsonProperty("reserve_base") val reserveBase: Long,
@JsonProperty("reserve_inc") val reserveInc: Long, @param:JsonProperty("reserve_inc") val reserveInc: Long,
@JsonProperty("seq") val seq: Long, @param:JsonProperty("seq") val seq: Long,
) )

View File

@@ -8,9 +8,9 @@ import org.bouncycastle.math.ec.ECPoint
import org.bouncycastle.util.io.pem.PemObject import org.bouncycastle.util.io.pem.PemObject
import org.bouncycastle.util.io.pem.PemReader import org.bouncycastle.util.io.pem.PemReader
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.FactoryBean import org.springframework.context.annotation.Bean
import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Repository import org.springframework.stereotype.Component
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
@@ -20,10 +20,17 @@ import java.security.PublicKey
import java.security.interfaces.ECPrivateKey import java.security.interfaces.ECPrivateKey
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
@Repository @Configuration
open class SignatureBeans {
@Bean
open fun signer(factory: ResponseSignerFactory): ResponseSigner =
factory.createSigner()
}
@Component
open class ResponseSignerFactory( open class ResponseSignerFactory(
@Autowired private val config: SignatureConfig, private val signatureConfig: SignatureConfig,
) : FactoryBean<ResponseSigner> { ) {
companion object { companion object {
private val log = LoggerFactory.getLogger(ResponseSignerFactory::class.java) private val log = LoggerFactory.getLogger(ResponseSignerFactory::class.java)
@@ -70,19 +77,15 @@ open class ResponseSignerFactory(
return ByteBuffer.wrap(fullId).asLongBuffer().get() return ByteBuffer.wrap(fullId).asLongBuffer().get()
} }
override fun getObject(): ResponseSigner { fun createSigner(): ResponseSigner {
if (!config.enabled) { if (!signatureConfig.enabled) {
return NoSigner() return NoSigner()
} }
if (config.privateKey == null) { if (signatureConfig.privateKey == null) {
log.warn("Private Key for response signature is not set") log.warn("Private Key for response signature is not set")
return NoSigner() return NoSigner()
} }
val key = readKey(config.algorithm, config.privateKey!!) val key = readKey(signatureConfig.algorithm, signatureConfig.privateKey!!)
return EcdsaSigner(key.first, key.second) return EcdsaSigner(key.first, key.second)
} }
override fun getObjectType(): Class<*>? {
return ResponseSigner::class.java
}
} }

View File

@@ -167,24 +167,24 @@ object SolanaChainSpecific : AbstractChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class SolanaWrapper( data class SolanaWrapper(
@JsonProperty("context") var context: SolanaContext, @param:JsonProperty("context") var context: SolanaContext,
@JsonProperty("value") var value: SolanaResult, @param:JsonProperty("value") var value: SolanaResult,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class SolanaContext( data class SolanaContext(
@JsonProperty("slot") var slot: Long, @param:JsonProperty("slot") var slot: Long,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class SolanaResult( data class SolanaResult(
@JsonProperty("block") var block: SolanaBlock, @param:JsonProperty("block") var block: SolanaBlock,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class SolanaBlock( data class SolanaBlock(
@JsonProperty("blockHeight") var height: Long, @param:JsonProperty("blockHeight") var height: Long,
@JsonProperty("blockTime") var timestamp: Long, @param:JsonProperty("blockTime") var timestamp: Long,
@JsonProperty("blockhash") var hash: String, @param:JsonProperty("blockhash") var hash: String,
@JsonProperty("previousBlockhash") var parent: String, @param:JsonProperty("previousBlockhash") var parent: String,
) )

View File

@@ -32,7 +32,7 @@ class SolanaUpstreamSettingsDetector(
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
private data class SolanaVersion( private data class SolanaVersion(
@JsonProperty("solana-core") @param:JsonProperty("solana-core")
val version: String, val version: String,
) )

View File

@@ -119,14 +119,14 @@ object StarknetChainSpecific : AbstractPollChainSpecific() {
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class StarknetBlock( data class StarknetBlock(
@JsonProperty("block_hash") var hash: String, @param:JsonProperty("block_hash") var hash: String,
@JsonProperty("block_number") var number: Long, @param:JsonProperty("block_number") var number: Long,
@JsonProperty("timestamp") var timestamp: Instant, @param:JsonProperty("timestamp") var timestamp: Instant,
@JsonProperty("parent_hash") var parent: String, @param:JsonProperty("parent_hash") var parent: String,
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class StarknetSyncing( data class StarknetSyncing(
@JsonProperty("current_block_num") var current: Long, @param:JsonProperty("current_block_num") var current: Long,
@JsonProperty("highest_block_num") var highest: Long, @param:JsonProperty("highest_block_num") var highest: Long,
) )

View File

@@ -18,7 +18,7 @@ class StarknetUpstreamSettingsDetector(
) )
} }
private fun detectNodeType(): Flux<Pair<String, String>?> { private fun detectNodeType(): Flux<Pair<String, String>> {
return upstream return upstream
.getIngressReader() .getIngressReader()
.read(pathfinderVersionRequest()) .read(pathfinderVersionRequest())

View File

@@ -1,26 +1,9 @@
spring: spring:
application: application:
max-metadata-size: ${MAX_METADATA_SIZE:16384} max-metadata-size: ${MAX_METADATA_SIZE:16384}
name: ${DSHACKLE_APP_NAME:dshackle} name: ${DSHACKLE_APP_NAME:dshackle}
zipkin:
enabled: ${ENABLE_COLLECT_SPANS:false}
base-url: ${ZIPKIN_URL:https://trace.drpc.dev/}
sleuth:
span-filter:
additional-span-name-patterns-to-ignore:
- "^grpcChannelExecutor$"
- ".+NativeSubscribe"
- ".+SubscribeHead"
- ".+SubscribeBalance"
- ".+SubscribeTxStatus"
- ".+SubscribeStatus"
- ".+SubscribeNodeStatus"
- ".+Describe"
- ".+ServerReflectionInfo"
spans:
collect:
long-span-threshold: ${LONG_SPAN_THRESHOLD:1000}
compatibility: compatibility:
enabled: ${DSHACKLE_COMPATIBILITY_ENABLED:true} enabled: ${DSHACKLE_COMPATIBILITY_ENABLED:true}

View File

@@ -4,7 +4,7 @@
<!-- You can override this to have a custom pattern --> <!-- You can override this to have a custom pattern -->
<property name="CONSOLE_LOG_PATTERN" <property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/> value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) [%X{traceId},%X{spanId}] %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!-- Appender to log to console --> <!-- Appender to log to console -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">

View File

@@ -29,7 +29,6 @@ import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
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 org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -55,7 +54,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -87,7 +86,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -125,7 +124,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -158,7 +157,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new NotNullQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new NotNullQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -191,7 +190,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new NotNullQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new NotNullQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -224,7 +223,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new NotNullQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new NotNullQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -255,7 +254,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -289,7 +288,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new NotLaggingQuorum(1), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new NotLaggingQuorum(1))
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -315,7 +314,7 @@ class QuorumRequestReaderSpec extends Specification {
Chain.ETHEREUM__MAINNET, Chain.ETHEREUM__MAINNET,
[up], Selector.empty [up], Selector.empty
) )
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))
@@ -365,7 +364,7 @@ class QuorumRequestReaderSpec extends Specification {
new Selector.HeightMatcher(100000000), new Selector.HeightMatcher(100000000),
) )
)) ))
def reader = new QuorumRequestReader(apis, new AlwaysQuorum(), Stub(Tracer)) def reader = new QuorumRequestReader(apis, new AlwaysQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_test", new ListParams())) def act = reader.read(new ChainRequest("eth_test", new ListParams()))

View File

@@ -7,7 +7,6 @@ import io.emeraldpay.dshackle.upstream.ChainException
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.rpcclient.ListParams import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import org.springframework.cloud.sleuth.Tracer
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -43,7 +42,7 @@ class BroadcastReaderSpec extends Specification {
Mono.just(new ChainResponse(result, null)) Mono.just(new ChainResponse(result, null))
} }
} }
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum(), Stub(Tracer)) def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"])))
then: then:
@@ -81,7 +80,7 @@ class BroadcastReaderSpec extends Specification {
1 * read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) >> 1 * read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) >>
Mono.error(new ChainException(1, "too low")) } Mono.error(new ChainException(1, "too low")) }
} }
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum(), Stub(Tracer)) def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"])))
then: then:
@@ -114,7 +113,7 @@ class BroadcastReaderSpec extends Specification {
0 * getId() >> "id" 0 * getId() >> "id"
0 * getIngressReader() >> Mock(Reader) 0 * getIngressReader() >> Mock(Reader)
} }
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum(), Stub(Tracer)) def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"])))
then: then:
@@ -152,7 +151,7 @@ class BroadcastReaderSpec extends Specification {
Mono.error(new ChainException(1, "too low")) Mono.error(new ChainException(1, "too low"))
} }
} }
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum(), Stub(Tracer)) def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum())
when: when:
def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) def act = reader.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"])))
then: then:
@@ -178,7 +177,7 @@ class BroadcastReaderSpec extends Specification {
0 * getId() >> "id" 0 * getId() >> "id"
0 * getIngressReader() >> Mock(Reader) 0 * getIngressReader() >> Mock(Reader)
} }
def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum(), Stub(Tracer)) def reader = new BroadcastReader([up, up1, up2], new Selector.EmptyMatcher(), null, new BroadcastQuorum())
when: when:
def act = reader def act = reader
.read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"]))) .read(new ChainRequest("eth_sendRawTransaction", new ListParams(["0x1"])))

View File

@@ -39,7 +39,6 @@ import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import io.emeraldpay.dshackle.upstream.signature.ResponseSigner import io.emeraldpay.dshackle.upstream.signature.ResponseSigner
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 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.test.StepVerifier import reactor.test.StepVerifier
@@ -67,7 +66,7 @@ class NativeCallSpec extends Specification {
config.cache = cacheConfig config.cache = cacheConfig
config.passthrough = passthrough config.passthrough = passthrough
new NativeCall(upstreams, signer, config, Stub(Tracer)) new NativeCall(upstreams, signer, config)
} }
def "Tries router first"() { def "Tries router first"() {

View File

@@ -45,10 +45,6 @@ class NativeSubscribeSpec extends Specification {
} }
def up = Mock(GenericMultistream) { def up = Mock(GenericMultistream) {
1 * it.start() 1 * it.start()
_ * it.getSubscriptionTopics() >> {
println("getSubscriptionTopics called")
return ["newHeads"]
}
_ * it.tryProxySubscribe(_, _) >> { _ * it.tryProxySubscribe(_, _) >> {
println("tryProxySubscribe called") println("tryProxySubscribe called")
return null return null

View File

@@ -28,7 +28,6 @@ import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumCachingReader import io.emeraldpay.dshackle.upstream.ethereum.EthereumCachingReader
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
class MultistreamHolderMock implements MultistreamHolder { class MultistreamHolderMock implements MultistreamHolder {
@@ -49,7 +48,7 @@ class MultistreamHolderMock implements MultistreamHolder {
upstreams[chain] = new GenericMultistream( upstreams[chain] = new GenericMultistream(
chain, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(), chain, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()), io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
) )
@@ -102,7 +101,7 @@ class MultistreamHolderMock implements MultistreamHolder {
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) { EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) {
super(chain, Schedulers.immediate(), null, upstreams, caches, Schedulers.boundedElastic(), super(chain, Schedulers.immediate(), null, upstreams, caches, Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(new BraveTracer(null, null, null)), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()), EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
) )

View File

@@ -52,10 +52,6 @@ class TestingCommons {
return new ApiReaderMock() return new ApiReaderMock()
} }
static TracerMock tracerMock() {
return new TracerMock(null, null, null)
}
static GenericUpstreamMock upstream() { static GenericUpstreamMock upstream() {
return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api()) return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api())
} }
@@ -99,7 +95,7 @@ class TestingCommons {
static Multistream multistream(GenericUpstreamMock up) { static Multistream multistream(GenericUpstreamMock up) {
return new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(), return new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()), EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
).tap { ).tap {
@@ -126,7 +122,7 @@ class TestingCommons {
static Multistream multistreamWithoutUpstreams(Chain chain) { static Multistream multistreamWithoutUpstreams(Chain chain) {
return new GenericMultistream(chain, Schedulers.immediate(), null, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(), return new GenericMultistream(chain, Schedulers.immediate(), null, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()), EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
) )
@@ -134,7 +130,7 @@ class TestingCommons {
static Multistream multistreamClassicWithoutUpstreams(Chain chain) { static Multistream multistreamClassicWithoutUpstreams(Chain chain) {
return new GenericMultistream(chain, Schedulers.immediate(), null, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(), return new GenericMultistream(chain, Schedulers.immediate(), null, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()), EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
) )

View File

@@ -1,64 +0,0 @@
package io.emeraldpay.dshackle.test
import brave.Tracer
import org.springframework.cloud.sleuth.CurrentTraceContext
import org.springframework.cloud.sleuth.Span
import org.springframework.cloud.sleuth.TraceContext
import org.springframework.cloud.sleuth.brave.bridge.BraveBaggageManager
import org.springframework.cloud.sleuth.brave.bridge.BraveSpan
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer
import spock.mock.DetachedMockFactory
class TracerMock extends BraveTracer {
private final spanMock = new SpanMock(null)
private static final mockFactory = new DetachedMockFactory()
TracerMock(Tracer tracer, CurrentTraceContext context, BraveBaggageManager braveBaggageManager) {
super(tracer, context, braveBaggageManager)
}
@Override
Span nextSpan(Span parent) {
return spanMock
}
@Override
Span currentSpan() {
return spanMock
}
@Override
SpanInScope withSpan(Span span) {
return mockFactory.Stub(SpanInScope)
}
private static class SpanMock extends BraveSpan {
SpanMock(brave.Span delegate) {
super(delegate)
}
@Override
Span start() {
return this
}
@Override
Span name(String name) {
return this
}
@Override
Span tag(String key, String value) {
return this
}
@Override
void end() {
}
@Override
TraceContext context() {
return mockFactory.Stub(TraceContext)
}
}
}

View File

@@ -55,7 +55,7 @@ class MultistreamSpec extends Specification {
def up2 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"])) def up2 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"]))
def aggr = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, [up1, up2], Caches.default(), def aggr = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, [up1, up2], Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
when: when:
@@ -190,7 +190,7 @@ class MultistreamSpec extends Specification {
def up3 = TestingCommons.upstream("test-3", "external") def up3 = TestingCommons.upstream("test-3", "external")
def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, [up1, up2, up3], Caches.default(), def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, [up1, up2, up3], Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
@@ -263,7 +263,7 @@ class MultistreamSpec extends Specification {
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"])) def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<GenericMultistream>(), Caches.default(), def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<GenericMultistream>(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
when: when:
@@ -294,7 +294,7 @@ class MultistreamSpec extends Specification {
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"])) def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<GenericMultistream>(), Caches.default(), def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<GenericMultistream>(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
def head1 = createBlock(250, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448401b") def head1 = createBlock(250, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448401b")
@@ -329,7 +329,7 @@ class MultistreamSpec extends Specification {
def up3 = TestingCommons.upstream("test-3", "external") def up3 = TestingCommons.upstream("test-3", "external")
def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(), def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, Schedulers.immediate(), null, new ArrayList<Upstream>(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
multistream.processUpstreamsEvents( multistream.processUpstreamsEvents(
@@ -367,7 +367,7 @@ class MultistreamSpec extends Specification {
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) { TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) {
super(chain, Schedulers.immediate(), null, upstreams, caches, super(chain, Schedulers.immediate(), null, upstreams, caches,
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()), EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(),
EthereumChainSpecific.INSTANCE.&localReaderBuilder, EthereumChainSpecific.INSTANCE.&localReaderBuilder,
StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())) StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
} }

View File

@@ -1,6 +1,7 @@
package io.emeraldpay.dshackle.upstream.bitcoin package io.emeraldpay.dshackle.upstream.bitcoin
import io.emeraldpay.dshackle.upstream.bitcoin.data.EsploraUnspent import io.emeraldpay.dshackle.upstream.bitcoin.data.EsploraUnspent
import org.bitcoinj.core.Address
/** /**
* Copyright (c) 2020 EmeraldPay, Inc * Copyright (c) 2020 EmeraldPay, Inc
@@ -18,13 +19,11 @@ import io.emeraldpay.dshackle.upstream.bitcoin.data.EsploraUnspent
* limitations under the License. * limitations under the License.
*/ */
import org.bitcoinj.core.Address
import org.bitcoinj.params.MainNetParams import org.bitcoinj.params.MainNetParams
import org.bitcoinj.params.TestNet3Params import org.bitcoinj.params.TestNet3Params
import org.mockserver.integration.ClientAndServer import org.mockserver.integration.ClientAndServer
import org.mockserver.model.HttpRequest import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpResponse import org.mockserver.model.HttpResponse
import org.springframework.util.SocketUtils
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -33,11 +32,9 @@ import java.time.Duration
class EsploraClientSpec extends Specification { class EsploraClientSpec extends Specification {
ClientAndServer mockServer ClientAndServer mockServer
int port = 23001
def setup() { def setup() {
port = SocketUtils.findAvailableTcpPort(23001) mockServer = ClientAndServer.startClientAndServer(0)
mockServer = ClientAndServer.startClientAndServer(port);
} }
def cleanup() { def cleanup() {
@@ -54,7 +51,7 @@ class EsploraClientSpec extends Specification {
).respond( ).respond(
HttpResponse.response(responseJson) HttpResponse.response(responseJson)
) )
def client = new EsploraClient(new URI("http://localhost:${port}"), null, null) def client = new EsploraClient(new URI("http://localhost:${mockServer.port}"), null, null)
when: when:
def act = client.getUtxo(Address.fromString(new MainNetParams(), "35vktkPo4wdK8Twu4VMiuPLdCx23XEykGY")) def act = client.getUtxo(Address.fromString(new MainNetParams(), "35vktkPo4wdK8Twu4VMiuPLdCx23XEykGY"))
@@ -95,7 +92,7 @@ class EsploraClientSpec extends Specification {
).respond( ).respond(
HttpResponse.response(responseJson) HttpResponse.response(responseJson)
) )
def client = new EsploraClient(new URI("http://localhost:${port}"), null, null) def client = new EsploraClient(new URI("http://localhost:${mockServer.port}"), null, null)
when: when:
def act = client.getTransactions(Address.fromString(TestNet3Params.get(), "tb1qyatuwvkfx8thy2ntmtuea6v42vp3zefqvll8kx")) def act = client.getTransactions(Address.fromString(TestNet3Params.get(), "tb1qyatuwvkfx8thy2ntmtuea6v42vp3zefqvll8kx"))
@@ -125,7 +122,7 @@ class EsploraClientSpec extends Specification {
).respond( ).respond(
HttpResponse.response("[]") HttpResponse.response("[]")
) )
def client = new EsploraClient(new URI("http://localhost:${port}"), null, null) def client = new EsploraClient(new URI("http://localhost:${mockServer.port}"), null, null)
when: when:
def act = client.getTransactions(Address.fromString(TestNet3Params.get(), "tb1qyatuwvkfx8thy2ntmtuea6v42vp3zefqvll8kx")) def act = client.getTransactions(Address.fromString(TestNet3Params.get(), "tb1qyatuwvkfx8thy2ntmtuea6v42vp3zefqvll8kx"))

View File

@@ -8,19 +8,18 @@ import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.data.DefaultContainer import io.emeraldpay.dshackle.data.DefaultContainer
import io.emeraldpay.dshackle.reader.RequestReader import io.emeraldpay.dshackle.reader.RequestReader
import io.emeraldpay.dshackle.reader.RequestReaderFactory import io.emeraldpay.dshackle.reader.RequestReaderFactory
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.* import io.emeraldpay.dshackle.upstream.*
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import io.emeraldpay.dshackle.upstream.ethereum.domain.Address import io.emeraldpay.dshackle.upstream.ethereum.domain.Address
import io.emeraldpay.dshackle.upstream.ethereum.domain.BlockHash import io.emeraldpay.dshackle.upstream.ethereum.domain.BlockHash
import io.emeraldpay.dshackle.upstream.ethereum.domain.TransactionId import io.emeraldpay.dshackle.upstream.ethereum.domain.TransactionId
import io.emeraldpay.dshackle.upstream.ethereum.domain.Wei import io.emeraldpay.dshackle.upstream.ethereum.domain.Wei
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionJson import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionJson
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionLogJson import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionLogJson
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionReceiptJson import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionReceiptJson
import io.emeraldpay.dshackle.upstream.finalization.FinalizationType
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
import org.apache.commons.collections4.Factory import org.apache.commons.collections4.Factory
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.test.StepVerifier import reactor.test.StepVerifier
@@ -48,7 +47,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create({ it.upstreamFilter.sort == Selector.Sort.safe }) >> Mock(RequestReader) { 1 * create({ it.upstreamFilter.sort == Selector.Sort.safe }) >> Mock(RequestReader) {
@@ -83,7 +82,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -110,7 +109,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -143,7 +142,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -175,7 +174,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -208,7 +207,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -241,7 +240,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -275,7 +274,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * cacheReceipt(Caches.Tag.REQUESTED, { DefaultContainer data -> data.txId.toHex() == hash1.substring(2) && data.height == 100 }) 1 * cacheReceipt(Caches.Tag.REQUESTED, { DefaultContainer data -> data.txId.toHex() == hash1.substring(2) && data.height == 100 })
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), caches, new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), caches, new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -300,7 +299,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() Stub(Multistream), Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -330,7 +329,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() up, Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -361,7 +360,7 @@ class EthereumDirectReaderSpec extends Specification {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() up, Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
1 * create(_) >> Mock(RequestReader) { 1 * create(_) >> Mock(RequestReader) {
@@ -399,7 +398,7 @@ class EthereumDirectReaderSpec extends Specification {
Global.objectMapper.writeValueAsBytes(json), null, 1, data, 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
) )
ethereumDirectReader.requestReaderFactory = Mock(RequestReaderFactory) { ethereumDirectReader.requestReaderFactory = Mock(RequestReaderFactory) {
2 * create(_) >> Mock(RequestReader) { 2 * create(_) >> Mock(RequestReader) {
@@ -439,7 +438,7 @@ class EthereumDirectReaderSpec extends Specification {
Global.objectMapper.writeValueAsBytes(json), null, 1, data, 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
) )
ethereumDirectReader.requestReaderFactory = Mock(RequestReaderFactory) { ethereumDirectReader.requestReaderFactory = Mock(RequestReaderFactory) {
2 * create(_) >> Mock(RequestReader) { 2 * create(_) >> Mock(RequestReader) {
@@ -472,7 +471,7 @@ class EthereumDirectReaderSpec extends Specification {
4 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET) 4 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
} }
EthereumDirectReader reader = new EthereumDirectReader( EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls, TestingCommons.tracerMock() up, Caches.default(), new CurrentBlockCache(), calls
) )
reader.requestReaderFactory = Mock(RequestReaderFactory) { reader.requestReaderFactory = Mock(RequestReaderFactory) {
4 * create(_) >> Mock(RequestReader) { 4 * create(_) >> Mock(RequestReader) {

View File

@@ -21,7 +21,6 @@ class EthereumLocalReaderSpec extends Specification {
TestingCommons.multistream(TestingCommons.api()), TestingCommons.multistream(TestingCommons.api()),
Caches.default(), Caches.default(),
ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)), ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)),
TestingCommons.tracerMock()
), ),
methods methods
) )
@@ -39,7 +38,6 @@ class EthereumLocalReaderSpec extends Specification {
TestingCommons.multistream(TestingCommons.api()), TestingCommons.multistream(TestingCommons.api()),
Caches.default(), Caches.default(),
ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)), ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)),
TestingCommons.tracerMock()
), ),
methods methods
) )

View File

@@ -26,7 +26,6 @@ import io.micrometer.core.instrument.Timer
import org.mockserver.integration.ClientAndServer import org.mockserver.integration.ClientAndServer
import org.mockserver.model.HttpRequest import org.mockserver.model.HttpRequest
import org.mockserver.model.HttpResponse import org.mockserver.model.HttpResponse
import org.springframework.util.SocketUtils
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
import spock.lang.Specification import spock.lang.Specification
@@ -35,7 +34,6 @@ import java.time.Duration
class JsonRpcHttpReaderSpec extends Specification { class JsonRpcHttpReaderSpec extends Specification {
ClientAndServer mockServer ClientAndServer mockServer
int port = 19332
RequestMetrics metrics = new RequestMetrics( RequestMetrics metrics = new RequestMetrics(
Timer.builder("test1").register(TestingCommons.meterRegistry), Timer.builder("test1").register(TestingCommons.meterRegistry),
Counter.builder("test2").register(TestingCommons.meterRegistry), Counter.builder("test2").register(TestingCommons.meterRegistry),
@@ -43,8 +41,7 @@ class JsonRpcHttpReaderSpec extends Specification {
) )
def setup() { def setup() {
port = SocketUtils.findAvailableTcpPort(19332) mockServer = ClientAndServer.startClientAndServer(0);
mockServer = ClientAndServer.startClientAndServer(port);
} }
def cleanup() { def cleanup() {
@@ -53,7 +50,7 @@ class JsonRpcHttpReaderSpec extends Specification {
def "Make a request"() { def "Make a request"() {
setup: setup:
JsonRpcHttpReader client = new JsonRpcHttpReader("localhost:${port}", 50, 50, metrics, Schedulers.boundedElastic(),null, null, [:]) JsonRpcHttpReader client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(),null, null, [:])
def resp = '{' + def resp = '{' +
' "jsonrpc": "2.0",' + ' "jsonrpc": "2.0",' +
' "result": "0x98de45",' + ' "result": "0x98de45",' +
@@ -74,7 +71,7 @@ class JsonRpcHttpReaderSpec extends Specification {
def "Produces RPC Exception on error status code"() { def "Produces RPC Exception on error status code"() {
setup: setup:
def client = new JsonRpcHttpReader("localhost:${port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:]) def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:])
mockServer.when( mockServer.when(
HttpRequest.request() HttpRequest.request()
).respond( ).respond(
@@ -97,7 +94,7 @@ class JsonRpcHttpReaderSpec extends Specification {
def "Tries to extract message if HTTP error if it still contains a JSON RPC message"() { def "Tries to extract message if HTTP error if it still contains a JSON RPC message"() {
setup: setup:
def client = new JsonRpcHttpReader("localhost:${port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:]) def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:])
mockServer.when( mockServer.when(
HttpRequest.request() HttpRequest.request()

View File

@@ -50,7 +50,7 @@ class EcdsaSignerSpec extends Specification {
def conf = new SignatureConfig() def conf = new SignatureConfig()
conf.enabled = true conf.enabled = true
conf.privateKey = "src/test/resources/signer/test_key" conf.privateKey = "src/test/resources/signer/test_key"
def signer = new ResponseSignerFactory(conf).getObject() as EcdsaSigner def signer = new ResponseSignerFactory(conf).createSigner() as EcdsaSigner
// To verify the test, check the hash of test key above: // To verify the test, check the hash of test key above:
// //
@@ -123,7 +123,7 @@ class EcdsaSignerSpec extends Specification {
verifier.initVerify(pk) verifier.initVerify(pk)
verifier.update("DSHACKLESIG/10/infura/${Hex.encodeHexString(sha256.digest(result))}".getBytes()) verifier.update("DSHACKLESIG/10/infura/${Hex.encodeHexString(sha256.digest(result))}".getBytes())
def signer = factory.getObject() as EcdsaSigner def signer = factory.createSigner() as EcdsaSigner
when: when:
def sig = signer.sign(10, result, up.id) def sig = signer.sign(10, result, up.id)

View File

@@ -11,7 +11,7 @@ class ResponseSignerFactorySpec extends Specification {
setup: setup:
def conf = new SignatureConfig() def conf = new SignatureConfig()
when: when:
def signer = new ResponseSignerFactory(conf).getObject() def signer = new ResponseSignerFactory(conf).createSigner()
then: then:
signer instanceof NoSigner signer instanceof NoSigner
} }
@@ -20,7 +20,7 @@ class ResponseSignerFactorySpec extends Specification {
setup: setup:
def conf = new SignatureConfig() def conf = new SignatureConfig()
when: when:
def signer = new ResponseSignerFactory(conf).getObject() def signer = new ResponseSignerFactory(conf).createSigner()
then: then:
signer instanceof NoSigner signer instanceof NoSigner
} }

View File

@@ -13,11 +13,9 @@ import io.emeraldpay.dshackle.upstream.Selector
import io.grpc.BindableService import io.grpc.BindableService
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.mockito.Mockito.mock
import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.context.TestConfiguration import org.springframework.boot.test.context.TestConfiguration
import org.springframework.cloud.sleuth.Tracer
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Import import org.springframework.context.annotation.Import
import org.springframework.context.annotation.Profile import org.springframework.context.annotation.Profile
@@ -57,7 +55,6 @@ class IntegrationTest {
fun testUpstreamTxMethodQuorumAndReader() { fun testUpstreamTxMethodQuorumAndReader() {
val ms = multistreamHolder.getUpstream(Chain.ETHEREUM__MAINNET) val ms = multistreamHolder.getUpstream(Chain.ETHEREUM__MAINNET)
val ethUpstream = ms.getUpstreams()[0] val ethUpstream = ms.getUpstreams()[0]
val tracer = mock<Tracer>()
val reqReader = RequestReaderFactory.default() val reqReader = RequestReaderFactory.default()
val txQuorum = ethUpstream.getMethods().createQuorumFor("eth_sendRawTransaction") val txQuorum = ethUpstream.getMethods().createQuorumFor("eth_sendRawTransaction")
@@ -69,7 +66,6 @@ class IntegrationTest {
Selector.UpstreamFilter.default, Selector.UpstreamFilter.default,
txQuorum, txQuorum,
null, null,
tracer,
), ),
) )
val txCountReader = reqReader.create( val txCountReader = reqReader.create(
@@ -78,7 +74,6 @@ class IntegrationTest {
Selector.UpstreamFilter.default, Selector.UpstreamFilter.default,
txCountQuorum, txCountQuorum,
null, null,
tracer,
), ),
) )

View File

@@ -29,7 +29,6 @@ import org.mockito.kotlin.mock
import org.mockito.kotlin.never import org.mockito.kotlin.never
import org.mockito.kotlin.verify import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever import org.mockito.kotlin.whenever
import org.springframework.cloud.sleuth.Tracer
import org.springframework.util.ResourceUtils import org.springframework.util.ResourceUtils
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Schedulers
@@ -194,7 +193,7 @@ class ReloadConfigTest {
ArrayList(), ArrayList(),
Caches.default(), Caches.default(),
Schedulers.boundedElastic(), Schedulers.boundedElastic(),
cs.makeCachingReaderBuilder(mock<Tracer>()), cs.makeCachingReaderBuilder(),
cs::localReaderBuilder, cs::localReaderBuilder,
cs.subscriptionBuilder(Schedulers.boundedElastic()), cs.subscriptionBuilder(Schedulers.boundedElastic()),
) )

View File

@@ -9,7 +9,6 @@ import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.MethodSource
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.springframework.cloud.sleuth.Tracer
class RequestReaderFactoryTest { class RequestReaderFactoryTest {
private val defaultFactory = RequestReaderFactory.Default() private val defaultFactory = RequestReaderFactory.Default()
@@ -26,7 +25,6 @@ class RequestReaderFactoryTest {
companion object { companion object {
private val ms = mock<Multistream>() private val ms = mock<Multistream>()
private val tracer = mock<Tracer>()
@JvmStatic @JvmStatic
fun data(): List<Arguments> { fun data(): List<Arguments> {
@@ -37,7 +35,6 @@ class RequestReaderFactoryTest {
Selector.UpstreamFilter(Selector.empty), Selector.UpstreamFilter(Selector.empty),
MaximumValueQuorum(), MaximumValueQuorum(),
null, null,
tracer,
), ),
), ),
Arguments.of( Arguments.of(
@@ -46,7 +43,6 @@ class RequestReaderFactoryTest {
Selector.UpstreamFilter(Selector.empty), Selector.UpstreamFilter(Selector.empty),
BroadcastQuorum(), BroadcastQuorum(),
null, null,
tracer,
), ),
), ),
) )

View File

@@ -13,7 +13,6 @@ import org.junit.jupiter.api.Test
import org.mockito.kotlin.doReturn import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock import org.mockito.kotlin.mock
import org.mockito.kotlin.spy import org.mockito.kotlin.spy
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.test.StepVerifier import reactor.test.StepVerifier
@@ -29,7 +28,6 @@ class NativeCallTest {
mock<MultistreamHolder>(), mock<MultistreamHolder>(),
mock<ResponseSigner>(), mock<ResponseSigner>(),
MainConfig(), MainConfig(),
mock<Tracer>(),
), ),
) { ) {
on { nativeCallResult(request) } doReturn Flux.just( on { nativeCallResult(request) } doReturn Flux.just(
@@ -59,7 +57,6 @@ class NativeCallTest {
mock<MultistreamHolder>(), mock<MultistreamHolder>(),
mock<ResponseSigner>(), mock<ResponseSigner>(),
MainConfig(), MainConfig(),
mock<Tracer>(),
), ),
) { ) {
on { nativeCallResult(request) } doReturn Flux.just( on { nativeCallResult(request) } doReturn Flux.just(
@@ -95,7 +92,6 @@ class NativeCallTest {
mock<MultistreamHolder>(), mock<MultistreamHolder>(),
mock<ResponseSigner>(), mock<ResponseSigner>(),
MainConfig(), MainConfig(),
mock<Tracer>(),
), ),
) { ) {
on { nativeCallResult(request) } doReturn Flux.just( on { nativeCallResult(request) } doReturn Flux.just(