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:
Vyacheslav
2023-08-07 20:17:18 +03:00
committed by GitHub
parent f94acd9955
commit e1cda3ca2c
16 changed files with 90 additions and 90 deletions

View File

@@ -75,6 +75,7 @@ class Describe(
when (it) { when (it) {
Capability.RPC -> BlockchainOuterClass.Capabilities.CAP_CALLS Capability.RPC -> BlockchainOuterClass.Capabilities.CAP_CALLS
Capability.BALANCE -> BlockchainOuterClass.Capabilities.CAP_BALANCE Capability.BALANCE -> BlockchainOuterClass.Capabilities.CAP_BALANCE
Capability.WS_HEAD -> BlockchainOuterClass.Capabilities.CAP_WS_HEAD
} }
} }
) )

View File

@@ -2,5 +2,6 @@ package io.emeraldpay.dshackle.upstream
enum class Capability { enum class Capability {
RPC, RPC,
BALANCE BALANCE,
WS_HEAD
} }

View File

@@ -1,10 +1,10 @@
package io.emeraldpay.dshackle.upstream.ethereum package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.upstream.Capability
import io.emeraldpay.dshackle.upstream.EgressSubscription import io.emeraldpay.dshackle.upstream.EgressSubscription
import io.emeraldpay.dshackle.upstream.Selector import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectLogs import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectLogs
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.ConnectNewHeads 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.dshackle.upstream.ethereum.subscribe.PendingTxesSource
import io.emeraldpay.etherjar.domain.Address import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.hex.Hex32 import io.emeraldpay.etherjar.hex.Hex32
@@ -23,27 +23,23 @@ open class EthereumEgressSubscription(
const val METHOD_NEW_HEADS = "newHeads" const val METHOD_NEW_HEADS = "newHeads"
const val METHOD_LOGS = "logs" const val METHOD_LOGS = "logs"
const val METHOD_SYNCING = "syncing"
const val METHOD_PENDING_TXES = "newPendingTransactions" 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) private val newHeads = ConnectNewHeads(upstream, scheduler)
open val logs = ConnectLogs(upstream, scheduler) open val logs = ConnectLogs(upstream, scheduler)
private val syncing = ConnectSyncing(upstream) override fun getAvailableTopics(): List<String> {
val subs = if (upstream.getCapabilities().contains(Capability.WS_HEAD)) {
override fun getAvailableTopics() = availableTopics listOf(METHOD_NEW_HEADS, METHOD_LOGS)
} else {
listOf()
}
return if (pendingTxesSource != null) {
subs.plus(METHOD_PENDING_TXES)
} else {
subs
}
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
override fun subscribe(topic: String, params: Any?, matcher: Selector.Matcher): Flux<out Any> { 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) return logs.create(paramsMap.address, paramsMap.topics).connect(matcher)
} }
if (topic == METHOD_SYNCING) {
return syncing.connect(matcher)
}
if (topic == METHOD_PENDING_TXES) { if (topic == METHOD_PENDING_TXES) {
return pendingTxesSource?.connect(matcher) ?: Flux.empty() return pendingTxesSource?.connect(matcher) ?: Flux.empty()
} }

View File

