problem: grpc upstream can change set of provided upstreams
solution: periodically recheck and update current list
This commit is contained in:
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.config
|
||||
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import io.infinitape.etherjar.rpc.RpcClient
|
||||
import spock.lang.Specification
|
||||
|
||||
class UpstreamsConfigReaderSpec extends Specification {
|
||||
@@ -194,4 +198,31 @@ class UpstreamsConfigReaderSpec extends Specification {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Parse config with invalid ids"() {
|
||||
setup:
|
||||
def config = this.class.getClassLoader().getResourceAsStream("upstreams-no-id.yaml")
|
||||
when:
|
||||
def act = reader.read(config)
|
||||
then:
|
||||
act != null
|
||||
act.upstreams.size() == 1
|
||||
with(act.upstreams.get(0)) {
|
||||
id == "test"
|
||||
}
|
||||
}
|
||||
|
||||
def "Invalidate wrong ids"() {
|
||||
expect:
|
||||
!reader.isValid(new UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>(id: id))
|
||||
where:
|
||||
id << ["", null, "a", "ab", "!ab", "foo bar", "foo@bar", "123test", "_test", "test/test"]
|
||||
}
|
||||
|
||||
def "Accept good ids"() {
|
||||
expect:
|
||||
reader.isValid(new UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>(id: id))
|
||||
where:
|
||||
id << ["test", "test_test", "test-test", "test123", "test1test", "foo_bar_12"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,8 +36,16 @@ class EthereumUpstreamMock extends EthereumUpstream {
|
||||
this(chain, api, new QuorumBasedMethods(TestingCommons.objectMapper(), chain))
|
||||
}
|
||||
|
||||
EthereumUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull DirectEthereumApi api) {
|
||||
this(id, chain, api, new QuorumBasedMethods(TestingCommons.objectMapper(), chain))
|
||||
}
|
||||
|
||||
EthereumUpstreamMock(@NotNull Chain chain, @NotNull DirectEthereumApi api, CallMethods methods) {
|
||||
super(chain, api, null,
|
||||
this("test", chain, api, methods)
|
||||
}
|
||||
|
||||
EthereumUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull DirectEthereumApi api, CallMethods methods) {
|
||||
super(id, chain, api, null,
|
||||
UpstreamsConfig.Options.getDefaults(), new NodeDetailsList.NodeDetails(1, new UpstreamsConfig.Labels()),
|
||||
methods)
|
||||
setLag(0)
|
||||
|
||||
@@ -38,7 +38,6 @@ class UpstreamsMock implements Upstreams {
|
||||
addUpstream(chain2, up2)
|
||||
}
|
||||
|
||||
@Override
|
||||
AggregatedUpstream addUpstream(@NotNull Chain chain, @NotNull Upstream up) {
|
||||
if (!upstreams.containsKey(chain)) {
|
||||
upstreams[chain] = new ChainUpstreams(chain, [up], TestingCommons.objectMapper())
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2019 ETCDEV GmbH
|
||||
*
|
||||
* 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
|
||||
|
||||
import io.emeraldpay.dshackle.quorum.AlwaysQuorum
|
||||
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import spock.lang.Specification
|
||||
|
||||
class AggregatedUpstreamSpec extends Specification {
|
||||
|
||||
def "Aggregates methods"() {
|
||||
setup:
|
||||
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, Stub(DirectEthereumApi), new DirectCallMethods(["eth_test1", "eth_test2"]))
|
||||
def up2 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, Stub(DirectEthereumApi), new DirectCallMethods(["eth_test2", "eth_test3"]))
|
||||
def aggr = new ChainUpstreams(Chain.ETHEREUM, [up1, up2], TestingCommons.objectMapper())
|
||||
when:
|
||||
aggr.onUpstreamsUpdated()
|
||||
def act = aggr.getMethods()
|
||||
then:
|
||||
act.isAllowed("eth_test1")
|
||||
act.isAllowed("eth_test2")
|
||||
act.isAllowed("eth_test3")
|
||||
act.getQuorumFor("eth_test1") instanceof AlwaysQuorum
|
||||
act.getQuorumFor("eth_test2") instanceof AlwaysQuorum
|
||||
act.getQuorumFor("eth_test3") instanceof AlwaysQuorum
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.emeraldpay.dshackle.upstream
|
||||
|
||||
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import io.infinitape.etherjar.rpc.RpcClient
|
||||
import spock.lang.Specification
|
||||
|
||||
class CurrentUpstreamsSpec extends Specification {
|
||||
|
||||
def "add upstream"() {
|
||||
setup:
|
||||
def current = new CurrentUpstreams(TestingCommons.objectMapper())
|
||||
def up = new EthereumUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
when:
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED))
|
||||
then:
|
||||
current.getAvailable() == [Chain.ETHEREUM]
|
||||
current.getUpstream(Chain.ETHEREUM).getAll()[0] == up
|
||||
}
|
||||
|
||||
def "add multiple upstreams"() {
|
||||
setup:
|
||||
def current = new CurrentUpstreams(TestingCommons.objectMapper())
|
||||
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(RpcClient)))
|
||||
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
when:
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED))
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED))
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up3, UpstreamChange.ChangeType.ADDED))
|
||||
then:
|
||||
current.getAvailable().toSet() == [Chain.ETHEREUM, Chain.ETHEREUM_CLASSIC].toSet()
|
||||
current.getUpstream(Chain.ETHEREUM).getAll().toSet() == [up1, up3].toSet()
|
||||
current.getUpstream(Chain.ETHEREUM_CLASSIC).getAll().toSet() == [up2].toSet()
|
||||
}
|
||||
|
||||
def "remove upstream"() {
|
||||
setup:
|
||||
def current = new CurrentUpstreams(TestingCommons.objectMapper())
|
||||
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(RpcClient)))
|
||||
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
def up1_del = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(RpcClient)))
|
||||
when:
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up1, UpstreamChange.ChangeType.ADDED))
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM_CLASSIC, up2, UpstreamChange.ChangeType.ADDED))
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up3, UpstreamChange.ChangeType.ADDED))
|
||||
current.update(new UpstreamChange(Chain.ETHEREUM, up1_del, UpstreamChange.ChangeType.REMOVED))
|
||||
then:
|
||||
current.getAvailable().toSet() == [Chain.ETHEREUM, Chain.ETHEREUM_CLASSIC].toSet()
|
||||
current.getUpstream(Chain.ETHEREUM).getAll().toSet() == [up3].toSet()
|
||||
current.getUpstream(Chain.ETHEREUM_CLASSIC).getAll().toSet() == [up2].toSet()
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ class FilteringApiIteratorSpec extends Specification {
|
||||
[test: "baz"]
|
||||
].collect {
|
||||
new EthereumUpstream(
|
||||
"test",
|
||||
Chain.ETHEREUM,
|
||||
new DirectEthereumApi(rpcClient, objectMapper, ethereumTargets),
|
||||
(EthereumWs) null,
|
||||
|
||||
@@ -70,7 +70,7 @@ class GrpcUpstreamSpec extends Specification {
|
||||
)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
@@ -129,7 +129,7 @@ class GrpcUpstreamSpec extends Specification {
|
||||
finished.complete(true)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
@@ -189,7 +189,7 @@ class GrpcUpstreamSpec extends Specification {
|
||||
finished.complete(true)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
def upstream = new GrpcUpstream("test", chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
|
||||
26
src/test/resources/upstreams-no-id.yaml
Normal file
26
src/test/resources/upstreams-no-id.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: v1
|
||||
|
||||
upstreams:
|
||||
- chain: ethereum
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
- chain: ethereum
|
||||
id: test
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
- chain: ethereum
|
||||
id: test/test
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
- chain: ethereum
|
||||
id: !test
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
Reference in New Issue
Block a user