Compare commits

..

3 Commits

Author SHA1 Message Date
rob
efe2233e19 track upstream base tag for the release watcher 2026-07-30 07:13:10 +00:00
rob
eab797893c build-image.sh: wire-correct version builds (tag-move so getVersion() exact-matches; dRPC edges track provider version strings) 2026-07-30 07:12:00 +00:00
rob
96f2885c44 fix: SIGHUP reload supports upstream and method removal
Reload no longer crashes on Chain.UNSPECIFIED, removes gRPC upstreams
by prefixed id with lifecycle cleanup, applies methods.disabled changes
synchronously, pushes status via existing SubscribeChainStatus streams,
and optionally closes client RPCs so edges reconnect. NativeCall returns
-32601 (CODE_METHOD_NOT_EXIST) for disabled/unknown methods.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-30 05:59:19 +00:00
22 changed files with 12 additions and 223 deletions

View File

@@ -1 +1 @@
v0.79.11 v0.79.10

View File

@@ -23,39 +23,10 @@ SUFFIX=${2:-ss1}
VER=${BASE_TAG#v} VER=${BASE_TAG#v}
git tag -f "$BASE_TAG" HEAD >/dev/null # local only - makes getVersion() exact-match git tag -f "$BASE_TAG" HEAD >/dev/null # local only - makes getVersion() exact-match
# Submodules are REQUIRED at build time (SSH urls -> rewrite to https; controller
# has no github key). The `public` submodule feeds foundation's resources
# (compatible-clients.yaml etc.) - foundation is consumed as a mavenLocal
# artifact, so it must be REPUBLISHED whenever the submodule moves, and the
# main build cleaned so jib can't reuse stale layers. Skipping any of this
# shipped a boot-crashing image on 2026-07-30 (ss1: Spring died on missing
# compatible-clients.yaml) - hence the hard gate below.
git config url."https://github.com/".insteadOf "git@github.com:" 2>/dev/null || true
git submodule sync -q && git submodule update --init --recursive
test -f foundation/src/main/resources/public/compatible-clients.yaml \
|| { echo "FATAL: public submodule missing compatible-clients.yaml"; exit 1; }
export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-21-openjdk-amd64}
./gradlew -p foundation publishToMavenLocal -q
./gradlew clean -q
docker build -q -t drpc-dshackle . >/dev/null docker build -q -t drpc-dshackle . >/dev/null
export JAVA_HOME=${JAVA_HOME:-/usr/lib/jvm/java-21-openjdk-amd64}
./gradlew jibDockerBuild -Djib.to.image="stakesquid/dshackle:${VER}-${SUFFIX}" -x test ./gradlew jibDockerBuild -Djib.to.image="stakesquid/dshackle:${VER}-${SUFFIX}" -x test
echo "--- resource gate (foundation jar inside the image):"
docker run --rm --entrypoint sh "stakesquid/dshackle:${VER}-${SUFFIX}" -c \
'ls /app/libs/foundation-*.jar >/dev/null' || { echo FATAL: no foundation jar; exit 1; }
python3 - "$VER" "$SUFFIX" <<'PY'
import subprocess, sys, zipfile, io
ver, suf = sys.argv[1], sys.argv[2]
jar = subprocess.run(["docker", "run", "--rm", "--entrypoint", "sh",
f"stakesquid/dshackle:{ver}-{suf}", "-c",
"cat /app/libs/foundation-*.jar"], capture_output=True).stdout
names = zipfile.ZipFile(io.BytesIO(jar)).namelist()
assert "public/compatible-clients.yaml" in names, "FATAL: compatible-clients.yaml not packaged"
print("resource gate OK: public/compatible-clients.yaml present in foundation jar")
PY
echo "--- wire version check (must equal stock ${VER}):" echo "--- wire version check (must equal stock ${VER}):"
docker run --rm --entrypoint cat "stakesquid/dshackle:${VER}-${SUFFIX}" \ docker run --rm --entrypoint cat "stakesquid/dshackle:${VER}-${SUFFIX}" \
/app/resources/version.properties /app/resources/version.properties

View File

