problem: allows multiple different endpoints for same upstream

solution: simplify config, allow to setup only one connection type per upstream
This commit is contained in:
Igor Artamonov
2019-07-27 22:31:49 -04:00
parent c5228c2cce
commit b996edabef
9 changed files with 365 additions and 526 deletions

View File

@@ -1,448 +0,0 @@
package io.emeraldpay.dshackle.config;
import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.introspector.GenericProperty;
import org.yaml.snakeyaml.introspector.MethodProperty;
import org.yaml.snakeyaml.introspector.Property;
import org.yaml.snakeyaml.introspector.PropertySubstitute;
import org.yaml.snakeyaml.nodes.MappingNode;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.ScalarNode;
import javax.annotation.Nullable;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class UpstreamsConfig {
private String version;
private List<DefaultOptions> defaultOptions;
private List<Upstream> upstreams;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public List<DefaultOptions> getDefaultOptions() {
return defaultOptions;
}
public void setDefaultOptions(List<DefaultOptions> defaultOptions) {
this.defaultOptions = defaultOptions;
}
public List<Upstream> getUpstreams() {
return upstreams;
}
public void setUpstreams(List<Upstream> upstreams) {
this.upstreams = upstreams;
}
public static class Options {
private Boolean disableSyncing;
private Integer minPeers;
private Integer quorum;
public Boolean getDisableSyncing() {
return disableSyncing;
}
public void setDisableSyncing(Boolean disableSyncing) {
this.disableSyncing = disableSyncing;
}
public Integer getMinPeers() {
return minPeers;
}
public void setMinPeers(Integer minPeers) {
if (minPeers < 0) {
throw new IllegalArgumentException("minPeers must be positive number");
}
this.minPeers = minPeers;
}
public Integer getQuorum() {
return quorum;
}
public void setQuorum(Integer quorum) {
if (quorum < 0) {
throw new IllegalArgumentException("quorum must be positive number");
}
this.quorum = quorum;
}
public Options merge(Options additional) {
if (additional == null) {
return this;
}
Options copy = new Options();
copy.setDisableSyncing(this.disableSyncing != null ? this.disableSyncing : additional.disableSyncing);
copy.setMinPeers(this.minPeers != null ? this.minPeers : additional.minPeers);
copy.setQuorum(this.quorum != null ? this.quorum : additional.quorum);
return copy;
}
public static Options getDefaults() {
Options options = new Options();
options.setDisableSyncing(true);
options.setMinPeers(1);
options.setQuorum(1);
return options;
}
}
public static class OptionsYaml extends TypeDescription {
public OptionsYaml() {
super(Options.class);
super.substituteProperty(new PropertySubstitute("disable-syncing", Boolean.class,
"getDisableSyncing", "setDisableSyncing"));
super.substituteProperty(new PropertySubstitute("min-peers", Integer.class,
"getMinPeers", "setMinPeers"));
}
}
public static class DefaultOptions extends Options {
private List<String> chains;
private Options options;
public List<String> getChains() {
return chains;
}
public void setChains(List<String> chains) {
this.chains = chains;
}
public Options getOptions() {
return options;
}
public void setOptions(Options options) {
this.options = options;
}
}
public static class Upstream {
private String id;
private String chain;
@Nullable
private String provider;
private List<Endpoint> endpoints = Collections.emptyList();
private Options options;
private boolean enabled = true;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getChain() {
return chain;
}
public void setChain(String chain) {
this.chain = chain;
}
@Nullable
public String getProvider() {
return provider;
}
public void setProvider(@Nullable String provider) {
this.provider = provider;
}
public List<Endpoint> getEndpoints() {
return endpoints;
}
public void setEndpoints(List<Endpoint> endpoints) {
this.endpoints = endpoints;
}
@Nullable
public Options getOptions() {
return options;
}
public void setOptions(Options options) {
this.options = options;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
public static class Endpoint {
private EndpointType type;
private URI url;
private String host;
private int port;
@Nullable
private Auth auth;
private Boolean enabled = true;
@Nullable
private URI origin;
public EndpointType getType() {
return type;
}
public void setType(EndpointType type) {
this.type = type;
}
public URI getUrl() {
return url;
}
public void setUrl(URI url) {
this.url = url;
}
@Nullable
public Auth getAuth() {
return auth;
}
public void setAuth(@Nullable Auth auth) {
this.auth = auth;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@Nullable
public URI getOrigin() {
return origin;
}
public void setOrigin(@Nullable URI origin) {
this.origin = origin;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
}
public static class Auth {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public static interface WithKey {
public String getKey();
public void setKey(String key);
}
public static class BasicAuth extends Auth implements WithKey {
private String key;
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
public static class TlsAuth extends Auth implements WithKey {
private String ca;
private String certificate;
private String key;
public String getCa() {
return ca;
}
public void setCa(String ca) {
this.ca = ca;
}
public String getCertificate() {
return certificate;
}
public void setCertificate(String certificate) {
this.certificate = certificate;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
}
public static class AuthYaml extends TypeDescription {
public AuthYaml() {
super(Auth.class);
}
public Class getImpl() {
try {
Field f = TypeDescription.class.getDeclaredField("impl");
f.setAccessible(true);
return (Class) f.get(this);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@Override
public Property getProperty(String name) {
if ("key".equals(name)) {
try {
return new MethodProperty(new PropertyDescriptor("key", WithKey.class, "getKey", "setKey"));
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
if ("ca".equals(name)) {
try {
return new MethodProperty(new PropertyDescriptor("ca", TlsAuth.class, "getCa", "setCa"));
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
if ("certificate".equals(name)) {
try {
return new MethodProperty(new PropertyDescriptor("certificate", TlsAuth.class, "getCertificate", "setCertificate"));
} catch (IntrospectionException e) {
e.printStackTrace();
}
}
return super.getProperty(name);
}
private Optional<ScalarNode> getValue(MappingNode mappingNode, String key) {
return mappingNode.getValue()
.stream()
.filter((n) -> n.getKeyNode() instanceof ScalarNode && n.getValueNode() instanceof ScalarNode)
.filter((n) -> {
ScalarNode sn = (ScalarNode)n.getKeyNode();
return "type".equals(sn.getValue());
})
.map((n) -> (ScalarNode)n.getValueNode())
.findFirst();
}
@Override
public Object newInstance(Node node) {
if (node instanceof MappingNode) {
MappingNode mappingNode = (MappingNode)node;
Optional<ScalarNode> type = getValue(mappingNode, "type");
if (type.isPresent()) {
if ("basic".equals(type.get().getValue())) {
return new BasicAuth();
} else if ("tls".equals(type.get().getValue())) {
return new TlsAuth();
} else {
throw new IllegalArgumentException("Unsupported auth type: " + type.get().getValue());
}
} else {
throw new IllegalArgumentException("Auth type is not set");
}
}
throw new IllegalArgumentException("Auth is invalid");
}
}
public static enum EndpointType {
JSON_RPC("json-rpc"),
WEBSOCKET("ws", "websocket"),
DSHACKLE("dshackle"),
UNKNOWN("unknown");
private final String[] code;
EndpointType(String ... code) {
this.code = code;
Arrays.sort(this.code);
}
public static EndpointType byName(String code) {
code = code.toLowerCase();
for (EndpointType t: EndpointType.values()) {
if (Arrays.binarySearch(t.code, code) >= 0) {
return t;
}
}
return UNKNOWN;
}
}
public static class EndpointTypeYaml extends TypeDescription {
public EndpointTypeYaml() {
super(EndpointType.class);
}
@Override
public Object newInstance(Node node) {
if (node instanceof ScalarNode) {
return EndpointType.byName(((ScalarNode)node).getValue());
}
throw new IllegalArgumentException("Invalid type: " + node.getClass());
}
}
}

View File

@@ -0,0 +1,120 @@
package io.emeraldpay.dshackle.config
import java.net.URI
import java.util.*
import kotlin.collections.ArrayList
class UpstreamsConfig {
var version: String? = null
var defaultOptions: MutableList<DefaultOptions> = ArrayList<DefaultOptions>()
var upstreams: MutableList<Upstream<*>> = ArrayList<Upstream<*>>()
open class Options {
@Deprecated("remove it")
var quorum: Int = 1
var disableSyncing: Boolean? = null
var minPeers: Int? = 1
set(minPeers) {
if (minPeers != null && minPeers < 0) {
throw IllegalArgumentException("minPeers must be positive number")
}
field = minPeers
}
fun merge(additional: Options?): Options {
if (additional == null) {
return this
}
val copy = Options()
copy.disableSyncing = if (this.disableSyncing != null) this.disableSyncing else additional.disableSyncing
copy.minPeers = if (this.minPeers != null) this.minPeers else additional.minPeers
return copy
}
companion object {
fun getDefaults(): Options {
val options = Options()
options.disableSyncing = true
options.minPeers = 1
return options
}
}
}
class DefaultOptions : Options() {
var chains: List<String>? = null
var options: Options? = null
}
class Upstream<T : UpstreamConnection> {
var id: String? = null
var chain: String? = null
var provider: String? = null
var options: Options? = null
var isEnabled = true
var connection: T? = null
}
open class UpstreamConnection
class GrpcConnection : UpstreamConnection() {
var host: String? = null
var port: Int = 0
var auth: TlsAuth? = null
}
class EthereumConnection : UpstreamConnection() {
var rpc: HttpEndpoint? = null
var ws: WsEndpoint? = null
}
class HttpEndpoint(val url: URI) {
var auth: Auth? = null
}
class WsEndpoint(val url: URI) {
var origin: URI? = null
}
open class Auth {
var type: String? = null
}
class BasicAuth : Auth() {
var key: String? = null
}
class TlsAuth : Auth() {
var ca: String? = null
var certificate: String? = null
var key: String? = null
}
enum class UpstreamType private constructor(vararg code: String) {
ETHEREUM_JSON_RPC("ethereum"),
DSHACKLE("dshackle", "grpc"),
UNKNOWN("unknown");
private val code: Array<String>
init {
this.code = code as Array<String>
Arrays.sort(this.code)
}
companion object {
fun byName(code: String): UpstreamType {
var code = code
code = code.toLowerCase()
for (t in UpstreamType.values()) {
if (Arrays.binarySearch(t.code, code) >= 0) {
return t
}
}
return UNKNOWN
}
}
}
}

View File

@@ -1,17 +1,198 @@
package io.emeraldpay.dshackle.config
import org.slf4j.LoggerFactory
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.nodes.CollectionNode
import org.yaml.snakeyaml.nodes.MappingNode
import org.yaml.snakeyaml.nodes.Node
import org.yaml.snakeyaml.nodes.ScalarNode
import java.io.InputStream
import java.io.InputStreamReader
import java.lang.IllegalArgumentException
import java.net.URI
class UpstreamsConfigReader {
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
fun read(input: InputStream): UpstreamsConfig {
val yaml = Yaml()
yaml.addTypeDescription(UpstreamsConfig.EndpointTypeYaml())
yaml.addTypeDescription(UpstreamsConfig.OptionsYaml())
yaml.addTypeDescription(UpstreamsConfig.AuthYaml())
return yaml.loadAs(input, UpstreamsConfig::class.java)
val configNode = asMappingNode(yaml.compose(InputStreamReader(input)))
val config = UpstreamsConfig()
config.version = getValueAsString(configNode, "version")
getList<MappingNode>(configNode, "defaultOptions")?.value?.forEach { opts ->
val defaultOptions = UpstreamsConfig.DefaultOptions()
config.defaultOptions.add(defaultOptions)
defaultOptions.chains = getListOfString(opts, "chains")
val options = UpstreamsConfig.Options()
defaultOptions.options = options
getMapping(opts, "options")?.let { values ->
getValueAsBool(values, "disable-syncing")?.let {
options.disableSyncing = it
}
getValueAsInt(values, "min-peers")?.let {
options.minPeers = it
}
}
}
config.upstreams = ArrayList<UpstreamsConfig.Upstream<*>>()
getList<MappingNode>(configNode, "upstreams")?.value?.forEach { upNode ->
val connNode = getMapping(upNode, "connection")
if (hasAny(connNode, "ethereum")) {
val connConfigNode = getMapping(connNode, "ethereum")!!
val upstream = UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>()
upstream.id = getValueAsString(upNode, "id")
upstream.provider = getValueAsString(upNode, "provider")
upstream.chain = getValueAsString(upNode, "chain")
config.upstreams.add(upstream)
val connection = UpstreamsConfig.EthereumConnection()
upstream.connection = connection
getMapping(connConfigNode, "rpc")?.let { node ->
getValueAsString(node, "url")?.let { url ->
val http = UpstreamsConfig.HttpEndpoint(URI(url))
connection.rpc = http
http.auth = readAuth(getMapping(node, "auth"))
}
}
getMapping(connConfigNode, "ws")?.let { node ->
getValueAsString(node, "url")?.let { url ->
val ws = UpstreamsConfig.WsEndpoint(URI(url))
connection.ws = ws
getValueAsString(node, "origin")?.let { origin ->
ws.origin = URI(origin)
}
// ws.auth = readAuth(getMapping(node, "auth"))
}
}
} else if (hasAny(connNode, "grpc")) {
val connConfigNode = getMapping(connNode, "grpc")!!
val upstream = UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>()
upstream.id = getValueAsString(upNode, "id")
upstream.provider = getValueAsString(upNode, "provider")
config.upstreams.add(upstream)
val connection = UpstreamsConfig.GrpcConnection()
upstream.connection = connection
getValueAsString(connConfigNode, "host")?.let {
connection.host = it
}
getValueAsInt(connConfigNode, "port")?.let {
connection.port = it
}
connection.auth = readAuth(getMapping(connConfigNode, "auth")) as UpstreamsConfig.TlsAuth?
}
}
return config
}
private fun readAuth(authNode: MappingNode?): UpstreamsConfig.Auth? {
return getValueAsString(authNode, "type")?.let {
return when (it) {
"tls" -> {
val auth = UpstreamsConfig.TlsAuth()
auth.ca = getValueAsString(authNode, "ca")
auth.certificate = getValueAsString(authNode, "certificate")
auth.key = getValueAsString(authNode, "key")
auth
}
"basic" -> {
val auth = UpstreamsConfig.BasicAuth()
auth.key = getValueAsString(authNode, "key")
auth
}
else -> {
log.warn("Invalid Auth type: $it")
null
}
}
}
}
private fun hasAny(mappingNode: MappingNode?, key: String): Boolean {
if (mappingNode == null) {
return false
}
return mappingNode.value
.stream()
.filter { n -> n.keyNode is ScalarNode }
.filter { n ->
val sn = n.keyNode as ScalarNode
key == sn.value
}.count() > 0
}
private fun <T> getValue(mappingNode: MappingNode?, key: String, type: Class<T>): T? {
if (mappingNode == null) {
return null
}
return mappingNode.value
.stream()
.filter { n -> n.keyNode is ScalarNode && type.isAssignableFrom(n.valueNode.javaClass) }
.filter { n ->
val sn = n.keyNode as ScalarNode
key == sn.value
}
.map { n -> n.valueNode as T }
.findFirst().let {
if (it.isPresent) {
it.get()
} else {
null
}
}
}
private fun getMapping(mappingNode: MappingNode?, key: String): MappingNode? {
return getValue(mappingNode, key, MappingNode::class.java)
}
private fun getValue(mappingNode: MappingNode?, key: String): ScalarNode? {
return getValue(mappingNode, key, ScalarNode::class.java)
}
private fun <T> getList(mappingNode: MappingNode?, key: String): CollectionNode<T>? {
return getValue(mappingNode, key, CollectionNode::class.java) as CollectionNode<T>
}
private fun getListOfString(mappingNode: MappingNode?, key: String): List<String>? {
return getList<ScalarNode>(mappingNode, key)?.value
?.map { it.value }
}
private fun getValueAsString(mappingNode: MappingNode?, key: String): String? {
return getValue(mappingNode, key)?.let {
return@let it.value
}
}
private fun getValueAsInt(mappingNode: MappingNode?, key: String): Int? {
return getValue(mappingNode, key)?.let {
return@let if (it.isPlain) {
it.value.toIntOrNull()
} else {
null
}
}
}
private fun getValueAsBool(mappingNode: MappingNode?, key: String): Boolean? {
return getValue(mappingNode, key)?.let {
return@let if (it.isPlain) {
it.value?.toLowerCase() == "true"
} else {
null
}
}
}
private fun asMappingNode(node: Node): MappingNode {
return if (MappingNode::class.java.isAssignableFrom(node.javaClass)) {
node as MappingNode
} else {
throw IllegalArgumentException("Not a map")
}
}
}

View File

@@ -41,10 +41,14 @@ open class ConfiguredUpstreams(
val config = readConfig()
val defaultOptions = buildDefaultOptions(config)
config.upstreams.forEach { up ->
val options = (up.options ?: UpstreamsConfig.Options())
.merge(UpstreamsConfig.Options.getDefaults())
if (up.provider == "dshackle") {
buildGrpcUpstream(up)
buildGrpcUpstream(up.connection as UpstreamsConfig.GrpcConnection, options)
} else {
buildEthereumUpstream(up, defaultOptions)
val chain = chainNames[up.chain] ?: return
buildEthereumUpstream(up.connection as UpstreamsConfig.EthereumConnection, chain, options)
}
}
}
@@ -69,7 +73,7 @@ open class ConfiguredUpstreams(
private fun buildDefaultOptions(config: UpstreamsConfig): HashMap<Chain, UpstreamsConfig.Options> {
val defaultOptions = HashMap<Chain, UpstreamsConfig.Options>()
config.defaultOptions.forEach { df ->
df.chains.forEach { chainName ->
df.chains?.forEach { chainName ->
chainNames[chainName]?.let { chain ->
var current = defaultOptions[chain]
if (current == null) {
@@ -77,56 +81,45 @@ open class ConfiguredUpstreams(
} else {
current = current.merge(df.options)
}
defaultOptions[chain] = current
defaultOptions[chain] = current!!
}
}
}
return defaultOptions
}
private fun buildEthereumUpstream(up: UpstreamsConfig.Upstream,
defaultOptions: HashMap<Chain, UpstreamsConfig.Options>) {
val chain = chainNames[up.chain] ?: return
private fun buildEthereumUpstream(up: UpstreamsConfig.EthereumConnection,
chain: Chain,
options: UpstreamsConfig.Options) {
var rpcApi: EthereumApi? = null
var wsApi: EthereumWs? = null
val urls = ArrayList<URI>()
up.endpoints.forEach { endpoint ->
if (endpoint.type == UpstreamsConfig.EndpointType.JSON_RPC) {
rpcApi = EthereumApi(
DefaultRpcClient(DefaultRpcTransport(endpoint.url)),
objectMapper,
chain
)
}
if (endpoint.type == UpstreamsConfig.EndpointType.WEBSOCKET) {
wsApi = EthereumWs(
endpoint.url,
endpoint.origin ?: URI("http://localhost")
)
wsApi!!.connect()
}
up.rpc?.let { endpoint ->
rpcApi = EthereumApi(
DefaultRpcClient(DefaultRpcTransport(endpoint.url)),
objectMapper,
chain
)
urls.add(endpoint.url)
}
up.ws?.let { endpoint ->
wsApi = EthereumWs(
endpoint.url,
endpoint.origin ?: URI("http://localhost")
)
wsApi!!.connect()
urls.add(endpoint.url)
}
val options = (up.options ?: UpstreamsConfig.Options())
.merge(defaultOptions[chain])
.merge(UpstreamsConfig.Options.getDefaults())
if (rpcApi != null) {
log.info("Using ${chain.chainName} upstream, at ${urls.joinToString()}")
getOrCreateUpstream(chain).addUpstream(EthereumUpstream(chain, rpcApi!!, wsApi, options))
}
}
private fun buildGrpcUpstream(up: UpstreamsConfig.Upstream) {
if (up.endpoints.size == 0) {
return
}
val options = (up.options ?: UpstreamsConfig.Options())
.merge(UpstreamsConfig.Options.getDefaults())
val endpoint = up.endpoints.first()
if (endpoint.type == UpstreamsConfig.EndpointType.DSHACKLE) {
private fun buildGrpcUpstream(up: UpstreamsConfig.GrpcConnection, options: UpstreamsConfig.Options) {
val endpoint = up
val ds = GrpcUpstreams(
endpoint.host,
endpoint.host!!,
endpoint.port ?: 443,
objectMapper,
options
@@ -140,7 +133,6 @@ open class ConfiguredUpstreams(
log.info("Subscribed to $it through gRPC at ${endpoint.host}:${endpoint.port}")
getOrCreateUpstream(it).addUpstream(ds.getOrCreate(it))
}
}
}
override fun getUpstream(chain: Chain): AggregatedUpstreams? {

View File

@@ -21,7 +21,7 @@ class UpstreamValidator(
if (syncing.get().isSyncing) {
return UpstreamAvailability.SYNCING
}
if (peerCount.get() < options.minPeers) {
if (options.minPeers != null && peerCount.get() < options.minPeers!!) {
return UpstreamAvailability.IMMATURE
}
return UpstreamAvailability.OK

View File

@@ -18,7 +18,6 @@ class UpstreamsConfigReaderSpec extends Specification {
size() == 1
with(get(0)) {
chains == ["ethereum"]
options.quorum == 1
options.minPeers == 3
options.disableSyncing
}
@@ -28,28 +27,26 @@ class UpstreamsConfigReaderSpec extends Specification {
id == "local"
chain == "ethereum"
provider == "geth"
endpoints.size() == 2
with(endpoints.get(0)) {
type == UpstreamsConfig.EndpointType.JSON_RPC
url == new URI("http://localhost:8545")
}
with(endpoints.get(1)) {
type == UpstreamsConfig.EndpointType.WEBSOCKET
url == new URI("ws://localhost:8546")
connection instanceof UpstreamsConfig.EthereumConnection
with((UpstreamsConfig.EthereumConnection)connection) {
rpc != null
rpc.url == new URI("http://localhost:8545")
ws != null
ws.url == new URI("ws://localhost:8546")
}
}
with(act.upstreams.get(1)) {
id == "infura"
chain == "ethereum"
provider == "infura"
endpoints.size() == 1
with(endpoints.get(0)) {
type == UpstreamsConfig.EndpointType.JSON_RPC
url == new URI("https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2")
auth instanceof UpstreamsConfig.BasicAuth
with((UpstreamsConfig.BasicAuth)auth) {
connection instanceof UpstreamsConfig.EthereumConnection
with((UpstreamsConfig.EthereumConnection)connection) {
rpc.url == new URI("https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2")
rpc.auth instanceof UpstreamsConfig.BasicAuth
with((UpstreamsConfig.BasicAuth)rpc.auth) {
key == "4fc258fe41a68149c199ad8f281f2015"
}
ws == null
}
}
@@ -66,11 +63,9 @@ class UpstreamsConfigReaderSpec extends Specification {
act.upstreams.size() == 1
with(act.upstreams.get(0)) {
id == "remote"
chain == "auto"
provider == "dshackle"
endpoints.size() == 1
with(endpoints.get(0)) {
type == UpstreamsConfig.EndpointType.DSHACKLE
connection instanceof UpstreamsConfig.GrpcConnection
with((UpstreamsConfig.GrpcConnection)connection) {
host == "10.2.0.15"
auth instanceof UpstreamsConfig.TlsAuth
with((UpstreamsConfig.TlsAuth)auth) {

View File

@@ -6,24 +6,25 @@ defaultOptions:
options:
disable-syncing: true
min-peers: 3
quorum: 1
upstreams:
- id: local
chain: ethereum
provider: geth
endpoints:
- type: json-rpc
url: "http://localhost:8545"
- type: ws
url: "ws://localhost:8546"
origin: "http://localhost"
connection:
ethereum:
rpc:
url: "http://localhost:8545"
ws:
url: "ws://localhost:8546"
origin: "http://localhost"
- id: infura
chain: ethereum
provider: infura
endpoints:
- type: json-rpc
url: "https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2"
auth:
type: basic
key: 4fc258fe41a68149c199ad8f281f2015
connection:
ethereum:
rpc:
url: "https://mainnet.infura.io/v3/fa28c968191849c1aff541ad1d8511f2"
auth:
type: basic
key: 4fc258fe41a68149c199ad8f281f2015

View File

@@ -6,14 +6,12 @@ defaultOptions:
options:
disable-syncing: true
min-peers: 3
quorum: 1
upstreams:
- id: remote
chain: auto
provider: dshackle
endpoints:
- type: dshackle
connection:
grpc:
host: "10.2.0.15"
auth:
type: tls