eth_filter fix review comments and tests
This commit is contained in:
@@ -69,6 +69,7 @@ open class UpstreamsConfig {
|
||||
|
||||
class Upstream<T : UpstreamConnection> {
|
||||
var id: String? = null
|
||||
var nodeId: Int? = null
|
||||
var chain: String? = null
|
||||
var options: Options? = null
|
||||
var isEnabled = true
|
||||
|
||||
@@ -31,6 +31,7 @@ class UpstreamsConfigReader(
|
||||
|
||||
private val log = LoggerFactory.getLogger(UpstreamsConfigReader::class.java)
|
||||
private val authConfigReader = AuthConfigReader()
|
||||
private val knownNodeIds: MutableSet<Int> = HashSet()
|
||||
|
||||
fun read(input: InputStream): UpstreamsConfig? {
|
||||
val configNode = readNode(input)
|
||||
@@ -236,11 +237,22 @@ class UpstreamsConfigReader(
|
||||
log.warn("Invalid id: $id")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return upstream.nodeId?.let {
|
||||
if (it !in 1..255) {
|
||||
log.warn("Invalid node-id: $it. Must be in range [1, 255].")
|
||||
false
|
||||
} else if (!knownNodeIds.add(it)) {
|
||||
log.warn("Duplicated node-id: $it. Must be in unique.")
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
} ?: true
|
||||
}
|
||||
|
||||
internal fun readUpstreamCommon(upNode: MappingNode, upstream: UpstreamsConfig.Upstream<*>) {
|
||||
upstream.id = getValueAsString(upNode, "id")
|
||||
upstream.nodeId = getValueAsInt(upNode, "node-id")
|
||||
upstream.options = tryReadOptions(upNode)
|
||||
upstream.methods = tryReadMethods(upNode)
|
||||
getValueAsBool(upNode, "enabled")?.let {
|
||||
|
||||
@@ -119,7 +119,7 @@ class QuorumRpcReader(
|
||||
.filter { it.isResolved() } // return nothing if not resolved
|
||||
.map { quorum ->
|
||||
// TODO find actual quorum number
|
||||
Result(quorum.getResult()!!, quorum.getSignature(), 1, quorum.getResolvedBy().map { it.hash() })
|
||||
Result(quorum.getResult()!!, quorum.getSignature(), 1, quorum.getResolvedBy().map { it.nodeId() })
|
||||
}
|
||||
.switchIfEmpty(defaultResult)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.startup
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import io.emeraldpay.dshackle.FileResolver
|
||||
import io.emeraldpay.dshackle.Global
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
@@ -81,7 +82,7 @@ open class ConfiguredUpstreams(
|
||||
log.debug("Start upstream ${up.id}")
|
||||
if (up.connection is UpstreamsConfig.GrpcConnection) {
|
||||
val options = up.options ?: UpstreamsConfig.Options()
|
||||
buildGrpcUpstream(up.cast(UpstreamsConfig.GrpcConnection::class.java), options)
|
||||
buildGrpcUpstream(up.nodeId, up.cast(UpstreamsConfig.GrpcConnection::class.java), options)
|
||||
} else {
|
||||
val chain = Global.chainById(up.chain)
|
||||
if (chain == Chain.UNSPECIFIED) {
|
||||
@@ -92,7 +93,7 @@ open class ConfiguredUpstreams(
|
||||
.merge(defaultOptions[chain] ?: UpstreamsConfig.Options.getDefaults())
|
||||
val upstream = when (BlockchainType.from(chain)) {
|
||||
BlockchainType.ETHEREUM -> {
|
||||
buildEthereumUpstream(up.cast(UpstreamsConfig.EthereumConnection::class.java), chain, options)
|
||||
buildEthereumUpstream(up.nodeId, up.cast(UpstreamsConfig.EthereumConnection::class.java), chain, options)
|
||||
}
|
||||
|
||||
BlockchainType.BITCOIN -> {
|
||||
@@ -101,6 +102,7 @@ open class ConfiguredUpstreams(
|
||||
|
||||
BlockchainType.ETHEREUM_POS -> {
|
||||
buildEthereumPosUpstream(
|
||||
up.nodeId,
|
||||
up.cast(UpstreamsConfig.EthereumPosConnection::class.java),
|
||||
chain,
|
||||
options
|
||||
@@ -167,6 +169,7 @@ open class ConfiguredUpstreams(
|
||||
}
|
||||
|
||||
private fun buildEthereumPosUpstream(
|
||||
nodeId: Int?,
|
||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumPosConnection>,
|
||||
chain: Chain,
|
||||
options: UpstreamsConfig.Options
|
||||
@@ -194,7 +197,7 @@ open class ConfiguredUpstreams(
|
||||
val hashUrl = conn.execution!!.let {
|
||||
if (it.preferHttp) it.rpc?.url ?: it.ws?.url else it.ws?.url ?: it.rpc?.url
|
||||
}
|
||||
val hash = getHash(hashUrl!!)
|
||||
val hash = getHash(nodeId, hashUrl!!)
|
||||
val upstream = EthereumPosRpcUpstream(
|
||||
config.id!!,
|
||||
hash,
|
||||
@@ -251,6 +254,7 @@ open class ConfiguredUpstreams(
|
||||
}
|
||||
|
||||
private fun buildEthereumUpstream(
|
||||
nodeId: Int?,
|
||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.EthereumConnection>,
|
||||
chain: Chain,
|
||||
options: UpstreamsConfig.Options
|
||||
@@ -275,7 +279,7 @@ open class ConfiguredUpstreams(
|
||||
val hashUrl = if (conn.preferHttp) conn.rpc?.url ?: conn.ws?.url else conn.ws?.url ?: conn.rpc?.url
|
||||
val upstream = EthereumRpcUpstream(
|
||||
config.id!!,
|
||||
getHash(hashUrl!!),
|
||||
getHash(nodeId, hashUrl!!),
|
||||
chain,
|
||||
options, config.role,
|
||||
methods,
|
||||
@@ -287,11 +291,12 @@ open class ConfiguredUpstreams(
|
||||
}
|
||||
|
||||
private fun buildGrpcUpstream(
|
||||
nodeId: Int?,
|
||||
config: UpstreamsConfig.Upstream<UpstreamsConfig.GrpcConnection>,
|
||||
options: UpstreamsConfig.Options
|
||||
) {
|
||||
val endpoint = config.connection!!
|
||||
val hash = getHash("${endpoint.host}:${endpoint.port}")
|
||||
val hash = getHash(nodeId, "${endpoint.host}:${endpoint.port}")
|
||||
val ds = GrpcUpstreams(
|
||||
config.id!!,
|
||||
hash,
|
||||
@@ -366,23 +371,25 @@ open class ConfiguredUpstreams(
|
||||
return connectorFactory
|
||||
}
|
||||
|
||||
private fun getHash(obj: Any): Byte {
|
||||
val hashCode = (obj.hashCode() % 255)
|
||||
val modifiers: List<Function<Int, Number>> = listOf(
|
||||
Function { i -> i },
|
||||
Function { i -> (-i) },
|
||||
Function { i -> 127 - abs(i) },
|
||||
Function { i -> abs(i) - 128 },
|
||||
)
|
||||
return modifiers.map {
|
||||
it.apply(hashCode).toByte()
|
||||
}.firstOrNull {
|
||||
hashes[it] != true
|
||||
}?.let {
|
||||
hashes[it] = true
|
||||
it
|
||||
} ?: (Byte.MIN_VALUE..Byte.MAX_VALUE).first {
|
||||
hashes[it.toByte()] != true
|
||||
}.toByte()
|
||||
}
|
||||
@VisibleForTesting
|
||||
private fun getHash(nodeId: Int?, obj: Any): Byte =
|
||||
nodeId?.toByte() ?: (obj.hashCode() % 255).let {
|
||||
if (it == 0) 1 else it
|
||||
}.let { nonZeroHash ->
|
||||
listOf<Function<Int, Int>>(
|
||||
Function { i -> i },
|
||||
Function { i -> (-i) },
|
||||
Function { i -> 127 - abs(i) },
|
||||
Function { i -> abs(i) - 128 },
|
||||
).map {
|
||||
it.apply(nonZeroHash).toByte()
|
||||
}.firstOrNull {
|
||||
hashes[it] != true
|
||||
}?.let {
|
||||
hashes[it] = true
|
||||
it
|
||||
} ?: (Byte.MIN_VALUE..Byte.MAX_VALUE).first {
|
||||
it != 0 && hashes[it.toByte()] != true
|
||||
}.toByte()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ abstract class DefaultUpstream(
|
||||
return targets ?: throw IllegalStateException("Methods are not set")
|
||||
}
|
||||
|
||||
override fun hash(): Byte = hash
|
||||
override fun nodeId(): Byte = hash
|
||||
|
||||
private val quorumByLabel = node?.let { QuorumForLabels(it) }
|
||||
?: QuorumForLabels(QuorumForLabels.QuorumItem.empty())
|
||||
|
||||
@@ -282,7 +282,7 @@ abstract class Multistream(
|
||||
return false
|
||||
}
|
||||
|
||||
override fun hash(): Byte = 0
|
||||
override fun nodeId(): Byte = 0
|
||||
|
||||
fun printStatus() {
|
||||
var height: Long? = null
|
||||
|
||||
@@ -397,15 +397,22 @@ class Selector {
|
||||
}
|
||||
}
|
||||
|
||||
class SameUpstreamMatcher(private val upstreamHash: Byte) : Matcher {
|
||||
class SameNodeMatcher(private val upstreamHash: Byte) : Matcher {
|
||||
override fun matches(up: Upstream): Boolean =
|
||||
up.hash() == upstreamHash
|
||||
up.nodeId() == upstreamHash
|
||||
|
||||
override fun describeInternal(): String =
|
||||
"upstream hash=$upstreamHash"
|
||||
"upstream node-id=${upstreamHash.toUByte()}"
|
||||
|
||||
override fun toString(): String {
|
||||
return "Matcher: ${describeInternal()}"
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other === this) return true
|
||||
if (other !is SameNodeMatcher) return false
|
||||
return other.upstreamHash == upstreamHash
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,5 +41,5 @@ interface Upstream {
|
||||
|
||||
fun <T : Upstream> cast(selfType: Class<T>): T
|
||||
|
||||
fun hash(): Byte
|
||||
fun nodeId(): Byte
|
||||
}
|
||||
|
||||
@@ -69,14 +69,12 @@ class EthereumCallSelector(
|
||||
return Mono.empty()
|
||||
}
|
||||
val filterId = list[0].toString()
|
||||
val hashHex = filterId.substring(filterId.length - 2)
|
||||
val hash = hashHex.toInt(16)
|
||||
|
||||
if (hash < 0 || hash > 255) {
|
||||
return Mono.empty()
|
||||
if(filterId.length < 4) {
|
||||
return Mono.just(Selector.SameNodeMatcher(0.toByte()))
|
||||
}
|
||||
|
||||
return Mono.just(Selector.SameUpstreamMatcher(hash.toByte()))
|
||||
val hashHex = filterId.substring(filterId.length - 2)
|
||||
val nodeId = hashHex.toInt(16)
|
||||
return Mono.just(Selector.SameNodeMatcher(nodeId.toByte()))
|
||||
}
|
||||
|
||||
private fun blockTagSelector(params: String, pos: Int, head: Head): Mono<Selector.Matcher> {
|
||||
|
||||
Reference in New Issue
Block a user