From bdcb7386cd494cc3a020ceae39cff353ca5bb05d Mon Sep 17 00:00:00 2001 From: KirillPamPam Date: Tue, 28 Mar 2023 15:16:39 +0400 Subject: [PATCH] Fix distance for fork and add parentHash to blockContainer (#186) * Fix distance for fork --- emerald-grpc | 2 +- .../dshackle/cache/BlocksRedisCache.kt | 1 + .../dshackle/cache/OnBlockRedisCache.kt | 1 + .../dshackle/data/BlockContainer.kt | 16 ++++++-- .../io/emeraldpay/dshackle/rpc/StreamHead.kt | 1 + .../dshackle/upstream/DistanceExtractor.kt | 5 ++- .../dshackle/upstream/HeadLagObserver.kt | 1 + .../dshackle/upstream/bitcoin/ExtractBlock.kt | 2 + .../upstream/grpc/BitcoinGrpcUpstream.kt | 6 ++- .../upstream/grpc/EthereumGrpcUpstream.kt | 6 ++- .../upstream/grpc/EthereumPosGrpcUpstream.kt | 6 ++- src/main/proto/cache.proto | 1 + .../dshackle/cache/BlockByHeightSpec.groovy | 8 +++- .../dshackle/cache/BlocksMemCacheSpec.groovy | 3 ++ .../dshackle/cache/CachesSpec.groovy | 7 ++++ .../cache/HeightByHashAddingSpec.groovy | 3 +- .../dshackle/cache/HeightCacheSpec.groovy | 3 +- .../dshackle/cache/ReceiptMemCacheSpec.groovy | 1 + .../dshackle/cache/TxMemCacheSpec.groovy | 1 + .../dshackle/rpc/StreamHeadSpec.groovy | 2 + .../rpc/TrackBitcoinAddressSpec.groovy | 4 +- .../dshackle/rpc/TrackBitcoinTxSpec.groovy | 9 +++-- .../rpc/TrackEthereumAddressSpec.groovy | 4 +- .../dshackle/rpc/TrackEthereumTxSpec.groovy | 9 +++-- .../dshackle/test/TestingCommons.groovy | 3 ++ .../dshackle/upstream/AbstractHeadSpec.groovy | 7 ++-- .../upstream/DistanceExtractorSpec.groovy | 38 ++++++++++++++++++- .../upstream/HeadLagObserverSpec.groovy | 3 ++ .../ethereum/DefaultEthereumHeadSpec.groovy | 4 ++ .../EthereumBlockValidatorSpec.groovy | 1 + .../ethereum/EthereumCachingReaderSpec.groovy | 4 ++ .../ethereum/EthereumWsHeadSpec.groovy | 5 +++ .../subscribe/ConnectBlockUpdatesSpec.groovy | 9 +++++ .../forkchoice/MostWorkForkChoiceSpec.groovy | 2 +- .../NoChoiceWithPriorityForkChoiceSpec.groovy | 2 +- .../forkchoice/PriorityForkChoiceSpec.groovy | 2 +- .../grpc/EthereumGrpcUpstreamSpec.groovy | 11 ++++++ 37 files changed, 163 insertions(+), 30 deletions(-) diff --git a/emerald-grpc b/emerald-grpc index 24d4660e..45b1c29d 160000 --- a/emerald-grpc +++ b/emerald-grpc @@ -1 +1 @@ -Subproject commit 24d4660e266de4f73d2c281128bb4031ae06c05a +Subproject commit 45b1c29d5194352d372a577c25acfcb7368811ac diff --git a/src/main/kotlin/io/emeraldpay/dshackle/cache/BlocksRedisCache.kt b/src/main/kotlin/io/emeraldpay/dshackle/cache/BlocksRedisCache.kt index be7b4b17..7910bf06 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/cache/BlocksRedisCache.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/cache/BlocksRedisCache.kt @@ -66,6 +66,7 @@ class BlocksRedisCache( false, value.value.toByteArray(), null, + BlockId.from(meta.parentHash.toByteArray()), meta.txHashesList.map { TxId(it.toByteArray()) } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/cache/OnBlockRedisCache.kt b/src/main/kotlin/io/emeraldpay/dshackle/cache/OnBlockRedisCache.kt index 44006440..47e1abf5 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/cache/OnBlockRedisCache.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/cache/OnBlockRedisCache.kt @@ -62,6 +62,7 @@ abstract class OnBlockRedisCache( .setHash(ByteString.copyFrom(block.hash.value)) .setHeight(block.height) .setDifficulty(ByteString.copyFrom(block.difficulty.toByteArray())) + .setParentHash(ByteString.copyFrom(block.parentHash?.value ?: byteArrayOf())) .setTimestamp(block.timestamp.toEpochMilli()) } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/data/BlockContainer.kt b/src/main/kotlin/io/emeraldpay/dshackle/data/BlockContainer.kt index b0454760..025636ff 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/data/BlockContainer.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/data/BlockContainer.kt @@ -35,9 +35,10 @@ class BlockContainer( val full: Boolean, json: ByteArray?, val parsed: Any?, + val parentHash: BlockId?, val transactions: List = emptyList(), val nodeRating: Int = 0, - val upstreamId: String = "" + val upstreamId: String = "", ) : SourceContainer(json, parsed) { val enriched: Boolean = transactions.isNotEmpty() @@ -45,6 +46,7 @@ class BlockContainer( @JvmStatic fun from(block: BlockJson<*>, raw: ByteArray, upstreamId: String): BlockContainer { val hasTransactions = !block.transactions?.filterIsInstance().isNullOrEmpty() + val parent = if (block.parentHash == null) null else BlockId.from(block.parentHash) return BlockContainer( height = block.number, hash = BlockId.from(block), @@ -54,7 +56,8 @@ class BlockContainer( json = raw, parsed = block, transactions = block.transactions?.map { TxId.from(it.hash) } ?: emptyList(), - upstreamId = upstreamId + upstreamId = upstreamId, + parentHash = parent ) } @@ -91,12 +94,17 @@ class BlockContainer( if (timestamp != other.timestamp) return false if (full != other.full) return false if (transactions != other.transactions) return false + if (parentHash != null && other.parentHash != null) { + if (parentHash != other.parentHash) return false + } return true } fun copyWithRating(nodeRating: Int): BlockContainer { - return BlockContainer(height, hash, difficulty, timestamp, full, json, parsed, transactions, nodeRating) + return BlockContainer( + height, hash, difficulty, timestamp, full, json, parsed, parentHash, transactions, nodeRating + ) } override fun hashCode(): Int { @@ -115,7 +123,6 @@ class BlockContainer( BlockJson().also { it.number = height it.hash = BlockHash.from(hash.value) - it.parentHash = BlockHash.empty() it.timestamp = timestamp it.difficulty = difficulty it.gasLimit = 0 @@ -123,6 +130,7 @@ class BlockContainer( it.logsBloom = Bloom.empty() it.miner = Address.empty() it.baseFeePerGas = Wei.ZERO + it.parentHash = if (parentHash != null) BlockHash.from(parentHash.value) else BlockHash.empty() } } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/rpc/StreamHead.kt b/src/main/kotlin/io/emeraldpay/dshackle/rpc/StreamHead.kt index 3d626c62..29fc1cee 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/rpc/StreamHead.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/rpc/StreamHead.kt @@ -55,6 +55,7 @@ class StreamHead( .setTimestamp(block.timestamp.toEpochMilli()) .setWeight(ByteString.copyFrom(block.difficulty.toByteArray())) .setBlockId(block.hash.toHex()) + .setParentBlockId(block.parentHash?.toHex() ?: "") .build() } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/DistanceExtractor.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/DistanceExtractor.kt index 677d1e7f..08cd9c69 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/DistanceExtractor.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/DistanceExtractor.kt @@ -19,9 +19,10 @@ class DistanceExtractor { fun extractPriorityDistance(top: BlockContainer, curr: BlockContainer): ChainDistance { return when { - curr.height > top.height -> ChainDistance.Fork + (curr.parentHash != null && curr.height - top.height == 1L) -> + if (curr.parentHash == top.hash) ChainDistance.Distance(0) else ChainDistance.Fork curr.height == top.height -> if (curr.hash == top.hash) ChainDistance.Distance(0) else ChainDistance.Fork - else -> ChainDistance.Distance(top.height - curr.height) + else -> ChainDistance.Distance((top.height - curr.height).coerceAtLeast(0)) } } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/HeadLagObserver.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/HeadLagObserver.kt index 054c4247..1712585e 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/HeadLagObserver.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/HeadLagObserver.kt @@ -61,6 +61,7 @@ abstract class HeadLagObserver( .sample(throttling) .flatMap(this::probeFollowers) .map { item -> + log.debug("Set to ${item.t2.getId()} lag = ${item.t1}") item.t2.setLag(item.t1) } } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/bitcoin/ExtractBlock.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/bitcoin/ExtractBlock.kt index 8049d886..3cc8ea68 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/bitcoin/ExtractBlock.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/bitcoin/ExtractBlock.kt @@ -56,6 +56,7 @@ class ExtractBlock { val data = objectMapper.readValue(json, Map::class.java) as Map val hash = data["hash"] as String? ?: throw IllegalArgumentException("Block JSON has no hash") + val parentHash = data["previousblockhash"] as String? ?: throw IllegalArgumentException("Block JSON has no previousblockhash") val transactions = (data["tx"] as List?)?.map(TxId.Companion::from) ?: emptyList() return BlockContainer( @@ -66,6 +67,7 @@ class ExtractBlock { false, json, data, + BlockId.from(parentHash), transactions ) } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt index 8f8fcfb4..fa408240 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/BitcoinGrpcUpstream.kt @@ -67,6 +67,9 @@ class BitcoinGrpcUpstream( private val extractBlock = ExtractBlock() private val defaultReader: JsonRpcReader = client.getReader() private val blockConverter: Function = Function { value -> + val parentHash = + if (value.parentBlockId.isBlank()) null + else BlockId.from(value.parentBlockId) val block = BlockContainer( value.height, BlockId.from(value.blockId), @@ -74,7 +77,8 @@ class BitcoinGrpcUpstream( Instant.ofEpochMilli(value.timestamp), false, null, - null + null, + parentHash ) block } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt index 724c0b9d..2c430e93 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstream.kt @@ -73,6 +73,9 @@ open class EthereumGrpcUpstream( Lifecycle { private val blockConverter: Function = Function { value -> + val parentHash = + if (value.parentBlockId.isBlank()) null + else BlockId.from(BlockHash.from("0x" + value.parentBlockId)) val block = BlockContainer( value.height, BlockId.from(BlockHash.from("0x" + value.blockId)), @@ -80,7 +83,8 @@ open class EthereumGrpcUpstream( Instant.ofEpochMilli(value.timestamp), false, null, - null + null, + parentHash ) block } diff --git a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumPosGrpcUpstream.kt b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumPosGrpcUpstream.kt index 2dcf6988..fa9c3ef3 100644 --- a/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumPosGrpcUpstream.kt +++ b/src/main/kotlin/io/emeraldpay/dshackle/upstream/grpc/EthereumPosGrpcUpstream.kt @@ -67,6 +67,9 @@ open class EthereumPosGrpcUpstream( Lifecycle { private val blockConverter: Function = Function { value -> + val parentHash = + if (value.parentBlockId.isBlank()) null + else BlockId.from(BlockHash.from("0x" + value.parentBlockId)) val block = BlockContainer( value.height, BlockId.from(BlockHash.from("0x" + value.blockId)), @@ -74,7 +77,8 @@ open class EthereumPosGrpcUpstream( Instant.ofEpochMilli(value.timestamp), false, null, - null + null, + parentHash ) block } diff --git a/src/main/proto/cache.proto b/src/main/proto/cache.proto index b4c2f229..04d0d344 100644 --- a/src/main/proto/cache.proto +++ b/src/main/proto/cache.proto @@ -31,6 +31,7 @@ message BlockMeta { bytes difficulty = 3; uint64 timestamp = 4; repeated bytes tx_hashes = 5; + bytes parent_hash = 6; } message TxMeta { diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/BlockByHeightSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/BlockByHeightSpec.groovy index d6d782ec..0ba19034 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/BlockByHeightSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/BlockByHeightSpec.groovy @@ -49,6 +49,7 @@ class BlockByHeightSpec extends Specification { block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block.uncles = [] block.transactions = List.of(tx) + block.parentHash = BlockHash.from(hash1) BlockContainer.from(block).with { blocks.add(it) @@ -77,6 +78,7 @@ class BlockByHeightSpec extends Specification { block1.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block1.uncles = [] block1.transactions = List.of(tx) + block1.parentHash = BlockHash.from(hash1) def block2 = new BlockJson() block2.number = 101 @@ -85,7 +87,7 @@ class BlockByHeightSpec extends Specification { block2.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block2.uncles = [] block2.transactions = List.of(tx) - + block2.parentHash = BlockHash.from(hash2) BlockContainer.from(block1).with { blocks.add(it) @@ -123,6 +125,7 @@ class BlockByHeightSpec extends Specification { block1.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block1.uncles = [] block1.transactions = List.of(tx) + block1.parentHash = BlockHash.from(hash1) def block2 = new BlockJson() block2.number = 100 @@ -131,6 +134,7 @@ class BlockByHeightSpec extends Specification { block2.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block2.uncles = [] block2.transactions = List.of(tx) + block2.parentHash = BlockHash.from(hash2) BlockContainer.from(block1).with { blocks.add(it) @@ -159,6 +163,7 @@ class BlockByHeightSpec extends Specification { block.hash = BlockHash.from(hash1) block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash1) // add only to heights BlockContainer.from(block).with { @@ -183,6 +188,7 @@ class BlockByHeightSpec extends Specification { block.hash = BlockHash.from(hash1) block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash1) // add only to blocks BlockContainer.from(block).with { diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/BlocksMemCacheSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/BlocksMemCacheSpec.groovy index a4b6e82b..0a3a1463 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/BlocksMemCacheSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/BlocksMemCacheSpec.groovy @@ -49,6 +49,7 @@ class BlocksMemCacheSpec extends Specification { block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block.uncles = [] block.transactions = List.of(tx) + block.parentHash = BlockHash.from(hash1) when: cache.add(BlockContainer.from(block)) @@ -73,6 +74,7 @@ class BlocksMemCacheSpec extends Specification { block.timestamp = Instant.now() block.uncles = [] block.transactions = List.of(tx) + block.parentHash = BlockHash.from(hash1) cache.add(BlockContainer.from(block)) } @@ -99,6 +101,7 @@ class BlocksMemCacheSpec extends Specification { block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block.uncles = [] block.transactions = [] + block.parentHash = BlockHash.from(hash1) when: cache.add(BlockContainer.from(block)) diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/CachesSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/CachesSpec.groovy index 3e47f69b..d926414e 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/CachesSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/CachesSpec.groovy @@ -57,6 +57,7 @@ class CachesSpec extends Specification { block1.totalDifficulty = BigInteger.ONE block1.timestamp = Instant.now() block1.transactions = [] + block1.parentHash = BlockHash.from(hash1) block1 = BlockContainer.from(block1) def block2 = new BlockJson() @@ -65,6 +66,7 @@ class CachesSpec extends Specification { block2.totalDifficulty = BigInteger.ONE block2.timestamp = Instant.now() block2.transactions = [] + block2.parentHash = BlockHash.from(hash2) block2 = BlockContainer.from(block2) when: @@ -98,6 +100,7 @@ class CachesSpec extends Specification { block1.hash = BlockHash.from(hash1) block1.totalDifficulty = BigInteger.ONE block1.timestamp = Instant.now() + block1.parentHash = BlockHash.from(hash1) block1 = BlockContainer.from(block1) def block2 = new BlockJson() @@ -105,6 +108,7 @@ class CachesSpec extends Specification { block2.hash = BlockHash.from(hash2) block2.totalDifficulty = BigInteger.ONE block2.timestamp = Instant.now() + block2.parentHash = BlockHash.from(hash2) block2 = BlockContainer.from(block2) when: @@ -138,6 +142,7 @@ class CachesSpec extends Specification { block.hash = BlockHash.from(hash1) block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash1) block.transactions = [ new TransactionRefJson(TransactionId.from(hash1)), new TransactionRefJson(TransactionId.from(hash2)), @@ -170,6 +175,7 @@ class CachesSpec extends Specification { block.totalDifficulty = BigInteger.ONE block.transactions = [tx1, tx2] block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash1) BlockContainer.from(block) } @@ -212,6 +218,7 @@ class CachesSpec extends Specification { block.totalDifficulty = BigInteger.ONE block.transactions = [tx1] block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash1) BlockContainer.from(block) } TxMemCache txCache = Mock() diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/HeightByHashAddingSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/HeightByHashAddingSpec.groovy index 47e46032..051065dd 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/HeightByHashAddingSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/HeightByHashAddingSpec.groovy @@ -27,7 +27,8 @@ class HeightByHashAddingSpec extends Specification { def block = new BlockContainer( 12079192L, BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32"), - BigInteger.ONE, Instant.now(), false, "".bytes, null, [], 0, "upstream" + BigInteger.ONE, Instant.now(), false, "".bytes, null, + BlockId.from("0xa6af163aab691919c595e2a466f0a7b01f1dff8cfd9631dee811df57064c2d32"), [], 0, "upstream" ) def "use memory if available"() { diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/HeightCacheSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/HeightCacheSpec.groovy index 89ed72c3..8577f3a5 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/HeightCacheSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/HeightCacheSpec.groovy @@ -15,7 +15,6 @@ */ package io.emeraldpay.dshackle.cache - import io.emeraldpay.dshackle.data.BlockContainer import io.emeraldpay.etherjar.domain.BlockHash import io.emeraldpay.etherjar.rpc.json.BlockJson @@ -42,6 +41,7 @@ class HeightCacheSpec extends Specification { block.hash = BlockHash.from(hash) block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash) cache.add(BlockContainer.from(block)) } @@ -68,6 +68,7 @@ class HeightCacheSpec extends Specification { block.hash = BlockHash.from(hash) block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() + block.parentHash = BlockHash.from(hash) cache.add(BlockContainer.from(block)) } cache.purge() diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/ReceiptMemCacheSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/ReceiptMemCacheSpec.groovy index 25b950f4..6f98c06d 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/ReceiptMemCacheSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/ReceiptMemCacheSpec.groovy @@ -86,6 +86,7 @@ class ReceiptMemCacheSpec extends Specification { false, "{}".bytes, null, + BlockId.from(receipt.blockHash), [TxId.from(receipt.transactionHash)], 0, "unknown" diff --git a/src/test/groovy/io/emeraldpay/dshackle/cache/TxMemCacheSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/cache/TxMemCacheSpec.groovy index b60d74ed..982a5547 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/cache/TxMemCacheSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/cache/TxMemCacheSpec.groovy @@ -135,6 +135,7 @@ class TxMemCacheSpec extends Specification { def block = new BlockJson() block.hash = BlockHash.from(hash1) + block.parentHash = BlockHash.from(hash1) block.number = 100 block.totalDifficulty = BigInteger.ONE block.timestamp = Instant.now() diff --git a/src/test/groovy/io/emeraldpay/dshackle/rpc/StreamHeadSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/rpc/StreamHeadSpec.groovy index 8bfc5ec2..93899680 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/rpc/StreamHeadSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/rpc/StreamHeadSpec.groovy @@ -62,6 +62,7 @@ class StreamHeadSpec extends Specification { return new BlockJson().with { it.number = i it.hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27${i}") + it.parentHash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27${i}") it.totalDifficulty = i * 1000 it.timestamp = Instant.ofEpochMilli(1566000000000 + i * 10000) return it @@ -74,6 +75,7 @@ class StreamHeadSpec extends Specification { .setTimestamp(it.timestamp.toEpochMilli()) .setBlockId(it.hash.toHex().substring(2)) .setWeight(ByteString.copyFrom(it.totalDifficulty.toByteArray())) + .setParentBlockId(it.parentHash.toHex().substring(2)) .setHeight(it.number) .build() } diff --git a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinAddressSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinAddressSpec.groovy index fab2eb54..2f93b0a6 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinAddressSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinAddressSpec.groovy @@ -268,7 +268,7 @@ class TrackBitcoinAddressSpec extends Specification { Head head = Mock(Head) { 1 * getFlux() >> Flux.concat( Flux.just( - new BlockContainer(0L, BlockId.from(hash1), BigInteger.ZERO, Instant.now(), false, null, null, [], 0, "TrackBitcoinAddressSpec") + new BlockContainer(0L, BlockId.from(hash1), BigInteger.ZERO, Instant.now(), false, null, null, BlockId.from(hash1), [], 0, "TrackBitcoinAddressSpec") ), blocks.asFlux() ) @@ -309,7 +309,7 @@ class TrackBitcoinAddressSpec extends Specification { StepVerifier.create(resp) .expectNext("0") .then { - blocks.tryEmitNext(new BlockContainer(1L, BlockId.from(hash1), BigInteger.ONE, Instant.now(), false, null, null, [], 0, "TrackBitcoinAddressSpec")) + blocks.tryEmitNext(new BlockContainer(1L, BlockId.from(hash1), BigInteger.ONE, Instant.now(), false, null, null, BlockId.from(hash1), [], 0, "TrackBitcoinAddressSpec")) } .expectNext("1230000") .then { diff --git a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinTxSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinTxSpec.groovy index 36ef0f59..faf6c48f 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinTxSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackBitcoinTxSpec.groovy @@ -142,7 +142,8 @@ class TrackBitcoinTxSpec extends Specification { def txid = "69cd44d7c641db82e69824523c7ac0c5c1e5628f025474529cf5ffe64527efc9" // start with the current block def next = Flux.fromIterable([10, 12, 13, 14, 15]).map { h -> - new BlockContainer(h.longValue(), BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f"), BigInteger.ONE, Instant.now(), false, null, null, [], 0, "unknown") + def hash = BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f") + new BlockContainer(h.longValue(), hash, BigInteger.ONE, Instant.now(), false, null, null, hash, [], 0, "unknown") } Head head = Mock(Head) { 1 * getFlux() >> next @@ -173,7 +174,8 @@ class TrackBitcoinTxSpec extends Specification { def txid = "69cd44d7c641db82e69824523c7ac0c5c1e5628f025474529cf5ffe64527efc9" // start with the current block def next = Flux.fromIterable([10, 12, 13]).map { h -> - new BlockContainer(h.longValue(), BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f"), BigInteger.ONE, Instant.now(), false, null, null, [], 0, "unknown") + def hash = BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f") + new BlockContainer(h.longValue(), hash, BigInteger.ONE, Instant.now(), false, null, null, hash, [], 0, "unknown") } Head head = Mock(Head) { 1 * getFlux() >> next @@ -268,7 +270,8 @@ class TrackBitcoinTxSpec extends Specification { ]) } def next = Flux.fromIterable([10, 11, 12]).map { h -> - new BlockContainer(h.longValue(), BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f"), BigInteger.ONE, Instant.now(), false, null, null, [], 0, "unknown") + def hash = BlockId.from("0000000000000000000895d1b9d3898700e1deecc3b0e69f439aa77875e6042f") + new BlockContainer(h.longValue(), hash, BigInteger.ONE, Instant.now(), false, null, null, hash, [], 0, "unknown") } Head head = Mock(Head) { _ * getFlux() >> next diff --git a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumAddressSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumAddressSpec.groovy index 9e2c6274..7d5ae6c0 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumAddressSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumAddressSpec.groovy @@ -87,10 +87,12 @@ class TrackEthereumAddressSpec extends Specification { .setBalance("65432") .build() + def hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22") def block2 = new BlockJson().with { it.number = 1 it.totalDifficulty = 100 - it.hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22") + it.hash = hash + it.parentHash = hash it.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) return it } diff --git a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumTxSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumTxSpec.groovy index e80ef4f7..d958891c 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumTxSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/rpc/TrackEthereumTxSpec.groovy @@ -47,7 +47,7 @@ class TrackEthereumTxSpec extends Specification { def chain = Common.ChainRef.CHAIN_ETHEREUM def txId = "0xba61ce4672751fd6086a9ac2b55547a5555af17535b6c0334ede2ecb6d64070a" - + def parent = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22") def "Gives details for an old transaction"() { setup: @@ -61,6 +61,7 @@ class TrackEthereumTxSpec extends Specification { it.hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22") it.timestamp = Instant.ofEpochMilli(156400000000) it.number = 100 + it.parentHash = parent it.totalDifficulty = BigInteger.valueOf(500) it } @@ -70,6 +71,7 @@ class TrackEthereumTxSpec extends Specification { it.timestamp = Instant.ofEpochMilli(156400200000) it.number = 108 it.totalDifficulty = BigInteger.valueOf(800) + it.parentHash = parent it.transactions = [] it } @@ -198,7 +200,7 @@ class TrackEthereumTxSpec extends Specification { def tx = new TrackEthereumTx.TxDetails(Chain.ETHEREUM, Instant.now(), TransactionId.from(txId), 6) def block = new BlockContainer( 100, BlockId.from(txId), BigInteger.ONE, Instant.now(), false, "".bytes, null, - [TxId.from(txId)], 0, "unknown" + BlockId.from(txId), [TxId.from(txId)], 0, "unknown" ) when: @@ -219,7 +221,7 @@ class TrackEthereumTxSpec extends Specification { def tx = new TrackEthereumTx.TxDetails(Chain.ETHEREUM, Instant.now(), TransactionId.from(txId), 6) def block = new BlockContainer( - 100, BlockId.from(txId), BigInteger.ONE, Instant.now(), false, "".bytes, null, + 100, BlockId.from(txId), BigInteger.ONE, Instant.now(), false, "".bytes, null, BlockId.from(txId), [TxId.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22")], 0, "unknown" ) @@ -247,6 +249,7 @@ class TrackEthereumTxSpec extends Specification { it.hash = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a2000${i}") it.timestamp = Instant.ofEpochMilli(156400000000 + i * 10000) it.setNumber(100L + i.longValue()) + it.parentHash = parent it.totalDifficulty = BigInteger.valueOf(500 + i) it } diff --git a/src/test/groovy/io/emeraldpay/dshackle/test/TestingCommons.groovy b/src/test/groovy/io/emeraldpay/dshackle/test/TestingCommons.groovy index d9d509cb..8a4bac7b 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/test/TestingCommons.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/test/TestingCommons.groovy @@ -123,6 +123,7 @@ class TestingCommons { static BlockContainer blockForEthereum(Long height) { BlockJson block = new BlockJson().tap { setNumber(height) + setParentHash(BlockHash.from("0xc4b01774e426325b50f0c709753ec7cf1f1774439d587dfb91f2a4eeb8179cde")) setHash(BlockHash.from("0xc4b01774e426325b50f0c709753ec7cf1f1774439d587dfb91f2a4eeb8179cde")) setTotalDifficulty(BigInteger.ONE) setTimestamp(predictableTimestamp(height, 14)) @@ -131,6 +132,7 @@ class TestingCommons { } static BlockContainer blockForBitcoin(Long height) { + def parent = BlockId.from(StringUtils.leftPad(height.toString(), 64, "0")) return new BlockContainer( height, BlockId.from(StringUtils.leftPad(height.toString(), 64, "0")), @@ -139,6 +141,7 @@ class TestingCommons { false, null, null, + parent, [], 0, "upstream" diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/AbstractHeadSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/AbstractHeadSpec.groovy index 7d0b766f..53add366 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/AbstractHeadSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/AbstractHeadSpec.groovy @@ -31,7 +31,7 @@ class AbstractHeadSpec extends Specification { def blocks = [1L, 2, 3, 4].collect { i -> byte[] hash = new byte[32] hash[0] = i as byte - new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, [], 0, "AbstractHeadSpec") + new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, BlockId.from(hash), [], 0, "AbstractHeadSpec") } def "Calls beforeBlock on each block"() { @@ -93,11 +93,12 @@ class AbstractHeadSpec extends Specification { setup: Sinks.Many source = Sinks.many().unicast().onBackpressureBuffer() def head = new TestHead() + def hash = BlockId.from(blocks[1].hash.value.clone().tap { it[1] = 0xff as byte }) def wrongblock = new BlockContainer( - blocks[1].height, BlockId.from(blocks[1].hash.value.clone().tap { it[1] = 0xff as byte }), + blocks[1].height, hash, blocks[1].difficulty - 1, Instant.now(), - false, null, null, [], 0, "AbstractHeadSpec" + false, null, null, hash, [], 0, "AbstractHeadSpec" ) when: head.follow(source.asFlux()) diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/DistanceExtractorSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/DistanceExtractorSpec.groovy index 0fe56a8a..69dba530 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/DistanceExtractorSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/DistanceExtractorSpec.groovy @@ -8,6 +8,8 @@ import spock.lang.Specification import java.time.Instant class DistanceExtractorSpec extends Specification { + BlockHash parent = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") + def "Correct distance for PoW"() { expect: def top = new BlockJson().with { @@ -15,6 +17,7 @@ class DistanceExtractorSpec extends Specification { it.totalDifficulty = topDiff it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915123") it.timestamp = Instant.now() + it.parentHash = parent return it } def curr = new BlockJson().with { @@ -22,6 +25,7 @@ class DistanceExtractorSpec extends Specification { it.totalDifficulty = currDiff it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915123") it.timestamp = Instant.now() + it.parentHash = parent return it } delta as DistanceExtractor.ChainDistance == DistanceExtractor.@Companion.extractPowDistance(BlockContainer.from(top), BlockContainer.from(curr)) @@ -49,6 +53,7 @@ class DistanceExtractorSpec extends Specification { it.totalDifficulty = 0 it.hash = BlockHash.from(hashA == 0 ? hash1 : hash2) it.timestamp = Instant.now() + it.parentHash = parent return it } def curr = new BlockJson().with { @@ -56,6 +61,7 @@ class DistanceExtractorSpec extends Specification { it.totalDifficulty = 0 it.hash = BlockHash.from(hashB == 0 ? hash1 : hash2) it.timestamp = Instant.now() + it.parentHash = top.hash return it } delta as DistanceExtractor.ChainDistance == DistanceExtractor.@Companion.extractPriorityDistance(BlockContainer.from(top), BlockContainer.from(curr)) @@ -67,8 +73,36 @@ class DistanceExtractorSpec extends Specification { 103 | 0 | 100 | 1 || new DistanceExtractor.ChainDistance.Distance(3) 150 | 0 | 100 | 1 || new DistanceExtractor.ChainDistance.Distance(50) - 100 | 0 | 101 | 1 || DistanceExtractor.ChainDistance.Fork.INSTANCE - 100 | 0 | 102 | 1 || DistanceExtractor.ChainDistance.Fork.INSTANCE + 100 | 0 | 101 | 1 || new DistanceExtractor.ChainDistance.Distance(0) + 100 | 0 | 102 | 1 || new DistanceExtractor.ChainDistance.Distance(0) 100 | 0 | 100 | 1 || DistanceExtractor.ChainDistance.Fork.INSTANCE } + + def "Correct distance if parentHash is null"() { + setup: + def hash1 = "0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915123" + def hash2 = "0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915124" + expect: + def top = new BlockJson().with { + it.number = topHeight + it.totalDifficulty = 0 + it.hash = BlockHash.from(hashA == 0 ? hash1 : hash2) + it.timestamp = Instant.now() + it.parentHash = parent + return it + } + def curr = new BlockJson().with { + it.number = currHeight + it.totalDifficulty = 0 + it.hash = BlockHash.from(hashB == 0 ? hash1 : hash2) + it.timestamp = Instant.now() + it.parentHash = null + return it + } + delta as DistanceExtractor.ChainDistance == DistanceExtractor.@Companion.extractPriorityDistance(BlockContainer.from(top), BlockContainer.from(curr)) + where: + topHeight | hashA | currHeight | hashB || delta + 105 | 0 | 100 | 0 || new DistanceExtractor.ChainDistance.Distance(5) + 100 | 0 | 101 | 1 || new DistanceExtractor.ChainDistance.Distance(0) + } } diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/HeadLagObserverSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/HeadLagObserverSpec.groovy index b667be41..090e043c 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/HeadLagObserverSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/HeadLagObserverSpec.groovy @@ -30,6 +30,7 @@ import java.time.Duration import java.time.Instant class HeadLagObserverSpec extends Specification { + def parent = BlockHash.from("0xa0e65cbc1b52a8ca60562112c6060552d882f16f34a9dba2ccdc05c0a6a27c22") def "Updates lag distance"() { setup: @@ -49,6 +50,7 @@ class HeadLagObserverSpec extends Specification { return BlockContainer.from( new BlockJson().tap { it.number = i + it.parentHash = parent it.totalDifficulty = 2000 + i it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915" + i) it.timestamp = Instant.now() @@ -91,6 +93,7 @@ class HeadLagObserverSpec extends Specification { return BlockContainer.from( new BlockJson().tap { it.number = i + it.parentHash = parent it.totalDifficulty = 2000 + i it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915" + i) it.timestamp = Instant.now() diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/DefaultEthereumHeadSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/DefaultEthereumHeadSpec.groovy index 4482bb9f..c5acc552 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/DefaultEthereumHeadSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/DefaultEthereumHeadSpec.groovy @@ -33,6 +33,7 @@ class DefaultEthereumHeadSpec extends Specification { DefaultEthereumHead head = new DefaultEthereumHead("upstream", new MostWorkForkChoice(), BlockValidator.ALWAYS_VALID) ObjectMapper objectMapper = Global.objectMapper + BlockHash parent = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915210") def blocks = (10L..20L).collect { i -> BlockContainer.from( @@ -41,6 +42,7 @@ class DefaultEthereumHeadSpec extends Specification { it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec89152" + i) it.totalDifficulty = 11 * i it.timestamp = Instant.now() + it.parentHash = parent }) } @@ -95,6 +97,7 @@ class DefaultEthereumHeadSpec extends Specification { it.number = blocks[3].height it.hash = BlockHash.from(blocks[3].hash.value) it.totalDifficulty = blocks[3].difficulty - 1 + it.parentHash = parent it.timestamp = Instant.now() }) head.follow(Flux.just(blocks[0], blocks[3], block3less)) @@ -113,6 +116,7 @@ class DefaultEthereumHeadSpec extends Specification { it.number = blocks[3].height it.hash = BlockHash.from(blocks[3].hash.value) it.totalDifficulty = blocks[3].difficulty + 1 + it.parentHash = parent it.timestamp = Instant.now() }) head.follow(Flux.just(blocks[0], blocks[3], block3less)) diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumBlockValidatorSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumBlockValidatorSpec.groovy index 571e415c..71c35a48 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumBlockValidatorSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumBlockValidatorSpec.groovy @@ -51,6 +51,7 @@ class EthereumBlockValidatorSpec extends Specification { block.timestamp, true, bytes, block, + BlockId.from(block.hash.toHex()), Collections.emptyList(), 1, "upstream" diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumCachingReaderSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumCachingReaderSpec.groovy index c76fce21..58ce62a3 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumCachingReaderSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumCachingReaderSpec.groovy @@ -43,6 +43,7 @@ class EthereumDirectReaderSpec extends Specification { hash = BlockHash.from(hash1) timestamp = Instant.now() totalDifficulty = BigInteger.ONE + parentHash = BlockHash.from(hash1) transactions = [] } def up = Mock(Multistream) { @@ -108,6 +109,7 @@ class EthereumDirectReaderSpec extends Specification { hash = BlockHash.from(hash1) timestamp = Instant.now() totalDifficulty = BigInteger.ONE + parentHash = BlockHash.from(hash1) transactions = [] } def up = Mock(Multistream) { @@ -341,6 +343,7 @@ class EthereumDirectReaderSpec extends Specification { setup: def json = new BlockJson().tap { number = 100 + parentHash = BlockHash.from(hash1) hash = BlockHash.from(hash1) timestamp = Instant.now() totalDifficulty = BigInteger.ONE @@ -386,6 +389,7 @@ class EthereumDirectReaderSpec extends Specification { hash = BlockHash.from(hash1) timestamp = Instant.now() totalDifficulty = BigInteger.ONE + parentHash = BlockHash.from(hash1) transactions = [] } def up = Mock(Multistream) { diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumWsHeadSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumWsHeadSpec.groovy index db7d2451..594b7cec 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumWsHeadSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/EthereumWsHeadSpec.groovy @@ -35,11 +35,14 @@ import java.time.temporal.ChronoUnit class EthereumWsHeadSpec extends Specification { + BlockHash parent = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200") + def "Fetch block"() { setup: def block = new BlockJson() block.number = 100 block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200") + block.parentHash = parent block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block.transactions = [ new TransactionRefJson(TransactionId.from("0x29229361dc5aa1ec66c323dc7a299e2b61a8c8dd2a3522d41255ec10eca25dd8")), @@ -80,8 +83,10 @@ class EthereumWsHeadSpec extends Specification { def block = new BlockJson() block.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) block.number = 103 + block.parentHash = parent block.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec8915200") def secondBlock = new BlockJson() + secondBlock.parentHash = parent secondBlock.timestamp = Instant.now().truncatedTo(ChronoUnit.SECONDS) secondBlock.number = 105 secondBlock.hash = BlockHash.from("0x29229361dc5aa1ec66c323dc7a299e2b61a8c8dd2a3522d41255ec10eca25dd8") diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/subscribe/ConnectBlockUpdatesSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/subscribe/ConnectBlockUpdatesSpec.groovy index cf5a6fb2..2307e774 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/subscribe/ConnectBlockUpdatesSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/ethereum/subscribe/ConnectBlockUpdatesSpec.groovy @@ -33,6 +33,8 @@ import java.time.Instant class ConnectBlockUpdatesSpec extends Specification { + BlockHash parent = BlockHash.from("0xe5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7db1350fd527fec4885c") + def "Extracts updates"() { setup: def connectBlockUpdates = new ConnectBlockUpdates(Stub(EthereumMultistream)) @@ -41,6 +43,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) @@ -74,6 +77,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) @@ -107,6 +111,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) @@ -117,6 +122,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) @@ -126,6 +132,7 @@ class ConnectBlockUpdatesSpec extends Specification { hash = BlockHash.from("0xdb1350fd527fec4885ce5be2159b2b7daf6b126babdcbaa349da668b92d6b8c7") number = 13412872 totalDifficulty = BigInteger.ONE + parentHash = parent timestamp = Instant.now() transactions = [ new TransactionRefJson(TransactionId.from("0x9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af6c88df9d65ccc")), @@ -168,6 +175,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) @@ -178,6 +186,7 @@ class ConnectBlockUpdatesSpec extends Specification { number = 13412871 totalDifficulty = BigInteger.ONE timestamp = Instant.now() + parentHash = parent transactions = [ new TransactionRefJson(TransactionId.from("0x6c88df9d65ccc9351db65676c3581b29483e8dabb71c48ef7671c44b0d5568af")), new TransactionRefJson(TransactionId.from("0x5c241a64e7ce536fdb6b8912091151f18a23dd71cc76a48a4b7d453e339efbe2")) diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/MostWorkForkChoiceSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/MostWorkForkChoiceSpec.groovy index e4488f23..594ac633 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/MostWorkForkChoiceSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/MostWorkForkChoiceSpec.groovy @@ -11,7 +11,7 @@ class MostWorkForkChoiceSpec extends Specification { def blocks = [1L, 2, 3, 4].collect { i -> byte[] hash = new byte[32] hash[0] = i as byte - new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, [], 0, "MostWorkForkChoiceSpec") + new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, BlockId.from(hash), [], 0, "MostWorkForkChoiceSpec") } def "filters blocks"() { diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/NoChoiceWithPriorityForkChoiceSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/NoChoiceWithPriorityForkChoiceSpec.groovy index de081b35..8a959dca 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/NoChoiceWithPriorityForkChoiceSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/NoChoiceWithPriorityForkChoiceSpec.groovy @@ -10,7 +10,7 @@ class NoChoiceWithPriorityForkChoiceSpec extends Specification { def blocks = [1L, 2, 3, 4].collect { i -> byte[] hash = new byte[32] hash[0] = i as byte - new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, [], 0, "NoChoiceWithPriorityForkChoiceSpec") + new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, BlockId.from(hash), [], 0, "NoChoiceWithPriorityForkChoiceSpec") } def "filters blocks"() { diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/PriorityForkChoiceSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/PriorityForkChoiceSpec.groovy index 7ef34289..37004a63 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/PriorityForkChoiceSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/forkchoice/PriorityForkChoiceSpec.groovy @@ -10,7 +10,7 @@ class PriorityForkChoiceSpec extends Specification { def blocks = [1L, 2, 3, 4].collect { i -> byte[] hash = new byte[32] hash[0] = i as byte - new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, [], i.toInteger(), "PriorityForkChoiceSpec") + new BlockContainer(i, BlockId.from(hash), BigInteger.valueOf(i), Instant.now(), false, null, null, BlockId.from(hash), [], i.toInteger(), "PriorityForkChoiceSpec") } def "filters blocks"() { def choice = new PriorityForkChoice() diff --git a/src/test/groovy/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstreamSpec.groovy b/src/test/groovy/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstreamSpec.groovy index 8ab3f420..c276ae1d 100644 --- a/src/test/groovy/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstreamSpec.groovy +++ b/src/test/groovy/io/emeraldpay/dshackle/upstream/grpc/EthereumGrpcUpstreamSpec.groovy @@ -51,6 +51,7 @@ class EthereumGrpcUpstreamSpec extends Specification { Timer.builder("test1").register(TestingCommons.meterRegistry), Counter.builder("test2").register(TestingCommons.meterRegistry) ) + BlockHash parent = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") def hash = (byte)123 def buildInfo = new BuildInfo("v0.0.1-test") @@ -65,6 +66,7 @@ class EthereumGrpcUpstreamSpec extends Specification { it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.totalDifficulty = new BigInteger("35bbde5595de6456", 16) it.timestamp = Instant.now() + it.parentHash = parent return it } api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1) @@ -81,6 +83,7 @@ class EthereumGrpcUpstreamSpec extends Specification { BlockchainOuterClass.ChainHead.newBuilder() .setBlockId(block1.hash.toHex().substring(2)) .setHeight(block1.number) + .setParentBlockId(parent.toHex().substring(2)) .setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray())) .build() ) @@ -115,6 +118,7 @@ class EthereumGrpcUpstreamSpec extends Specification { it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.totalDifficulty = new BigInteger("35bbde5595de6456", 16) it.timestamp = Instant.now() + it.parentHash = parent return it } def block2 = new BlockJson().with { @@ -122,6 +126,7 @@ class EthereumGrpcUpstreamSpec extends Specification { it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a") it.totalDifficulty = new BigInteger("35bbde5595de6455", 16) it.timestamp = Instant.now() + it.parentHash = parent return it } api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1) @@ -139,6 +144,7 @@ class EthereumGrpcUpstreamSpec extends Specification { BlockchainOuterClass.ChainHead.newBuilder() .setBlockId(block1.hash.toHex().substring(2)) .setHeight(block1.number) + .setParentBlockId(parent.toHex().substring(2)) .setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray())) .build() ) @@ -147,6 +153,7 @@ class EthereumGrpcUpstreamSpec extends Specification { BlockchainOuterClass.ChainHead.newBuilder() .setBlockId(block2.hash.toHex().substring(2)) .setHeight(block2.number) + .setParentBlockId(parent.toHex().substring(2)) .setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray())) .build() ) @@ -185,6 +192,7 @@ class EthereumGrpcUpstreamSpec extends Specification { it.hash = BlockHash.from("0x50d26e119968e791970d84a7bf5d0ec474d3ec2ef85d5ec8915210ac6bc09ad7") it.totalDifficulty = new BigInteger("35bbde5595de6456", 16) it.timestamp = Instant.now() + it.parentHash = parent return it } def block2 = new BlockJson().with { @@ -192,6 +200,7 @@ class EthereumGrpcUpstreamSpec extends Specification { it.hash = BlockHash.from("0x3ec2ebf5d0ec474d0ac6bc50d2770d8409ad76e119968e7919f85d5ec891521a") it.totalDifficulty = new BigInteger("35bbde5595de6457", 16) it.timestamp = Instant.now() + it.parentHash = parent return it } api.answer("eth_getBlockByHash", [block1.hash.toHex(), false], block1) @@ -208,6 +217,7 @@ class EthereumGrpcUpstreamSpec extends Specification { BlockchainOuterClass.ChainHead.newBuilder() .setBlockId(block1.hash.toHex().substring(2)) .setHeight(block1.number) + .setParentBlockId(parent.toHex().substring(2)) .setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray())) .build() ) @@ -215,6 +225,7 @@ class EthereumGrpcUpstreamSpec extends Specification { BlockchainOuterClass.ChainHead.newBuilder() .setBlockId(block2.hash.toHex().substring(2)) .setHeight(block2.number) + .setParentBlockId(parent.toHex().substring(2)) .setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray())) .build() )