diff --git a/src/main/scala/io/iohk/ethereum/consensus/pow/EthashDAGManager.scala b/src/main/scala/io/iohk/ethereum/consensus/pow/EthashDAGManager.scala index b989e69827..7072ed033b 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/pow/EthashDAGManager.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/pow/EthashDAGManager.scala @@ -17,7 +17,7 @@ class EthashDAGManager(blockCreator: EthashBlockCreator) extends Logger { (currentEpoch, currentEpochDag, currentEpochDagSize) match { case (Some(`epoch`), Some(dag), Some(dagSize)) => (dag, dagSize) case _ => - val seed = EthashUtils.seed(blockNumber) + val seed = EthashUtils.seed(blockNumber, blockCreator.blockchainConfig.ecip1099BlockNumber.toLong) val dagSize = EthashUtils.dagSize(epoch) val dagNumHashes = (dagSize / EthashUtils.HASH_BYTES).toInt val dag = diff --git a/src/main/scala/io/iohk/ethereum/consensus/pow/EthashUtils.scala b/src/main/scala/io/iohk/ethereum/consensus/pow/EthashUtils.scala index b49a7edc90..9aa8c3ed08 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/pow/EthashUtils.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/pow/EthashUtils.scala @@ -61,10 +61,15 @@ object EthashUtils { // scalastyle:on magic.number - private def epochBeforeEcip1099(blockNumber: Long): Long = blockNumber / EPOCH_LENGTH_BEFORE_ECIP_1099 + // computes seed for epoch of given blockNumber + // this also involves the non-ECIP1099 epoch of the first blocks of the + // ECIP1099 epoch, to make sure every block in the latter results in the same + // seed being calculated, would there be a cache miss. + def seed(blockNumber: Long, ecip1099ActivationBlock: Long): ByteString = { + val epochLength = calcEpochLength(blockNumber, ecip1099ActivationBlock) + val startBlock = (blockNumber / epochLength) * epochLength + 1 + val epoch = startBlock / EPOCH_LENGTH_BEFORE_ECIP_1099 - def seed(blockNumber: Long): ByteString = { - val epoch = epochBeforeEcip1099(blockNumber) (BigInt(0) until epoch) .foldLeft(ByteString(Hex.decode("00" * 32))) { case (b, _) => kec256(b) } } diff --git a/src/main/scala/io/iohk/ethereum/consensus/pow/validators/EthashBlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/consensus/pow/validators/EthashBlockHeaderValidator.scala index 7c38135673..b37a6f8f1e 100644 --- a/src/main/scala/io/iohk/ethereum/consensus/pow/validators/EthashBlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/consensus/pow/validators/EthashBlockHeaderValidator.scala @@ -42,7 +42,7 @@ class EthashBlockHeaderValidator(blockchainConfig: BlockchainConfig) { } val epoch = EthashUtils.epoch(blockHeader.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong) - val seed = EthashUtils.seed(blockHeader.number.toLong) + val seed = EthashUtils.seed(blockHeader.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong) val powCacheData = getPowCacheData(epoch, seed) val proofOfWork = hashimotoLight( diff --git a/src/main/scala/io/iohk/ethereum/jsonrpc/EthMiningService.scala b/src/main/scala/io/iohk/ethereum/jsonrpc/EthMiningService.scala index 8d0907ddb5..145cc0d3ea 100644 --- a/src/main/scala/io/iohk/ethereum/jsonrpc/EthMiningService.scala +++ b/src/main/scala/io/iohk/ethereum/jsonrpc/EthMiningService.scala @@ -21,6 +21,7 @@ import scala.collection.concurrent.{TrieMap, Map => ConcurrentMap} import scala.concurrent.duration.FiniteDuration import scala.language.existentials import io.iohk.ethereum.transactions.TransactionPicker +import io.iohk.ethereum.utils.BlockchainConfig object EthMiningService { @@ -45,6 +46,7 @@ object EthMiningService { class EthMiningService( blockchain: Blockchain, + blockchainConfig: BlockchainConfig, ledger: Ledger, jsonRpcConfig: JsonRpcConfig, ommersPool: ActorRef, @@ -92,7 +94,7 @@ class EthMiningService( Right( GetWorkResponse( powHeaderHash = ByteString(kec256(BlockHeader.getEncodedWithoutNonce(pb.block.header))), - dagSeed = EthashUtils.seed(pb.block.header.number.toLong), + dagSeed = EthashUtils.seed(pb.block.header.number.toLong, blockchainConfig.ecip1099BlockNumber.toLong), target = ByteString((BigInt(2).pow(256) / pb.block.header.difficulty).toByteArray) ) ) diff --git a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala index f893c737f3..f359dbc7cc 100644 --- a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala @@ -405,6 +405,7 @@ trait EthInfoServiceBuilder { trait EthMiningServiceBuilder { self: BlockchainBuilder + with BlockchainConfigBuilder with LedgerBuilder with JSONRpcConfigBuilder with OmmersPoolBuilder @@ -414,6 +415,7 @@ trait EthMiningServiceBuilder { lazy val ethMiningService = new EthMiningService( blockchain, + blockchainConfig, ledger, jsonRpcConfig, ommersPool, diff --git a/src/test/scala/io/iohk/ethereum/consensus/pow/EthashUtilsSpec.scala b/src/test/scala/io/iohk/ethereum/consensus/pow/EthashUtilsSpec.scala index 2c1a5a0a68..61dfeafe45 100644 --- a/src/test/scala/io/iohk/ethereum/consensus/pow/EthashUtilsSpec.scala +++ b/src/test/scala/io/iohk/ethereum/consensus/pow/EthashUtilsSpec.scala @@ -10,6 +10,8 @@ import org.scalatest.matchers.should.Matchers import scala.annotation.tailrec import io.iohk.ethereum.SuperSlow +import io.iohk.ethereum.utils.ByteStringUtils +import org.scalatest.prop.TableFor2 class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyChecks with SuperSlow { @@ -18,8 +20,21 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC val ecip1099forkBlockNumber: Long = 11460000 "Ethash" should "generate correct hash" in { - forAll(Gen.choose[Long](0, 15000000L)) { blockNumber => - seed(blockNumber) shouldBe seedForBlockReference(blockNumber) + val seedEpoch0 = ByteStringUtils.string2hash("0000000000000000000000000000000000000000000000000000000000000000") + val seedEpoch1 = ByteStringUtils.string2hash("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563") + val seedEpoch382 = ByteStringUtils.string2hash("d3d0aa11197dcdcfcb3ad3c73d415af47299bddb47fda6081d31d9dd06462f6a") + val seedEpoch383 = ByteStringUtils.string2hash("bf532874eb434842e7a3e4acd113fe454541651872760d9b95d11d7f90ca25dc") + val table: TableFor2[Long, ByteString] = Table( + ("blockNumber", "referenceSeed"), + (0, seedEpoch0), + (1, seedEpoch0), + (30_000, seedEpoch1), + (ecip1099forkBlockNumber, seedEpoch382), + (ecip1099forkBlockNumber + 30_000, seedEpoch382), + (ecip1099forkBlockNumber + 60_000, seedEpoch383) + ) + forAll(table) { (blockNumber, referenceSeed) => + seed(blockNumber, ecip1099forkBlockNumber) shouldBe referenceSeed } } @@ -55,7 +70,7 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC val blockNumber = 486382 val _epoch = epoch(blockNumber, ecip1099forkBlockNumber) - val _seed = seed(blockNumber) + val _seed = seed(blockNumber, ecip1099forkBlockNumber) val cache = makeCache(_epoch, _seed) val proofOfWork = hashimotoLight(hash, nonce, dagSize(_epoch), cache) @@ -129,7 +144,7 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC forAll(table) { (blockNumber, hashWithoutNonce, nonce, mixHash) => val _epoch = epoch(blockNumber, ecip1099forkBlockNumber) - val _seed = seed(blockNumber) + val _seed = seed(blockNumber, ecip1099forkBlockNumber) val cache = makeCache(_epoch, _seed) val proofOfWork = hashimotoLight(Hex.decode(hashWithoutNonce), Hex.decode(nonce), dagSize(_epoch), cache) @@ -137,17 +152,4 @@ class EthashUtilsSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyC } } } - - def seedForBlockReference(blockNumber: BigInt): ByteString = { - @tailrec - def go(current: BigInt, currentHash: ByteString): ByteString = { - if (current < EPOCH_LENGTH_BEFORE_ECIP_1099) { - currentHash - } else { - go(current - EPOCH_LENGTH_BEFORE_ECIP_1099, kec256(currentHash)) - } - } - - go(blockNumber, ByteString(Hex.decode("00" * 32))) - } } diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/EthMiningServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/EthMiningServiceSpec.scala index dd98168e12..7f293233ff 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/EthMiningServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/EthMiningServiceSpec.scala @@ -249,6 +249,7 @@ class EthMiningServiceSpec lazy val ethMiningService = new EthMiningService( blockchain, + blockchainConfig, ledger, jsonRpcConfig, ommersPool.ref, diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala index d7f6be4f33..873ca8eb7f 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala @@ -85,6 +85,7 @@ class JsonRpcControllerFixture(implicit system: ActorSystem) val ethMiningService = new EthMiningService( blockchain, + blockchainConfig, ledger, config, ommersPool.ref,