@@ -23,12 +23,14 @@ import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.reader.JsonRpcReader import io.emeraldpay.dshackle.reader.JsonRpcReader
import io.emeraldpay.dshackle.startup.QuorumForLabels import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.upstream.Capability
import io.emeraldpay.dshackle.upstream.Head import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Upstream import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.dshackle.upstream.UpstreamAvailability import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.calls.CallMethods import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.ethereum.connectors.ConnectorFactory import io.emeraldpay.dshackle.upstream.ethereum.connectors.ConnectorFactory
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector 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 io.emeraldpay.dshackle.upstream.ethereum.subscribe.EthereumLabelsDetector
import org.springframework.context.Lifecycle import org.springframework.context.Lifecycle
import reactor.core.Disposable import reactor.core.Disposable
@@ -51,6 +53,16 @@ open class EthereumLikeRpcUpstream(
private var validatorSubscription: Disposable? = null 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) { override fun setCaches(caches: Caches) {
if (connector is CachesEnabled) { if (connector is CachesEnabled) {
connector.setCaches(caches) connector.setCaches(caches)

View File

@@ -33,11 +33,7 @@ abstract class EthereumLikeUpstream(
val chainConfig: ChainsConfig.ChainConfig val chainConfig: ChainsConfig.ChainConfig
) : DefaultUpstream(id, hash, options, role, targets, node, chainConfig) { ) : DefaultUpstream(id, hash, options, role, targets, node, chainConfig) {
private val capabilities = if (options.providesBalance != false) { private val capabilities = setOf(Capability.RPC, Capability.BALANCE)
setOf(Capability.RPC, Capability.BALANCE)
} else {
setOf(Capability.RPC)
}
override fun getCapabilities(): Set<Capability> { override fun getCapabilities(): Set<Capability> {
return capabilities return capabilities

View File

@@ -8,6 +8,8 @@ import io.emeraldpay.dshackle.upstream.ethereum.EthereumIngressSubscription
interface EthereumConnector : Lifecycle { interface EthereumConnector : Lifecycle {
fun getHead(): Head fun getHead(): Head
fun getConnectorMode(): EthereumConnectorFactory.ConnectorMode
fun getIngressReader(): JsonRpcReader fun getIngressReader(): JsonRpcReader
fun getIngressSubscription(): EthereumIngressSubscription fun getIngressSubscription(): EthereumIngressSubscription

View File

@@ -26,7 +26,7 @@ import reactor.core.scheduler.Scheduler
import java.time.Duration import java.time.Duration
class EthereumRpcConnector( class EthereumRpcConnector(
connectorType: ConnectorMode, private val connectorType: ConnectorMode,
private val directReader: JsonRpcReader, private val directReader: JsonRpcReader,
wsFactory: EthereumWsConnectionPoolFactory?, wsFactory: EthereumWsConnectionPoolFactory?,
id: String, id: String,
@@ -43,6 +43,8 @@ class EthereumRpcConnector(
private val log = LoggerFactory.getLogger(EthereumRpcConnector::class.java) private val log = LoggerFactory.getLogger(EthereumRpcConnector::class.java)
} }
override fun getConnectorMode() = connectorType
init { init {
pool = wsFactory?.create(null) pool = wsFactory?.create(null)

View File

@@ -45,6 +45,8 @@ class EthereumWsConnector(
subscriptions = EthereumWsIngressSubscription(wsSubscriptions) subscriptions = EthereumWsIngressSubscription(wsSubscriptions)
} }
override fun getConnectorMode() = EthereumConnectorFactory.ConnectorMode.WS_ONLY
override fun start() { override fun start() {
pool.connect() pool.connect()
head.start() head.start()

View File

@@ -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
}
}
}

View File

@@ -13,6 +13,7 @@ class RemoteCapabilities {
when { when {
BlockchainOuterClass.Capabilities.CAP_BALANCE == value -> Capability.BALANCE BlockchainOuterClass.Capabilities.CAP_BALANCE == value -> Capability.BALANCE
BlockchainOuterClass.Capabilities.CAP_CALLS == value -> Capability.RPC BlockchainOuterClass.Capabilities.CAP_CALLS == value -> Capability.RPC
BlockchainOuterClass.Capabilities.CAP_WS_HEAD == value -> Capability.WS_HEAD
else -> null else -> null
} }
}.toSet() }.toSet()

View File

@@ -7,16 +7,24 @@ import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstreamValidator import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstreamValidator
import io.emeraldpay.dshackle.upstream.ethereum.connectors.ConnectorFactory import io.emeraldpay.dshackle.upstream.ethereum.connectors.ConnectorFactory
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
class ConnectorFactoryMock implements ConnectorFactory { class ConnectorFactoryMock implements ConnectorFactory {
Reader<JsonRpcRequest, JsonRpcResponse> api Reader<JsonRpcRequest, JsonRpcResponse> api
Head head Head head
ConnectorMode mode
ConnectorFactoryMock(Reader<JsonRpcRequest, JsonRpcResponse> api, Head head) { ConnectorFactoryMock(Reader<JsonRpcRequest, JsonRpcResponse> api, Head head) {
this(api, head, ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD)
}
ConnectorFactoryMock(Reader<JsonRpcRequest, JsonRpcResponse> api, Head head, ConnectorMode mode) {
this.api = api this.api = api
this.head = head this.head = head
this.mode = mode
} }
boolean isValid() { boolean isValid() {
@@ -24,6 +32,6 @@ class ConnectorFactoryMock implements ConnectorFactory {
} }
EthereumConnector create(DefaultUpstream upstream, EthereumUpstreamValidator validator, Chain chain, boolean skipEnhance) { EthereumConnector create(DefaultUpstream upstream, EthereumUpstreamValidator validator, Chain chain, boolean skipEnhance) {
return new EthereumConnectorMock(api, head) return new EthereumConnectorMock(api, head, this.mode)
} }
} }

View File

@@ -5,17 +5,27 @@ import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.ethereum.EthereumIngressSubscription import io.emeraldpay.dshackle.upstream.ethereum.EthereumIngressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.NoEthereumIngressSubscription import io.emeraldpay.dshackle.upstream.ethereum.NoEthereumIngressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnector
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
class EthereumConnectorMock implements EthereumConnector { class EthereumConnectorMock implements EthereumConnector {
Reader<JsonRpcRequest, JsonRpcResponse> api Reader<JsonRpcRequest, JsonRpcResponse> api
Head head Head head
EthereumConnectorMock(Reader<JsonRpcRequest, JsonRpcResponse> api, Head head) { ConnectorMode mode
EthereumConnectorMock(Reader<JsonRpcRequest, JsonRpcResponse> api, Head head, ConnectorMode mode) {
this.api = api this.api = api
this.mode = mode
this.head = head this.head = head
} }
@Override
ConnectorMode getConnectorMode() {
return this.mode
}
@Override @Override
Reader<JsonRpcRequest, JsonRpcResponse> getIngressReader() { Reader<JsonRpcRequest, JsonRpcResponse> getIngressReader() {
return this.api return this.api

View File

@@ -27,6 +27,7 @@ import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.upstream.UpstreamAvailability import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.calls.* import io.emeraldpay.dshackle.upstream.calls.*
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
@@ -49,7 +50,7 @@ class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
} }
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, Map<String, String> labels) { EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, Map<String, String> labels) {
this(id, chain, api, allMethods(), labels) this(id, chain, api, allMethods(), labels, ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD)
} }
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) { EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
@@ -61,16 +62,16 @@ class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
} }
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) { EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
this(id, chain, api, methods, Collections.<String, String>emptyMap()) this(id, chain, api, methods, Collections.<String, String>emptyMap(), ConnectorMode.RPC_REQUESTS_WITH_WS_HEAD)
} }
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods, Map<String, String> labels) { EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods, Map<String, String> labels, ConnectorMode mode) {
super(id, (byte)id.hashCode(), chain, super(id, (byte)id.hashCode(), chain,
getOpts(), getOpts(),
UpstreamsConfig.UpstreamRole.PRIMARY, UpstreamsConfig.UpstreamRole.PRIMARY,
methods, methods,
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(labels)), new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(labels)),
new ConnectorFactoryMock(api, new EthereumHeadMock()), new ConnectorFactoryMock(api, new EthereumHeadMock(), mode),
ChainConfig.default(), ChainConfig.default(),
true true
) )

View File

@@ -30,6 +30,7 @@ import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory.ConnectorMode
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.etherjar.domain.BlockHash import io.emeraldpay.etherjar.domain.BlockHash
@@ -62,6 +63,10 @@ class TestingCommons {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api()) return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api())
} }
static EthereumPosRpcUpstreamMock upstream(String id, ConnectorMode mode) {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api(), EthereumPosRpcUpstreamMock.allMethods(), Collections.<String, String>emptyMap(), mode)
}
static EthereumPosRpcUpstreamMock upstream(String id, String provider) { static EthereumPosRpcUpstreamMock upstream(String id, String provider) {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api(), Collections.singletonMap("provider", provider)) return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api(), Collections.singletonMap("provider", provider))
} }

