Files
dshackle/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumDirectReaderSpec.groovy
Vyacheslav Shebanov d1ad77a345 problem: users want to verify that response are coming from their nodes
solution: edge node can sign received valued
Co-authored-by: Igor Artamonov <igor@artamonov.ru>
2022-05-26 23:00:01 -04:00

267 lines
9.8 KiB
Groovy

package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.CurrentBlockCache
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
import io.emeraldpay.dshackle.quorum.QuorumRpcReader
import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.ApiSource
import io.emeraldpay.dshackle.upstream.Head
import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.grpc.Chain
import io.emeraldpay.etherjar.domain.Address
import io.emeraldpay.etherjar.domain.BlockHash
import io.emeraldpay.etherjar.domain.TransactionId
import io.emeraldpay.etherjar.domain.Wei
import io.emeraldpay.etherjar.rpc.json.BlockJson
import io.emeraldpay.etherjar.rpc.json.TransactionJson
import org.apache.commons.collections4.Factory
import reactor.core.publisher.Mono
import reactor.test.StepVerifier
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
class EthereumDirectReaderSpec extends Specification {
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
String address1 = "0xe0aadb0a012dbcdc529c4c743d3e0385a0b54d3d"
def "Reads block by hash"() {
setup:
def json = new BlockJson().tap {
number = 100
hash = BlockHash.from(hash1)
timestamp = Instant.now()
totalDifficulty = BigInteger.ONE
transactions = []
}
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes(json), null, 1
)
)
}
}
when:
def act = reader.blockReader.read(BlockHash.from(hash1))
then:
StepVerifier.create(act)
.expectNextMatches { block ->
block.hash.toHexWithPrefix() == hash1
}
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Produce empty result on non-existing block"() {
setup:
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getBlockByHash", [hash1, false])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes(null), null, 1
)
)
}
}
when:
def act = reader.blockReader.read(BlockHash.from(hash1))
then:
StepVerifier.create(act)
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Reads block by height"() {
setup:
def json = new BlockJson().tap {
number = 100
hash = BlockHash.from(hash1)
timestamp = Instant.now()
totalDifficulty = BigInteger.ONE
transactions = []
}
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getBlockByNumber", ["0x64", false])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes(json), null, 1
)
)
}
}
when:
def act = reader.blockByHeightReader.read(100)
then:
StepVerifier.create(act)
.expectNextMatches { block ->
block.hash.toHexWithPrefix() == hash1
}
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Reads tx"() {
setup:
def json = new TransactionJson().tap {
hash = TransactionId.from(hash1)
blockNumber = 100
blockHash = BlockHash.from(hash1)
}
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes(json), null, 1
)
)
}
}
when:
def act = reader.txReader.read(TransactionId.from(hash1))
then:
StepVerifier.create(act)
.expectNextMatches { block ->
block.hash.toHexWithPrefix() == hash1
}
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Produce empty on non-existing tx"() {
setup:
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getTransactionByHash", [hash1])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes(null), null, 1
)
)
}
}
when:
def act = reader.txReader.read(TransactionId.from(hash1))
then:
StepVerifier.create(act)
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Reads balance - height is unknown"() {
setup:
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
1 * getHead() >> Mock(Head) {
1 * getCurrentHeight() >> null
}
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "latest"])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes("0x100"), null, 1
)
)
}
}
when:
def act = reader.balanceReader.read(Address.from(address1))
then:
StepVerifier.create(act)
.expectNext(Wei.from("0x100"))
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Reads balance - height is known"() {
setup:
def up = Mock(Multistream) {
1 * getApiSource(_) >> Stub(ApiSource)
1 * getHead() >> Mock(Head) {
1 * getCurrentHeight() >> 11_061_691
}
}
def calls = Mock(Factory) {
1 * create() >> new DefaultEthereumMethods(Chain.ETHEREUM)
}
EthereumDirectReader reader = new EthereumDirectReader(
up, Caches.default(), new CurrentBlockCache(), calls
)
reader.quorumReaderFactory = Mock(QuorumReaderFactory) {
1 * create(_, _, _) >> Mock(Reader) {
1 * read(new JsonRpcRequest("eth_getBalance", [address1, "0xa8c9bb"])) >> Mono.just(
new QuorumRpcReader.Result(
Global.objectMapper.writeValueAsBytes("0x100"), null, 1
)
)
}
}
when:
def act = reader.balanceReader.read(Address.from(address1))
then:
StepVerifier.create(act)
.expectNext(Wei.from("0x100"))
.expectComplete()
.verify(Duration.ofSeconds(1))
}
}