@@ -849,18 +849,6 @@ rpc:
password: "${ETH_PASSWORD}" password: "${ETH_PASSWORD}"
---- ----
| `rpc.bearer-auth` + `rpc.bearer-auth.token`
a| HTTP Bearer token authorization (`Authorization: Bearer <token>` header), if required by the remote server. +
Cannot be used together with `basic-auth`.
Value can also reference env variables, for example:
[source,yaml]
----
rpc:
url: "https://ethereum.com:8545"
bearer-auth:
token: "${ETH_TOKEN}"
----
| `ws.url` | `ws.url`
| WebSocket URL to connect to. | WebSocket URL to connect to.
Optional, but optimizes performance if it's available. Optional, but optimizes performance if it's available.
@@ -871,9 +859,6 @@ Optional, but optimizes performance if it's available.
| `ws.basic-auth` + ... | `ws.basic-auth` + ...
| WebSocket Basic Auth configuration, if required by the remote server | WebSocket Basic Auth configuration, if required by the remote server
| `ws.bearer-auth` + `ws.bearer-auth.token`
| WebSocket Bearer token authorization, if required by the remote server
| `ws.frameSize` | `ws.frameSize`
| WebSocket frame size limit. | WebSocket frame size limit.
Ex `1kb`, `1024` (same as `1kb), `2mb`, etc. Ex `1kb`, `1024` (same as `1kb), `2mb`, etc.

View File

