detecting correct available subscriptions for multistream via capabilities (#274)
Helps us to distinguish which multistreams can be used for subscriptions
This commit is contained in:
@@ -75,6 +75,7 @@ class Describe(
|
||||
when (it) {
|
||||
Capability.RPC -> BlockchainOuterClass.Capabilities.CAP_CALLS
|
||||
Capability.BALANCE -> BlockchainOuterClass.Capabilities.CAP_BALANCE
|
||||
Capability.WS_HEAD -> BlockchainOuterClass.Capabilities.CAP_WS_HEAD
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,5 +2,6 @@ package io.emeraldpay.dshackle.upstream
|
||||
|
||||
enum class Capability {
|
||||
RPC,
|
||||
BALANCE
|
||||
BALANCE,
|
||||
WS_HEAD
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.emeraldpay.dshackle.upstream.ethereum
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Capability
|
||||
import io.emeraldpay.dshackle.upstream.EgressSubscription
|
||||
import io.emeraldpay.dshackle.upstream.Selector
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectLogs
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectNewHeads
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectSyncing
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.PendingTxesSource
|
||||
import io.emeraldpay.etherjar.domain.Address
|
||||
import io.emeraldpay.etherjar.hex.Hex32
|
||||
@@ -23,27 +23,23 @@ open class EthereumEgressSubscription(
|
||||
|
||||
const val METHOD_NEW_HEADS = "newHeads"
|
||||
const val METHOD_LOGS = "logs"
|
||||
const val METHOD_SYNCING = "syncing"
|
||||
const val METHOD_PENDING_TXES = "newPendingTransactions"
|
||||
}
|
||||
|
||||
private val availableTopics = listOf(
|
||||
METHOD_NEW_HEADS,
|
||||
METHOD_LOGS,
|
||||
METHOD_SYNCING,
|
||||
).let {
|
||||
if (pendingTxesSource != null) {
|
||||
it + METHOD_PENDING_TXES
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
private val newHeads = ConnectNewHeads(upstream, scheduler)
|
||||
open val logs = ConnectLogs(upstream, scheduler)
|
||||
private val syncing = ConnectSyncing(upstream)
|
||||
|
||||
override fun getAvailableTopics() = availableTopics
|
||||
override fun getAvailableTopics(): List<String> {
|
||||
val subs = if (upstream.getCapabilities().contains(Capability.WS_HEAD)) {
|
||||
listOf(METHOD_NEW_HEADS, METHOD_LOGS)
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
return if (pendingTxesSource != null) {
|
||||
subs.plus(METHOD_PENDING_TXES)
|
||||
} else {
|
||||
subs
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun subscribe(topic: String, params: Any?, matcher: Selector.Matcher): Flux<out Any> {
|
||||
@@ -62,9 +58,6 @@ open class EthereumEgressSubscription(
|
||||
}
|
||||
return logs.create(paramsMap.address, paramsMap.topics).connect(matcher)
|
||||
}
|
||||
if (topic == METHOD_SYNCING) {
|
||||
return syncing.connect(matcher)
|
||||
}
|
||||
if (topic == METHOD_PENDING_TXES) {
|
||||
return pendingTxesSource?.connect(matcher) ?: Flux.empty()
|
||||
}
|
||||
|
||||
@@ -23,12 +23,14 @@ import io.emeraldpay.dshackle.config.ChainsConfig
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.reader.JsonRpcReader
|
||||
import io.emeraldpay.dshackle.startup.QuorumForLabels
|
||||
import io.emeraldpay.dshackle.upstream.Capability
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.Upstream
|
||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||
import io.emeraldpay.dshackle.upstream.calls.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.connectors.ConnectorFactory
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.EthereumLabelsDetector
|
||||
import org.springframework.context.Lifecycle
|
||||
import reactor.core.Disposable
|
||||
@@ -51,6 +53,16 @@ open class EthereumLikeRpcUpstream(
|
||||
|
||||
private var validatorSubscription: Disposable? = null
|
||||
|
||||
override fun getCapabilities(): Set<Capability> {
|
||||
return when (connector.getConnectorMode()) {
|
||||
EthereumConnectorFactory.ConnectorMode.WS_ONLY,
|
||||
EthereumConnectorFactory.ConnectorMode.RPC_REQUESTS_WITH_MIXED_HEAD,
|
||||
EthereumConnectorFactory.ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD ->
|
||||
setOf(Capability.RPC, Capability.BALANCE, Capability.WS_HEAD)
|
||||
EthereumConnectorFactory.ConnectorMode.RPC_ONLY -> setOf(Capability.RPC, Capability.BALANCE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setCaches(caches: Caches) {
|
||||
if (connector is CachesEnabled) {
|
||||
connector.setCaches(caches)
|
||||
|
||||
@@ -33,11 +33,7 @@ abstract class EthereumLikeUpstream(
|
||||
val chainConfig: ChainsConfig.ChainConfig
|
||||
) : DefaultUpstream(id, hash, options, role, targets, node, chainConfig) {
|
||||
|
||||
private val capabilities = if (options.providesBalance != false) {
|
||||
setOf(Capability.RPC, Capability.BALANCE)
|
||||
} else {
|
||||
setOf(Capability.RPC)
|
||||
}
|
||||
private val capabilities = setOf(Capability.RPC, Capability.BALANCE)
|
||||
|
||||
override fun getCapabilities(): Set<Capability> {
|
||||
return capabilities
|
||||
|
||||
@@ -8,6 +8,8 @@ import io.emeraldpay.dshackle.upstream.ethereum.EthereumIngressSubscription
|
||||
interface EthereumConnector : Lifecycle {
|
||||
fun getHead(): Head
|
||||
|
||||
fun getConnectorMode(): EthereumConnectorFactory.ConnectorMode
|
||||
|
||||
fun getIngressReader(): JsonRpcReader
|
||||
|
||||
fun getIngressSubscription(): EthereumIngressSubscription
|
||||
|
||||
@@ -26,7 +26,7 @@ import reactor.core.scheduler.Scheduler
|
||||
import java.time.Duration
|
||||
|
||||
class EthereumRpcConnector(
|
||||
connectorType: ConnectorMode,
|
||||
private val connectorType: ConnectorMode,
|
||||
private val directReader: JsonRpcReader,
|
||||
wsFactory: EthereumWsConnectionPoolFactory?,
|
||||
id: String,
|
||||
@@ -43,6 +43,8 @@ class EthereumRpcConnector(
|
||||
private val log = LoggerFactory.getLogger(EthereumRpcConnector::class.java)
|
||||
}
|
||||
|
||||
override fun getConnectorMode() = connectorType
|
||||
|
||||
init {
|
||||
pool = wsFactory?.create(null)
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ class EthereumWsConnector(
|
||||
subscriptions = EthereumWsIngressSubscription(wsSubscriptions)
|
||||
}
|
||||
|
||||
override fun getConnectorMode() = EthereumConnectorFactory.ConnectorMode.WS_ONLY
|
||||
|
||||
override fun start() {
|
||||
pool.connect()
|
||||
head.start()
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 EmeraldPay, Inc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.upstream.ethereum.subscribe
|
||||
|
||||
import io.emeraldpay.dshackle.upstream.Selector
|
||||
import io.emeraldpay.dshackle.upstream.SubscriptionConnect
|
||||
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeMultistream
|
||||
import reactor.core.publisher.Flux
|
||||
import java.time.Duration
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
class ConnectSyncing(
|
||||
private val upstream: EthereumLikeMultistream
|
||||
) : SubscriptionConnect<Boolean> {
|
||||
|
||||
private var connected: Flux<Boolean>? = null
|
||||
private val connectLock = ReentrantLock()
|
||||
|
||||
override fun connect(matcher: Selector.Matcher): Flux<Boolean> {
|
||||
val current = connected
|
||||
if (current != null) {
|
||||
return current
|
||||
}
|
||||
connectLock.withLock {
|
||||
val currentRecheck = connected
|
||||
if (currentRecheck != null) {
|
||||
return currentRecheck
|
||||
}
|
||||
val created = upstream.observeStatus()
|
||||
.map { it != UpstreamAvailability.OK }
|
||||
.publish()
|
||||
.refCount(1, Duration.ofSeconds(60))
|
||||
.doFinally {
|
||||
// forget it on disconnect, so next time it's recreated
|
||||
connected = null
|
||||
}
|
||||
connected = created
|
||||
return created
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ class RemoteCapabilities {
|
||||
when {
|
||||
BlockchainOuterClass.Capabilities.CAP_BALANCE == value -> Capability.BALANCE
|
||||
BlockchainOuterClass.Capabilities.CAP_CALLS == value -> Capability.RPC
|
||||
BlockchainOuterClass.Capabilities.CAP_WS_HEAD == value -> Capability.WS_HEAD
|
||||
else -> null
|
||||
}
|
||||
}.toSet()
|
||||
|
||||
Reference in New Issue
Block a user