solution: setup custom upstream methods
This commit is contained in:
@@ -174,4 +174,24 @@ class UpstreamsConfigReaderSpec extends Specification {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "Parse config with methods"() {
|
||||
setup:
|
||||
def config = this.class.getClassLoader().getResourceAsStream("upstreams-methods.yaml")
|
||||
when:
|
||||
def act = reader.read(config)
|
||||
then:
|
||||
act != null
|
||||
with(act.upstreams.get(0)) {
|
||||
methods != null
|
||||
with(methods) {
|
||||
enabled.size() == 1
|
||||
enabled.first().name == "parity_trace"
|
||||
|
||||
disabled.size() == 2
|
||||
disabled.toList()[0].name == "eth_getBlockByNumber"
|
||||
disabled.toList()[1].name == "admin_shutdown"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.test
|
||||
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.upstream.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.NodeDetailsList
|
||||
import io.emeraldpay.dshackle.upstream.QuorumBasedMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumHead
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
@@ -29,7 +33,13 @@ class EthereumUpstreamMock extends EthereumUpstream {
|
||||
EthereumHeadMock ethereumHeadMock = new EthereumHeadMock()
|
||||
|
||||
EthereumUpstreamMock(@NotNull Chain chain, @NotNull DirectEthereumApi api) {
|
||||
super(chain, api)
|
||||
this(chain, api, new QuorumBasedMethods(TestingCommons.objectMapper(), chain))
|
||||
}
|
||||
|
||||
EthereumUpstreamMock(@NotNull Chain chain, @NotNull DirectEthereumApi api, CallMethods methods) {
|
||||
super(chain, api, null,
|
||||
UpstreamsConfig.Options.getDefaults(), new NodeDetailsList.NodeDetails(1, new UpstreamsConfig.Labels()),
|
||||
methods)
|
||||
setLag(0)
|
||||
setStatus(UpstreamAvailability.OK)
|
||||
}
|
||||
|
||||
@@ -20,9 +20,12 @@ import com.fasterxml.jackson.databind.DeserializationFeature
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule
|
||||
import io.emeraldpay.dshackle.upstream.AggregatedUpstream
|
||||
import io.emeraldpay.dshackle.upstream.CallMethods
|
||||
import io.emeraldpay.dshackle.upstream.ChainUpstreams
|
||||
import io.emeraldpay.dshackle.upstream.DirectCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.QuorumBasedMethods
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
|
||||
import io.emeraldpay.grpc.Chain
|
||||
import io.infinitape.etherjar.rpc.JacksonRpcConverter
|
||||
import io.infinitape.etherjar.rpc.RpcClient
|
||||
@@ -57,6 +60,10 @@ class TestingCommons {
|
||||
}
|
||||
|
||||
static AggregatedUpstream aggregatedUpstream(DirectEthereumApi api) {
|
||||
return new ChainUpstreams(Chain.ETHEREUM, [upstream(api)], new DirectCallMethods(), objectMapper())
|
||||
return aggregatedUpstream(upstream(api))
|
||||
}
|
||||
|
||||
static AggregatedUpstream aggregatedUpstream(EthereumUpstream up) {
|
||||
return new ChainUpstreams(Chain.ETHEREUM, [up], objectMapper())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class UpstreamsMock implements Upstreams {
|
||||
@Override
|
||||
AggregatedUpstream addUpstream(@NotNull Chain chain, @NotNull Upstream up) {
|
||||
if (!upstreams.containsKey(chain)) {
|
||||
upstreams[chain] = new ChainUpstreams(chain, [up], targetFor(chain), TestingCommons.objectMapper())
|
||||
upstreams[chain] = new ChainUpstreams(chain, [up], TestingCommons.objectMapper())
|
||||
} else {
|
||||
upstreams[chain].addUpstream(up)
|
||||
}
|
||||
@@ -64,7 +64,7 @@ class UpstreamsMock implements Upstreams {
|
||||
}
|
||||
|
||||
@Override
|
||||
QuorumBasedMethods targetFor(@NotNull Chain chain) {
|
||||
QuorumBasedMethods getDefaultMethods(@NotNull Chain chain) {
|
||||
if (target[chain] == null) {
|
||||
QuorumBasedMethods targets = new QuorumBasedMethods(TestingCommons.objectMapper(), chain)
|
||||
target[chain] = targets
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* 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 spock.lang.Specification
|
||||
|
||||
class AggregatedCallMethodsSpec extends Specification {
|
||||
|
||||
def "Returns quorum from delegate that owns it"() {
|
||||
setup:
|
||||
def quorum = new AlwaysQuorum()
|
||||
def delegate1 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_no_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> false
|
||||
}
|
||||
def delegate2 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> true
|
||||
1 * getQuorumFor("eth_test") >> quorum
|
||||
}
|
||||
def aggregate = new AggregatedCallMethods([delegate1, delegate2])
|
||||
when:
|
||||
def act = aggregate.getQuorumFor("eth_test")
|
||||
then:
|
||||
act == quorum
|
||||
}
|
||||
|
||||
def "Allowed if any allowed"() {
|
||||
setup:
|
||||
def delegate1 = new DirectCallMethods(["eth_no_test", "foo_bar"] as Set)
|
||||
def delegate2 = new DirectCallMethods(["eth_test", "foo_bar"] as Set)
|
||||
def aggregate = new AggregatedCallMethods([delegate1, delegate2])
|
||||
when:
|
||||
def act = aggregate.isAllowed("eth_test")
|
||||
then:
|
||||
act
|
||||
|
||||
when:
|
||||
act = aggregate.isAllowed("eth_no_test")
|
||||
then:
|
||||
act
|
||||
|
||||
when:
|
||||
act = aggregate.isAllowed("foo_bar")
|
||||
then:
|
||||
act
|
||||
|
||||
when:
|
||||
act = aggregate.isAllowed("nothing")
|
||||
then:
|
||||
!act
|
||||
}
|
||||
|
||||
def "Supported has all methods"() {
|
||||
setup:
|
||||
def delegate1 = new DirectCallMethods(["eth_no_test", "foo_bar"] as Set)
|
||||
def delegate2 = new DirectCallMethods(["eth_test", "foo_bar"] as Set)
|
||||
def aggregate = new AggregatedCallMethods([delegate1, delegate2])
|
||||
when:
|
||||
def act = aggregate.getSupportedMethods()
|
||||
then:
|
||||
act.sort() == ["eth_test", "eth_no_test", "foo_bar"].sort()
|
||||
}
|
||||
|
||||
def "Hardcoded if any hardcoded"() {
|
||||
setup:
|
||||
def delegate1 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_no_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> false
|
||||
1 * isAllowed("eth_no_test") >> true
|
||||
|
||||
1 * isHardcoded("eth_no_test") >> false
|
||||
}
|
||||
def delegate2 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> true
|
||||
1 * isAllowed("eth_no_test") >> false
|
||||
|
||||
1 * isHardcoded("eth_test") >> true
|
||||
}
|
||||
def aggregate = new AggregatedCallMethods([delegate1, delegate2])
|
||||
when:
|
||||
def act = aggregate.isHardcoded("eth_test")
|
||||
then:
|
||||
act
|
||||
|
||||
when:
|
||||
act = aggregate.isHardcoded("eth_no_test")
|
||||
then:
|
||||
!act
|
||||
}
|
||||
|
||||
def "Execute hardcoded on delegate that owns it"() {
|
||||
setup:
|
||||
def delegate1 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_no_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> false
|
||||
}
|
||||
def delegate2 = Mock(CallMethods) {
|
||||
_ * getSupportedMethods() >> ["eth_test", "foo_bar"]
|
||||
1 * isAllowed("eth_test") >> true
|
||||
1 * isHardcoded("eth_test") >> true
|
||||
1 * executeHardcoded("eth_test") >> "hello"
|
||||
}
|
||||
def aggregate = new AggregatedCallMethods([delegate1, delegate2])
|
||||
when:
|
||||
def act = aggregate.executeHardcoded("eth_test")
|
||||
then:
|
||||
act == "hello"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* 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 spock.lang.Specification
|
||||
|
||||
class ManagedCallMethodsSpec extends Specification {
|
||||
|
||||
def "Gets quorum for enabled method"() {
|
||||
setup:
|
||||
def managed = new ManagedCallMethods(
|
||||
new DirectCallMethods(),
|
||||
["eth_test"] as Set,
|
||||
[] as Set
|
||||
)
|
||||
when:
|
||||
def act = managed.getQuorumFor("eth_test")
|
||||
then:
|
||||
act instanceof AlwaysQuorum
|
||||
}
|
||||
|
||||
def "Allowed contacts all enabled + delegate"() {
|
||||
setup:
|
||||
def managed = new ManagedCallMethods(
|
||||
new DirectCallMethods(["eth_test2"] as Set),
|
||||
["eth_test"] as Set,
|
||||
[] as Set
|
||||
)
|
||||
when:
|
||||
def act = managed.getSupportedMethods()
|
||||
then:
|
||||
act.sort() == ["eth_test", "eth_test2"].sort()
|
||||
}
|
||||
|
||||
def "Disabled removed from delegate"() {
|
||||
setup:
|
||||
def managed = new ManagedCallMethods(
|
||||
new DirectCallMethods(["eth_test2", "foo_bar"] as Set),
|
||||
["eth_test"] as Set,
|
||||
["foo_bar"] as Set
|
||||
)
|
||||
when:
|
||||
def act = managed.getSupportedMethods()
|
||||
then:
|
||||
act.sort() == ["eth_test", "eth_test2"].sort()
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class SelectorSpec extends Specification {
|
||||
when:
|
||||
def act = Selector.convertToMatcher(null)
|
||||
then:
|
||||
act.class == Selector.EmptyMatcher
|
||||
act.class == Selector.AnyLabelMatcher
|
||||
}
|
||||
|
||||
def "Convert LABEL match"() {
|
||||
|
||||
@@ -20,8 +20,10 @@ import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.ReactorBlockchainGrpc
|
||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||
import io.emeraldpay.dshackle.test.EthereumApiMock
|
||||
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
|
||||
import io.emeraldpay.dshackle.test.MockServer
|
||||
import io.emeraldpay.dshackle.test.TestingCommons
|
||||
import io.emeraldpay.dshackle.upstream.DirectCallMethods
|
||||
import io.emeraldpay.dshackle.upstream.QuorumBasedMethods
|
||||
import io.emeraldpay.dshackle.upstream.Upstreams
|
||||
import io.emeraldpay.dshackle.upstream.grpc.EthereumGrpcTransport
|
||||
@@ -45,7 +47,9 @@ class EthereumGrpcTransportSpec extends Specification {
|
||||
|
||||
def callData = [:]
|
||||
def otherSideUpstreams = Mock(Upstreams)
|
||||
def otherSideAggr = TestingCommons.aggregatedUpstream(otherSideApi)
|
||||
def otherSideAggr = TestingCommons.aggregatedUpstream(
|
||||
new EthereumUpstreamMock(Chain.ETHEREUM, otherSideApi, new DirectCallMethods(["eth_test"]))
|
||||
)
|
||||
|
||||
def otherSideNativeCall = new NativeCall(otherSideUpstreams, objectMapper)
|
||||
otherSideApi.upstream = otherSideAggr
|
||||
@@ -88,7 +92,9 @@ class EthereumGrpcTransportSpec extends Specification {
|
||||
|
||||
def callData = [:]
|
||||
def otherSideUpstreams = Mock(Upstreams)
|
||||
def otherSideAggr = TestingCommons.aggregatedUpstream(otherSideApi)
|
||||
def otherSideAggr = TestingCommons.aggregatedUpstream(
|
||||
new EthereumUpstreamMock(Chain.ETHEREUM, otherSideApi, new DirectCallMethods(["eth_test", "eth_test2"]))
|
||||
)
|
||||
def otherSideNativeCall = new NativeCall(otherSideUpstreams, objectMapper)
|
||||
otherSideApi.upstream = otherSideAggr
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ class GrpcUpstreamSpec extends Specification {
|
||||
|
||||
MockServer mockServer = new MockServer()
|
||||
ObjectMapper objectMapper = TestingCommons.objectMapper()
|
||||
def ethereumTargets = new QuorumBasedMethods(objectMapper, Chain.ETHEREUM)
|
||||
|
||||
def "Subscribe to head"() {
|
||||
setup:
|
||||
@@ -71,8 +70,11 @@ class GrpcUpstreamSpec extends Specification {
|
||||
)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper, ethereumTargets)
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
.build())
|
||||
when:
|
||||
upstream.start()
|
||||
def h = upstream.head.getFlux().next().block(Duration.ofSeconds(1))
|
||||
@@ -127,8 +129,11 @@ class GrpcUpstreamSpec extends Specification {
|
||||
finished.complete(true)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper, ethereumTargets)
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
.build())
|
||||
when:
|
||||
upstream.start()
|
||||
finished.get()
|
||||
@@ -184,8 +189,11 @@ class GrpcUpstreamSpec extends Specification {
|
||||
finished.complete(true)
|
||||
}
|
||||
})
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper, ethereumTargets)
|
||||
def upstream = new GrpcUpstream(chain, client, objectMapper)
|
||||
upstream.setLag(0)
|
||||
upstream.init(BlockchainOuterClass.DescribeChain.newBuilder()
|
||||
.addAllSupportedMethods(["eth_getBlockByHash"])
|
||||
.build())
|
||||
when:
|
||||
upstream.start()
|
||||
finished.get()
|
||||
|
||||
26
src/test/resources/upstreams-methods.yaml
Normal file
26
src/test/resources/upstreams-methods.yaml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: v1
|
||||
|
||||
defaultOptions:
|
||||
- chains:
|
||||
- ethereum
|
||||
options:
|
||||
min-peers: 3
|
||||
|
||||
upstreams:
|
||||
- id: local
|
||||
chain: ethereum
|
||||
options:
|
||||
min-peers: 7
|
||||
methods:
|
||||
enabled:
|
||||
- name: "parity_trace"
|
||||
disabled:
|
||||
- name: "eth_getBlockByNumber"
|
||||
- name: "admin_shutdown"
|
||||
connection:
|
||||
ethereum:
|
||||
rpc:
|
||||
url: "http://localhost:8545"
|
||||
ws:
|
||||
url: "ws://localhost:8546"
|
||||
origin: "http://localhost"
|
||||
Reference in New Issue
Block a user