@@ -33,10 +33,6 @@ class AuthConfig {
val password: String, val password: String,
) : ClientAuth() ) : ClientAuth()
class ClientBearerAuth(
val token: String,
) : ClientAuth()
class ClientTlsAuth( class ClientTlsAuth(
var ca: String? = null, var ca: String? = null,
var certificate: String? = null, var certificate: String? = null,

View File

@@ -40,18 +40,6 @@ class AuthConfigReader : YamlConfigReader<AuthConfig>() {
} }
} }
fun readClientBearerAuth(node: MappingNode?): AuthConfig.ClientBearerAuth? {
return getMapping(node, "bearer-auth")?.let { authNode ->
val token = getValueAsString(authNode, "token")
if (token != null) {
AuthConfig.ClientBearerAuth(token)
} else {
log.warn("Bearer auth is not fully configured, token is required")
null
}
}
}
fun readClientTls(node: MappingNode?): AuthConfig.ClientTlsAuth? { fun readClientTls(node: MappingNode?): AuthConfig.ClientTlsAuth? {
return getMapping(node, "tls")?.let { authNode -> return getMapping(node, "tls")?.let { authNode ->
val auth = AuthConfig.ClientTlsAuth() val auth = AuthConfig.ClientTlsAuth()

View File

@@ -136,14 +136,12 @@ data class UpstreamsConfig(
constructor(url: URI) : this(url, DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE) constructor(url: URI) : this(url, DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
var basicAuth: AuthConfig.ClientBasicAuth? = null var basicAuth: AuthConfig.ClientBasicAuth? = null
var bearerAuth: AuthConfig.ClientBearerAuth? = null
var tls: AuthConfig.ClientTlsAuth? = null var tls: AuthConfig.ClientTlsAuth? = null
} }
data class WsEndpoint(val url: URI) { data class WsEndpoint(val url: URI) {
var origin: URI? = null var origin: URI? = null
var basicAuth: AuthConfig.ClientBasicAuth? = null var basicAuth: AuthConfig.ClientBasicAuth? = null
var bearerAuth: AuthConfig.ClientBearerAuth? = null
var frameSize: Int? = null var frameSize: Int? = null
var msgSize: Int? = null var msgSize: Int? = null
var connections: Int = 1 var connections: Int = 1

View File

@@ -147,7 +147,6 @@ class UpstreamsConfigReader(
getValueAsString(node, "url")?.let { url -> getValueAsString(node, "url")?.let { url ->
val http = UpstreamsConfig.HttpEndpoint(URI(url), DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE) val http = UpstreamsConfig.HttpEndpoint(URI(url), DEFAULT_MAX_CONNECTIONS, DEFAULT_QUEUE_SIZE)
http.basicAuth = authConfigReader.readClientBasicAuth(node) http.basicAuth = authConfigReader.readClientBasicAuth(node)
http.bearerAuth = readBearerAuth(node, http.basicAuth, url)
http.tls = authConfigReader.readClientTls(node) http.tls = authConfigReader.readClientTls(node)
connection.esplora = http connection.esplora = http
} }
@@ -174,19 +173,6 @@ class UpstreamsConfigReader(
return connection return connection
} }
private fun readBearerAuth(
node: MappingNode?,
basicAuth: AuthConfig.ClientBasicAuth?,
url: String,
): AuthConfig.ClientBearerAuth? {
val bearerAuth = authConfigReader.readClientBearerAuth(node)
if (bearerAuth != null && basicAuth != null) {
log.warn("Both basic-auth and bearer-auth are configured for $url, basic-auth is used")
return null
}
return bearerAuth
}
private fun readRpcConfig(connConfigNode: MappingNode): UpstreamsConfig.HttpEndpoint? { private fun readRpcConfig(connConfigNode: MappingNode): UpstreamsConfig.HttpEndpoint? {
return getMapping(connConfigNode, "rpc")?.let { node -> return getMapping(connConfigNode, "rpc")?.let { node ->
val maxConnections = getValueAsInt(node, "max-connections") ?: DEFAULT_MAX_CONNECTIONS val maxConnections = getValueAsInt(node, "max-connections") ?: DEFAULT_MAX_CONNECTIONS
@@ -195,7 +181,6 @@ class UpstreamsConfigReader(
getValueAsString(node, "url")?.let { url -> getValueAsString(node, "url")?.let { url ->
val http = UpstreamsConfig.HttpEndpoint(URI(url), maxConnections, queueSize) val http = UpstreamsConfig.HttpEndpoint(URI(url), maxConnections, queueSize)
http.basicAuth = authConfigReader.readClientBasicAuth(node) http.basicAuth = authConfigReader.readClientBasicAuth(node)
http.bearerAuth = readBearerAuth(node, http.basicAuth, url)
http.tls = authConfigReader.readClientTls(node) http.tls = authConfigReader.readClientTls(node)
http http
} }
@@ -239,7 +224,6 @@ class UpstreamsConfigReader(
ws.origin = URI(origin) ws.origin = URI(origin)
} }
ws.basicAuth = authConfigReader.readClientBasicAuth(node) ws.basicAuth = authConfigReader.readClientBasicAuth(node)
ws.bearerAuth = readBearerAuth(node, ws.basicAuth, url)
getValueAsBytes(node, "frameSize")?.let { getValueAsBytes(node, "frameSize")?.let {
if (it < 65_535) { if (it < 65_535) {

View File

@@ -52,7 +52,7 @@ class BitcoinUpstreamCreator(
fileResolver.resolve(ca).readBytes() fileResolver.resolve(ca).readBytes()
} }
} }
EsploraClient(endpoint.url, endpoint.basicAuth, tls, endpoint.bearerAuth) EsploraClient(endpoint.url, endpoint.basicAuth, tls)
} }
val extractBlock = ExtractBlock() val extractBlock = ExtractBlock()

View File

@@ -84,7 +84,6 @@ open class GenericConnectorFactoryCreator(
monitoringCfg.nettyMetricsConfig.enabled, monitoringCfg.nettyMetricsConfig.enabled,
httpScheduler, httpScheduler,
customHeaders, customHeaders,
conn.bearerAuth,
) )
} }
} }
@@ -107,7 +106,6 @@ open class GenericConnectorFactoryCreator(
).apply { ).apply {
config = endpoint config = endpoint
basicAuth = endpoint.basicAuth basicAuth = endpoint.basicAuth
bearerAuth = endpoint.bearerAuth
this.customHeaders = customHeaders this.customHeaders = customHeaders
} }
val wsApi = WsConnectionPoolFactory( val wsApi = WsConnectionPoolFactory(

View File

@@ -21,7 +21,6 @@ class BasicHttpFactory(
private val nettyMetricsEnabled: Boolean, private val nettyMetricsEnabled: Boolean,
private val httpScheduler: Scheduler, private val httpScheduler: Scheduler,
private val customHeaders: Map<String, String> = emptyMap(), private val customHeaders: Map<String, String> = emptyMap(),
private val bearerAuth: AuthConfig.ClientBearerAuth? = null,
) : HttpFactory { ) : HttpFactory {
private val log = LoggerFactory.getLogger(this::class.java) private val log = LoggerFactory.getLogger(this::class.java)
@@ -51,8 +50,8 @@ class BasicHttpFactory(
) )
if (chain.type.apiType == ApiType.REST) { if (chain.type.apiType == ApiType.REST) {
return RestHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, chain, basicAuth, tls, customHeaders, bearerAuth) return RestHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, chain, basicAuth, tls, customHeaders)
} }
return JsonRpcHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, basicAuth, tls, customHeaders, bearerAuth) return JsonRpcHttpReader(url, maxConnections, queueSize, metrics, httpScheduler, basicAuth, tls, customHeaders)
} }
} }

