problem: keeps data (block/tx) as business object, which requires special treating

solution: keep as bytes (json string) + meta
This commit is contained in:
Igor Artamonov
2020-04-14 00:25:25 -04:00
parent 0ab68ea782
commit 9cf1827fe1
73 changed files with 1235 additions and 565 deletions

View File

@@ -1,15 +1,23 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.json.BlockJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
import java.time.Instant
import java.time.temporal.ChronoUnit
class BlockByHeightSpec extends Specification {
String hash1 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
String hash2 = "0x4aabdaff9acd2f30d15e00ab5dfd5f6c56ba4ea1c968a7ff8d3f34de70153b33"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Fetch with all data available"() {
setup:
def blocks = new BlocksMemCache()
@@ -18,16 +26,22 @@ class BlockByHeightSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block.uncles = []
block.transactions = []
blocks.add(block)
heights.add(block)
BlockContainer.from(block, objectMapper).with {
blocks.add(it)
heights.add(it)
}
def blocksByHeight = new BlockByHeight(heights, blocks)
when:
def act = blocksByHeight.read(100).block()
then:
act == block
objectMapper.readValue(act.json, BlockJson) == block
}
def "Fetch correct blocks if multiple"() {
@@ -38,26 +52,40 @@ class BlockByHeightSpec extends Specification {
def block1 = new BlockJson<TransactionRefJson>()
block1.number = 100
block1.hash = BlockHash.from(hash1)
block1.totalDifficulty = BigInteger.ONE
block1.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block1.uncles = []
block1.transactions = []
def block2 = new BlockJson<TransactionRefJson>()
block2.number = 101
block2.hash = BlockHash.from(hash2)
block2.totalDifficulty = BigInteger.ONE
block2.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block2.uncles = []
block2.transactions = []
blocks.add(block1)
heights.add(block1)
blocks.add(block2)
heights.add(block2)
BlockContainer.from(block1, objectMapper).with {
blocks.add(it)
heights.add(it)
}
BlockContainer.from(block2, objectMapper).with {
blocks.add(it)
heights.add(it)
}
def blocksByHeight = new BlockByHeight(heights, blocks)
when:
def act = blocksByHeight.read(100).block()
then:
act == block1
objectMapper.readValue(act.json, BlockJson) == block1
when:
act = blocksByHeight.read(101).block()
then:
act == block2
objectMapper.readValue(act.json, BlockJson) == block2
}
def "Fetch last block if updated"() {
@@ -68,21 +96,34 @@ class BlockByHeightSpec extends Specification {
def block1 = new BlockJson<TransactionRefJson>()
block1.number = 100
block1.hash = BlockHash.from(hash1)
block1.totalDifficulty = BigInteger.ONE
block1.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block1.uncles = []
block1.transactions = []
def block2 = new BlockJson<TransactionRefJson>()
block2.number = 100
block2.hash = BlockHash.from(hash2)
block2.totalDifficulty = BigInteger.ONE
block2.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block2.uncles = []
block2.transactions = []
blocks.add(block1)
heights.add(block1)
blocks.add(block2)
heights.add(block2)
BlockContainer.from(block1, objectMapper).with {
blocks.add(it)
heights.add(it)
}
BlockContainer.from(block2, objectMapper).with {
blocks.add(it)
heights.add(it)
}
def blocksByHeight = new BlockByHeight(heights, blocks)
when:
def act = blocksByHeight.read(100).block()
then:
act == block2
objectMapper.readValue(act.json, BlockJson) == block2
}
def "Fetch nothing if block expired"() {
@@ -93,9 +134,13 @@ class BlockByHeightSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
// add only to heights
heights.add(block)
BlockContainer.from(block, objectMapper).with {
heights.add(it)
}
def blocksByHeight = new BlockByHeight(heights, blocks)
@@ -113,9 +158,13 @@ class BlockByHeightSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
// add only to blocks
blocks.add(block)
BlockContainer.from(block, objectMapper).with {
blocks.add(it)
}
def blocksByHeight = new BlockByHeight(heights, blocks)

View File

@@ -15,12 +15,18 @@
*/
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.domain.TransactionId
import io.infinitape.etherjar.rpc.json.BlockJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
import java.time.Instant
import java.time.temporal.ChronoUnit
class BlocksMemCacheSpec extends Specification {
String hash1 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
@@ -28,18 +34,24 @@ class BlocksMemCacheSpec extends Specification {
String hash3 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
String hash4 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Add and read"() {
setup:
def cache = new BlocksMemCache()
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block.uncles = []
block.transactions = []
when:
cache.add(block)
def act = cache.read(BlockHash.from(hash1)).block()
cache.add(BlockContainer.from(block, objectMapper))
def act = cache.read(BlockId.from(hash1)).block()
then:
act == block
objectMapper.readValue(act.json, BlockJson) == block
}
def "Keeps only configured amount"() {
@@ -48,17 +60,22 @@ class BlocksMemCacheSpec extends Specification {
[hash1]
when:
[hash1, hash2, hash3, hash4].eachWithIndex{ String hash, int i ->
[hash1, hash2, hash3, hash4].eachWithIndex { String hash, int i ->
def block = new BlockJson<TransactionRefJson>()
block.number = 100 + i
block.hash = BlockHash.from(hash)
cache.add(block)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
block.uncles = []
block.transactions = []
cache.add(BlockContainer.from(block, objectMapper))
}
def act1 = cache.read(BlockHash.from(hash1)).block()
def act2 = cache.read(BlockHash.from(hash2)).block()
def act3 = cache.read(BlockHash.from(hash3)).block()
def act4 = cache.read(BlockHash.from(hash4)).block()
def act1 = cache.read(BlockId.from(hash1)).block()
def act2 = cache.read(BlockId.from(hash2)).block()
def act3 = cache.read(BlockId.from(hash3)).block()
def act4 = cache.read(BlockId.from(hash4)).block()
then:
act2.hash.toHex() == hash2
act3.hash.toHex() == hash3

View File

@@ -1,5 +1,8 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.test.IntegrationTestingCommons
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.grpc.Chain
@@ -24,6 +27,7 @@ class BlocksRedisCacheSpec extends Specification {
String hash3 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
String hash4 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def setup() {
RedisClient client = IntegrationTestingCommons.redis()
@@ -40,15 +44,17 @@ class BlocksRedisCacheSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
block.hash = BlockHash.from(hash1)
block.transactions = []
block.uncles = []
when:
cache.add(block).subscribe()
def act = cache.read(BlockHash.from(hash1)).block()
cache.add(BlockContainer.from(block, objectMapper)).subscribe()
def act = cache.read(BlockId.from(hash1)).block()
then:
act == block
act != null
objectMapper.readValue(act.json, BlockJson) == block
}
def "Evict existing block"() {
@@ -59,19 +65,20 @@ class BlocksRedisCacheSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
when:
cache.add(block).subscribe()
def act = cache.read(BlockHash.from(hash2)).block()
cache.add(BlockContainer.from(block, objectMapper)).subscribe()
def act = cache.read(BlockId.from(hash2)).block()
then:
act == block
objectMapper.readValue(act.json, BlockJson) == block
when:
cache.evict(block.hash).subscribe()
act = cache.read(BlockHash.from(hash2)).block()
cache.evict(BlockId.from(block.hash)).subscribe()
act = cache.read(BlockId.from(hash2)).block()
then:
act == null
@@ -85,22 +92,25 @@ class BlocksRedisCacheSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
when:
cache.add(block).subscribe()
def act = cache.read(BlockHash.from(hash2)).block()
cache.add(BlockContainer.from(block, objectMapper)).subscribe()
def act = cache.read(BlockId.from(hash2)).block()
then:
act == block
act != null
objectMapper.readValue(act.json, BlockJson) == block
when:
cache.evict(BlockHash.from(hash3)).subscribe()
act = cache.read(BlockHash.from(hash2)).block()
cache.evict(BlockId.from(hash3)).subscribe()
act = cache.read(BlockId.from(hash2)).block()
then:
act == block
act != null
objectMapper.readValue(act.json, BlockJson) == block
}
}

View File

@@ -1,5 +1,9 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.TxContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.domain.TransactionId
import io.infinitape.etherjar.rpc.json.BlockJson
@@ -7,11 +11,14 @@ import io.infinitape.etherjar.rpc.json.TransactionJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
import java.time.Instant
class CachesSpec extends Specification {
String hash1 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
String hash2 = "0x4aabdaff9acd2f30d15e00ab5dfd5f6c56ba4ea1c968a7ff8d3f34de70153b33"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Evict txes if block updated"() {
setup:
@@ -19,6 +26,7 @@ class CachesSpec extends Specification {
HeightCache heightCache = Mock()
BlocksMemCache blocksCache = Mock()
def caches = Caches.newBuilder()
.setObjectMapper(objectMapper)
.setTxByHash(txCache)
.setBlockByHeight(heightCache)
.setBlockByHash(blocksCache)
@@ -27,10 +35,18 @@ class CachesSpec extends Specification {
def block1 = new BlockJson()
block1.number = 100
block1.hash = BlockHash.from(hash1)
block1.totalDifficulty = BigInteger.ONE
block1.timestamp = Instant.now()
block1.transactions = []
block1 = BlockContainer.from(block1, objectMapper)
def block2 = new BlockJson()
block2.number = 100
block2.hash = BlockHash.from(hash2)
block2.totalDifficulty = BigInteger.ONE
block2.timestamp = Instant.now()
block2.transactions = []
block2 = BlockContainer.from(block2, objectMapper)
when:
caches.cache(Caches.Tag.LATEST, block1)
@@ -53,6 +69,7 @@ class CachesSpec extends Specification {
HeightCache heightCache = Mock()
BlocksMemCache blocksCache = Mock()
def caches = Caches.newBuilder()
.setObjectMapper(objectMapper)
.setTxByHash(txCache)
.setBlockByHeight(heightCache)
.setBlockByHash(blocksCache)
@@ -61,10 +78,16 @@ class CachesSpec extends Specification {
def block1 = new BlockJson()
block1.number = 100
block1.hash = BlockHash.from(hash1)
block1.totalDifficulty = BigInteger.ONE
block1.timestamp = Instant.now()
block1 = BlockContainer.from(block1, objectMapper)
def block2 = new BlockJson()
block2.number = 100
block2.hash = BlockHash.from(hash2)
block2.totalDifficulty = BigInteger.ONE
block2.timestamp = Instant.now()
block2 = BlockContainer.from(block2, objectMapper)
when:
caches.cache(Caches.Tag.LATEST, block1)
@@ -87,6 +110,7 @@ class CachesSpec extends Specification {
HeightCache heightCache = Mock()
BlocksMemCache blocksCache = Mock()
def caches = Caches.newBuilder()
.setObjectMapper(TestingCommons.objectMapper())
.setTxByHash(txCache)
.setBlockByHeight(heightCache)
.setBlockByHash(blocksCache)
@@ -95,13 +119,15 @@ class CachesSpec extends Specification {
def block = new BlockJson()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
block.transactions = [
new TransactionRefJson(TransactionId.from(hash1)),
new TransactionRefJson(TransactionId.from(hash2)),
]
when:
caches.cache(Caches.Tag.REQUESTED, block)
caches.cache(Caches.Tag.REQUESTED, BlockContainer.from(block, objectMapper))
then:
0 * txCache.add(_)
}
@@ -112,6 +138,7 @@ class CachesSpec extends Specification {
HeightCache heightCache = Mock()
BlocksMemCache blocksCache = Mock()
def caches = Caches.newBuilder()
.setObjectMapper(TestingCommons.objectMapper())
.setTxByHash(txCache)
.setBlockByHeight(heightCache)
.setBlockByHash(blocksCache)
@@ -134,12 +161,15 @@ class CachesSpec extends Specification {
def block = new BlockJson()
block.number = 100
block.hash = BlockHash.from(hash1)
block.totalDifficulty = BigInteger.ONE
block.transactions = [tx1, tx2]
block.timestamp = Instant.now()
block = BlockContainer.from(block, objectMapper)
when:
caches.cache(Caches.Tag.REQUESTED, block)
then:
1 * txCache.add(tx1)
1 * txCache.add(tx2)
1 * txCache.add(TxContainer.from(tx1, objectMapper))
1 * txCache.add(TxContainer.from(tx2, objectMapper))
}
}

View File

@@ -1,5 +1,10 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.data.TxContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.domain.TransactionId
import io.infinitape.etherjar.rpc.json.BlockJson
@@ -7,7 +12,9 @@ import io.infinitape.etherjar.rpc.json.TransactionJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
class BlocksWithTxCacheSpec extends Specification {
import java.time.Instant
class EthereumBlocksWithTxCacheSpec extends Specification {
// sorted
String hash1 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
@@ -15,6 +22,8 @@ class BlocksWithTxCacheSpec extends Specification {
String hash3 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
String hash4 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def tx1 = new TransactionJson().with {
it.blockNumber = 100
it.blockHash = BlockHash.from(hash1)
@@ -50,6 +59,8 @@ class BlocksWithTxCacheSpec extends Specification {
def block1 = new BlockJson().with {
it.number = 100
it.hash = BlockHash.from(hash1)
it.totalDifficulty = BigInteger.ONE
it.timestamp = Instant.now()
it.transactions = [
new TransactionRefJson(tx1.hash),
new TransactionRefJson(tx2.hash)
@@ -61,6 +72,8 @@ class BlocksWithTxCacheSpec extends Specification {
def block2 = new BlockJson().with {
it.number = 101
it.hash = BlockHash.from(hash3)
it.totalDifficulty = BigInteger.ONE
it.timestamp = Instant.now()
it.transactions = [
new TransactionRefJson(tx3.hash)
]
@@ -71,6 +84,8 @@ class BlocksWithTxCacheSpec extends Specification {
def block3 = new BlockJson().with {
it.number = 102
it.hash = BlockHash.from(hash4)
it.totalDifficulty = BigInteger.ONE
it.timestamp = Instant.now()
it.transactions = []
it
}
@@ -81,21 +96,26 @@ class BlocksWithTxCacheSpec extends Specification {
def txes = new TxMemCache()
def blocks = new BlocksMemCache()
txes.add(tx1)
txes.add(tx2)
txes.add(tx3)
txes.add(tx4)
blocks.add(block1)
blocks.add(block2)
blocks.add(block3)
txes.add(TxContainer.from(tx1, objectMapper))
txes.add(TxContainer.from(tx2, objectMapper))
txes.add(TxContainer.from(tx3, objectMapper))
txes.add(TxContainer.from(tx4, objectMapper))
blocks.add(BlockContainer.from(block1, objectMapper))
blocks.add(BlockContainer.from(block2, objectMapper))
blocks.add(BlockContainer.from(block3, objectMapper))
def full = new BlocksWithTxCache(blocks, txes)
def full = new EthereumBlocksWithTxCache(objectMapper, blocks, txes)
when:
def act = full.read(block1.hash).block()
def act = full.read(BlockId.from(block1.hash)).block()
then:
act != null
when:
act = objectMapper.readValue(act.json, BlockJson)
then:
act.hash == BlockHash.from(hash1)
act.number == 100
act.transactions.size() == 2
@@ -119,9 +139,15 @@ class BlocksWithTxCacheSpec extends Specification {
// request second block
when:
act = full.read(block2.hash).block()
act = full.read(BlockId.from(block2.hash)).block()
then:
act != null
when:
act = objectMapper.readValue(act.json, BlockJson)
then:
act.hash == BlockHash.from(hash3)
act.number == 101
act.transactions.size() == 1
@@ -136,23 +162,23 @@ class BlocksWithTxCacheSpec extends Specification {
def txes = new TxMemCache()
def blocks = new BlocksMemCache()
txes.add(tx1)
txes.add(tx2)
txes.add(tx3)
txes.add(tx4)
blocks.add(block1)
blocks.add(block2)
blocks.add(block3)
txes.add(TxContainer.from(tx1, objectMapper))
txes.add(TxContainer.from(tx2, objectMapper))
txes.add(TxContainer.from(tx3, objectMapper))
txes.add(TxContainer.from(tx4, objectMapper))
blocks.add(BlockContainer.from(block1, objectMapper))
blocks.add(BlockContainer.from(block2, objectMapper))
blocks.add(BlockContainer.from(block3, objectMapper))
def full = new BlocksWithTxCache(blocks, txes)
def full = new EthereumBlocksWithTxCache(objectMapper, blocks, txes)
when:
def act = full.read(block3.hash).block()
def act = full.read(BlockId.from(block3.hash)).block()
then:
act != null
act.hash == BlockHash.from(hash4)
act.number == 102
act.hash == BlockId.from(hash4)
act.height == 102
act.transactions.size() == 0
}
@@ -161,13 +187,13 @@ class BlocksWithTxCacheSpec extends Specification {
def txes = new TxMemCache()
def blocks = new BlocksMemCache()
txes.add(tx1)
blocks.add(block1) //missing tx2 in cache
txes.add(TxContainer.from(tx1, objectMapper))
blocks.add(BlockContainer.from(block1, objectMapper)) //missing tx2 in cache
def full = new BlocksWithTxCache(blocks, txes)
def full = new EthereumBlocksWithTxCache(objectMapper, blocks, txes)
when:
def act = full.read(block1.hash).block()
def act = full.read(BlockId.from(block1.hash)).block()
then:
act == null
@@ -178,14 +204,14 @@ class BlocksWithTxCacheSpec extends Specification {
def txes = new TxMemCache()
def blocks = new BlocksMemCache()
txes.add(tx1)
txes.add(tx2)
txes.add(tx3)
txes.add(TxContainer.from(tx1, objectMapper))
txes.add(TxContainer.from(tx2, objectMapper))
txes.add(TxContainer.from(tx3, objectMapper))
def full = new BlocksWithTxCache(blocks, txes)
def full = new EthereumBlocksWithTxCache(objectMapper, blocks, txes)
when:
def act = full.read(block1.hash).block()
def act = full.read(BlockId.from(block1.hash)).block()
then:
act == null

View File

@@ -1,10 +1,15 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.json.BlockJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
import java.time.Instant
class HeightCacheSpec extends Specification {
String hash1 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
@@ -12,16 +17,20 @@ class HeightCacheSpec extends Specification {
String hash3 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
String hash4 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Add and read"() {
setup:
def cache = new HeightCache()
when:
[hash1, hash2, hash3, hash4].eachWithIndex{ String hash, int i ->
[hash1, hash2, hash3, hash4].eachWithIndex { String hash, int i ->
def block = new BlockJson<TransactionRefJson>()
block.number = 100 + i
block.hash = BlockHash.from(hash)
cache.add(block)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
cache.add(BlockContainer.from(block, objectMapper))
}
def act1 = cache.read(100).block()
@@ -41,11 +50,13 @@ class HeightCacheSpec extends Specification {
[hash1]
when:
[hash1, hash2, hash3, hash4].eachWithIndex{ String hash, int i ->
[hash1, hash2, hash3, hash4].eachWithIndex { String hash, int i ->
def block = new BlockJson<TransactionRefJson>()
block.number = 100 + i
block.hash = BlockHash.from(hash)
cache.add(block)
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
cache.add(BlockContainer.from(block, objectMapper))
}
def act1 = cache.read(100).block()

View File

@@ -1,5 +1,11 @@
package io.emeraldpay.dshackle.cache
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.data.TxContainer
import io.emeraldpay.dshackle.data.TxId
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.domain.TransactionId
import io.infinitape.etherjar.rpc.json.BlockJson
@@ -7,6 +13,8 @@ import io.infinitape.etherjar.rpc.json.TransactionJson
import io.infinitape.etherjar.rpc.json.TransactionRefJson
import spock.lang.Specification
import java.time.Instant
class TxMemCacheSpec extends Specification {
String hash1 = "0xd3f34def3c56ba4e701540d15edaff9acd2a1c968a7ff83b3300ab5dfd5f6aab"
@@ -14,6 +22,8 @@ class TxMemCacheSpec extends Specification {
String hash3 = "0x40d15edaff9acdabd2a1c96fd5f683b3300aad34e7015f34def3c56ba8a7ffb5"
String hash4 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Add and read"() {
setup:
def cache = new TxMemCache()
@@ -23,10 +33,10 @@ class TxMemCacheSpec extends Specification {
tx.blockNumber = 100
when:
cache.add(tx)
def act = cache.read(TransactionId.from(hash1)).block()
cache.add(TxContainer.from(tx, objectMapper))
def act = cache.read(TxId.from(hash1)).block()
then:
act == tx
objectMapper.readValue(act.json, TransactionJson.class) == tx
}
def "Keeps only configured amount"() {
@@ -34,18 +44,18 @@ class TxMemCacheSpec extends Specification {
def cache = new TxMemCache(3)
when:
[hash1, hash2, hash3, hash4].eachWithIndex{ String hash, int i ->
[hash1, hash2, hash3, hash4].eachWithIndex { String hash, int i ->
def tx = new TransactionJson()
tx.blockNumber = 100 + i
tx.blockHash = BlockHash.from(hash)
tx.hash = TransactionId.from(hash)
cache.add(tx)
cache.add(TxContainer.from(tx, objectMapper))
}
def act1 = cache.read(TransactionId.from(hash1)).block()
def act2 = cache.read(TransactionId.from(hash2)).block()
def act3 = cache.read(TransactionId.from(hash3)).block()
def act4 = cache.read(TransactionId.from(hash4)).block()
def act1 = cache.read(TxId.from(hash1)).block()
def act2 = cache.read(TxId.from(hash2)).block()
def act3 = cache.read(TxId.from(hash3)).block()
def act4 = cache.read(TxId.from(hash4)).block()
then:
act2.hash.toHex() == hash2
act3.hash.toHex() == hash3
@@ -63,22 +73,22 @@ class TxMemCacheSpec extends Specification {
tx.blockNumber = 100
tx.blockHash = BlockHash.from(hash1)
tx.hash = TransactionId.from(hash)
cache.add(tx)
cache.add(TxContainer.from(tx, objectMapper))
}
[hash3, hash4].eachWithIndex{ String hash, int i ->
[hash3, hash4].eachWithIndex { String hash, int i ->
def tx = new TransactionJson()
tx.blockNumber = 101
tx.blockHash = BlockHash.from(hash2)
tx.hash = TransactionId.from(hash)
cache.add(tx)
cache.add(TxContainer.from(tx, objectMapper))
}
cache.evict(BlockHash.from(hash1))
cache.evict(BlockId.from(hash1))
def act1 = cache.read(TransactionId.from(hash1)).block()
def act2 = cache.read(TransactionId.from(hash2)).block()
def act3 = cache.read(TransactionId.from(hash3)).block()
def act4 = cache.read(TransactionId.from(hash4)).block()
def act1 = cache.read(TxId.from(hash1)).block()
def act2 = cache.read(TxId.from(hash2)).block()
def act3 = cache.read(TxId.from(hash3)).block()
def act4 = cache.read(TxId.from(hash4)).block()
then:
act1 == null
@@ -97,30 +107,32 @@ class TxMemCacheSpec extends Specification {
tx.blockNumber = 100
tx.blockHash = BlockHash.from(hash1)
tx.hash = TransactionId.from(hash)
cache.add(tx)
cache.add(TxContainer.from(tx, objectMapper))
}
[hash3, hash4].eachWithIndex{ String hash, int i ->
def tx = new TransactionJson()
tx.blockNumber = 100
tx.blockHash = BlockHash.from(hash2)
tx.hash = TransactionId.from(hash)
cache.add(tx)
cache.add(TxContainer.from(tx, objectMapper))
}
def block = new BlockJson<TransactionRefJson>()
block.hash = BlockHash.from(hash1)
block.number = 100
block.totalDifficulty = BigInteger.ONE
block.timestamp = Instant.now()
block.transactions = [
new TransactionRefJson(TransactionId.from(hash1)),
new TransactionRefJson(TransactionId.from(hash2)),
]
cache.evict(block)
cache.evict(BlockContainer.from(block, objectMapper))
def act1 = cache.read(TransactionId.from(hash1)).block()
def act2 = cache.read(TransactionId.from(hash2)).block()
def act3 = cache.read(TransactionId.from(hash3)).block()
def act4 = cache.read(TransactionId.from(hash4)).block()
def act1 = cache.read(TxId.from(hash1)).block()
def act2 = cache.read(TxId.from(hash2)).block()
def act3 = cache.read(TxId.from(hash3)).block()
def act4 = cache.read(TxId.from(hash4)).block()
then:
act1 == null

View File

@@ -1,5 +1,9 @@
package io.emeraldpay.dshackle.cache
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.TxContainer
import io.emeraldpay.dshackle.data.TxId
import io.emeraldpay.dshackle.test.IntegrationTestingCommons
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.grpc.Chain
@@ -26,6 +30,8 @@ class TxRedisCacheSpec extends Specification {
String hash4 = "0xa4e7a75dfd5f6a83b3304dc56bfa0abfd3fef01540d15edafc9683f9acd2a13b"
TxRedisCache cache
def objectMapper = TestingCommons.objectMapper()
def setup() {
RedisClient client = IntegrationTestingCommons.redis()
StatefulRedisConnection<String, String> connection = client.connect();
@@ -39,6 +45,7 @@ class TxRedisCacheSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
block.hash = BlockHash.from(hash1)
block.transactions = []
block.uncles = []
@@ -51,10 +58,11 @@ class TxRedisCacheSpec extends Specification {
tx.nonce = 0
when:
cache.add(tx, block).subscribe()
def act = cache.read(TransactionId.from(hash1)).block()
cache.add(TxContainer.from(tx, objectMapper), BlockContainer.from(block, objectMapper)).subscribe()
def act = cache.read(TxId.from(hash1)).block()
then:
act == tx
act != null
objectMapper.readValue(act.json, TransactionJson) == tx
}
def "Evict single tx"() {
@@ -62,6 +70,7 @@ class TxRedisCacheSpec extends Specification {
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
@@ -74,14 +83,15 @@ class TxRedisCacheSpec extends Specification {
tx.nonce = 0
when:
cache.add(tx, block).subscribe()
def act = cache.read(tx.hash).block()
cache.add(TxContainer.from(tx, objectMapper), BlockContainer.from(block, objectMapper)).subscribe()
def act = cache.read(TxId.from(tx.hash)).block()
then:
act == tx
act != null
objectMapper.readValue(act.json, TransactionJson) == tx
when:
cache.evict(tx.hash).subscribe()
act = cache.read(tx.hash).block()
cache.evict(TxId.from(tx.hash)).subscribe()
act = cache.read(TxId.from(tx.hash)).block()
then:
act == null
}
@@ -92,6 +102,7 @@ class TxRedisCacheSpec extends Specification {
block1.hash = BlockHash.from(hash1)
block1.number = 100
block1.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block1.totalDifficulty = BigInteger.ONE
block1.transactions = [
new TransactionRefJson(TransactionId.from(hash1)),
new TransactionRefJson(TransactionId.from(hash2)),
@@ -100,6 +111,7 @@ class TxRedisCacheSpec extends Specification {
block2.hash = BlockHash.from(hash2)
block2.number = 101
block2.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block2.totalDifficulty = BigInteger.ONE
block2.transactions = [
new TransactionRefJson(TransactionId.from(hash3)),
new TransactionRefJson(TransactionId.from(hash4)),
@@ -112,7 +124,7 @@ class TxRedisCacheSpec extends Specification {
tx.hash = TransactionId.from(hash)
tx.value = Wei.ofEthers(i)
tx.nonce = 0
cache.add(tx, block1).subscribe()
cache.add(TxContainer.from(tx, objectMapper), BlockContainer.from(block1, objectMapper)).subscribe()
}
[hash3, hash4].eachWithIndex{ String hash, int i ->
def tx = new TransactionJson()
@@ -121,16 +133,16 @@ class TxRedisCacheSpec extends Specification {
tx.hash = TransactionId.from(hash)
tx.value = Wei.ofEthers(i)
tx.nonce = 0
cache.add(tx, block2).subscribe()
cache.add(TxContainer.from(tx, objectMapper), BlockContainer.from(block2, objectMapper)).subscribe()
}
cache.evict(block1).subscribe()
cache.evict(BlockContainer.from(block1, objectMapper)).subscribe()
def act1 = cache.read(TransactionId.from(hash1)).block()
def act2 = cache.read(TransactionId.from(hash2)).block()
def act3 = cache.read(TransactionId.from(hash3)).block()
def act4 = cache.read(TransactionId.from(hash4)).block()
def act1 = cache.read(TxId.from(hash1)).block()
def act2 = cache.read(TxId.from(hash2)).block()
def act3 = cache.read(TxId.from(hash3)).block()
def act4 = cache.read(TxId.from(hash4)).block()
then:
act1 == null

View File

@@ -15,10 +15,13 @@
*/
package io.emeraldpay.dshackle.rpc
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.EthereumUpstreamMock
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.test.UpstreamsMock
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
import io.emeraldpay.dshackle.upstream.Upstream
@@ -37,6 +40,8 @@ import java.time.Instant
class StreamHeadSpec extends Specification {
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Errors on unavailable chain"() {
setup:
def upstreams = new UpstreamsMock(Chain.ETHEREUM, Stub(EthereumUpstream))
@@ -83,9 +88,9 @@ class StreamHeadSpec extends Specification {
)
then:
StepVerifier.create(flux.take(2))
.then { upstream.nextBlock(blocks[0]) }
.then { upstream.nextBlock(BlockContainer.from(blocks[0], objectMapper)) }
.expectNext(heads[0])
.then { upstream.nextBlock(blocks[1]) }
.then { upstream.nextBlock(BlockContainer.from(blocks[1], objectMapper)) }
.expectNext(heads[1])
.expectComplete()
.verify(Duration.ofSeconds(1))

View File

@@ -17,6 +17,7 @@ package io.emeraldpay.dshackle.rpc
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.test.UpstreamsMock
import io.emeraldpay.dshackle.upstream.Upstreams
@@ -32,6 +33,8 @@ import reactor.test.StepVerifier
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit
class TrackEthereumAddressSpec extends Specification {
@@ -94,6 +97,7 @@ class TrackEthereumAddressSpec extends Specification {
it.number = 1
it.totalDifficulty = 100
it.hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22")
it.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
return it
}
@@ -115,7 +119,7 @@ class TrackEthereumAddressSpec extends Specification {
assert trackAddress.isTracked(Chain.ETHEREUM, Address.from(address1))
}
.then {
upstreamMock.nextBlock(block2)
upstreamMock.nextBlock(BlockContainer.from(block2, TestingCommons.objectMapper()))
}
.expectNext(exp2)
.thenCancel()

View File

@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.rpc
import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.test.UpstreamsMock
import io.emeraldpay.dshackle.upstream.Upstreams
@@ -63,6 +64,7 @@ class TrackEthereumTxSpec extends Specification {
it.timestamp = Instant.ofEpochMilli(156400200000)
it.number = 108
it.totalDifficulty = BigInteger.valueOf(800)
it.transactions = []
it
}
@@ -75,15 +77,17 @@ class TrackEthereumTxSpec extends Specification {
it
}
blockJson.transactions = [new TransactionRefJson(txJson.hash)]
def exp1 = BlockchainOuterClass.TxStatus.newBuilder()
.setTxId(txId)
.setBroadcasted(true)
.setMined(true)
.setConfirmations(8 + 1)
.setBlock(
Common.BlockInfo.newBuilder()
.setHeight(blockJson.number)
.setWeight(ByteString.copyFrom(blockJson.totalDifficulty.toByteArray()))
.setTxId(txId)
.setBroadcasted(true)
.setMined(true)
.setConfirmations(8 + 1)
.setBlock(
Common.BlockInfo.newBuilder()
.setHeight(blockJson.number)
.setWeight(ByteString.copyFrom(blockJson.totalDifficulty.toByteArray()))
.setBlockId(blockJson.hash.toHex().substring(2))
.setTimestamp(blockJson.timestamp.toEpochMilli())
).build()
@@ -96,7 +100,7 @@ class TrackEthereumTxSpec extends Specification {
apiMock.answer("eth_getTransactionByHash", [txId], txJson)
apiMock.answer("eth_getBlockByHash", [blockJson.hash.toHex(), false], blockJson)
upstreamMock.nextBlock(blockHeadJson)
upstreamMock.nextBlock(BlockContainer.from(blockHeadJson, TestingCommons.objectMapper()))
when:
def flux = trackTx.add(Mono.just(req))
@@ -292,7 +296,7 @@ class TrackEthereumTxSpec extends Specification {
def nextBlock = { int i ->
return {
println("block $i");
upstreamMock.nextBlock(blocks[i])
upstreamMock.nextBlock(BlockContainer.from(blocks[i], TestingCommons.objectMapper()))
} as Runnable
}

View File

@@ -15,26 +15,25 @@
*/
package io.emeraldpay.dshackle.test
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.upstream.ethereum.EthereumHead
import io.infinitape.etherjar.domain.TransactionId
import io.infinitape.etherjar.rpc.json.BlockJson
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import reactor.core.publisher.TopicProcessor
class EthereumHeadMock implements EthereumHead {
private TopicProcessor<BlockJson<TransactionId>> bus = TopicProcessor.create()
private BlockJson<TransactionId> latest
private TopicProcessor<BlockContainer> bus = TopicProcessor.create()
private BlockContainer latest
void nextBlock(BlockJson<TransactionId> block) {
void nextBlock(BlockContainer block) {
assert block != null
latest = block
bus.onNext(block)
}
@Override
Flux<BlockJson<TransactionId>> getFlux() {
Flux<BlockContainer> getFlux() {
return Flux.concat(Mono.justOrEmpty(latest), bus).distinctUntilChanged()
}
}

View File

@@ -16,6 +16,7 @@
package io.emeraldpay.dshackle.test
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.startup.QuorumForLabels
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
@@ -47,12 +48,12 @@ class EthereumUpstreamMock extends EthereumUpstream {
EthereumUpstreamMock(@NotNull String id, @NotNull Chain chain, @NotNull DirectEthereumApi api, CallMethods methods) {
super(id, chain, api, null,
UpstreamsConfig.Options.getDefaults(), new QuorumForLabels.QuorumItem(1, new UpstreamsConfig.Labels()),
methods)
methods, TestingCommons.objectMapper())
setLag(0)
setStatus(UpstreamAvailability.OK)
}
void nextBlock(BlockJson<TransactionId> block) {
void nextBlock(BlockContainer block) {
ethereumHeadMock.nextBlock(block)
}

View File

@@ -76,7 +76,7 @@ class TestingCommons {
}
static AggregatedUpstream aggregatedUpstream(EthereumUpstream up) {
return new EthereumChainUpstreams(Chain.ETHEREUM, [up], Caches.default(), objectMapper())
return new EthereumChainUpstreams(Chain.ETHEREUM, [up], Caches.default(objectMapper()), objectMapper())
}
static CachesFactory emptyCaches() {

View File

@@ -41,7 +41,7 @@ class UpstreamsMock implements Upstreams {
AggregatedUpstream addUpstream(@NotNull Chain chain, @NotNull Upstream up) {
if (!upstreams.containsKey(chain)) {
upstreams[chain] = new EthereumChainUpstreams(chain, [up], Caches.default(), TestingCommons.objectMapper())
upstreams[chain] = new EthereumChainUpstreams(chain, [up], Caches.default(TestingCommons.objectMapper()), TestingCommons.objectMapper())
} else {
upstreams[chain].addUpstream(up)
}

View File

@@ -31,7 +31,7 @@ class AggregatedUpstreamSpec extends Specification {
setup:
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, Stub(DirectEthereumApi), new DirectCallMethods(["eth_test1", "eth_test2"]))
def up2 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, Stub(DirectEthereumApi), new DirectCallMethods(["eth_test2", "eth_test3"]))
def aggr = new EthereumChainUpstreams(Chain.ETHEREUM, [up1, up2], Caches.default(), TestingCommons.objectMapper())
def aggr = new EthereumChainUpstreams(Chain.ETHEREUM, [up1, up2], Caches.default(TestingCommons.objectMapper()), TestingCommons.objectMapper())
when:
aggr.onUpstreamsUpdated()
def act = aggr.getMethods()

View File

@@ -1,11 +1,14 @@
package io.emeraldpay.dshackle.upstream
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.cache.BlockByHeight
import io.emeraldpay.dshackle.cache.BlocksMemCache
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.HeightCache
import io.emeraldpay.dshackle.cache.TxMemCache
import io.emeraldpay.dshackle.reader.EmptyReader
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.data.TxId
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.ethereum.EthereumHead
import io.infinitape.etherjar.domain.BlockHash
@@ -18,34 +21,47 @@ import reactor.test.StepVerifier
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
import java.time.temporal.ChronoUnit
class CachingEthereumApiSpec extends Specification {
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Get blockNumber from head"() {
setup:
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.default(),
objectMapper,
Caches.default(objectMapper),
head
)
1 * head.getFlux() >> Flux.just(new BlockJson<TransactionRefJson>(number: 100))
1 * head.getFlux() >> Flux.just(BlockContainer.from(
new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
difficulty: 1,
totalDifficulty: BigInteger.ONE,
timestamp: Instant.now().truncatedTo(ChronoUnit.SECONDS)
),
objectMapper
))
when:
def act = api.execute(1, "eth_blockNumber", []).map { new String(it)}
def act = api.execute(1, "eth_blockNumber", []).map { new String(it) }
then:
StepVerifier.create(act)
.expectNext('{"jsonrpc":"2.0","id":1,"result":"0x64"}')
.expectComplete()
.verify(Duration.ofSeconds(3))
.expectNext('{"jsonrpc":"2.0","id":1,"result":"0x64"}')
.expectComplete()
.verify(Duration.ofSeconds(3))
}
def "Return empty if block is not cached"() {
setup:
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.default(),
objectMapper,
Caches.default(objectMapper),
head
)
when:
@@ -62,18 +78,26 @@ class CachingEthereumApiSpec extends Specification {
def cache = new BlocksMemCache();
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(cache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(cache).build(),
head
)
cache.add(new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")))
cache.add(BlockContainer.from(
new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.ofEpochSecond(0x5e95313a)
),
objectMapper
))
when:
def act = api.execute(1, "eth_getBlockByHash", ["0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58", false]).map { new String(it)}
def act = api.execute(1, "eth_getBlockByHash", ["0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58", false]).map { new String(it) }
then:
StepVerifier.create(act)
.expectNext('{"jsonrpc":"2.0","id":1,"result":{"number":"0x64","hash":"0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58","transactions":[],"uncles":[]}}')
.expectNext('{"jsonrpc":"2.0","id":1,"result":{"number":"0x64","hash":"0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58","timestamp":"0x5e95313a","transactions":[],"totalDifficulty":"0x1","uncles":[]}}')
.expectComplete()
.verify(Duration.ofSeconds(3))
}
@@ -84,20 +108,25 @@ class CachingEthereumApiSpec extends Specification {
def heightCache = new HeightCache()
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(blocksCache).setBlockByHeight(heightCache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).setBlockByHeight(heightCache).build(),
head
)
def block = new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
heightCache.add(block)
blocksCache.add(block)
def block = new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.ofEpochSecond(0x5e95313a)
)
heightCache.add(BlockContainer.from(block, objectMapper))
blocksCache.add(BlockContainer.from(block, objectMapper))
when:
def act = api.execute(1, "eth_getBlockByNumber", ["0x64", false]).map { new String(it)}
def act = api.execute(1, "eth_getBlockByNumber", ["0x64", false]).map { new String(it) }
then:
StepVerifier.create(act)
.expectNext('{"jsonrpc":"2.0","id":1,"result":{"number":"0x64","hash":"0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58","transactions":[],"uncles":[]}}')
.expectNext('{"jsonrpc":"2.0","id":1,"result":{"number":"0x64","hash":"0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58","timestamp":"0x5e95313a","transactions":[],"totalDifficulty":"0x1","uncles":[]}}')
.expectComplete()
.verify(Duration.ofSeconds(3))
}
@@ -108,18 +137,23 @@ class CachingEthereumApiSpec extends Specification {
def txCache = Mock(TxMemCache)
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(blocksCache).setTxByHash(txCache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).setTxByHash(txCache).build(),
head
)
def block = new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
def block = new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.now().truncatedTo(ChronoUnit.SECONDS)
)
when:
def act = api.readBlockByHash(1, "eth_getBlockByHash", ["0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58", false]).block()
then:
act != null
1 * blocksCache.read(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(block)
1 * blocksCache.read(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(BlockContainer.from(block, objectMapper))
0 * txCache.read(_)
}
@@ -129,11 +163,16 @@ class CachingEthereumApiSpec extends Specification {
def txCache = Mock(TxMemCache)
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(blocksCache).setTxByHash(txCache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).setTxByHash(txCache).build(),
head
)
def block = new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
def block = new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.now().truncatedTo(ChronoUnit.SECONDS)
)
block.transactions = [
new TransactionRefJson(TransactionId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073"))
]
@@ -143,8 +182,8 @@ class CachingEthereumApiSpec extends Specification {
then:
act == null
1 * blocksCache.read(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(block)
1 * txCache.read(TransactionId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073")) >> Mono.empty()
1 * blocksCache.read(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(BlockContainer.from(block, objectMapper))
1 * txCache.read(TxId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073")) >> Mono.empty()
}
def "Uses base cache when requested, by height"() {
@@ -154,19 +193,24 @@ class CachingEthereumApiSpec extends Specification {
def heightCache = Mock(HeightCache)
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(blocksCache).setTxByHash(txCache).setBlockByHeight(heightCache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).setTxByHash(txCache).setBlockByHeight(heightCache).build(),
head
)
def block = new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
def block = new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.now().truncatedTo(ChronoUnit.SECONDS)
)
when:
def act = api.readBlockByNumber(1, "eth_getBlockByNumber", ["0x64", false]).block()
then:
act != null
1 * heightCache.read(100) >> Mono.just(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
1 * blocksCache.read(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(block)
1 * heightCache.read(100) >> Mono.just(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
1 * blocksCache.read(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(BlockContainer.from(block, objectMapper))
0 * txCache.read(_)
}
@@ -177,11 +221,16 @@ class CachingEthereumApiSpec extends Specification {
def heightCache = Mock(HeightCache)
def head = Mock(EthereumHead.class)
def api = new CachingEthereumApi(
TestingCommons.objectMapper(),
Caches.newBuilder().setBlockByHash(blocksCache).setTxByHash(txCache).setBlockByHeight(heightCache).build(),
objectMapper,
Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).setTxByHash(txCache).setBlockByHeight(heightCache).build(),
head
)
def block = new BlockJson<TransactionRefJson>(number: 100, hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
def block = new BlockJson<TransactionRefJson>(
number: 100,
hash: BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"),
totalDifficulty: BigInteger.ONE,
timestamp: Instant.now().truncatedTo(ChronoUnit.SECONDS)
)
block.transactions = [
new TransactionRefJson(TransactionId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073"))
]
@@ -191,8 +240,8 @@ class CachingEthereumApiSpec extends Specification {
then:
act == null
1 * heightCache.read(100) >> Mono.just(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
1 * blocksCache.read(BlockHash.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(block)
1 * txCache.read(TransactionId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073")) >> Mono.empty()
1 * heightCache.read(100) >> Mono.just(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58"))
1 * blocksCache.read(BlockId.from("0x5b4590a9905fa1c9cc273f32e6dc63b4c512f0ee14edc6fa41c26b416a7b5d58")) >> Mono.just(BlockContainer.from(block, objectMapper))
1 * txCache.read(TxId.from("0x0500219f2b147f3013e9030d585e8e5d45401ebd2620a42c879c0d5d1b754073")) >> Mono.empty()
}
}

View File

@@ -53,7 +53,7 @@ class FilteredApisSpec extends Specification {
(EthereumWs) null,
new UpstreamsConfig.Options(),
new QuorumForLabels.QuorumItem(1, UpstreamsConfig.Labels.fromMap(it)),
ethereumTargets
ethereumTargets, TestingCommons.objectMapper()
)
}
def matcher = new Selector.LabelMatcher("test", ["foo"])

View File

@@ -15,23 +15,31 @@
*/
package io.emeraldpay.dshackle.upstream.ethereum
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.json.BlockJson
import reactor.core.publisher.Flux
import reactor.test.StepVerifier
import spock.lang.Specification
import java.time.Instant
class DefaultEthereumHeadSpec extends Specification {
DefaultEthereumHead head = new DefaultEthereumHead()
ObjectMapper objectMapper = TestingCommons.objectMapper()
def blocks = (10L..20L).collect { i ->
new BlockJson().with {
it.number = 10000L + i
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec89152" + i)
it.totalDifficulty = 11 * i
return it
}
BlockContainer.from(
new BlockJson().with {
it.number = 10000L + i
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec89152" + i)
it.totalDifficulty = 11 * i
it.timestamp = Instant.now()
return it
}, objectMapper)
}
def "Starts to follow"() {
@@ -80,12 +88,14 @@ class DefaultEthereumHeadSpec extends Specification {
def "Ignores less difficult"() {
when:
def block3less = new BlockJson().with {
it.number = blocks[3].number
it.hash = blocks[3].hash
it.totalDifficulty = blocks[3].totalDifficulty - 1
return it
}
def block3less = BlockContainer.from(
new BlockJson().with {
it.number = blocks[3].height
it.hash = BlockHash.from(blocks[3].hash.value)
it.totalDifficulty = blocks[3].difficulty - 1
it.timestamp = Instant.now()
return it
}, objectMapper)
head.follow(Flux.just(blocks[0], blocks[3], block3less))
def act = head.flux
then:
@@ -97,12 +107,14 @@ class DefaultEthereumHeadSpec extends Specification {
def "Replaces with more difficult"() {
when:
def block3less = new BlockJson().with {
it.number = blocks[3].number
it.hash = blocks[3].hash
it.totalDifficulty = blocks[3].totalDifficulty + 1
return it
}
def block3less = BlockContainer.from(
new BlockJson().with {
it.number = blocks[3].height
it.hash = BlockHash.from(blocks[3].hash.value)
it.totalDifficulty = blocks[3].difficulty + 1
it.timestamp = Instant.now()
return it
}, objectMapper)
head.follow(Flux.just(blocks[0], blocks[3], block3less))
def act = head.flux
then:

View File

@@ -15,8 +15,12 @@
*/
package io.emeraldpay.dshackle.upstream.ethereum
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.HeadLagObserver
import io.emeraldpay.dshackle.upstream.Upstream
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.json.BlockJson
import reactor.core.publisher.Flux
import reactor.core.publisher.TopicProcessor
@@ -25,9 +29,12 @@ import reactor.util.function.Tuples
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
class EthereumHeadLagObserverSpec extends Specification {
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Updates lag distance"() {
setup:
EthereumHead master = Mock()
@@ -43,11 +50,15 @@ class EthereumHeadLagObserverSpec extends Specification {
}
def blocks = [100, 101, 102].collect { i ->
return new BlockJson().with {
it.number = i
it.totalDifficulty = 2000 + i
return it
}
return BlockContainer.from(
new BlockJson().with {
it.number = i
it.totalDifficulty = 2000 + i
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915" + i)
it.timestamp = Instant.now()
return it
},
objectMapper)
}
def masterBus = TopicProcessor.create()
@@ -83,11 +94,15 @@ class EthereumHeadLagObserverSpec extends Specification {
Upstream up = Mock()
def blocks = [100, 101, 102].collect { i ->
return new BlockJson().with {
it.number = i
it.totalDifficulty = 2000 + i
return it
}
return BlockContainer.from(
new BlockJson().with {
it.number = i
it.totalDifficulty = 2000 + i
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915" + i)
it.timestamp = Instant.now()
return it
},
objectMapper)
}
def upblocks = Flux.fromIterable(blocks)
@@ -109,14 +124,18 @@ class EthereumHeadLagObserverSpec extends Specification {
def top = new BlockJson().with {
it.number = topHeight
it.totalDifficulty = topDiff
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915123")
it.timestamp = Instant.now()
return it
}
def curr = new BlockJson().with {
it.number = currHeight
it.totalDifficulty = currDiff
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915123")
it.timestamp = Instant.now()
return it
}
delta as Long == observer.extractDistance(top, curr)
delta as Long == observer.extractDistance(BlockContainer.from(top, objectMapper), BlockContainer.from(curr, objectMapper))
where:
topHeight | topDiff | currHeight | currDiff | delta
100 | 1000 | 100 | 1000 | 0

View File

@@ -1,8 +1,11 @@
package io.emeraldpay.dshackle.upstream.ethereum
import com.fasterxml.jackson.databind.ObjectMapper
import io.emeraldpay.dshackle.cache.BlocksMemCache
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.HeightCache
import io.emeraldpay.dshackle.data.BlockContainer
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.test.TestingCommons
import io.infinitape.etherjar.domain.BlockHash
import io.infinitape.etherjar.rpc.ReactorRpcClient
@@ -19,26 +22,30 @@ import java.time.temporal.ChronoUnit
class EthereumWsSpec extends Specification {
ObjectMapper objectMapper = TestingCommons.objectMapper()
def "Uses cache to fetch block"() {
setup:
ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient)
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock)
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock, objectMapper)
def blocksCache = Mock(BlocksMemCache)
def caches = Caches.newBuilder().setBlockByHash(blocksCache).build()
def caches = Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).build()
ws.setCaches(caches)
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block.totalDifficulty = BigInteger.ONE
when:
ws.onNewBlock(block)
then:
1 * blocksCache.read(BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")) >> Mono.just(block)
1 * blocksCache.read(BlockId.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")) >> Mono.just(BlockContainer.from(block, objectMapper))
StepVerifier.create(ws.flux.take(1))
.expectNext(block)
.expectNext(BlockContainer.from(block, objectMapper))
.expectComplete()
.verify(Duration.ofSeconds(1))
}
@@ -47,16 +54,18 @@ class EthereumWsSpec extends Specification {
setup:
ReactorRpcClient rpcClient = Stub(ReactorRpcClient)
def apiMock = TestingCommons.api(rpcClient)
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock)
def ws = new EthereumWs(new URI("http://localhost"), new URI("http://localhost"), apiMock, objectMapper)
def blocksCache = Mock(BlocksMemCache)
def caches = Caches.newBuilder().setBlockByHash(blocksCache).build()
def caches = Caches.newBuilder().setObjectMapper(objectMapper).setBlockByHash(blocksCache).build()
ws.setCaches(caches)
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200")
block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS)
block.transactions = []
block.uncles = []
block.totalDifficulty = BigInteger.ONE
apiMock.answerOnce("eth_getBlockByHash", ["0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200", false], block)
@@ -66,7 +75,7 @@ class EthereumWsSpec extends Specification {
then:
1 * blocksCache.read(_) >> Mono.empty()
StepVerifier.create(ws.flux.take(1))
.expectNext(block)
.expectNext(BlockContainer.from(block, objectMapper))
.expectComplete()
.verify(Duration.ofSeconds(1))
}

View File

@@ -20,6 +20,7 @@ import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainGrpc
import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.data.BlockId
import io.emeraldpay.dshackle.test.MockServer
import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.UpstreamAvailability
@@ -32,6 +33,7 @@ import io.infinitape.etherjar.rpc.json.BlockJson
import spock.lang.Specification
import java.time.Duration
import java.time.Instant
import java.util.concurrent.CompletableFuture
class EthereumGrpcUpstreamSpec extends Specification {
@@ -48,6 +50,7 @@ class EthereumGrpcUpstreamSpec extends Specification {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
@@ -81,7 +84,7 @@ class EthereumGrpcUpstreamSpec extends Specification {
then:
callData.chain == Chain.ETHEREUM.id
upstream.status == UpstreamAvailability.OK
h.hash == BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
h.hash == BlockId.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
}
def "Follows difficulty, ignores less difficult"() {
@@ -91,12 +94,14 @@ class EthereumGrpcUpstreamSpec extends Specification {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
return it
}
def block2 = new BlockJson().with {
it.number = 650247
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
it.totalDifficulty = new BigInteger("35bbde5595de6455", 16)
it.timestamp = Instant.now()
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
@@ -136,8 +141,8 @@ class EthereumGrpcUpstreamSpec extends Specification {
def h = upstream.head.getFlux().take(Duration.ofSeconds(1)).last().block()
then:
upstream.status == UpstreamAvailability.OK
h.hash == BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
h.number == 650246
h.hash == BlockId.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
h.height == 650246
}
def "Follows difficulty"() {
@@ -150,12 +155,14 @@ class EthereumGrpcUpstreamSpec extends Specification {
it.number = 650246
it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7")
it.totalDifficulty = new BigInteger("35bbde5595de6456", 16)
it.timestamp = Instant.now()
return it
}
def block2 = new BlockJson().with {
it.number = 650247
it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
it.totalDifficulty = new BigInteger("35bbde5595de6457", 16)
it.timestamp = Instant.now()
return it
}
api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1)
@@ -197,7 +204,7 @@ class EthereumGrpcUpstreamSpec extends Specification {
def h = upstream.head.getFlux().take(Duration.ofSeconds(1)).last().block()
then:
upstream.status == UpstreamAvailability.OK
h.hash == BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
h.number == 650247
h.hash == BlockId.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a")
h.height == 650247
}
}