ethereum as generic upstream (#331)

- merge all Ethereum implementations to Generic upstreams and multistreams
- merge evm-pos and pow to ethereum
This commit is contained in:
a10zn8
2023-10-31 16:28:15 +03:00
committed by GitHub
parent 4ea4fa5d81
commit 08cbffe7d1
69 changed files with 826 additions and 2790 deletions

View File

@@ -47,14 +47,4 @@ class CacheConfigReaderSpec extends Specification {
//later may be not null if we support something else besides Redis
act == null
}
def "Local read disabled"() {
setup:
def config = this.class.getClassLoader().getResourceAsStream("cache-local-router-disabled.yaml")
when:
def act = reader.read(config)
then:
!act.requestsCacheEnabled
}
}

View File

@@ -54,7 +54,7 @@ class NativeCallSpec extends Specification {
ObjectMapper objectMapper = Global.objectMapper
def nativeCall(MultistreamHolder upstreams = null, ResponseSigner signer = null, Boolean enableCache = true, Boolean passthrough = false) {
def nativeCall(MultistreamHolder upstreams = null, ResponseSigner signer = null, Boolean passthrough = false) {
if (upstreams == null) {
upstreams = Stub(MultistreamHolder)
@@ -65,7 +65,6 @@ class NativeCallSpec extends Specification {
def config = new MainConfig()
def cacheConfig = new CacheConfig()
cacheConfig.requestsCacheEnabled = enableCache
config.cache = cacheConfig
config.passthrough = passthrough
@@ -77,7 +76,7 @@ class NativeCallSpec extends Specification {
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.just(new JsonRpcResponse("1".bytes, null))
}
def upstream = Mock(Multistream) {
1 * getLocalReader(_) >> Mono.just(routedApi)
1 * getLocalReader() >> Mono.just(routedApi)
}
def nativeCall = nativeCall()
@@ -98,7 +97,7 @@ class NativeCallSpec extends Specification {
1 * read(new JsonRpcRequest("eth_test", [])) >> Mono.error(new RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Test message"))
}
def upstream = Mock(Multistream) {
1 * getLocalReader(_) >> Mono.just(routedApi)
1 * getLocalReader() >> Mono.just(routedApi)
}
def nativeCall = nativeCall()

View File

@@ -21,7 +21,7 @@ import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.test.MultistreamHolderMock
import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.ethereum.EthereumEgressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import io.emeraldpay.dshackle.upstream.signature.NoSigner
import reactor.core.publisher.Flux
import reactor.test.StepVerifier
@@ -42,7 +42,7 @@ class NativeSubscribeSpec extends Specification {
def subscribe = Mock(EthereumEgressSubscription) {
1 * it.subscribe("newHeads", null, _ as Selector.AnyLabelMatcher) >> Flux.just("{}")
}
def up = Mock(EthereumPosMultiStream) {
def up = Mock(GenericMultistream) {
1 * it.tryProxySubscribe(_ as Selector.AnyLabelMatcher, call) >> null
1 * it.getEgressSubscription() >> subscribe
}
@@ -81,7 +81,7 @@ class NativeSubscribeSpec extends Specification {
ok
}, _ as Selector.AnyLabelMatcher) >> Flux.just("{}")
}
def up = Mock(EthereumPosMultiStream) {
def up = Mock(GenericMultistream) {
1 * it.tryProxySubscribe(_ as Selector.AnyLabelMatcher, call) >> null
1 * it.getEgressSubscription() >> subscribe
}
@@ -104,7 +104,7 @@ class NativeSubscribeSpec extends Specification {
.setChainValue(Chain.ETHEREUM__MAINNET.id)
.setMethod("newHeads")
.build()
def up = Mock(EthereumPosMultiStream) {
def up = Mock(GenericMultistream) {
1 * it.tryProxySubscribe(_ as Selector.AnyLabelMatcher, call) >> Flux.just("{}")
0 * it.getEgressSubscription()
}

View File

@@ -16,17 +16,15 @@
*/
package io.emeraldpay.dshackle.rpc
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.MultistreamHolderMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.etherjar.domain.BlockHash
import io.emeraldpay.etherjar.rpc.json.TransactionRefJson
@@ -39,11 +37,9 @@ import java.time.Instant
class StreamHeadSpec extends Specification {
ObjectMapper objectMapper = Global.objectMapper
def "Errors on unavailable chain"() {
setup:
def upstreams = new MultistreamHolderMock(Chain.ETHEREUM__MAINNET, Stub(EthereumLikeRpcUpstream))
def upstreams = new MultistreamHolderMock(Chain.ETHEREUM__MAINNET, Stub(GenericUpstream))
def streamHead = new StreamHead(upstreams)
when:
def flux = streamHead.add(
@@ -80,7 +76,7 @@ class StreamHeadSpec extends Specification {
.build()
}
def upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
def upstream = new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
def upstreams = new MultistreamHolderMock(Chain.ETHEREUM__MAINNET, upstream)
def streamHead = new StreamHead(upstreams)
when:

View File

@@ -1,87 +0,0 @@
/**
* Copyright (c) 2019 ETCDEV GmbH
* Copyright (c) 2020 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.test
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.calls.*
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import org.jetbrains.annotations.NotNull
import io.emeraldpay.dshackle.foundation.ChainOptions
import org.reactivestreams.Publisher
class EthereumRpcUpstreamMock extends EthereumLikeRpcUpstream {
EthereumHeadMock ethereumHeadMock
static CallMethods allMethods() {
new AggregatedCallMethods([
new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET),
new DefaultBitcoinMethods(),
new DirectCallMethods(["eth_test"])
])
}
EthereumRpcUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
this(chain, api, allMethods())
}
EthereumRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
this(id, chain, api, allMethods())
}
EthereumRpcUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
this("test", chain, api, methods)
}
EthereumRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
super(id, id.hashCode().byteValue(), chain,
ChainOptions.PartialOptions.getDefaults().buildOptions(),
UpstreamsConfig.UpstreamRole.PRIMARY,
methods,
new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
new ConnectorFactoryMock(api, new EthereumHeadMock()),
ChainsConfig.ChainConfig.default(),
false,
null
)
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
setLag(0)
setStatus(UpstreamAvailability.OK)
start()
}
void nextBlock(BlockContainer block) {
this.ethereumHeadMock.nextBlock(block)
}
void setBlocks(Publisher<BlockContainer> blocks) {
this.ethereumHeadMock.predefined = blocks
}
@Override
String toString() {
return "Upstream mock ${getId()}"
}
}

View File

@@ -2,7 +2,7 @@ package io.emeraldpay.dshackle.test
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.ethereum.EthereumIngressSubscription
import io.emeraldpay.dshackle.upstream.IngressSubscription
import io.emeraldpay.dshackle.upstream.ethereum.NoEthereumIngressSubscription
import io.emeraldpay.dshackle.upstream.generic.connectors.GenericConnector
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
@@ -47,7 +47,7 @@ class GenericConnectorMock implements GenericConnector {
}
@Override
EthereumIngressSubscription getIngressSubscription() {
IngressSubscription getIngressSubscription() {
return NoEthereumIngressSubscription.DEFAULT
}
}

View File

@@ -24,14 +24,14 @@ import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.calls.*
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import org.jetbrains.annotations.NotNull
import org.reactivestreams.Publisher
import io.emeraldpay.dshackle.foundation.ChainOptions
class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
class GenericUpstreamMock extends GenericUpstream {
EthereumHeadMock ethereumHeadMock
static CallMethods allMethods() {
@@ -42,36 +42,40 @@ class EthereumPosRpcUpstreamMock extends EthereumLikeRpcUpstream {
])
}
EthereumPosRpcUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
GenericUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
this(chain, api, allMethods())
}
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, Map<String, String> labels) {
GenericUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, Map<String, String> labels) {
this(id, chain, api, allMethods(), labels)
}
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
GenericUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api) {
this(id, chain, api, allMethods())
}
EthereumPosRpcUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
GenericUpstreamMock(@NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
this("test", chain, api, methods)
}
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
GenericUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods) {
this(id, chain, api, methods, Collections.<String, String>emptyMap())
}
EthereumPosRpcUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods, Map<String, String> labels) {
super(id, (byte)id.hashCode(), chain,
GenericUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods methods, Map<String, String> labels) {
super(id,
chain,
(byte)id.hashCode(),
getOpts(),
UpstreamsConfig.UpstreamRole.PRIMARY,
methods,
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(labels)),
new ConnectorFactoryMock(api, new EthereumHeadMock()),
ChainConfig.default(),
true,
null
new ConnectorFactoryMock(api, new EthereumHeadMock()),
null,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&validator,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&labelDetector,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.&subscriptionTopics,
)
this.ethereumHeadMock = this.getHead() as EthereumHeadMock
setLag(0)

View File

@@ -20,13 +20,13 @@ import io.emeraldpay.dshackle.BlockchainType
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.upstream.*
import io.emeraldpay.dshackle.upstream.generic.*
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinMultistream
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinRpcUpstream
import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumCachingReader
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
import org.jetbrains.annotations.NotNull
import org.springframework.cloud.sleuth.brave.bridge.BraveTracer
import reactor.core.scheduler.Schedulers
@@ -42,13 +42,16 @@ class MultistreamHolderMock implements MultistreamHolder {
Multistream addUpstream(@NotNull Chain chain, @NotNull Upstream up) {
if (!upstreams.containsKey(chain)) {
if (BlockchainType.from(chain) == BlockchainType.EVM_POS) {
if (up instanceof EthereumPosMultiStream) {
if (BlockchainType.from(chain) == BlockchainType.ETHEREUM) {
if (up instanceof GenericMultistream) {
upstreams[chain] = up
} else if (up instanceof EthereumLikeRpcUpstream) {
upstreams[chain] = new EthereumPosMultiStream(
chain, [up as EthereumLikeRpcUpstream], Caches.default(),
Schedulers.boundedElastic(), TestingCommons.tracerMock()
} else if (up instanceof GenericUpstream) {
upstreams[chain] = new GenericMultistream(
chain, [up as GenericUpstream], Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic())
)
} else {
throw new IllegalArgumentException("Unsupported upstream type ${up.class}")
@@ -90,21 +93,24 @@ class MultistreamHolderMock implements MultistreamHolder {
return upstreams.values().toList()
}
static class EthereumMultistreamMock extends EthereumPosMultiStream {
static class EthereumMultistreamMock extends GenericMultistream {
EthereumCachingReader customReader = null
CallMethods customMethods = null
Head customHead = null
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches, Schedulers.boundedElastic(), new BraveTracer(null, null, null))
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches, Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(new BraveTracer(null, null, null)),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
}
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams) {
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams) {
this(chain, upstreams, Caches.default())
}
EthereumMultistreamMock(@NotNull Chain chain, @NotNull EthereumLikeRpcUpstream upstream) {
EthereumMultistreamMock(@NotNull Chain chain, @NotNull GenericUpstream upstream) {
this(chain, [upstream])
}

View File

@@ -28,10 +28,10 @@ import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
import io.emeraldpay.etherjar.domain.BlockHash
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.etherjar.domain.TransactionId
@@ -54,44 +54,49 @@ class TestingCommons {
return new TracerMock(null, null, null)
}
static EthereumPosRpcUpstreamMock upstream() {
return new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api())
static GenericUpstreamMock upstream() {
return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api())
}
static EthereumPosRpcUpstreamMock upstream(String id) {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api())
static GenericUpstreamMock upstream(String id) {
return new GenericUpstreamMock(id, Chain.ETHEREUM__MAINNET, api())
}
static EthereumPosRpcUpstreamMock upstream(String id, String provider) {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api(), Collections.singletonMap("provider", provider))
static GenericUpstreamMock upstream(String id, String provider) {
return new GenericUpstreamMock(id, Chain.ETHEREUM__MAINNET, api(), Collections.singletonMap("provider", provider))
}
static EthereumPosRpcUpstreamMock upstream(String id, Reader<JsonRpcRequest, JsonRpcResponse> api) {
return new EthereumPosRpcUpstreamMock(id, Chain.ETHEREUM__MAINNET, api)
static GenericUpstreamMock upstream(String id, Reader<JsonRpcRequest, JsonRpcResponse> api) {
return new GenericUpstreamMock(id, Chain.ETHEREUM__MAINNET, api)
}
static EthereumPosRpcUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api) {
return new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
static GenericUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api) {
return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api)
}
static EthereumPosRpcUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, String method) {
static GenericUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, String method) {
return upstream(api, [method])
}
static EthereumPosRpcUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, List<String> methods) {
return new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api, new DirectCallMethods(methods))
static GenericUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, List<String> methods) {
return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api, new DirectCallMethods(methods))
}
static EthereumPosRpcUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods callMethods) {
return new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api, callMethods)
static GenericUpstreamMock upstream(Reader<JsonRpcRequest, JsonRpcResponse> api, CallMethods callMethods) {
return new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, api, callMethods)
}
static Multistream multistream(Reader<JsonRpcRequest, JsonRpcResponse> api) {
return multistream(upstream(api))
}
static Multistream multistream(EthereumPosRpcUpstreamMock up) {
return new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, [up], Caches.default(), Schedulers.boundedElastic(), tracerMock()).tap {
static Multistream multistream(GenericUpstreamMock up) {
return new GenericMultistream(Chain.ETHEREUM__MAINNET, [up], Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()),
).tap {
start()
}
}
@@ -112,11 +117,17 @@ class TestingCommons {
}
static Multistream multistreamWithoutUpstreams(Chain chain) {
return new EthereumPosMultiStream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(), tracerMock())
return new GenericMultistream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
}
static Multistream multistreamClassicWithoutUpstreams(Chain chain) {
return new EthereumMultistream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(), tracerMock())
return new GenericMultistream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
}
static FileResolver fileResolver() {

View File

@@ -1,253 +0,0 @@
package io.emeraldpay.dshackle.upstream
import kotlin.jvm.functions.Function1
import org.jetbrains.annotations.NotNull
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import spock.lang.Specification
import java.time.Duration
import java.util.function.Function
class AbstractChainFeesSpec extends Specification {
Function<List<String>, List<String>> extractTx = { it }
def "Iterates N last blocks"() {
setup:
def ups = Mock(Multistream) {
1 * getHead() >> Mock(Head) {
1 * it.getCurrentHeight() >> 234
}
}
def fees = new TestChainFees(5, ups, Stub(Function1))
when:
def act = fees.usingBlocks(3).collectList().block(Duration.ofSeconds(1)).toSorted()
then:
act == [232L, 233L, 234L]
}
def "Limits iteration to configured"() {
setup:
def ups = Mock(Multistream) {
1 * getHead() >> Mock(Head) {
1 * it.getCurrentHeight() >> 234
}
}
def fees = new TestChainFees(3, ups, Stub(Function1))
when:
def act = fees.usingBlocks(5).collectList().block(Duration.ofSeconds(1)).toSorted()
then:
act == [232L, 233L, 234L]
}
def "TxAtPos 0 for empty list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 0)
when:
def act = txAt.get([])
then:
act == null
}
def "TxAtPos 0 for single item list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 0)
when:
def act = txAt.get(["t_1"])
then:
act == "t_1"
}
def "TxAtPos 0 for many item list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 0)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_3"
}
def "TxAtPos 1 for many item list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 1)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_2"
}
def "TxAtPos 2 for many item list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 2)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_1"
}
def "TxAtPos 5 for 3 item list"() {
setup:
def txAt = new AbstractChainFees.TxAtPos<List<String>, String>(extractTx, 5)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_1"
}
def "TxAtBottom for empty list"() {
setup:
def txAt = new AbstractChainFees.TxAtBottom<List<String>, String>(extractTx)
when:
def act = txAt.get([])
then:
act == null
}
def "TxAtBottom for single item list"() {
setup:
def txAt = new AbstractChainFees.TxAtBottom<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1"])
then:
act == "t_1"
}
def "TxAtBottom for multi item list"() {
setup:
def txAt = new AbstractChainFees.TxAtBottom<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_3"
}
def "TxAtTop for empty list"() {
setup:
def txAt = new AbstractChainFees.TxAtTop<List<String>, String>(extractTx)
when:
def act = txAt.get([])
then:
act == null
}
def "TxAtTop for single item list"() {
setup:
def txAt = new AbstractChainFees.TxAtTop<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1"])
then:
act == "t_1"
}
def "TxAtTop for multi item list"() {
setup:
def txAt = new AbstractChainFees.TxAtTop<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_1"
}
def "TxAtMiddle for empty list"() {
setup:
def txAt = new AbstractChainFees.TxAtMiddle<List<String>, String>(extractTx)
when:
def act = txAt.get([])
then:
act == null
}
def "TxAtMiddle for single item list"() {
setup:
def txAt = new AbstractChainFees.TxAtMiddle<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1"])
then:
act == "t_1"
}
def "TxAtMiddle for 3 item list"() {
setup:
def txAt = new AbstractChainFees.TxAtMiddle<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1", "t_2", "t_3"])
then:
act == "t_2"
}
def "TxAtMiddle for 4 item list"() {
setup:
def txAt = new AbstractChainFees.TxAtMiddle<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1", "t_2", "t_3", "t_4"])
then:
act == "t_2" || act == "t_3"
}
def "TxAtMiddle for 5 item list"() {
setup:
def txAt = new AbstractChainFees.TxAtMiddle<List<String>, String>(extractTx)
when:
def act = txAt.get(["t_1", "t_2", "t_3", "t_4", "t_5"])
then:
act == "t_3"
}
class TestChainFees extends AbstractChainFees {
TestChainFees(int heightLimit, @NotNull Multistream upstreams, @NotNull Function1 extractTx) {
super(heightLimit, upstreams, extractTx)
}
@Override
Mono readFeesAt(long height, @NotNull TxAt selector) {
return null
}
@Override
Function<Flux, Mono> feeAggregation(@NotNull Mode mode) {
return null
}
@Override
Function getResponseBuilder() {
return null
}
}
}

View File

@@ -17,8 +17,7 @@ package io.emeraldpay.dshackle.upstream
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.EthereumRpcUpstreamMock
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons
import spock.lang.Specification
@@ -27,7 +26,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "add upstream"() {
setup:
def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up = new EthereumPosRpcUpstreamMock("test", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up = new GenericUpstreamMock("test", Chain.ETHEREUM__MAINNET, TestingCommons.api())
when:
current.getUpstream(Chain.ETHEREUM__MAINNET).onUpstreamChange(new UpstreamChangeEvent(Chain.ETHEREUM__MAINNET, up, UpstreamChangeEvent.ChangeType.ADDED))
then:
@@ -38,9 +37,9 @@ class CurrentMultistreamHolderSpec extends Specification {
def "add multiple upstreams"() {
setup:
def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC__MAINNET, TestingCommons.api())
def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM_CLASSIC__MAINNET, TestingCommons.api())
def up3 = new GenericUpstreamMock("test3", Chain.ETHEREUM__MAINNET, TestingCommons.api())
when:
current.getUpstream(Chain.ETHEREUM__MAINNET).onUpstreamChange(new UpstreamChangeEvent(Chain.ETHEREUM__MAINNET, up1, UpstreamChangeEvent.ChangeType.ADDED))
current.getUpstream(Chain.ETHEREUM_CLASSIC__MAINNET).onUpstreamChange(new UpstreamChangeEvent(Chain.ETHEREUM_CLASSIC__MAINNET, up2, UpstreamChangeEvent.ChangeType.ADDED))
@@ -55,10 +54,10 @@ class CurrentMultistreamHolderSpec extends Specification {
def "remove upstream"() {
setup:
def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC__MAINNET, TestingCommons.api())
def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up1_del = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM_CLASSIC__MAINNET, TestingCommons.api())
def up3 = new GenericUpstreamMock("test3", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up1_del = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
when:
current.getUpstream(Chain.ETHEREUM__MAINNET).onUpstreamChange(new UpstreamChangeEvent(Chain.ETHEREUM__MAINNET, up1, UpstreamChangeEvent.ChangeType.ADDED))
current.getUpstream(Chain.ETHEREUM_CLASSIC__MAINNET).onUpstreamChange(new UpstreamChangeEvent(Chain.ETHEREUM_CLASSIC__MAINNET, up2, UpstreamChangeEvent.ChangeType.ADDED))
@@ -73,7 +72,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "available after adding"() {
setup:
def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api())
when:
def act = current.isAvailable(Chain.ETHEREUM__MAINNET)

View File

@@ -24,7 +24,7 @@ import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.test.EthereumApiStub
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
import io.emeraldpay.dshackle.upstream.generic.connectors.GenericConnectorFactory
import io.emeraldpay.dshackle.upstream.forkchoice.MostWorkForkChoice
import reactor.core.scheduler.Schedulers
@@ -44,13 +44,15 @@ class FilteredApisSpec extends Specification {
def "Verifies labels"() {
setup:
def i = 0
List<EthereumLikeRpcUpstream> upstreams = [
def cs = io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific.INSTANCE
List<GenericUpstream> upstreams = [
[test: "foo"],
[test: "bar"],
[test: "foo", test2: "baz"],
[test: "foo"],
[test: "baz"]
].collect {
def httpFactory = Mock(HttpFactory) {
create(_, _) >> Stub(JsonRpcHttpReader)
}
@@ -64,18 +66,20 @@ class FilteredApisSpec extends Specification {
Schedulers.boundedElastic(),
Duration.ofSeconds(12)
)
new EthereumLikeRpcUpstream(
new GenericUpstream(
"test",
(byte) 123,
Chain.ETHEREUM__MAINNET,
(byte) 123,
new ChainOptions.PartialOptions().buildOptions(),
UpstreamsConfig.UpstreamRole.PRIMARY,
ethereumTargets,
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
connectorFactory,
ChainsConfig.ChainConfig.default(),
false,
null
connectorFactory,
null,
cs.&validator,
cs.&labelDetector,
cs.&subscriptionTopics,
)
}
def matcher = new Selector.LabelMatcher("test", ["foo"])

View File

@@ -25,15 +25,17 @@ import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.startup.UpstreamChangeEvent
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumLikeRpcUpstream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumChainSpecific
import io.emeraldpay.dshackle.upstream.generic.GenericUpstream
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.grpc.EthereumPosGrpcUpstream
import io.emeraldpay.dshackle.upstream.grpc.GenericGrpcUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.dshackle.upstream.starknet.StarknetChainSpecific
import io.emeraldpay.etherjar.domain.BlockHash
import io.emeraldpay.etherjar.rpc.json.TransactionRefJson
import org.jetbrains.annotations.NotNull
@@ -51,9 +53,13 @@ class MultistreamSpec extends Specification {
def "Aggregates methods"() {
setup:
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def up2 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"]))
def aggr = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, [up1, up2], Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def up2 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"]))
def aggr = new GenericMultistream(Chain.ETHEREUM__MAINNET, [up1, up2], Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
when:
aggr.onUpstreamsUpdated()
def act = aggr.getMethods()
@@ -184,7 +190,11 @@ class MultistreamSpec extends Specification {
def up1 = TestingCommons.upstream("test-1", "internal")
def up2 = TestingCommons.upstream("test-2", "external")
def up3 = TestingCommons.upstream("test-3", "external")
def multistream = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, [up1, up2, up3], Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, [up1, up2, up3], Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
expect:
multistream.getHead(new Selector.LabelMatcher("provider", ["internal"])).is(up1.ethereumHeadMock)
@@ -205,14 +215,14 @@ class MultistreamSpec extends Specification {
.setMethod("newHeads")
.build()
def up1 = Mock(EthereumPosGrpcUpstream) {
def up1 = Mock(GenericGrpcUpstream) {
1 * isGrpc() >> true
1 * getId() >> "internal"
_ * getId() >> "internal"
1 * getLabels() >> [UpstreamsConfig.Labels.fromMap(Collections.singletonMap("provider", "internal"))]
1 * proxySubscribe(call) >> Flux.just("{}")
}
def up2 = Mock(EthereumPosGrpcUpstream) {
1 * getId() >> "external"
def up2 = Mock(GenericUpstream) {
_ * getId() >> "external"
1 * getLabels() >> [UpstreamsConfig.Labels.fromMap(Collections.singletonMap("provider", "external"))]
}
def multiStream = new TestEthereumPosMultistream(Chain.ETHEREUM__MAINNET, [up1, up2], Caches.default())
@@ -235,9 +245,9 @@ class MultistreamSpec extends Specification {
.setMethod("newHeads")
.build()
def up2 = Mock(EthereumPosGrpcUpstream) {
def up2 = Mock(GenericUpstream) {
1 * isGrpc() >> false
1 * getId() >> "2"
_ * getId() >> "2"
1 * getLabels() >> [UpstreamsConfig.Labels.fromMap(Collections.singletonMap("provider", "internal"))]
}
def multiStream = new TestEthereumPosMultistream(Chain.ETHEREUM__MAINNET, [up2], Caches.default())
@@ -251,9 +261,13 @@ class MultistreamSpec extends Specification {
def "Change ms methods based on upstream availability"() {
setup:
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
def up2 = new EthereumPosRpcUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumLikeRpcUpstream>(), Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, new ArrayList<GenericMultistream>(), Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
when:
ms.onUpstreamChange(
new UpstreamChangeEvent(Chain.ETHEREUM__MAINNET, up1, UpstreamChangeEvent.ChangeType.ADDED)
@@ -278,9 +292,13 @@ class MultistreamSpec extends Specification {
def "Filter older blocks on multistream head"() {
setup:
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
def up2 = new EthereumPosRpcUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, new ArrayList<EthereumLikeRpcUpstream>(), Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
def up1 = new GenericUpstreamMock("test1", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2", "eth_test3"]))
def up2 = new GenericUpstreamMock("test2", Chain.ETHEREUM__MAINNET, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def ms = new GenericMultistream(Chain.ETHEREUM__MAINNET, new ArrayList<GenericMultistream>(), Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
def head1 = createBlock(250, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448401b")
def head2 = createBlock(270, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448402b")
def head3 = createBlock(100, "0x0d050c785de17179f935b9b93aca09c442964cc59972c71ae68e74731448412b")
@@ -311,7 +329,11 @@ class MultistreamSpec extends Specification {
def up1 = TestingCommons.upstream("test-1", "internal")
def up2 = TestingCommons.upstream("test-2", "external")
def up3 = TestingCommons.upstream("test-3", "external")
def multistream = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, [up1, up2, up3], Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
def multistream = new GenericMultistream(Chain.ETHEREUM__MAINNET, [up1, up2, up3], Caches.default(),
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
EthereumChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
def observer = multistream.lagObserver
multistream.onUpstreamsUpdated()
@@ -335,15 +357,19 @@ class MultistreamSpec extends Specification {
.build()
}
class TestEthereumPosMultistream extends EthereumPosMultiStream {
class TestEthereumPosMultistream extends GenericMultistream {
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<EthereumLikeRpcUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches, Schedulers.boundedElastic(), TestingCommons.tracerMock())
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<GenericUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches,
Schedulers.boundedElastic(),
EthereumChainSpecific.INSTANCE.makeCachingReaderBuilder(TestingCommons.tracerMock()),
EthereumChainSpecific.INSTANCE.&localReaderBuilder,
StarknetChainSpecific.INSTANCE.subscriptionBuilder(Schedulers.boundedElastic()))
}
@NotNull
@Override
Mono<Reader<JsonRpcRequest, JsonRpcResponse>> getLocalReader(boolean localEnabled) {
Mono<Reader<JsonRpcRequest, JsonRpcResponse>> getLocalReader() {
return null
}

View File

@@ -1,91 +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
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.ReaderMock
import io.emeraldpay.dshackle.upstream.ApiSource
import io.emeraldpay.dshackle.upstream.FilteredApis
import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.erc20.ERC20Token
import io.emeraldpay.etherjar.hex.HexData
import io.emeraldpay.etherjar.rpc.json.TransactionCallJson
import spock.lang.Specification
import java.time.Duration
class ERC20BalanceSpec extends Specification {
def "Gets balance from upstream"() {
setup:
ReaderMock api = new ReaderMock()
.with(
new JsonRpcRequest("eth_call", [
new TransactionCallJson().tap { json ->
json.setTo(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
json.setData(HexData.from("0x70a0823100000000000000000000000016c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
},
"latest"
]),
JsonRpcResponse.ok('"0x0000000000000000000000000000000000000000000000000000001f28d72868"')
)
EthereumLikeRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
ERC20Token token = new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
ERC20Balance query = new ERC20Balance()
when:
def act = query.getBalance(upstream, token, Address.from("0x16c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
.block(Duration.ofSeconds(1))
then:
act.toLong() == 0x1f28d72868
}
def "Gets balance from api source"() {
setup:
ReaderMock api = new ReaderMock()
.with(
new JsonRpcRequest("eth_call", [
new TransactionCallJson().tap { json ->
json.setTo(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
json.setData(HexData.from("0x70a0823100000000000000000000000016c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
},
"latest"
]),
JsonRpcResponse.ok('"0x0000000000000000000000000000000000000000000000000000001f28d72868"')
)
EthereumLikeRpcUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, api)
ERC20Token token = new ERC20Token(Address.from("0x54EedeAC495271d0F6B175474E89094C44Da98b9"))
ERC20Balance query = new ERC20Balance()
ApiSource apiSource = new FilteredApis(
Chain.ETHEREUM__MAINNET, [upstream], Selector.empty
)
when:
def act = query.getBalance(apiSource, token, Address.from("0x16c15c65ad00b6dfbcc2cb8a7b6c2d0103a3883b"))
.block(Duration.ofSeconds(1))
then:
act.toLong() == 0x1f28d72868
}
}

View File

@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.PendingTxesSource
import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.hex.Hex32
@@ -28,7 +29,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read empty logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([:])
@@ -39,7 +40,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read single address logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
address: "0x829bd824b016326a401d083b33d092293333a830"
@@ -64,7 +65,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "ignores invalid address for logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
address: "829bd824b016326a401d083b33d092293333a830"
@@ -77,7 +78,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read multi address logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
address: ["0x829bd824b016326a401d083b33d092293333a830", "0x401d083b33d092293333a83829bd824b016326a0"]
@@ -93,7 +94,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read single topic logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
topics: "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
@@ -118,7 +119,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read invalid topic for request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
topics: [
@@ -136,7 +137,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read multi topic logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
topics: [
@@ -155,7 +156,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def "read full logs request"() {
setup:
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe = new EthereumEgressSubscription(TestingCommons.emptyMultistream() as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
when:
def act = ethereumSubscribe.readLogsRequest([
address: "0x298d492e8c1d909d3f63bc4a36c66c64acb3d695",
@@ -180,7 +181,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
def up1 = TestingCommons.upstream("test")
up1.getConnectorMock().setLiveness(Flux.just(false))
def ethereumSubscribe1 = new EthereumEgressSubscription(TestingCommons.multistream(up1) as EthereumPosMultiStream, Schedulers.boundedElastic(), null)
def ethereumSubscribe1 = new EthereumEgressSubscription(TestingCommons.multistream(up1) as GenericMultistream, Schedulers.boundedElastic(), null)
then:
ethereumSubscribe1.getAvailableTopics() == []
when:
@@ -188,7 +189,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
up2.getConnectorMock().setLiveness(Flux.just(true))
up2.stop()
up2.start()
def ethereumSubscribe2 = new EthereumEgressSubscription(TestingCommons.multistream(up2) as EthereumPosMultiStream, Schedulers.boundedElastic(), null)
def ethereumSubscribe2 = new EthereumEgressSubscription(TestingCommons.multistream(up2) as GenericMultistream, Schedulers.boundedElastic(), null)
then:
ethereumSubscribe2.getAvailableTopics().toSet() == [EthereumEgressSubscription.METHOD_LOGS, EthereumEgressSubscription.METHOD_NEW_HEADS].toSet()
when:
@@ -196,7 +197,7 @@ class EthereumEgressSubscriptionSpec extends Specification {
up3.getConnectorMock().setLiveness(Flux.just(true))
up3.stop()
up3.start()
def ethereumSubscribe3 = new EthereumEgressSubscription(TestingCommons.multistream(up3) as EthereumPosMultiStream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
def ethereumSubscribe3 = new EthereumEgressSubscription(TestingCommons.multistream(up3) as GenericMultistream, Schedulers.boundedElastic(), Stub(PendingTxesSource))
then:
ethereumSubscribe3.getAvailableTopics().toSet() == [EthereumEgressSubscription.METHOD_LOGS, EthereumEgressSubscription.METHOD_NEW_HEADS, EthereumEgressSubscription.METHOD_PENDING_TXES].toSet()

View File

@@ -1,41 +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
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionJsonSnapshot
import io.emeraldpay.etherjar.domain.Wei
import spock.lang.Specification
class EthereumLegacyFeesSpec extends Specification {
def "Extract fee from"() {
setup:
def block = new BlockJson()
// 0x75cc01873a9818bf426a8b23d83450bf18530a822fd4fe9e86a416a5554176a6
def tx = new TransactionJsonSnapshot().tap {
it.gasPrice = Wei.ofUnits(8, Wei.Unit.GWEI)
}
def fees = new EthereumLegacyFees(Stub(EthereumMultistream), Stub(EthereumCachingReader), 10)
when:
def act = fees.extractFee(block, tx)
then:
act.priority == Wei.ofUnits(8, Wei.Unit.GWEI)
act.max == Wei.ofUnits(8, Wei.Unit.GWEI)
act.base == Wei.ZERO
}
}

View File

@@ -30,8 +30,7 @@ class EthereumLocalReaderSpec extends Specification {
TestingCommons.tracerMock()
),
methods,
new EmptyHead(),
true
new EmptyHead()
)
when:
def act = router.read(new JsonRpcRequest("eth_coinbase", [])).block(Duration.ofSeconds(1))
@@ -50,8 +49,7 @@ class EthereumLocalReaderSpec extends Specification {
TestingCommons.tracerMock()
),
methods,
new EmptyHead(),
true
new EmptyHead()
)
when:
def act = router.read(new JsonRpcRequest("eth_getTransactionByHash", ["test"], 10))
@@ -75,7 +73,7 @@ class EthereumLocalReaderSpec extends Specification {
}
}
def methods = new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
def router = new EthereumLocalReader(reader, methods, head, true)
def router = new EthereumLocalReader(reader, methods, head)
when:
def act = router.getBlockByNumber(["latest", false])
@@ -103,7 +101,7 @@ class EthereumLocalReaderSpec extends Specification {
}
}
def methods = new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
def router = new EthereumLocalReader(reader, methods, head, true)
def router = new EthereumLocalReader(reader, methods, head)
when:
def act = router.getBlockByNumber(["earliest", false])
@@ -131,7 +129,7 @@ class EthereumLocalReaderSpec extends Specification {
}
}
def methods = new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
def router = new EthereumLocalReader(reader, methods, head, true)
def router = new EthereumLocalReader(reader, methods, head)
when:
def act = router.getBlockByNumber(["0x123ef", false])
@@ -155,7 +153,7 @@ class EthereumLocalReaderSpec extends Specification {
_ * blocksByHeightAsCont() >> new EmptyReader<>()
}
def methods = new DefaultEthereumMethods(Chain.ETHEREUM__MAINNET)
def router = new EthereumLocalReader(reader, methods, head, true)
def router = new EthereumLocalReader(reader, methods, head)
when:
def act = router.getBlockByNumber(["0x0", true])

View File

@@ -1,150 +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
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.ChainFees
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.ethereum.json.TransactionJsonSnapshot
import io.emeraldpay.etherjar.domain.TransactionId
import io.emeraldpay.etherjar.domain.Wei
import io.emeraldpay.etherjar.rpc.json.TransactionRefJson
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import spock.lang.Specification
import java.time.Duration
class EthereumPriorityFeesSpec extends Specification {
def "Extract fee from EIP1559 tx"() {
setup:
// 13756007
def block = new BlockJson().tap {
it.baseFeePerGas = new Wei(104197355513)
}
// 0x5da50f35a51e56ecd4313417b1c30f9c088222f3f8763701effe14f3dd18b6cc
def tx = new TransactionJsonSnapshot().tap {
it.type = 2
it.maxFeePerGas = Wei.ofUnits(999, Wei.Unit.GWEI)
it.maxPriorityFeePerGas = Wei.ofUnits(5.0001, Wei.Unit.GWEI)
}
def fees = new EthereumPriorityFees(Stub(EthereumMultistream), Stub(EthereumCachingReader), 10)
when:
def act = fees.extractFee(block, tx)
then:
act.priority == Wei.ofUnits(5.0001, Wei.Unit.GWEI)
act.max == Wei.ofUnits(999, Wei.Unit.GWEI)
act.base == new Wei(104197355513)
}
def "Extract fee from legacy tx"() {
setup:
// 13756007
def block = new BlockJson().tap {
it.baseFeePerGas = new Wei(104197355513)
}
// 0x1f507982bef0f11a8304287d41f228b5f1dda1114a446ee781c3d95ef4a7b891
def tx = new TransactionJsonSnapshot().tap {
it.type = 0
// 109.564020111 Gwei
it.gasPrice = Wei.from("0x198286458f")
}
def fees = new EthereumPriorityFees(Stub(EthereumMultistream), Stub(EthereumCachingReader), 10)
when:
def act = fees.extractFee(block, tx)
then:
// as difference between base minimum and actually paid
act.priority == Wei.ofUnits(5.366664598, Wei.Unit.GWEI)
act.max == Wei.from("0x198286458f")
act.base == new Wei(104197355513)
}
def "Calculates average fee"() {
setup:
def inputs = [
new EthereumFees.EthereumFee(Wei.ofEthers(1), Wei.ofEthers(0.5), Wei.ofEthers(0.75), Wei.ofEthers(0.5)),
new EthereumFees.EthereumFee(Wei.ofEthers(0.75), Wei.ofEthers(0.5), Wei.ofEthers(0.75), Wei.ofEthers(0.5)),
new EthereumFees.EthereumFee(Wei.ofEthers(0.6), Wei.ofEthers(0.2), Wei.ofEthers(0.6), Wei.ofEthers(0.5)),
]
def fees = new EthereumPriorityFees(Stub(EthereumMultistream), Stub(EthereumCachingReader), 10)
when:
def act = Flux.fromIterable(inputs)
.transform(fees.feeAggregation(ChainFees.Mode.AVG_LAST))
.next().block(Duration.ofSeconds(1))
then:
act.priority == Wei.ofEthers(0.4) // 0.5 + 0.5 + 0.2
act.paid == Wei.ofEthers(0.7) // 0.75 + 0.75 + 0.6
}
def "Estimate"() {
setup:
def block1 = new BlockJson().tap {
it.baseFeePerGas = new Wei(92633661632)
it.transactions = [
new TransactionRefJson(TransactionId.from("0x00000000fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
new TransactionRefJson(TransactionId.from("0x11111111fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
new TransactionRefJson(TransactionId.from("0x22222222fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
]
}
def block2 = new BlockJson().tap {
it.baseFeePerGas = new Wei(104197355513)
it.transactions = [
new TransactionRefJson(TransactionId.from("0x33333333fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
new TransactionRefJson(TransactionId.from("0x44444444fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
new TransactionRefJson(TransactionId.from("0x55555555fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")),
]
}
def tx1 = new TransactionJsonSnapshot().tap {
it.type = 2
it.maxFeePerGas = Wei.ofUnits(150, Wei.Unit.GWEI)
it.maxPriorityFeePerGas = Wei.ofUnits(3, Wei.Unit.GWEI)
}
def tx2 = new TransactionJsonSnapshot().tap {
it.type = 2
it.maxFeePerGas = Wei.ofUnits(200, Wei.Unit.GWEI)
it.maxPriorityFeePerGas = Wei.ofUnits(6, Wei.Unit.GWEI)
}
def ups = Mock(EthereumMultistream) {
1 * getHead() >> Mock(Head) {
1 * getCurrentHeight() >> 13756007
}
}
def reader = Mock(EthereumCachingReader) {
_ * it.blocksByHeightParsed() >> Mock(Reader) {
1 * it.read(13756006) >> Mono.just(block1)
1 * it.read(13756007) >> Mono.just(block2)
}
_ * it.txByHash() >> Mock(Reader) {
1 * it.read(TransactionId.from("0x22222222fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")) >> Mono.just(tx1)
1 * it.read(TransactionId.from("0x55555555fad596cad644b785a8a74f6580ceec9ae13c8aa174f819c0223b8c77")) >> Mono.just(tx2)
}
}
def fees = new EthereumPriorityFees(ups, reader, 10)
when:
def act = fees.estimate(ChainFees.Mode.AVG_LAST, 2).block(Duration.ofSeconds(1))
then:
act.hasEthereumExtended()
act.ethereumExtended.priority == "4500000000"
act.ethereumExtended.max == "175000000000"
act.ethereumExtended.expect == "102915508572"
}
}

View File

@@ -24,6 +24,7 @@ import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcError
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.dshackle.upstream.Upstream
import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.hex.HexData
import io.emeraldpay.etherjar.rpc.RpcResponseError
@@ -37,16 +38,20 @@ import java.time.Duration
import static io.emeraldpay.dshackle.Chain.ETHEREUM__MAINNET
import static io.emeraldpay.dshackle.Chain.OPTIMISM__MAINNET
import static io.emeraldpay.dshackle.upstream.UpstreamAvailability.*
import static io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstreamValidator.ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstreamValidator.ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstreamValidator.ValidateUpstreamSettingsResult.UPSTREAM_VALID
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR
import static io.emeraldpay.dshackle.upstream.ValidateUpstreamSettingsResult.UPSTREAM_VALID
import static java.util.Collections.emptyList
import io.emeraldpay.dshackle.config.ChainsConfig.ChainConfig
class EthereumUpstreamValidatorSpec extends Specification {
def conf = ChainConfig.defaultWithContract("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def "Resolve to final availability"() {
setup:
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, Stub(EthereumLikeUpstream), ChainOptions.PartialOptions.getDefaults().buildOptions())
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, Stub(Upstream), ChainOptions.PartialOptions.getDefaults().buildOptions(), conf)
expect:
validator.resolve(Tuples.of(sync, peers)) == exp
where:
@@ -67,8 +72,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = false
}.buildOptions()
def up = Mock(EthereumLikeUpstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
@@ -87,7 +92,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("eth_syncing", [], false)
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
@@ -100,7 +105,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateSyncing = true
}.buildOptions()
def up = Mock(EthereumLikeUpstream) {
def up = Mock(Upstream) {
2 * getIngressReader() >> Mock(Reader) { reader ->
2 * reader.read(_) >>> [
Mono.just(new JsonRpcResponse('true'.getBytes(), null)),
@@ -112,7 +117,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
1 * head.onSyncingNode(false)
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
@@ -132,7 +137,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("eth_syncing", [], [startingBlock: 100, currentBlock: 50])
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
@@ -150,7 +155,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("eth_syncing", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable"))
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateSyncing().block(Duration.ofSeconds(1))
@@ -164,8 +169,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
it.validatePeers = false
it.minPeers = 10
}.buildOptions()
def up = Mock(EthereumLikeUpstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -180,8 +185,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
it.validatePeers = true
it.minPeers = 0
}.buildOptions()
def up = Mock(EthereumLikeUpstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def up = Mock(Upstream)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -201,7 +206,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("net_peerCount", [], "0x5")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -220,7 +225,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("net_peerCount", [], "0xa")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -239,7 +244,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("net_peerCount", [], "0xff")
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -258,7 +263,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
answer("net_peerCount", [], new RpcResponseError(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unavailable"))
}
)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validatePeers().block(Duration.ofSeconds(1))
@@ -272,7 +277,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
it.validateCalllimit = false
it.validateChain = false
}.buildOptions()
def up = Mock(EthereumLikeUpstream) {
def up = Mock(Upstream) {
2 * getIngressReader() >>
Mock(Reader) {
1 * read(new JsonRpcRequest("eth_blockNumber", [])) >> Mono.just(new JsonRpcResponse('"0x10ff9be"'.getBytes(), null))
@@ -280,7 +285,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options)
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
when:
def act = validator.validateUpstreamSettingsOnStartup()
@@ -293,7 +298,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
}.buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
3 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_call", [new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
@@ -304,8 +309,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
//"0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
@@ -317,7 +322,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateChain = false
}.buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
3 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_call", [new TransactionCallJson(
Address.from("0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"),
@@ -328,8 +333,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
@@ -341,7 +346,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateCalllimit = false
}.buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
4 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse('"0x1"'.getBytes(), null))
1 * read(new JsonRpcRequest("net_version", emptyList())) >> Mono.just(new JsonRpcResponse('"1"'.getBytes(), null))
@@ -350,8 +355,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
@@ -363,7 +368,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def options = ChainOptions.PartialOptions.getDefaults().tap {
it.validateCalllimit = false
}.buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
4 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse('"0x1"'.getBytes(), null))
1 * read(new JsonRpcRequest("net_version", emptyList())) >> Mono.just(new JsonRpcResponse('"1"'.getBytes(), null))
@@ -372,8 +377,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(OPTIMISM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(OPTIMISM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
@@ -383,7 +388,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def "Upstream is valid if all setting are valid"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
5 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse('"0x1"'.getBytes(), null))
1 * read(new JsonRpcRequest("net_version", emptyList())) >> Mono.just(new JsonRpcResponse('"1"'.getBytes(), null))
@@ -396,8 +401,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:
@@ -407,7 +412,7 @@ class EthereumUpstreamValidatorSpec extends Specification {
def "Upstream is not valid if there are errors"() {
setup:
def options = ChainOptions.PartialOptions.getDefaults().buildOptions()
def up = Mock(EthereumLikeRpcUpstream) {
def up = Mock(Upstream) {
5 * getIngressReader() >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_chainId", emptyList())) >> Mono.just(new JsonRpcResponse(null, new JsonRpcError(1, "Too long")))
1 * read(new JsonRpcRequest("net_version", emptyList())) >> Mono.just(new JsonRpcResponse(null, new JsonRpcError(1, "Too long")))
@@ -420,8 +425,8 @@ class EthereumUpstreamValidatorSpec extends Specification {
Mono.just(new JsonRpcResponse('"result"'.getBytes(), null))
}
}
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96")
def validator = new EthereumUpstreamValidator(ETHEREUM__MAINNET, up, options, conf)
// "0x32268860cAAc2948Ab5DdC7b20db5a420467Cf96"
when:
def act = validator.validateUpstreamSettingsOnStartup()
then:

View File

@@ -18,7 +18,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.BlockValidator
import io.emeraldpay.dshackle.upstream.DefaultUpstream
@@ -41,7 +41,7 @@ import java.time.temporal.ChronoUnit
class EthereumWsHeadSpec extends Specification {
BlockHash parent = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
DefaultUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
DefaultUpstream upstream = new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
def "Fetch block"() {
setup:

View File

@@ -1,7 +1,7 @@
package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.MockWSServer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.DefaultUpstream
@@ -15,7 +15,7 @@ import spock.lang.Specification
import java.time.Duration
class WsConnectionImplRealSpec extends Specification {
DefaultUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
DefaultUpstream upstream = new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
static SLEEP = 500

View File

@@ -17,7 +17,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.test.EthereumPosRpcUpstreamMock
import io.emeraldpay.dshackle.test.GenericUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.DefaultUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
@@ -32,7 +32,7 @@ import spock.lang.Specification
import java.time.Duration
class WsConnectionImplSpec extends Specification {
DefaultUpstream upstream = new EthereumPosRpcUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
DefaultUpstream upstream = new GenericUpstreamMock(Chain.ETHEREUM__MAINNET, TestingCommons.api())
def "Makes a RPC call"() {
setup:

View File

@@ -20,7 +20,7 @@ import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.data.TxId
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import io.emeraldpay.etherjar.domain.BlockHash
import io.emeraldpay.etherjar.domain.TransactionId
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
@@ -38,7 +38,7 @@ class ConnectBlockUpdatesSpec extends Specification {
def "Extracts updates"() {
setup:
def connectBlockUpdates = new ConnectBlockUpdates(Stub(EthereumMultistream), Schedulers.boundedElastic())
def connectBlockUpdates = new ConnectBlockUpdates(Stub(GenericMultistream), Schedulers.boundedElastic())
def block = BlockContainer.from(new BlockJson<TransactionRefJson>().tap {
hash = BlockHash.from("0xe5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7db1350fd527fec4885c")
number = 13412871
@@ -72,7 +72,7 @@ class ConnectBlockUpdatesSpec extends Specification {
def "Produce DROP updates for replaced block"() {
setup:
def connectBlockUpdates = new ConnectBlockUpdates(Stub(EthereumMultistream), Schedulers.boundedElastic())
def connectBlockUpdates = new ConnectBlockUpdates(Stub(GenericMultistream), Schedulers.boundedElastic())
def block = BlockContainer.from(new BlockJson<TransactionRefJson>().tap {
hash = BlockHash.from("0xe5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7db1350fd527fec4885c")
number = 13412871
@@ -106,7 +106,7 @@ class ConnectBlockUpdatesSpec extends Specification {
def "Gets prev version if available"() {
setup:
def connectBlockUpdates = new ConnectBlockUpdates(Stub(EthereumMultistream), Schedulers.boundedElastic())
def connectBlockUpdates = new ConnectBlockUpdates(Stub(GenericMultistream), Schedulers.boundedElastic())
def block1 = BlockContainer.from(new BlockJson<TransactionRefJson>().tap {
hash = BlockHash.from("0xe5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7db1350fd527fec4885c")
number = 13412871
@@ -170,7 +170,7 @@ class ConnectBlockUpdatesSpec extends Specification {
def "Marks old txes as dropped before producing a new version of same block"() {
setup:
def connectBlockUpdates = new ConnectBlockUpdates(Stub(EthereumMultistream), Schedulers.boundedElastic())
def connectBlockUpdates = new ConnectBlockUpdates(Stub(GenericMultistream), Schedulers.boundedElastic())
def block1 = BlockContainer.from(new BlockJson<TransactionRefJson>().tap {
hash = BlockHash.from("0xe5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7db1350fd527fec4885c")
number = 13412871
@@ -232,7 +232,7 @@ class ConnectBlockUpdatesSpec extends Specification {
def head = Mock(Head) {
1 * getFlux() >> Flux.never()
}
def up = Mock(EthereumMultistream) {
def up = Mock(GenericMultistream) {
1 * getEnrichedHead(Selector.empty) >> head
}
def connectBlockUpdates = new ConnectBlockUpdates(up, Schedulers.boundedElastic())

View File

@@ -16,7 +16,6 @@
package io.emeraldpay.dshackle.upstream.ethereum.subscribe
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.subscribe.json.LogMessage
import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.domain.BlockHash
@@ -91,7 +90,7 @@ class ConnectLogsSpec extends Specification {
def "Filter is empty"() {
setup:
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic())
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream(), Schedulers.boundedElastic())
when:
def input = Flux.fromIterable([
log1, log2, log3, log4
@@ -109,7 +108,7 @@ class ConnectLogsSpec extends Specification {
def "Filter by address"() {
setup:
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic())
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream(), Schedulers.boundedElastic())
when:
def input = Flux.fromIterable([
log1, log2
@@ -124,7 +123,7 @@ class ConnectLogsSpec extends Specification {
def "Filter by topic"() {
setup:
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic())
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream(), Schedulers.boundedElastic())
when:
def input = Flux.fromIterable([
log1, log2, log3, log4
@@ -141,7 +140,7 @@ class ConnectLogsSpec extends Specification {
def "Filter by address and topic"() {
setup:
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream() as EthereumPosMultiStream, Schedulers.boundedElastic())
def connectLogs = new ConnectLogs(TestingCommons.emptyMultistream(), Schedulers.boundedElastic())
when:
def input = Flux.fromIterable([
log1, log2, log3, log4

View File

@@ -3,7 +3,7 @@ package io.emeraldpay.dshackle.upstream.ethereum.subscribe
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Selector
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.generic.GenericMultistream
import reactor.core.publisher.Flux
import reactor.core.scheduler.Schedulers
import reactor.test.StepVerifier
@@ -18,7 +18,7 @@ class ConnectNewHeadsSpec extends Specification {
TestingCommons.blockForEthereum(100)
])
}
def up = Mock(EthereumMultistream) {
def up = Mock(GenericMultistream) {
1 * getHead(Selector.empty) >> head
}
ConnectNewHeads connectNewHeads = new ConnectNewHeads(up, Schedulers.boundedElastic())

View File

@@ -1,264 +0,0 @@
/**
* Copyright (c) 2019 ETCDEV GmbH
* Copyright (c) 2020 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.grpc
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainGrpc
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.config.ChainsConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.test.MockGrpcServer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.BuildInfo
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.ethereum.json.BlockJson
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcGrpcClient
import io.emeraldpay.dshackle.upstream.rpcclient.RpcMetrics
import io.emeraldpay.etherjar.domain.BlockHash
import io.grpc.stub.StreamObserver
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Timer
import reactor.core.scheduler.Schedulers
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
import java.util.concurrent.CompletableFuture
class EthereumGrpcUpstreamSpec extends Specification {
MockGrpcServer mockServer = new MockGrpcServer()
ObjectMapper objectMapper = Global.objectMapper
RpcMetrics metrics = new RpcMetrics(
Timer.builder("test1").register(TestingCommons.meterRegistry),
Counter.builder("test2").register(TestingCommons.meterRegistry)
)
BlockHash parent = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
def hash = (byte)123
def buildInfo = new BuildInfo("v0.0.1-test")
def "Subscribe to head"() {
setup:
def callData = [:]
def chain = Chain.ETHEREUM__MAINNET
def api = TestingCommons.api()
def block1 = new BlockJson().with {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
it.parentHash = parent
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
def client = mockServer.clientForServer(new BlockchainGrpc.BlockchainImplBase() {
@Override
void nativeCall(BlockchainOuterClass.NativeCallRequest request, StreamObserver<BlockchainOuterClass.NativeCallReplyItem> responseObserver) {
api.nativeCall(request, responseObserver)
}
@Override
void subscribeHead(Common.Chain request, StreamObserver<BlockchainOuterClass.ChainHead> responseObserver) {
callData.chain = request.getTypeValue()
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setBlockId(block1.hash.toHex().substring(2))
.setHeight(block1.number)
.setParentBlockId(parent.toHex().substring(2))
.setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray()))
.build()
)
}
})
def upstream = new EthereumGrpcUpstream("test", hash, UpstreamsConfig.UpstreamRole.PRIMARY, chain, client, new JsonRpcGrpcClient(client, chain, metrics), null, ChainsConfig.ChainConfig.default(), Schedulers.boundedElastic())
upstream.setLag(0)
upstream.update(
BlockchainOuterClass.DescribeChain.newBuilder()
.setStatus(BlockchainOuterClass.ChainStatus.newBuilder().setQuorum(1).setAvailabilityValue(UpstreamAvailability.OK.grpcId))
.addAllSupportedMethods(["eth_getBlockByHash"])
.build(),
BlockchainOuterClass.BuildInfo.newBuilder()
.setVersion(buildInfo.version)
.build(),
)
when:
new Thread({ Thread.sleep(50); upstream.head.start() }).start()
def h = upstream.head.getFlux().next().block(Duration.ofSeconds(1))
then:
callData.chain == Chain.ETHEREUM__MAINNET.id
upstream.status == UpstreamAvailability.OK
upstream.getBuildInfo() == buildInfo
h.hash == BlockId.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
}
def "Follows difficulty, ignores less difficult"() {
setup:
def api = TestingCommons.api()
def block1 = new BlockJson().with {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
it.parentHash = parent
return it
}
def block2 = new BlockJson().with {
it.number = 650247
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
it.totalDifficulty = new BigInteger("35bbde5595de6455", 16)
it.timestamp = Instant.now()
it.parentHash = parent
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
api.answer("eth_getBlockByHash", [block2.hash.toHex(), false], block2)
def client = mockServer.clientForServer(new BlockchainGrpc.BlockchainImplBase() {
@Override
void nativeCall(BlockchainOuterClass.NativeCallRequest request, StreamObserver<BlockchainOuterClass.NativeCallReplyItem> responseObserver) {
api.nativeCall(request, responseObserver)
}
@Override
void subscribeHead(Common.Chain request, StreamObserver<BlockchainOuterClass.ChainHead> responseObserver) {
new Thread({
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setBlockId(block1.hash.toHex().substring(2))
.setHeight(block1.number)
.setParentBlockId(parent.toHex().substring(2))
.setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray()))
.build()
)
Thread.sleep(100)
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setBlockId(block2.hash.toHex().substring(2))
.setHeight(block2.number)
.setParentBlockId(parent.toHex().substring(2))
.setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray()))
.build()
)
}).start()
}
})
def upstream = new EthereumGrpcUpstream("test", hash, UpstreamsConfig.UpstreamRole.PRIMARY, Chain.ETHEREUM__MAINNET, client, new JsonRpcGrpcClient(client, Chain.ETHEREUM__MAINNET, metrics), null, ChainsConfig.ChainConfig.default(), Schedulers.boundedElastic())
upstream.setLag(0)
upstream.update(
BlockchainOuterClass.DescribeChain.newBuilder()
.setStatus(BlockchainOuterClass.ChainStatus.newBuilder().setQuorum(1).setAvailabilityValue(UpstreamAvailability.OK.grpcId))
.addAllSupportedMethods(["eth_getBlockByHash"])
.build(),
BlockchainOuterClass.BuildInfo.newBuilder()
.setVersion(buildInfo.version)
.build(),
)
when:
new Thread({ Thread.sleep(50); upstream.head.start() }).start()
def h = upstream.head.getFlux().take(Duration.ofSeconds(1)).last().block(Duration.ofSeconds(2))
then:
upstream.status == UpstreamAvailability.OK
upstream.getBuildInfo() == buildInfo
h.hash == BlockId.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
h.height == 650246
}
def "Follows difficulty"() {
setup:
def callData = [:]
def finished = new CompletableFuture<Boolean>()
def chain = Chain.ETHEREUM__MAINNET
def api = TestingCommons.api()
def block1 = new BlockJson().with {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
it.parentHash = parent
return it
}
def block2 = new BlockJson().with {
it.number = 650247
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
it.totalDifficulty = new BigInteger("35bbde5595de6457", 16)
it.timestamp = Instant.now()
it.parentHash = parent
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
api.answer("eth_getBlockByHash", [block2.hash.toHex(), false], block2)
def client = mockServer.clientForServer(new BlockchainGrpc.BlockchainImplBase() {
@Override
void nativeCall(BlockchainOuterClass.NativeCallRequest request, StreamObserver<BlockchainOuterClass.NativeCallReplyItem> responseObserver) {
api.nativeCall(request, responseObserver)
}
@Override
void subscribeHead(Common.Chain request, StreamObserver<BlockchainOuterClass.ChainHead> responseObserver) {
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setBlockId(block1.hash.toHex().substring(2))
.setHeight(block1.number)
.setParentBlockId(parent.toHex().substring(2))
.setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray()))
.build()
)
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setBlockId(block2.hash.toHex().substring(2))
.setHeight(block2.number)
.setParentBlockId(parent.toHex().substring(2))
.setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray()))
.build()
)
finished.complete(true)
}
})
def upstream = new EthereumGrpcUpstream("test", hash, UpstreamsConfig.UpstreamRole.PRIMARY, chain, client, new JsonRpcGrpcClient(client, chain, metrics), null, ChainsConfig.ChainConfig.default(), Schedulers.boundedElastic())
upstream.setLag(0)
upstream.update(
BlockchainOuterClass.DescribeChain.newBuilder()
.setStatus(BlockchainOuterClass.ChainStatus.newBuilder().setQuorum(1).setAvailabilityValue(UpstreamAvailability.OK.grpcId))
.addAllSupportedMethods(["eth_getBlockByHash"])
.build(),
BlockchainOuterClass.BuildInfo.newBuilder()
.setVersion(buildInfo.version)
.build(),
)
when:
new Thread({ Thread.sleep(50); upstream.head.start() }).start()
finished.get()
def h = upstream.head.getFlux().take(Duration.ofSeconds(1)).last().block(Duration.ofSeconds(2))
then:
upstream.status == UpstreamAvailability.OK
upstream.getBuildInfo() == buildInfo
h.hash == BlockId.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
h.height == 650247
}
private BlockchainOuterClass.DescribeChain describe(List<String> methods) {
return BlockchainOuterClass.DescribeChain.newBuilder()
.setStatus(BlockchainOuterClass.ChainStatus.newBuilder().setQuorum(1).setAvailabilityValue(UpstreamAvailability.OK.grpcId))
.addAllSupportedMethods(methods)
.build()
}
}