View File

@@ -28,7 +28,6 @@ abstract class HttpReader(
basicAuth: AuthConfig.ClientBasicAuth? = null, basicAuth: AuthConfig.ClientBasicAuth? = null,
tlsCAAuth: ByteArray? = null, tlsCAAuth: ByteArray? = null,
customHeaders: Map<String, String> = emptyMap(), customHeaders: Map<String, String> = emptyMap(),
bearerAuth: AuthConfig.ClientBearerAuth? = null,
) : ChainReader { ) : ChainReader {
constructor() : this("", 1500, 1000, null) constructor() : this("", 1500, 1000, null)
@@ -67,13 +66,6 @@ abstract class HttpReader(
build = build.headers(headers) build = build.headers(headers)
} }
if (basicAuth == null) {
bearerAuth?.let { auth ->
val headers = Consumer { h: HttpHeaders -> h.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}") }
build = build.headers(headers)
}
}
if (customHeaders.isNotEmpty()) { if (customHeaders.isNotEmpty()) {
val headers = Consumer { h: HttpHeaders -> val headers = Consumer { h: HttpHeaders ->
customHeaders.forEach { (key, value) -> customHeaders.forEach { (key, value) ->

View File

@@ -34,11 +34,10 @@ import java.security.cert.X509Certificate
import java.util.Base64 import java.util.Base64
import java.util.function.Consumer import java.util.function.Consumer
class EsploraClient @JvmOverloads constructor( class EsploraClient(
private val url: URI, private val url: URI,
basicAuth: AuthConfig.ClientBasicAuth? = null, basicAuth: AuthConfig.ClientBasicAuth? = null,
tlsCAAuth: ByteArray? = null, tlsCAAuth: ByteArray? = null,
bearerAuth: AuthConfig.ClientBearerAuth? = null,
) { ) {
companion object { companion object {
@@ -63,13 +62,6 @@ class EsploraClient @JvmOverloads constructor(
build = build.headers(headers) build = build.headers(headers)
} }
if (basicAuth == null) {
bearerAuth?.let { auth ->
val headers = Consumer { h: HttpHeaders -> h.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}") }
build = build.headers(headers)
}
}
tlsCAAuth?.let { auth -> tlsCAAuth?.let { auth ->
val cf = CertificateFactory.getInstance("X.509") val cf = CertificateFactory.getInstance("X.509")
val cert = cf.generateCertificate(ByteArrayInputStream(auth)) as X509Certificate val cert = cf.generateCertificate(ByteArrayInputStream(auth)) as X509Certificate

View File

@@ -21,7 +21,6 @@ open class WsConnectionFactory(
) { ) {
var basicAuth: AuthConfig.ClientBasicAuth? = null var basicAuth: AuthConfig.ClientBasicAuth? = null
var bearerAuth: AuthConfig.ClientBearerAuth? = null
var config: UpstreamsConfig.WsEndpoint? = null var config: UpstreamsConfig.WsEndpoint? = null
var customHeaders: Map<String, String> = emptyMap() var customHeaders: Map<String, String> = emptyMap()
@@ -48,7 +47,7 @@ open class WsConnectionFactory(
} }
open fun createWsConnection(connIndex: Int = 0): WsConnection = open fun createWsConnection(connIndex: Int = 0): WsConnection =
WsConnectionImpl(uri, origin, basicAuth, metrics(connIndex), scheduler, eventsScheduler, customHeaders, bearerAuth).also { ws -> WsConnectionImpl(uri, origin, basicAuth, metrics(connIndex), scheduler, eventsScheduler, customHeaders).also { ws ->
config?.frameSize?.let { config?.frameSize?.let {
ws.frameSize = it ws.frameSize = it
} }

View File

@@ -67,7 +67,6 @@ open class WsConnectionImpl(
private val scheduler: Scheduler, private val scheduler: Scheduler,
private val eventsScheduler: Scheduler, private val eventsScheduler: Scheduler,
private val customHeaders: Map<String, String> = emptyMap(), private val customHeaders: Map<String, String> = emptyMap(),
private val bearerAuth: AuthConfig.ClientBearerAuth? = null,
) : AutoCloseable, WsConnection, Cloneable { ) : AutoCloseable, WsConnection, Cloneable {
companion object { companion object {
@@ -228,11 +227,6 @@ open class WsConnectionImpl(
val base64password = Base64.getEncoder().encodeToString(tmp.toByteArray()) val base64password = Base64.getEncoder().encodeToString(tmp.toByteArray())
headers.add(HttpHeaderNames.AUTHORIZATION, "Basic $base64password") headers.add(HttpHeaderNames.AUTHORIZATION, "Basic $base64password")
} }
if (basicAuth == null) {
bearerAuth?.let { auth ->
headers.add(HttpHeaderNames.AUTHORIZATION, "Bearer ${auth.token}")
}
}
customHeaders.forEach { (key, value) -> customHeaders.forEach { (key, value) ->
headers.add(key, value) headers.add(key, value)
} }

View File

@@ -36,8 +36,7 @@ class RestHttpReader(
basicAuth: AuthConfig.ClientBasicAuth? = null, basicAuth: AuthConfig.ClientBasicAuth? = null,
tlsCAAuth: ByteArray? = null, tlsCAAuth: ByteArray? = null,
customHeaders: Map<String, String> = emptyMap(), customHeaders: Map<String, String> = emptyMap(),
bearerAuth: AuthConfig.ClientBearerAuth? = null, ) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders) {
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders, bearerAuth) {
private val parser = ResponseRpcParser() private val parser = ResponseRpcParser()
private val requestParser = RestRequestParser private val requestParser = RestRequestParser

View File

@@ -36,7 +36,7 @@ import java.util.function.Function
/** /**
* JSON RPC client * JSON RPC client
*/ */
class JsonRpcHttpReader @JvmOverloads constructor( class JsonRpcHttpReader(
target: String, target: String,
maxConnections: Int, maxConnections: Int,
queueSize: Int, queueSize: Int,
@@ -45,8 +45,7 @@ class JsonRpcHttpReader @JvmOverloads constructor(
basicAuth: AuthConfig.ClientBasicAuth? = null, basicAuth: AuthConfig.ClientBasicAuth? = null,
tlsCAAuth: ByteArray? = null, tlsCAAuth: ByteArray? = null,
customHeaders: Map<String, String> = emptyMap(), customHeaders: Map<String, String> = emptyMap(),
bearerAuth: AuthConfig.ClientBearerAuth? = null, ) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders) {
) : HttpReader(target, maxConnections, queueSize, metrics, basicAuth, tlsCAAuth, customHeaders, bearerAuth) {
private val parser = ResponseRpcParser() private val parser = ResponseRpcParser()
private val streamParser = JsonRpcStreamParser() private val streamParser = JsonRpcStreamParser()

View File

@@ -35,29 +35,6 @@ class AuthConfigReaderSpec extends Specification {
act.password == "258fe4149c199ad8f2811a68f20154fc" act.password == "258fe4149c199ad8f2811a68f20154fc"
} }
def "Read bearer-auth for client"() {
setup:
def yaml =
"bearer-auth:\n" +
" token: 9c199ad8f281f20154fc258fe41a6814"
when:
def act = reader.readClientBearerAuth(reader.readNode(yaml))
then:
act != null
act.token == "9c199ad8f281f20154fc258fe41a6814"
}
def "Read bearer-auth without token as null"() {
setup:
def yaml =
"bearer-auth:\n" +
" something: else"
when:
def act = reader.readClientBearerAuth(reader.readNode(yaml))
then:
act == null
}
def "Read tls for client"() { def "Read tls for client"() {
setup: setup:
def yaml = def yaml =

View File

@@ -75,38 +75,6 @@ class UpstreamsConfigReaderSpec extends Specification {
} }
} }
def "Parse config with bearer auth"() {
setup:
def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-bearer-auth.yaml")
when:
def act = reader.readInternal(config)
then:
act != null
act.upstreams.size() == 2
with(act.upstreams.get(0)) {
id == "local"
connection instanceof UpstreamsConfig.RpcConnection
with((UpstreamsConfig.RpcConnection) connection) {
rpc.basicAuth == null
rpc.bearerAuth != null
rpc.bearerAuth.token == "9c199ad8f281f20154fc258fe41a6814"
ws != null
ws.basicAuth == null
ws.bearerAuth != null
ws.bearerAuth.token == "258fe4149c199ad8f2811a68f20154fc"
}
}
with(act.upstreams.get(1)) {
id == "both-auth"
connection instanceof UpstreamsConfig.RpcConnection
with((UpstreamsConfig.RpcConnection) connection) {
// when both are configured basic-auth wins and bearer-auth is dropped
rpc.basicAuth != null
rpc.bearerAuth == null
}
}
}
def "Parse websocket-only config"() { def "Parse websocket-only config"() {
setup: setup:
def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-ws-only.yaml") def config = this.class.getClassLoader().getResourceAsStream("configs/upstreams-ws-only.yaml")

View File

@@ -16,7 +16,6 @@
package io.emeraldpay.dshackle.upstream.rpcclient package io.emeraldpay.dshackle.upstream.rpcclient
import io.emeraldpay.dshackle.config.AuthConfig
import io.emeraldpay.dshackle.test.TestingCommons import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.ChainException import io.emeraldpay.dshackle.upstream.ChainException
import io.emeraldpay.dshackle.upstream.ChainRequest import io.emeraldpay.dshackle.upstream.ChainRequest
@@ -70,28 +69,6 @@ class JsonRpcHttpReaderSpec extends Specification {
new String(act.result) == '"0x98de45"' new String(act.result) == '"0x98de45"'
} }
def "Make a request with bearer auth"() {
setup:
def bearerAuth = new AuthConfig.ClientBearerAuth("test-token-123")
JsonRpcHttpReader client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:], bearerAuth)
def resp = '{' +
' "jsonrpc": "2.0",' +
' "result": "0x98de45",' +
' "error": null,' +
' "id": 15' +
'}'
mockServer.when(
HttpRequest.request().withHeader("authorization", "Bearer test-token-123")
).respond(
HttpResponse.response(resp)
)
when:
def act = client.read(new ChainRequest("test", new ListParams())).block(Duration.ofSeconds(5))
then:
act.error == null
new String(act.result) == '"0x98de45"'
}
def "Produces RPC Exception on error status code"() { def "Produces RPC Exception on error status code"() {
setup: setup:
def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:]) def client = new JsonRpcHttpReader("localhost:${mockServer.port}", 50, 50, metrics, Schedulers.boundedElastic(), null, null, [:])

View File

@@ -1,27 +0,0 @@
version: v1
upstreams:
- id: local
chain: ethereum
connection:
ethereum:
rpc:
url: "http://localhost:8545"
bearer-auth:
token: 9c199ad8f281f20154fc258fe41a6814
ws:
url: "ws://localhost:8546"
origin: "http://localhost"
bearer-auth:
token: 258fe4149c199ad8f2811a68f20154fc
- id: both-auth
chain: ethereum
connection:
ethereum:
rpc:
url: "http://localhost:8545"
basic-auth:
username: 4fc258fe41a68149c199ad8f281f2015
password: 1a68f20154fc258fe4149c199ad8f281
bearer-auth:
token: 9c199ad8f281f20154fc258fe41a6814