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 ->
|
casting[BlockchainType.from(event.chain)]?.let { cast ->
|
||||||
multistreamHolder.getUpstream(event.chain).let { up ->
|
multistreamHolder.getUpstream(event.chain).let { up ->
|
||||||
val reader = up.cast(cast).getReader()
|
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_getBlockTransactionCountByHash",
|
||||||
"eth_getUncleCountByBlockHash",
|
"eth_getUncleCountByBlockHash",
|
||||||
"eth_getBlockByHash",
|
"eth_getBlockByHash",
|
||||||
|
"eth_getBlockByNumber",
|
||||||
"eth_getTransactionByHash",
|
"eth_getTransactionByHash",
|
||||||
"eth_getTransactionByBlockHashAndIndex",
|
"eth_getTransactionByBlockHashAndIndex",
|
||||||
|
"eth_getTransactionByBlockNumberAndIndex",
|
||||||
|
"eth_getTransactionReceipt",
|
||||||
"eth_getStorageAt",
|
"eth_getStorageAt",
|
||||||
"eth_getCode",
|
"eth_getCode",
|
||||||
"eth_getUncleByBlockHashAndIndex",
|
"eth_getUncleByBlockHashAndIndex",
|
||||||
@@ -89,9 +92,6 @@ class DefaultEthereumMethods(
|
|||||||
private val headVerifiedMethods = listOf(
|
private val headVerifiedMethods = listOf(
|
||||||
"eth_getBlockTransactionCountByNumber",
|
"eth_getBlockTransactionCountByNumber",
|
||||||
"eth_getUncleCountByBlockNumber",
|
"eth_getUncleCountByBlockNumber",
|
||||||
"eth_getBlockByNumber",
|
|
||||||
"eth_getTransactionByBlockNumberAndIndex",
|
|
||||||
"eth_getTransactionReceipt",
|
|
||||||
"eth_getUncleByBlockNumberAndIndex",
|
"eth_getUncleByBlockNumberAndIndex",
|
||||||
"eth_feeHistory"
|
"eth_feeHistory"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,11 +16,13 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.calls
|
package io.emeraldpay.dshackle.upstream.calls
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
import io.emeraldpay.etherjar.hex.HexQuantity
|
import io.emeraldpay.etherjar.hex.HexQuantity
|
||||||
|
import org.bouncycastle.util.encoders.DecoderException
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
@@ -31,7 +33,8 @@ import java.util.Objects
|
|||||||
* The implementation is specific for Ethereum.
|
* The implementation is specific for Ethereum.
|
||||||
*/
|
*/
|
||||||
class EthereumCallSelector(
|
class EthereumCallSelector(
|
||||||
private val heightReader: Reader<BlockId, Long>
|
private val heightReader: Reader<BlockId, Long>,
|
||||||
|
private val caches: Caches
|
||||||
) {
|
) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -45,6 +48,11 @@ class EthereumCallSelector(
|
|||||||
// no "eth_getStorageAt" because it has different structure, and therefore separate logic
|
// no "eth_getStorageAt" because it has different structure, and therefore separate logic
|
||||||
"eth_call"
|
"eth_call"
|
||||||
).sorted()
|
).sorted()
|
||||||
|
|
||||||
|
private val GET_BY_HASH_OR_NUMBER_METHODS = setOf(
|
||||||
|
"eth_getBlockByHash", "eth_getBlockByNumber",
|
||||||
|
"eth_getTransactionByBlockHashAndIndex", "eth_getTransactionByBlockNumberAndIndex"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val objectMapper = Global.objectMapper
|
private val objectMapper = Global.objectMapper
|
||||||
@@ -60,6 +68,8 @@ class EthereumCallSelector(
|
|||||||
return blockTagSelector(params, 2, head)
|
return blockTagSelector(params, 2, head)
|
||||||
} else if (method in DefaultEthereumMethods.withFilterIdMethods) {
|
} else if (method in DefaultEthereumMethods.withFilterIdMethods) {
|
||||||
return sameUpstreamMatcher(params)
|
return sameUpstreamMatcher(params)
|
||||||
|
} else if (method in GET_BY_HASH_OR_NUMBER_METHODS) {
|
||||||
|
return blockMethodSelector(method, params)
|
||||||
}
|
}
|
||||||
return Mono.empty()
|
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> {
|
private fun blockByHeight(blockNumber: String): Mono<Selector.Matcher> {
|
||||||
return try {
|
return try {
|
||||||
Mono.just(Selector.HeightMatcher(HexQuantity.from(blockNumber).value.longValueExact()))
|
Mono.just(Selector.HeightMatcher(HexQuantity.from(blockNumber).value.longValueExact()))
|
||||||
|
|||||||
@@ -66,8 +66,9 @@ class EthereumDirectReader(
|
|||||||
}
|
}
|
||||||
blockByHeightReader = object : Reader<Long, BlockContainer> {
|
blockByHeightReader = object : Reader<Long, BlockContainer> {
|
||||||
override fun read(key: Long): Mono<BlockContainer> {
|
override fun read(key: Long): Mono<BlockContainer> {
|
||||||
|
val heightMatcher = Selector.HeightMatcher(key)
|
||||||
val request = JsonRpcRequest("eth_getBlockByNumber", listOf(HexQuantity.from(key).toHex(), false))
|
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> {
|
txReader = object : Reader<TransactionId, TxContainer> {
|
||||||
@@ -143,8 +144,12 @@ class EthereumDirectReader(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
private fun readBlock(request: JsonRpcRequest, id: String): Mono<BlockContainer> {
|
private fun readBlock(
|
||||||
return readWithQuorum(request)
|
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")))
|
.timeout(Defaults.timeoutInternal, Mono.error(TimeoutException("Block not read $id")))
|
||||||
.retryWhen(Retry.fixedDelay(3, Duration.ofMillis(200)))
|
.retryWhen(Retry.fixedDelay(3, Duration.ofMillis(200)))
|
||||||
.flatMap { blockbytes ->
|
.flatMap { blockbytes ->
|
||||||
@@ -163,11 +168,11 @@ class EthereumDirectReader(
|
|||||||
/**
|
/**
|
||||||
* Read from an Upstream applying a Quorum specific for that request
|
* 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)
|
return Mono.just(quorumReaderFactory)
|
||||||
.map {
|
.map {
|
||||||
it.create(
|
it.create(
|
||||||
up.getApiSource(Selector.empty),
|
up.getApiSource(matcher),
|
||||||
callMethodsFactory.create().createQuorumFor(request.method),
|
callMethodsFactory.create().createQuorumFor(request.method),
|
||||||
// we do not use Signer for internal requests because it doesn't make much sense
|
// we do not use Signer for internal requests because it doesn't make much sense
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -15,19 +15,25 @@
|
|||||||
*/
|
*/
|
||||||
package io.emeraldpay.dshackle.upstream.calls
|
package io.emeraldpay.dshackle.upstream.calls
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.cache.BlocksMemCache
|
||||||
|
import io.emeraldpay.dshackle.cache.Caches
|
||||||
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.data.BlockId
|
import io.emeraldpay.dshackle.data.BlockId
|
||||||
import io.emeraldpay.dshackle.reader.Reader
|
import io.emeraldpay.dshackle.reader.Reader
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.Selector
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.test.StepVerifier
|
||||||
import spock.lang.Specification
|
import spock.lang.Specification
|
||||||
|
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
class EthereumCallSelectorSpec extends Specification {
|
class EthereumCallSelectorSpec extends Specification {
|
||||||
|
|
||||||
def "Get height matcher for latest balance"() {
|
def "Get height matcher for latest balance"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
1 * getCurrentHeight() >> 100
|
1 * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -39,7 +45,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get height matcher for latest call"() {
|
def "Get height matcher for latest call"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
1 * getCurrentHeight() >> 100
|
1 * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get height matcher for latest storageAt"() {
|
def "Get height matcher for latest storageAt"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
1 * getCurrentHeight() >> 100
|
1 * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -63,7 +69,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get height matcher for balance on block"() {
|
def "Get height matcher for balance on block"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100
|
_ * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -78,7 +84,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
def heights = Mock(Reader) {
|
def heights = Mock(Reader) {
|
||||||
1 * it.read(BlockId.from("0xc90f1c8c125a4d5b90742f16947bdb1d10516f173fd7fc51223d10499de2a812")) >> Mono.just(8606722L)
|
1 * it.read(BlockId.from("0xc90f1c8c125a4d5b90742f16947bdb1d10516f173fd7fc51223d10499de2a812")) >> Mono.just(8606722L)
|
||||||
}
|
}
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(heights)
|
EthereumCallSelector callSelector = new EthereumCallSelector(heights, Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 9128116
|
_ * getCurrentHeight() >> 9128116
|
||||||
}
|
}
|
||||||
@@ -90,7 +96,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "No matcher for invalid height"() {
|
def "No matcher for invalid height"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100
|
_ * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -103,7 +109,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "No matcher for negative height"() {
|
def "No matcher for negative height"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100
|
_ * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -115,7 +121,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "No matcher for negative long"() {
|
def "No matcher for negative long"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100
|
_ * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -128,7 +134,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "No matcher for pending balance"() {
|
def "No matcher for pending balance"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
_ * getCurrentHeight() >> 100
|
_ * getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -140,7 +146,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get height matcher with EIP-1898"() {
|
def "Get height matcher with EIP-1898"() {
|
||||||
setup:
|
setup:
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader))
|
EthereumCallSelector callSelector = new EthereumCallSelector(Stub(Reader), Stub(Caches))
|
||||||
def head = Stub(Head)
|
def head = Stub(Head)
|
||||||
when:
|
when:
|
||||||
def act = callSelector.getMatcher("eth_call", '["0x0000", {"blockNumber": "0x100"}]', head, false).block()
|
def act = callSelector.getMatcher("eth_call", '["0x0000", {"blockNumber": "0x100"}]', head, false).block()
|
||||||
@@ -153,7 +159,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
def heights = Mock(Reader) {
|
def heights = Mock(Reader) {
|
||||||
1 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.just(12079192L)
|
1 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.just(12079192L)
|
||||||
}
|
}
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(heights)
|
EthereumCallSelector callSelector = new EthereumCallSelector(heights, Stub(Caches))
|
||||||
def head = Stub(Head)
|
def head = Stub(Head)
|
||||||
when:
|
when:
|
||||||
def act = callSelector.getMatcher("eth_call",
|
def act = callSelector.getMatcher("eth_call",
|
||||||
@@ -168,7 +174,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
def heights = Mock(Reader) {
|
def heights = Mock(Reader) {
|
||||||
0 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.just(12079192L)
|
0 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.just(12079192L)
|
||||||
}
|
}
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(heights)
|
EthereumCallSelector callSelector = new EthereumCallSelector(heights, Stub(Caches))
|
||||||
def head = Stub(Head)
|
def head = Stub(Head)
|
||||||
when:
|
when:
|
||||||
def act = callSelector.getMatcher("eth_call",
|
def act = callSelector.getMatcher("eth_call",
|
||||||
@@ -183,7 +189,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
def heights = Mock(Reader) {
|
def heights = Mock(Reader) {
|
||||||
1 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.empty()
|
1 * it.read(BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32")) >> Mono.empty()
|
||||||
}
|
}
|
||||||
EthereumCallSelector callSelector = new EthereumCallSelector(heights)
|
EthereumCallSelector callSelector = new EthereumCallSelector(heights, Stub(Caches))
|
||||||
def head = Mock(Head) {
|
def head = Mock(Head) {
|
||||||
1 * it.getCurrentHeight() >> 100
|
1 * it.getCurrentHeight() >> 100
|
||||||
}
|
}
|
||||||
@@ -197,7 +203,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get same matcher for getFilterChanges method"() {
|
def "Get same matcher for getFilterChanges method"() {
|
||||||
setup:
|
setup:
|
||||||
def callSelector = new EthereumCallSelector(Mock(Reader))
|
def callSelector = new EthereumCallSelector(Mock(Reader), Stub(Caches))
|
||||||
def head = Mock(Head)
|
def head = Mock(Head)
|
||||||
|
|
||||||
expect:
|
expect:
|
||||||
@@ -214,7 +220,7 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
|
|
||||||
def "Get empty matcher for getFilterChanges method without params"() {
|
def "Get empty matcher for getFilterChanges method without params"() {
|
||||||
setup:
|
setup:
|
||||||
def callSelector = new EthereumCallSelector(Mock(Reader))
|
def callSelector = new EthereumCallSelector(Mock(Reader), Stub(Caches))
|
||||||
def head = Mock(Head)
|
def head = Mock(Head)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
@@ -223,4 +229,82 @@ class EthereumCallSelectorSpec extends Specification {
|
|||||||
then:
|
then:
|
||||||
act == null
|
act == null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Get height matcher for getByHash and getTransactionByBlockHash methods"() {
|
||||||
|
setup:
|
||||||
|
def hash = "0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32"
|
||||||
|
def block = new BlockContainer(
|
||||||
|
12079192L, BlockId.from(hash),
|
||||||
|
BigInteger.ONE, Instant.now(), false, "".bytes, null, [], 0, "upstream"
|
||||||
|
)
|
||||||
|
def blockByHashCache = Mock(BlocksMemCache) {
|
||||||
|
1 * read(BlockId.from(hash)) >> Mono.just(block)
|
||||||
|
}
|
||||||
|
def cache = Caches.newBuilder().setBlockByHash(blockByHashCache).build()
|
||||||
|
def callSelector = new EthereumCallSelector(Stub(Reader), cache)
|
||||||
|
def head = Stub(Head)
|
||||||
|
|
||||||
|
when:
|
||||||
|
def act = callSelector.getMatcher(
|
||||||
|
method, '["0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32", false]',
|
||||||
|
head, false
|
||||||
|
)
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext(new Selector.HeightMatcher(12079192L))
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
where:
|
||||||
|
method << ["eth_getTransactionByBlockHashAndIndex", "eth_getBlockByHash"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "Get height matcher for getByNumber and getTransactionByBlockNumber methods"() {
|
||||||
|
setup:
|
||||||
|
def cache = Stub(Caches)
|
||||||
|
def callSelector = new EthereumCallSelector(Stub(Reader), cache)
|
||||||
|
def head = Stub(Head)
|
||||||
|
|
||||||
|
when:
|
||||||
|
def act = callSelector.getMatcher(
|
||||||
|
method, '["0xfbfe3b", false]',
|
||||||
|
head, false
|
||||||
|
)
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext(new Selector.HeightMatcher(16514619L))
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
where:
|
||||||
|
method << ["eth_getTransactionByBlockNumberAndIndex", "eth_getBlockByNumber"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "No height matcher for getByHash method"() {
|
||||||
|
setup:
|
||||||
|
def hash = "0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32"
|
||||||
|
def blockByHashCache = Mock(BlocksMemCache) {
|
||||||
|
1 * read(BlockId.from(hash)) >> resultFromCache
|
||||||
|
}
|
||||||
|
def cache = Caches.newBuilder().setBlockByHash(blockByHashCache).build()
|
||||||
|
def callSelector = new EthereumCallSelector(Stub(Reader), cache)
|
||||||
|
def head = Stub(Head)
|
||||||
|
|
||||||
|
when:
|
||||||
|
def act = callSelector.getMatcher(
|
||||||
|
"eth_getBlockByHash", '["0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32", false]',
|
||||||
|
head, false
|
||||||
|
)
|
||||||
|
|
||||||
|
then:
|
||||||
|
StepVerifier.create(act)
|
||||||
|
.expectNext()
|
||||||
|
.expectComplete()
|
||||||
|
.verify(Duration.ofSeconds(1))
|
||||||
|
|
||||||
|
where:
|
||||||
|
resultFromCache << [Mono.empty(), Mono.error(new RuntimeException())]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import io.emeraldpay.dshackle.reader.Reader
|
|||||||
import io.emeraldpay.dshackle.upstream.ApiSource
|
import io.emeraldpay.dshackle.upstream.ApiSource
|
||||||
import io.emeraldpay.dshackle.upstream.Head
|
import io.emeraldpay.dshackle.upstream.Head
|
||||||
import io.emeraldpay.dshackle.upstream.Multistream
|
import io.emeraldpay.dshackle.upstream.Multistream
|
||||||
|
import io.emeraldpay.dshackle.upstream.Selector
|
||||||
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
|
||||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
@@ -108,7 +109,7 @@ class EthereumDirectReaderSpec extends Specification {
|
|||||||
transactions = []
|
transactions = []
|
||||||
}
|
}
|
||||||
def up = Mock(Multistream) {
|
def up = Mock(Multistream) {
|
||||||
1 * getApiSource(_) >> Stub(ApiSource)
|
1 * getApiSource(new Selector.HeightMatcher(100)) >> Stub(ApiSource)
|
||||||
}
|
}
|
||||||
def calls = Mock(Factory) {
|
def calls = Mock(Factory) {
|
||||||
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
|
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
|
||||||
|
|||||||
Reference in New Issue
Block a user