View File

@@ -15,7 +15,10 @@
*/ */
package io.emeraldpay.dshackle.upstream.ethereum package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.test.EthereumRpcUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.IngressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.connectors.EthereumConnectorFactory
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.PendingTxesSource import io.emeraldpay.dshackle.upstream.ethereum.subscribe.PendingTxesSource
import io.emeraldpay.etherjar.domain.Address import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.hex.Hex32 import io.emeraldpay.etherjar.hex.Hex32
@@ -172,4 +175,23 @@ class EthereumEgressSubscriptionSpec extends Specification {
Hex32.from("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") Hex32.from("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925")
] ]
} }
def "get available subscriptions"() {
when:
def up1 = TestingCommons.upstream("test", EthereumConnectorFactory.ConnectorMode.RPC_ONLY)
def ethereumSubscribe1 = new EthereumEgressSubscription(TestingCommons.multistream(up1) as EthereumPosMultiStream, Schedulers.boundedElastic(), null)
then:
ethereumSubscribe1.getAvailableTopics() == []
when:
def up2 = TestingCommons.upstream("test")
def ethereumSubscribe2 = new EthereumEgressSubscription(TestingCommons.multistream(up2) as EthereumPosMultiStream, Schedulers.boundedElastic(), null)
then:
ethereumSubscribe2.getAvailableTopics().toSet() == [EthereumEgressSubscription.METHOD_LOGS, EthereumEgressSubscription.METHOD_NEW_HEADS].toSet()
when:
def up3 = TestingCommons.upstream("test")
def ethereumSubscribe3 = new EthereumEgressSubscription(TestingCommons.multistream(up2) as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
then:
ethereumSubscribe3.getAvailableTopics().toSet() == [EthereumEgressSubscription.METHOD_LOGS, EthereumEgressSubscription.METHOD_NEW_HEADS, EthereumEgressSubscription.METHOD_PENDING_TXES].toSet()
}
} }