Skip to content

Commit c38c2c2

Browse files
bsuiericJaap van der Plas
and
Jaap van der Plas
authored
[ETCM-797] Implement a correct seed calculation for block validation
* fix bug seed calculation * [ETCM-797] add testcase for ecip 1099 seed bug * [ETCM-797] add debug endpoint to request a specific block (by number) from connected peers * refactor EthashUtils.seed and add a comment * Revert "[ETCM-797] add debug endpoint to request a specific block (by number) from connected peers" This reverts commit 48b39b5. * remove ecip1099ActivationBlock parameter from EthashDAGManager Co-authored-by: Jaap van der Plas <[email protected]>
1 parent d1d70c2 commit c38c2c2

File tree

8 files changed

+36
-23
lines changed

8 files changed

+36
-23
lines changed

src/main/scala/io/iohk/ethereum/consensus/pow/EthashDAGManager.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class EthashDAGManager(blockCreator: EthashBlockCreator) extends Logger {
1717
(currentEpoch, currentEpochDag, currentEpochDagSize) match {
1818
case (Some(`epoch`), Some(dag), Some(dagSize)) => (dag, dagSize)
1919
case _ =>
20-
val seed = EthashUtils.seed(blockNumber)
20+
val seed = EthashUtils.seed(blockNumber, blockCreator.blockchainConfig.ecip1099BlockNumber.toLong)
2121
val dagSize = EthashUtils.dagSize(epoch)
2222
val dagNumHashes = (dagSize / EthashUtils.HASH_BYTES).toInt
2323
val dag =

src/main/scala/io/iohk/ethereum/consensus/pow/EthashUtils.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ object EthashUtils {
6161

6262
// scalastyle:on magic.number
6363

64-
private def epochBeforeEcip1099(blockNumber: Long): Long = blockNumber / EPOCH_LENGTH_BEFORE_ECIP_1099
64+
// computes seed for epoch of given blockNumber
65+
// this also involves the non-ECIP1099 epoch of the first blocks of the
66+
// ECIP1099 epoch, to make sure every block in the latter results in the same
67+
// seed being calculated, would there be a cache miss.
68+
def seed(blockNumber: Long, ecip1099ActivationBlock: Long): ByteString = {
69+
val epochLength = calcEpochLength(blockNumber, ecip1099ActivationBlock)
70+
val startBlock = (blockNumber / epochLength) * epochLength + 1
71+
val epoch = startBlock / EPOCH_LENGTH_BEFORE_ECIP_1099
6572

66-
def seed(blockNumber: Long): ByteString = {
67-
val epoch = epochBeforeEcip1099(blockNumber)
6873
(BigInt(0) until epoch)
6974
.foldLeft(ByteString(Hex.decode("00" * 32))) { case (b, _) => kec256(b) }
7075
}

src/main/scala/io/iohk/ethereum/consensus/pow/validators/EthashBlockHeaderValidator.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class EthashBlockHeaderValidator(blockchainConfig: BlockchainConfig) {
4242
}
4343

4444
val epoch = EthashUtils.epoch(blockHeader.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong)
45-
val seed = EthashUtils.seed(blockHeader.number.toLong)
45+
val seed = EthashUtils.seed(blockHeader.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong)
4646
val powCacheData = getPowCacheData(epoch, seed)
4747

4848
val proofOfWork = hashimotoLight(

src/main/scala/io/iohk/ethereum/jsonrpc/EthMiningService.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.collection.concurrent.{TrieMap, Map => ConcurrentMap}
2121
import scala.concurrent.duration.FiniteDuration
2222
import scala.language.existentials
2323
import io.iohk.ethereum.transactions.TransactionPicker
24+
import io.iohk.ethereum.utils.BlockchainConfig
2425

2526
object EthMiningService {
2627

@@ -45,6 +46,7 @@ object EthMiningService {
4546

4647
class EthMiningService(
4748
blockchain: Blockchain,
49+
blockchainConfig: BlockchainConfig,
4850
ledger: Ledger,
4951
jsonRpcConfig: JsonRpcConfig,
5052
ommersPool: ActorRef,
@@ -92,7 +94,7 @@ class EthMiningService(
9294
Right(
9395
GetWorkResponse(
9496
powHeaderHash = ByteString(kec256(BlockHeader.getEncodedWithoutNonce(pb.block.header))),
95-
dagSeed = EthashUtils.seed(pb.block.header.number.toLong),
97+
dagSeed = EthashUtils.seed(pb.block.header.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong),
9698
target = ByteString((BigInt(2).pow(256) / pb.block.header.difficulty).toByteArray)
9799
)
98100
)

src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ trait EthInfoServiceBuilder {
405405

406406
trait EthMiningServiceBuilder {
407407
self: BlockchainBuilder
408+
with BlockchainConfigBuilder
408409
with LedgerBuilder
409410
with JSONRpcConfigBuilder
410411
with OmmersPoolBuilder
@@ -414,6 +415,7 @@ trait EthMiningServiceBuilder {
414415

415416
lazy val ethMiningService = new EthMiningService(
416417
blockchain,
418+
blockchainConfig,
417419
ledger,
418420
jsonRpcConfig,
419421
ommersPool,

src/test/scala/io/iohk/ethereum/consensus/pow/EthashUtilsSpec.scala

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import org.scalatest.matchers.should.Matchers
1010

1111
import scala.annotation.tailrec
1212
import io.iohk.ethereum.SuperSlow
13+
import io.iohk.ethereum.utils.ByteStringUtils
14+
import org.scalatest.prop.TableFor2
1315

1416
class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks with SuperSlow {
1517

@@ -18,8 +20,21 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC
1820
val ecip1099forkBlockNumber: Long = 11460000
1921

2022
"Ethash" should "generate correct hash" in {
21-
forAll(Gen.choose[Long](0, 15000000L)) { blockNumber =>
22-
seed(blockNumber) shouldBe seedForBlockReference(blockNumber)
23+
val seedEpoch0 = ByteStringUtils.string2hash("0000000000000000000000000000000000000000000000000000000000000000")
24+
val seedEpoch1 = ByteStringUtils.string2hash("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
25+
val seedEpoch382 = ByteStringUtils.string2hash("d3d0aa11197dcdcfcb3ad3c73d415af47299bddb47fda6081d31d9dd06462f6a")
26+
val seedEpoch383 = ByteStringUtils.string2hash("bf532874eb434842e7a3e4acd113fe454541651872760d9b95d11d7f90ca25dc")
27+
val table: TableFor2[Long, ByteString] = Table(
28+
("blockNumber", "referenceSeed"),
29+
(0, seedEpoch0),
30+
(1, seedEpoch0),
31+
(30_000, seedEpoch1),
32+
(ecip1099forkBlockNumber, seedEpoch382),
33+
(ecip1099forkBlockNumber + 30_000, seedEpoch382),
34+
(ecip1099forkBlockNumber + 60_000, seedEpoch383)
35+
)
36+
forAll(table) { (blockNumber, referenceSeed) =>
37+
seed(blockNumber, ecip1099forkBlockNumber) shouldBe referenceSeed
2338
}
2439
}
2540

@@ -55,7 +70,7 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC
5570

5671
val blockNumber = 486382
5772
val _epoch = epoch(blockNumber, ecip1099forkBlockNumber)
58-
val _seed = seed(blockNumber)
73+
val _seed = seed(blockNumber, ecip1099forkBlockNumber)
5974
val cache = makeCache(_epoch, _seed)
6075
val proofOfWork = hashimotoLight(hash, nonce, dagSize(_epoch), cache)
6176

@@ -129,25 +144,12 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC
129144

130145
forAll(table) { (blockNumber, hashWithoutNonce, nonce, mixHash) =>
131146
val _epoch = epoch(blockNumber, ecip1099forkBlockNumber)
132-
val _seed = seed(blockNumber)
147+
val _seed = seed(blockNumber, ecip1099forkBlockNumber)
133148
val cache = makeCache(_epoch, _seed)
134149
val proofOfWork =
135150
hashimotoLight(Hex.decode(hashWithoutNonce), Hex.decode(nonce), dagSize(_epoch), cache)
136151
proofOfWork.mixHash shouldBe ByteString(Hex.decode(mixHash))
137152
}
138153
}
139154
}
140-
141-
def seedForBlockReference(blockNumber: BigInt): ByteString = {
142-
@tailrec
143-
def go(current: BigInt, currentHash: ByteString): ByteString = {
144-
if (current < EPOCH_LENGTH_BEFORE_ECIP_1099) {
145-
currentHash
146-
} else {
147-
go(current - EPOCH_LENGTH_BEFORE_ECIP_1099, kec256(currentHash))
148-
}
149-
}
150-
151-
go(blockNumber, ByteString(Hex.decode("00" * 32)))
152-
}
153155
}

src/test/scala/io/iohk/ethereum/jsonrpc/EthMiningServiceSpec.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ class EthMiningServiceSpec
249249

250250
lazy val ethMiningService = new EthMiningService(
251251
blockchain,
252+
blockchainConfig,
252253
ledger,
253254
jsonRpcConfig,
254255
ommersPool.ref,

src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class JsonRpcControllerFixture(implicit system: ActorSystem)
8585

8686
val ethMiningService = new EthMiningService(
8787
blockchain,
88+
blockchainConfig,
8889
ledger,
8990
config,
9091
ommersPool.ref,

0 commit comments

Comments
 (0)