Add height matcher for methods by block
This commit is contained in:
@@ -83,7 +83,10 @@ open class NativeCall(
|
||||
casting[BlockchainType.from(event.chain)]?.let { cast ->
|
||||
multistreamHolder.getUpstream(event.chain).let { up ->
|
||||
val reader = up.cast(cast).getReader()
|
||||
ethereumCallSelectors.putIfAbsent(event.chain, EthereumCallSelector(reader.heightByHash()))
|
||||
ethereumCallSelectors.putIfAbsent(
|
||||
event.chain,
|
||||
EthereumCallSelector(reader.heightByHash(), up.caches)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,11 @@ class DefaultEthereumMethods(
|
||||
"eth_getBlockTransactionCountByHash",
|
||||
"eth_getUncleCountByBlockHash",
|
||||
"eth_getBlockByHash",
|
||||
"eth_getBlockByNumber",
|
||||
"eth_getTransactionByHash",
|
||||
"eth_getTransactionByBlockHashAndIndex",
|
||||
"eth_getTransactionByBlockNumberAndIndex",
|
||||
"eth_getTransactionReceipt",
|
||||
"eth_getStorageAt",
|
||||
"eth_getCode",
|
||||
"eth_getUncleByBlockHashAndIndex",
|
||||
@@ -89,9 +92,6 @@ class DefaultEthereumMethods(
|
||||
private val headVerifiedMethods = listOf(
|
||||
"eth_getBlockTransactionCountByNumber",
|
||||
"eth_getUncleCountByBlockNumber",
|
||||
"eth_getBlockByNumber",
|
||||
"eth_getTransactionByBlockNumberAndIndex",
|
||||
"eth_getTransactionReceipt",
|
||||
"eth_getUncleByBlockNumberAndIndex",
|
||||
"eth_feeHistory"
|
||||
)
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
package io.emeraldpay.dshackle.upstream.calls
|
||||
|
||||
import io.emeraldpay.dshackle.Global
|
||||
import io.emeraldpay.dshackle.cache.Caches
|
||||
import io.emeraldpay.dshackle.data.BlockId
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.upstream.Head
|
||||
import io.emeraldpay.dshackle.upstream.Selector
|
||||
import io.emeraldpay.etherjar.hex.HexQuantity
|
||||
import org.bouncycastle.util.encoders.DecoderException
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
import java.util.Collections
|
||||
@@ -31,7 +33,8 @@ import java.util.Objects
|
||||
* The implementation is specific for Ethereum.
|
||||
*/
|
||||
class EthereumCallSelector(
|
||||
private val heightReader: Reader<BlockId, Long>
|
||||
private val heightReader: Reader<BlockId, Long>,
|
||||
private val caches: Caches
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -45,6 +48,11 @@ class EthereumCallSelector(
|
||||
// no "eth_getStorageAt" because it has different structure, and therefore separate logic
|
||||
"eth_call"
|
||||
).sorted()
|
||||
|
||||
private val GET_BY_HASH_OR_NUMBER_METHODS = setOf(
|
||||
"eth_getBlockByHash", "eth_getBlockByNumber",
|
||||
"eth_getTransactionByBlockHashAndIndex", "eth_getTransactionByBlockNumberAndIndex"
|
||||
)
|
||||
}
|
||||
|
||||
private val objectMapper = Global.objectMapper
|
||||
@@ -60,6 +68,8 @@ class EthereumCallSelector(
|
||||
return blockTagSelector(params, 2, head)
|
||||
} else if (method in DefaultEthereumMethods.withFilterIdMethods) {
|
||||
return sameUpstreamMatcher(params)
|
||||
} else if (method in GET_BY_HASH_OR_NUMBER_METHODS) {
|
||||
return blockMethodSelector(method, params)
|
||||
}
|
||||
return Mono.empty()
|
||||
}
|
||||
@@ -119,6 +129,32 @@ class EthereumCallSelector(
|
||||
}
|
||||
}
|
||||
|
||||
private fun blockMethodSelector(method: String, params: String): Mono<Selector.Matcher> {
|
||||
val list = objectMapper.readerFor(Any::class.java).readValues<Any>(params).readAll()
|
||||
if (list.isEmpty()) {
|
||||
return Mono.empty()
|
||||
}
|
||||
val hashOrNumber = Objects.toString(list[0])
|
||||
|
||||
return when (method) {
|
||||
"eth_getTransactionByBlockHashAndIndex", "eth_getBlockByHash" -> blockByHashFromCache(hashOrNumber)
|
||||
"eth_getTransactionByBlockNumberAndIndex", "eth_getBlockByNumber" -> blockByHeight(hashOrNumber)
|
||||
else -> Mono.empty()
|
||||
}
|
||||
}
|
||||
|
||||
private fun blockByHashFromCache(blockHash: String): Mono<Selector.Matcher> {
|
||||
return try {
|
||||
caches.getBlocksByHash()
|
||||
.read(BlockId.from(blockHash))
|
||||
.onErrorResume { Mono.empty() }
|
||||
.map { Selector.HeightMatcher(it.height) }
|
||||
} catch (e: DecoderException) {
|
||||
log.warn("Invalid blockHash: $blockHash")
|
||||
Mono.empty()
|
||||
}
|
||||
}
|
||||
|
||||
private fun blockByHeight(blockNumber: String): Mono<Selector.Matcher> {
|
||||
return try {
|
||||
Mono.just(Selector.HeightMatcher(HexQuantity.from(blockNumber).value.longValueExact()))
|
||||
|
||||
@@ -66,8 +66,9 @@ class EthereumDirectReader(
|
||||
}
|
||||
blockByHeightReader = object : Reader<Long, BlockContainer> {
|
||||
override fun read(key: Long): Mono<BlockContainer> {
|
||||
val heightMatcher = Selector.HeightMatcher(key)
|
||||
val request = JsonRpcRequest("eth_getBlockByNumber", listOf(HexQuantity.from(key).toHex(), false))
|
||||
return readBlock(request, key.toString())
|
||||
return readBlock(request, key.toString(), heightMatcher)
|
||||
}
|
||||
}
|
||||
txReader = object : Reader<TransactionId, TxContainer> {
|
||||
@@ -143,8 +144,12 @@ class EthereumDirectReader(
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun readBlock(request: JsonRpcRequest, id: String): Mono<BlockContainer> {
|
||||
return readWithQuorum(request)
|
||||
private fun readBlock(
|
||||
request: JsonRpcRequest,
|
||||
id: String,
|
||||
matcher: Selector.Matcher = Selector.empty
|
||||
): Mono<BlockContainer> {
|
||||
return readWithQuorum(request, matcher)
|
||||
.timeout(Defaults.timeoutInternal, Mono.error(TimeoutException("Block not read $id")))
|
||||
.retryWhen(Retry.fixedDelay(3, Duration.ofMillis(200)))
|
||||
.flatMap { blockbytes ->
|
||||
@@ -163,11 +168,11 @@ class EthereumDirectReader(
|
||||
/**
|
||||
* Read from an Upstream applying a Quorum specific for that request
|
||||
*/
|
||||
private fun readWithQuorum(request: JsonRpcRequest): Mono<ByteArray> {
|
||||
private fun readWithQuorum(request: JsonRpcRequest, matcher: Selector.Matcher = Selector.empty): Mono<ByteArray> {
|
||||
return Mono.just(quorumReaderFactory)
|
||||
.map {
|
||||
it.create(
|
||||
up.getApiSource(Selector.empty),
|
||||
up.getApiSource(matcher),
|
||||
callMethodsFactory.create().createQuorumFor(request.method),
|
||||
// we do not use Signer for internal requests because it doesn't make much sense
|
||||
null
|
||||
|
||||
Reference in New Issue
Block a user