From 6afe5219f7e89bf60f4685c3f2d9283925de19e5 Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Sat, 5 Aug 2017 00:23:38 +0200 Subject: [PATCH 01/20] DB snapshot based regression test --- build.sbt | 6 +- .../components/SharedLevelDBDataSources.scala | 4 +- src/snappy/resources/example.conf | 27 +++++++ src/snappy/resources/logback-test.xml | 13 ++++ .../io/iohk/ethereum/snappy/Config.scala | 14 ++++ .../iohk/ethereum/snappy/Prerequisites.scala | 58 +++++++++++++++ .../iohk/ethereum/snappy/ProgressLogger.scala | 22 ++++++ .../io/iohk/ethereum/snappy/SnappyTest.scala | 70 +++++++++++++++++++ 8 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/snappy/resources/example.conf create mode 100644 src/snappy/resources/logback-test.xml create mode 100644 src/snappy/scala/io/iohk/ethereum/snappy/Config.scala create mode 100644 src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala create mode 100644 src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala create mode 100644 src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala diff --git a/build.sbt b/build.sbt index adf9149264..22376c777b 100644 --- a/build.sbt +++ b/build.sbt @@ -47,13 +47,15 @@ val Integration = config("it") extend Test val Evm = config("evm") extend Test +val Snappy = config("snappy") extend Test + val root = project.in(file(".")) - .configs(Integration) - .configs(Evm) + .configs(Integration, Evm, Snappy) .settings(commonSettings: _*) .settings(libraryDependencies ++= dep) .settings(inConfig(Integration)(Defaults.testSettings) : _*) .settings(inConfig(Evm)(Defaults.testSettings) : _*) + .settings(inConfig(Snappy)(Defaults.testSettings) : _*) scalacOptions := Seq( "-unchecked", diff --git a/src/main/scala/io/iohk/ethereum/db/components/SharedLevelDBDataSources.scala b/src/main/scala/io/iohk/ethereum/db/components/SharedLevelDBDataSources.scala index 8ca9d0914d..cfc25729cc 100644 --- a/src/main/scala/io/iohk/ethereum/db/components/SharedLevelDBDataSources.scala +++ b/src/main/scala/io/iohk/ethereum/db/components/SharedLevelDBDataSources.scala @@ -5,9 +5,9 @@ import io.iohk.ethereum.utils.Config trait SharedLevelDBDataSources extends DataSourcesComponent { - val dataSource = LevelDBDataSource(Config.Db.LevelDb) + lazy val dataSource = LevelDBDataSource(Config.Db.LevelDb) - val dataSources = new DataSources { + lazy val dataSources = new DataSources { override val blockBodiesDataSource: DataSource = dataSource diff --git a/src/snappy/resources/example.conf b/src/snappy/resources/example.conf new file mode 100644 index 0000000000..1c6ea5f4e4 --- /dev/null +++ b/src/snappy/resources/example.conf @@ -0,0 +1,27 @@ +include "application.conf" + +snappy { + source-db-path = "path/to/source-db" + target-db-path = "path/to/target-db" + target-block = 1000000 +} + +mantins.blockchain { + frontier-block-number = "0" + homestead-block-number = "1150000" + eip150-block-number = "2500000" + eip155-block-number = "3000000" + eip160-block-number = "3000000" + difficulty-bomb-pause-block-number = "3000000" + difficulty-bomb-continue-block-number = "5000000" + dao-fork-block-number = "1920000" + dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + account-start-nonce = "0" + chain-id = "3d" + custom-genesis-file = null + monetary-policy { + first-era-block-reward = "5000000000000000000" + era-duration = 5000000 + reward-reduction-rate = 0.2 + } +} diff --git a/src/snappy/resources/logback-test.xml b/src/snappy/resources/logback-test.xml new file mode 100644 index 0000000000..693f5afe37 --- /dev/null +++ b/src/snappy/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} %-5level %logger{18} - %msg%n + + + + + + + + diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala new file mode 100644 index 0000000000..c9e2b969e5 --- /dev/null +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala @@ -0,0 +1,14 @@ +package io.iohk.ethereum.snappy + +import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig} + +import scala.util.Try + +class Config(rootConfig: TypesafeConfig = ConfigFactory.load()) { + + private val snappyConf = rootConfig.getConfig("snappy") + + val sourceDbPath: String = snappyConf.getString("source-db-path") + val targetDbPath: String = snappyConf.getString("target-db-path") + val targetBlock: Option[BigInt] = Try(snappyConf.getString("target-block")).toOption.map(BigInt(_)) +} diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala new file mode 100644 index 0000000000..8b06ce953b --- /dev/null +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala @@ -0,0 +1,58 @@ +package io.iohk.ethereum.snappy + +import io.iohk.ethereum.blockchain.data.GenesisDataLoader +import io.iohk.ethereum.db.components.Storages.PruningModeComponent +import io.iohk.ethereum.db.components.{SharedLevelDBDataSources, Storages} +import io.iohk.ethereum.db.dataSource.{LevelDBDataSource, LevelDbConfig} +import io.iohk.ethereum.db.storage.pruning.ArchivePruning +import io.iohk.ethereum.domain.BlockchainImpl +import io.iohk.ethereum.nodebuilder.{BlockchainConfigBuilder, LedgerBuilder, ValidatorsBuilder} +import io.iohk.ethereum.utils.Config.DbConfig + +class Prerequisites(config: Config) { + + trait NoPruning extends PruningModeComponent { + val pruningMode = ArchivePruning + } + + private def levelDb(dbPath: String): LevelDBDataSource = + LevelDBDataSource ( + new LevelDbConfig { + val verifyChecksums: Boolean = true + val paranoidChecks: Boolean = true + val createIfMissing: Boolean = true + val path: String = dbPath + } + ) + + val sourceStorages = new SharedLevelDBDataSources with NoPruning with Storages.DefaultStorages { + override lazy val dataSource = levelDb(config.sourceDbPath) + } + + val targetStorages = new SharedLevelDBDataSources with NoPruning with Storages.DefaultStorages { + override lazy val dataSource = levelDb(config.targetDbPath) + } + + val sourceBlockchain = BlockchainImpl(sourceStorages.storages) + val targetBlockchain = BlockchainImpl(targetStorages.storages) + + private val components = + new LedgerBuilder + with ValidatorsBuilder + with BlockchainConfigBuilder + + + val ledger = components.ledger + + val validators = components.validators + + val genesisLoader = new GenesisDataLoader( + targetStorages.dataSource, + BlockchainImpl(targetStorages.storages), + ArchivePruning, + components.blockchainConfig, + new DbConfig { val batchSize: Int = 1000 } + ) + + genesisLoader.loadGenesisData() +} diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala new file mode 100644 index 0000000000..9ccd6d9267 --- /dev/null +++ b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala @@ -0,0 +1,22 @@ +package io.iohk.ethereum.snappy + +import io.iohk.ethereum.utils.Logger + +import scala.concurrent.duration.FiniteDuration + +class ProgressLogger(targetN: BigInt, interval: FiniteDuration) extends Logger { + + private var lastUpdateMillis: Long = 0 + + def start(): Unit = + log.info(s"Starting to execute $targetN blocks") + + def update(n: BigInt): Unit = { + val now = System.currentTimeMillis() + if (now - lastUpdateMillis > interval.toMillis || n == targetN) { + lastUpdateMillis = now + val percent = f"${n.toDouble / targetN.toDouble * 100}%.1f" + log.info(s"Executed $n ($percent%) blocks") + } + } +} diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala new file mode 100644 index 0000000000..a1c8b9a06d --- /dev/null +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -0,0 +1,70 @@ +package io.iohk.ethereum.snappy + +import java.io.File + +import io.iohk.ethereum.domain.{Block, Blockchain} +import io.iohk.ethereum.utils.Logger +import org.apache.commons.io.FileUtils +import org.scalatest.{FreeSpec, Matchers} + +import scala.concurrent.duration._ + +class SnappyTest extends FreeSpec with Matchers with Logger { + + "Blockchain regression test" in { + + val config = new Config() + removeTargetDir(config.targetDbPath) + + val pre = new Prerequisites(config) + import pre._ + + val targetN = config.targetBlock.getOrElse(findHighestBlockNumber(sourceBlockchain)) + val progLog = new ProgressLogger(targetN, 2.seconds) + + progLog.start() + + for (n <- BigInt(1) to targetN) { + val block: Block = sourceBlockchain.getBlockByNumber(n) + .getOrElse(fail(s"Failed to retrieve block by number: $n")) + + val expectedReceipts = sourceBlockchain.getReceiptsByHash(block.header.hash) + .getOrElse(fail(s"Failed to retrieve receipts for block number: $n")) + + val result = ledger.executeBlock(block, targetStorages.storages, validators) + + result match { + case Left(error) => + fail(s"Failed to execute block $n: $error") + + case Right(receipts) => + receipts shouldEqual expectedReceipts + } + + targetBlockchain.save(block) + + progLog.update(n) + } + } + + private def findHighestBlockNumber(blockchain: Blockchain, n: BigInt = 1000000, lastN: BigInt = 0): BigInt = + if (n <= 0) + fail("No block found in the source DB!") + else if (n == lastN) + n + else { + val newN = blockchain.getBlockByNumber(n) match { + case Some(_) => if (n > lastN) n + n - lastN else (n + lastN) / 2 + case None => (n + lastN) / 2 + } + findHighestBlockNumber(blockchain, newN, n) + } + + private def removeTargetDir(dirPath: String): Unit = { + val dir = new File(dirPath) + if (dir.exists() && dir.isDirectory) { + FileUtils.deleteDirectory(dir) + } + } + +} From a3ee5a268cd9e8d63f196c40e16e4732b8a5f918 Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Mon, 7 Aug 2017 22:26:12 +0200 Subject: [PATCH 02/20] DB snapshot based regression test: improvements: start from N, ETA logging --- src/snappy/resources/example.conf | 5 ++- .../io/iohk/ethereum/snappy/Config.scala | 1 + .../iohk/ethereum/snappy/ProgressLogger.scala | 21 ++++++++-- .../io/iohk/ethereum/snappy/SnappyTest.scala | 39 ++++++++----------- 4 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/snappy/resources/example.conf b/src/snappy/resources/example.conf index 1c6ea5f4e4..5200e5ea80 100644 --- a/src/snappy/resources/example.conf +++ b/src/snappy/resources/example.conf @@ -3,10 +3,11 @@ include "application.conf" snappy { source-db-path = "path/to/source-db" target-db-path = "path/to/target-db" - target-block = 1000000 + start-block = null + target-block = null } -mantins.blockchain { +mantis.blockchain { frontier-block-number = "0" homestead-block-number = "1150000" eip150-block-number = "2500000" diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala index c9e2b969e5..f4e21a40cc 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala @@ -10,5 +10,6 @@ class Config(rootConfig: TypesafeConfig = ConfigFactory.load()) { val sourceDbPath: String = snappyConf.getString("source-db-path") val targetDbPath: String = snappyConf.getString("target-db-path") + val startBlock: Option[BigInt] = Try(snappyConf.getString("start-block")).toOption.map(BigInt(_)) val targetBlock: Option[BigInt] = Try(snappyConf.getString("target-block")).toOption.map(BigInt(_)) } diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala index 9ccd6d9267..dd4abb7df0 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala @@ -4,19 +4,32 @@ import io.iohk.ethereum.utils.Logger import scala.concurrent.duration.FiniteDuration -class ProgressLogger(targetN: BigInt, interval: FiniteDuration) extends Logger { +class ProgressLogger(startN: BigInt, targetN: BigInt, interval: FiniteDuration) extends Logger { + private val startTimestamp = System.currentTimeMillis() private var lastUpdateMillis: Long = 0 def start(): Unit = - log.info(s"Starting to execute $targetN blocks") + log.info(s"About to execute blocks $startN through $targetN (${targetN - startN + 1} total)") def update(n: BigInt): Unit = { val now = System.currentTimeMillis() if (now - lastUpdateMillis > interval.toMillis || n == targetN) { lastUpdateMillis = now - val percent = f"${n.toDouble / targetN.toDouble * 100}%.1f" - log.info(s"Executed $n ($percent%) blocks") + val percent = n.toDouble / targetN.toDouble * 100 + log.info(f"Executed blocks up to $n ($percent%.1f%%). ETA: ${eta(now, n)}") } } + + private def eta(now: Long, n: BigInt): String = { + val elapsed = (now - startTimestamp) / 1000 + if (n - startN > 0 && elapsed > 0) { + val r = (targetN - startN + 1).toDouble / (n - startN + 1).toDouble + val estimated = elapsed * r + val h = (estimated / 3600).toInt + val m = ((estimated % 3600) / 60).toInt + val s = (estimated % 60).toInt + f"$h%02d:$m%02d:$s%02d" + } else "N/A" + } } diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index a1c8b9a06d..e66986d817 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -1,10 +1,7 @@ package io.iohk.ethereum.snappy -import java.io.File - import io.iohk.ethereum.domain.{Block, Blockchain} import io.iohk.ethereum.utils.Logger -import org.apache.commons.io.FileUtils import org.scalatest.{FreeSpec, Matchers} import scala.concurrent.duration._ @@ -14,17 +11,16 @@ class SnappyTest extends FreeSpec with Matchers with Logger { "Blockchain regression test" in { val config = new Config() - removeTargetDir(config.targetDbPath) - val pre = new Prerequisites(config) import pre._ - val targetN = config.targetBlock.getOrElse(findHighestBlockNumber(sourceBlockchain)) - val progLog = new ProgressLogger(targetN, 2.seconds) + val startN = config.startBlock.getOrElse(findHighestBlockNumber(targetBlockchain) - 1).max(1) + val targetN = config.targetBlock.getOrElse(findHighestBlockNumber(sourceBlockchain)).max(1) + val progLog = new ProgressLogger(startN, targetN, 2.seconds) progLog.start() - for (n <- BigInt(1) to targetN) { + for (n <- startN to targetN) { val block: Block = sourceBlockchain.getBlockByNumber(n) .getOrElse(fail(s"Failed to retrieve block by number: $n")) @@ -47,23 +43,22 @@ class SnappyTest extends FreeSpec with Matchers with Logger { } } - private def findHighestBlockNumber(blockchain: Blockchain, n: BigInt = 1000000, lastN: BigInt = 0): BigInt = - if (n <= 0) - fail("No block found in the source DB!") - else if (n == lastN) + private def findHighestBlockNumber(blockchain: Blockchain, n: BigInt = 1000000, bottom: BigInt = 0, top: BigInt = -1): BigInt = { + if (top - bottom == 1) n - else { - val newN = blockchain.getBlockByNumber(n) match { - case Some(_) => if (n > lastN) n + n - lastN else (n + lastN) / 2 - case None => (n + lastN) / 2 - } - findHighestBlockNumber(blockchain, newN, n) + + else if (top < 0) { + def candidates(n: BigInt): Stream[BigInt] = n #:: candidates(n + 100000) + val newTop = candidates(1).find(n => blockchain.getBlockByNumber(n).isEmpty).get + findHighestBlockNumber(blockchain, newTop / 2, 0, newTop) } - private def removeTargetDir(dirPath: String): Unit = { - val dir = new File(dirPath) - if (dir.exists() && dir.isDirectory) { - FileUtils.deleteDirectory(dir) + else { + val (newBottom, newTop) = blockchain.getBlockByNumber(n) match { + case Some(_) => (n, top) + case None => (bottom, n) + } + findHighestBlockNumber(blockchain, (bottom + top) / 2, newBottom, newTop) } } From ab1bc67e4cecc34a2ff81785ab6e0157c40be459 Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Thu, 10 Aug 2017 16:15:29 +0200 Subject: [PATCH 03/20] DB snapshot based regression test: support 2 testing modes: single-DB/dual-DB --- src/snappy/resources/example.conf | 24 +++++++++ .../io/iohk/ethereum/snappy/Config.scala | 18 ++++++- .../iohk/ethereum/snappy/Prerequisites.scala | 52 +++++++++++++------ .../io/iohk/ethereum/snappy/SnappyTest.scala | 34 ++++++++---- 4 files changed, 100 insertions(+), 28 deletions(-) diff --git a/src/snappy/resources/example.conf b/src/snappy/resources/example.conf index 5200e5ea80..a7656f24de 100644 --- a/src/snappy/resources/example.conf +++ b/src/snappy/resources/example.conf @@ -1,12 +1,36 @@ include "application.conf" +# Settings specific to the Snappy test snappy { + + # Test mode can be one of: single-db, dual-db + # - single-db: we execute blocks on a read only state (any modifications will be discarded afterwards), and we compare + # receipts' root hashes with the state saved in the source DB. Thus this mode requires that the source + # snapshot was created without any pruning + # - dual-db: we only read blocks from the source DB, and we save all the intermediate state to the target DB, thus + # the scope of the test is increased by DB writes and the source snapshot may have been created with + # pruning enabled + test-mode = "dual-db" + + # Path to the DB snapshot that contains all the blocks to be executed (and state for single-db) source-db-path = "path/to/source-db" + + # Path to the DB where the state resulting from execution is saved (for single-db this is ignored) target-db-path = "path/to/target-db" + + # The initial block number for the test. For dual-db blocks at least until this number must have been executed and + # save in the target DB. This value can be null in which case for: + # - single-db: default to 1 + # - dual-db: highest block found in target DB start-block = null + + # The last block to be executed marking the test complete. Value can be null - meaning highest block number found + # in the source DB target-block = null } +# This overrides the relevant section from the client configuration. May be omitted for the classic main-net. +# Don't forget to provide and configure the genesis JSON file for test/private networks. mantis.blockchain { frontier-block-number = "0" homestead-block-number = "1150000" diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala index f4e21a40cc..1f7eb28995 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Config.scala @@ -1,15 +1,29 @@ package io.iohk.ethereum.snappy import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig} +import io.iohk.ethereum.snappy.Config.{DualDB, SingleDB, TestMode} import scala.util.Try -class Config(rootConfig: TypesafeConfig = ConfigFactory.load()) { +object Config { + def apply(): Config = new Config(ConfigFactory.load()) + + sealed trait TestMode + case object SingleDB extends TestMode + case object DualDB extends TestMode +} + +class Config(rootConfig: TypesafeConfig) { private val snappyConf = rootConfig.getConfig("snappy") + val mode: TestMode = snappyConf.getString("test-mode").toLowerCase match { + case "single-db" => SingleDB + case "dual-db" => DualDB + } + val sourceDbPath: String = snappyConf.getString("source-db-path") - val targetDbPath: String = snappyConf.getString("target-db-path") + lazy val targetDbPath: String = snappyConf.getString("target-db-path") val startBlock: Option[BigInt] = Try(snappyConf.getString("start-block")).toOption.map(BigInt(_)) val targetBlock: Option[BigInt] = Try(snappyConf.getString("target-block")).toOption.map(BigInt(_)) } diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala index 8b06ce953b..2949054bd3 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala @@ -5,16 +5,25 @@ import io.iohk.ethereum.db.components.Storages.PruningModeComponent import io.iohk.ethereum.db.components.{SharedLevelDBDataSources, Storages} import io.iohk.ethereum.db.dataSource.{LevelDBDataSource, LevelDbConfig} import io.iohk.ethereum.db.storage.pruning.ArchivePruning -import io.iohk.ethereum.domain.BlockchainImpl +import io.iohk.ethereum.domain.{Blockchain, BlockchainImpl} +import io.iohk.ethereum.ledger.Ledger import io.iohk.ethereum.nodebuilder.{BlockchainConfigBuilder, LedgerBuilder, ValidatorsBuilder} +import io.iohk.ethereum.snappy.Config.{DualDB, SingleDB} +import io.iohk.ethereum.snappy.Prerequisites._ import io.iohk.ethereum.utils.Config.DbConfig +import io.iohk.ethereum.validators.Validators -class Prerequisites(config: Config) { +object Prerequisites { trait NoPruning extends PruningModeComponent { val pruningMode = ArchivePruning } + trait Storages extends SharedLevelDBDataSources with NoPruning with Storages.DefaultStorages +} + +class Prerequisites(config: Config) { + private def levelDb(dbPath: String): LevelDBDataSource = LevelDBDataSource ( new LevelDbConfig { @@ -25,16 +34,21 @@ class Prerequisites(config: Config) { } ) - val sourceStorages = new SharedLevelDBDataSources with NoPruning with Storages.DefaultStorages { + val sourceStorages: Storages = new Storages { override lazy val dataSource = levelDb(config.sourceDbPath) } - val targetStorages = new SharedLevelDBDataSources with NoPruning with Storages.DefaultStorages { - override lazy val dataSource = levelDb(config.targetDbPath) + val targetStorages: Option[Storages] = config.mode match { + case DualDB => + Some(new Storages { + override lazy val dataSource = levelDb(config.targetDbPath) + }) + + case SingleDB => None } - val sourceBlockchain = BlockchainImpl(sourceStorages.storages) - val targetBlockchain = BlockchainImpl(targetStorages.storages) + val sourceBlockchain: Blockchain = BlockchainImpl(sourceStorages.storages) + val targetBlockchain: Option[Blockchain] = targetStorages.map(ts => BlockchainImpl(ts.storages)) private val components = new LedgerBuilder @@ -42,17 +56,21 @@ class Prerequisites(config: Config) { with BlockchainConfigBuilder - val ledger = components.ledger + val ledger: Ledger = components.ledger - val validators = components.validators + val validators: Validators = components.validators - val genesisLoader = new GenesisDataLoader( - targetStorages.dataSource, - BlockchainImpl(targetStorages.storages), - ArchivePruning, - components.blockchainConfig, - new DbConfig { val batchSize: Int = 1000 } - ) + targetStorages.foreach { ts => + val genesisLoader = new GenesisDataLoader( + ts.dataSource, + BlockchainImpl(ts.storages), + ArchivePruning, + components.blockchainConfig, + new DbConfig { + val batchSize: Int = 1000 + } + ) - genesisLoader.loadGenesisData() + genesisLoader.loadGenesisData() + } } diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index e66986d817..3791796576 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -1,6 +1,7 @@ package io.iohk.ethereum.snappy -import io.iohk.ethereum.domain.{Block, Blockchain} +import io.iohk.ethereum.domain.{Block, Blockchain, Receipt} +import io.iohk.ethereum.snappy.Prerequisites.Storages import io.iohk.ethereum.utils.Logger import org.scalatest.{FreeSpec, Matchers} @@ -8,14 +9,18 @@ import scala.concurrent.duration._ class SnappyTest extends FreeSpec with Matchers with Logger { - "Blockchain regression test" in { + val config = Config() + val pre = new Prerequisites(config) + import pre._ - val config = new Config() - val pre = new Prerequisites(config) - import pre._ + "Blockchain regression test" in { - val startN = config.startBlock.getOrElse(findHighestBlockNumber(targetBlockchain) - 1).max(1) + val startN = targetBlockchain match { + case Some(tb) => config.startBlock.getOrElse(findHighestBlockNumber(tb) - 1).max(1) + case None => BigInt(1) + } val targetN = config.targetBlock.getOrElse(findHighestBlockNumber(sourceBlockchain)).max(1) + val progLog = new ProgressLogger(startN, targetN, 2.seconds) progLog.start() @@ -27,7 +32,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { val expectedReceipts = sourceBlockchain.getReceiptsByHash(block.header.hash) .getOrElse(fail(s"Failed to retrieve receipts for block number: $n")) - val result = ledger.executeBlock(block, targetStorages.storages, validators) + val result = executeBlock(block) result match { case Left(error) => @@ -37,12 +42,23 @@ class SnappyTest extends FreeSpec with Matchers with Logger { receipts shouldEqual expectedReceipts } - targetBlockchain.save(block) - progLog.update(n) } } + private def executeBlock(block: Block): Either[Any, Seq[Receipt]] = + targetStorages match { + case Some(storages) => + val result = ledger.executeBlock(block, storages.storages, validators) + targetBlockchain.foreach(_.save(block)) + result + + case None => + // this seems to discard failures, for better errors messages we might want to implement a different method (simulateBlock?) + val result = ledger.prepareBlock(block, sourceStorages.storages, validators) + Right(result.blockResult.receipts) + } + private def findHighestBlockNumber(blockchain: Blockchain, n: BigInt = 1000000, bottom: BigInt = 0, top: BigInt = -1): BigInt = { if (top - bottom == 1) n From 79aa53ddd508cbd0f3d1f5619c25e948f1732d6e Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Fri, 11 Aug 2017 14:58:17 +0200 Subject: [PATCH 04/20] DB snapshot based regression test: progress logger fix --- src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala | 2 +- src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala index dd4abb7df0..048db13d9b 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/ProgressLogger.scala @@ -25,7 +25,7 @@ class ProgressLogger(startN: BigInt, targetN: BigInt, interval: FiniteDuration) val elapsed = (now - startTimestamp) / 1000 if (n - startN > 0 && elapsed > 0) { val r = (targetN - startN + 1).toDouble / (n - startN + 1).toDouble - val estimated = elapsed * r + val estimated = elapsed * (r - 1) val h = (estimated / 3600).toInt val m = ((estimated % 3600) / 60).toInt val s = (estimated % 60).toInt diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index 3791796576..e5dd7f0112 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -1,7 +1,6 @@ package io.iohk.ethereum.snappy import io.iohk.ethereum.domain.{Block, Blockchain, Receipt} -import io.iohk.ethereum.snappy.Prerequisites.Storages import io.iohk.ethereum.utils.Logger import org.scalatest.{FreeSpec, Matchers} @@ -21,7 +20,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { } val targetN = config.targetBlock.getOrElse(findHighestBlockNumber(sourceBlockchain)).max(1) - val progLog = new ProgressLogger(startN, targetN, 2.seconds) + val progLog = new ProgressLogger(startN, targetN, 5.seconds) progLog.start() From 7d7916742f9cfe459c76fc23acb77a24ecf01ed1 Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Fri, 11 Aug 2017 16:03:17 +0200 Subject: [PATCH 05/20] DB snapshot based regression test: custom assertion --- .../scala/io/iohk/ethereum/snappy/SnappyTest.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index e5dd7f0112..30affa05e9 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -38,7 +38,11 @@ class SnappyTest extends FreeSpec with Matchers with Logger { fail(s"Failed to execute block $n: $error") case Right(receipts) => - receipts shouldEqual expectedReceipts + if (receipts == expectedReceipts) { + targetBlockchain.foreach(_.save(block)) + } else { + fail(s"Block $n did not execute correctly.\n$receipts did not equal $expectedReceipts") + } } progLog.update(n) @@ -48,9 +52,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { private def executeBlock(block: Block): Either[Any, Seq[Receipt]] = targetStorages match { case Some(storages) => - val result = ledger.executeBlock(block, storages.storages, validators) - targetBlockchain.foreach(_.save(block)) - result + ledger.executeBlock(block, storages.storages, validators) case None => // this seems to discard failures, for better errors messages we might want to implement a different method (simulateBlock?) @@ -63,7 +65,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { n else if (top < 0) { - def candidates(n: BigInt): Stream[BigInt] = n #:: candidates(n + 100000) + def candidates(n: BigInt): Stream[BigInt] = n #:: candidates(n + 1000000) val newTop = candidates(1).find(n => blockchain.getBlockByNumber(n).isEmpty).get findHighestBlockNumber(blockchain, newTop / 2, 0, newTop) } From d4343e7f47183019533d68b8f9c6d3bfa90972b3 Mon Sep 17 00:00:00 2001 From: Nicolas Tallar Date: Thu, 24 Aug 2017 10:36:21 -0300 Subject: [PATCH 06/20] Node encoding and hash caching --- .../MerklePatriciaTreeIntegrationSuite.scala | 25 +++++++++++++- .../txExecTest/util/DumpChainActor.scala | 4 +-- .../ethereum/mpt/MerklePatriciaTrie.scala | 32 +++++++++--------- .../scala/io/iohk/ethereum/mpt/Node.scala | 33 +++++++++++++++---- 4 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala b/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala index 8dab66703e..90ede57674 100644 --- a/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala +++ b/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala @@ -5,7 +5,7 @@ import java.nio.ByteBuffer import java.nio.file.Files import java.security.MessageDigest -import io.iohk.ethereum.ObjectGenerators +import io.iohk.ethereum.{ObjectGenerators, crypto} import io.iohk.ethereum.crypto.kec256 import io.iohk.ethereum.db.dataSource.{EphemDataSource, LevelDBDataSource, LevelDbConfig} import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, NodeStorage, ReferenceCountNodeStorage} @@ -147,6 +147,29 @@ class MerklePatriciaTreeIntegrationSuite extends FunSuite else assert(rootHash.take(4) == "da8a" && rootHash.drop(rootHash.length - 4) == "0ca4") } } + + ignore("MPT benchmark") { + withNodeStorage { ns => + val hashFn = crypto.kec256(_: Array[Byte]) + + val defaultByteArraySer = MerklePatriciaTrie.defaultByteArraySerializable + val EmptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](ns)(defaultByteArraySer, defaultByteArraySer) + + var t = System.currentTimeMillis() + (1 to 20000000).foldLeft(EmptyTrie){case (trie, i) => + val k = hashFn(("hello" + i).getBytes) + val v = hashFn(("world" + i).getBytes) + + if (i % 100000 == 0) { + val newT = System.currentTimeMillis() + val delta = (newT - t) / 1000.0 + t = newT + log.debug(s"=== $i elements put, time for batch is: $delta sec") + } + trie.put(k, v) + } + } + } } trait PersistentStorage { diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainActor.scala b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainActor.scala index 00d7373e85..db8716e43b 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainActor.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainActor.scala @@ -94,7 +94,7 @@ class DumpChainActor(peerManager: ActorRef, peerMessageBus: ActorRef, startBlock val children = nodes.flatMap { case n: BranchNode => n.children.collect { case Some(Left(h)) => h } - case ExtensionNode(_, Left(h)) => Seq(h) + case ExtensionNode(_, Left(h), _, _) => Seq(h) case n: LeafNode => Seq.empty case _ => Seq.empty } @@ -125,7 +125,7 @@ class DumpChainActor(peerManager: ActorRef, peerMessageBus: ActorRef, startBlock val cNodes = NodeData(contractNodes).values.indices.map(i => NodeData(contractNodes).getMptNode(i)) contractChildren = contractChildren ++ cNodes.flatMap { case n: BranchNode => n.children.collect { case Some(Left(h)) => h } - case ExtensionNode(_, Left(h)) => Seq(h) + case ExtensionNode(_, Left(h), _, _) => Seq(h) case _ => Seq.empty } diff --git a/src/main/scala/io/iohk/ethereum/mpt/MerklePatriciaTrie.scala b/src/main/scala/io/iohk/ethereum/mpt/MerklePatriciaTrie.scala index e8d3b8c149..4d0ff76fcf 100644 --- a/src/main/scala/io/iohk/ethereum/mpt/MerklePatriciaTrie.scala +++ b/src/main/scala/io/iohk/ethereum/mpt/MerklePatriciaTrie.scala @@ -70,7 +70,7 @@ object MerklePatriciaTrie { val nodeEncoded = if (nodeId.length < 32) nodeId else source.get(ByteString(nodeId)).getOrElse(throw MPTException(s"Node not found ${Hex.toHexString(nodeId)}, trie is inconsistent")) - decodeRLP[MptNode](nodeEncoded) + decodeRLP[MptNode](nodeEncoded).withCachedHash(nodeId).withCachedRlpEncoded(nodeEncoded) } private def matchingLength(a: Array[Byte], b: Array[Byte]): Int = a.zip(b).takeWhile(t => t._1 == t._2).length @@ -236,16 +236,16 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] @tailrec private def get(node: MptNode, searchKey: Array[Byte]): Option[Array[Byte]] = node match { - case LeafNode(key, value) => + case LeafNode(key, value, _, _) => if (key.toArray[Byte] sameElements searchKey) Some(value.toArray[Byte]) else None - case extNode@ExtensionNode(sharedKey, _) => + case extNode@ExtensionNode(sharedKey, _, _, _) => val (commonKey, remainingKey) = searchKey.splitAt(sharedKey.length) if (searchKey.length >= sharedKey.length && (sharedKey sameElements commonKey)) { val nextNode = getNextNode(extNode, nodeStorage) get(nextNode, remainingKey) } else None - case branch@BranchNode(_, terminator) => + case branch@BranchNode(_, terminator, _, _) => if (searchKey.isEmpty) terminator.map(_.toArray[Byte]) else getChild(branch, searchKey(0), nodeStorage) match { case Some(child) => get(child, searchKey.slice(1, searchKey.length)) @@ -260,7 +260,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } private def putInLeafNode(node: LeafNode, searchKey: Array[Byte], value: Array[Byte]): NodeInsertResult = { - val LeafNode(existingKey, storedValue) = node + val LeafNode(existingKey, storedValue, _, _) = node matchingLength(existingKey.toArray[Byte], searchKey) match { case ml if ml == existingKey.length && ml == searchKey.length => // We are trying to insert a leaf node that has the same key as this one but different value so we need to @@ -304,7 +304,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } private def putInExtensionNode(extensionNode: ExtensionNode, searchKey: Array[Byte], value: Array[Byte]): NodeInsertResult = { - val ExtensionNode(sharedKey, next) = extensionNode + val ExtensionNode(sharedKey, next, _, _) = extensionNode matchingLength(sharedKey.toArray[Byte], searchKey) match { case 0 => // There is no common prefix with the node which means we have to replace it for a branch node @@ -349,7 +349,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } private def putInBranchNode(branchNode: BranchNode, searchKey: Array[Byte], value: Array[Byte]): NodeInsertResult = { - val BranchNode(children, _) = branchNode + val BranchNode(children, _, _, _) = branchNode if (searchKey.isEmpty) { // The key is empty, the branch node should now be a terminator node with the new value asociated with it val newBranchNode = BranchNode(children, Some(ByteString(value))) @@ -395,13 +395,13 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] private def removeFromBranchNode(node: BranchNode, searchKey: Array[Byte]): NodeRemoveResult = (node, searchKey.isEmpty) match { // They key matches a branch node but it's value doesn't match the key - case (BranchNode(_, None), true) => NodeRemoveResult(hasChanged = false, newNode = None) + case (BranchNode(_, None, _, _), true) => NodeRemoveResult(hasChanged = false, newNode = None) // We want to delete Branch node value - case (BranchNode(children, _), true) => + case (BranchNode(children, _, _, _), true) => // We need to remove old node and fix it because we removed the value val fixedNode = fix(BranchNode(children, None), nodeStorage, Nil) NodeRemoveResult(hasChanged = true, newNode = Some(fixedNode), toDeleteFromStorage = Seq(node), toUpdateInStorage = Seq(fixedNode)) - case (branchNode@BranchNode(children, optStoredValue), false) => + case (branchNode@BranchNode(children, optStoredValue, _, _), false) => // We might be trying to remove a node that's inside one of the 16 mapped nibbles val searchKeyHead = searchKey(0) getChild(branchNode, searchKeyHead, nodeStorage) map { child => @@ -435,7 +435,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } private def removeFromLeafNode(leafNode: LeafNode, searchKey: Array[Byte]): NodeRemoveResult = { - val LeafNode(existingKey, _) = leafNode + val LeafNode(existingKey, _, _, _) = leafNode if (existingKey sameElements searchKey) { // We found the node to delete NodeRemoveResult(hasChanged = true, newNode = None, toDeleteFromStorage = Seq(leafNode)) @@ -444,7 +444,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } private def removeFromExtensionNode(extensionNode: ExtensionNode, searchKey: Array[Byte]): NodeRemoveResult = { - val ExtensionNode(sharedKey, _) = extensionNode + val ExtensionNode(sharedKey, _, _, _) = extensionNode val cp = matchingLength(sharedKey.toArray[Byte], searchKey) if (cp == sharedKey.length) { // A child node of this extension is removed, so move forward @@ -487,7 +487,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] */ @tailrec private def fix(node: MptNode, nodeStorage: NodesKeyValueStorage, notStoredYet: Seq[MptNode]): MptNode = node match { - case BranchNode(children, optStoredValue) => + case BranchNode(children, optStoredValue, _, _) => val usedIndexes = children.indices.foldLeft[Seq[Int]](Nil) { (acc, i) => if (children(i).isDefined) i +: acc else acc @@ -500,7 +500,7 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] case (Nil, Some(value)) => LeafNode(ByteString.empty, value) case _ => node } - case extensionNode@ExtensionNode(sharedKey, _) => + case extensionNode@ExtensionNode(sharedKey, _, _, _) => val nextNode = extensionNode.next match { case Left(nextHash) => // If the node is not in the extension node then it might be a node to be inserted at the end of this remove @@ -512,9 +512,9 @@ class MerklePatriciaTrie[K, V] private (private val rootHash: Option[Array[Byte] } val newNode = nextNode match { // Compact Two extensions into one - case ExtensionNode(subSharedKey, subNext) => ExtensionNode(sharedKey ++ subSharedKey, subNext) + case ExtensionNode(subSharedKey, subNext, _, _) => ExtensionNode(sharedKey ++ subSharedKey, subNext) // Compact the extension and the leaf into the same leaf node - case LeafNode(subRemainingKey, subValue) => LeafNode(sharedKey ++ subRemainingKey, subValue) + case LeafNode(subRemainingKey, subValue, _, _) => LeafNode(sharedKey ++ subRemainingKey, subValue) // It's ok case _: BranchNode => node } diff --git a/src/main/scala/io/iohk/ethereum/mpt/Node.scala b/src/main/scala/io/iohk/ethereum/mpt/Node.scala index 3082626b20..da47a3fe85 100644 --- a/src/main/scala/io/iohk/ethereum/mpt/Node.scala +++ b/src/main/scala/io/iohk/ethereum/mpt/Node.scala @@ -7,13 +7,19 @@ import io.iohk.ethereum.rlp.{encode => encodeRLP} /** * Trie elements */ -sealed trait MptNode { +sealed abstract class MptNode { + val cachedHash: Option[Array[Byte]] + val cachedRlpEncoded: Option[Array[Byte]] import MerklePatriciaTrie._ - lazy val encode: Array[Byte] = encodeRLP[MptNode](this) + def withCachedHash(cachedHash: Array[Byte]): MptNode - lazy val hash: Array[Byte] = Node.hashFn(encode) + def withCachedRlpEncoded(cachedEncode: Array[Byte]): MptNode + + lazy val encode: Array[Byte] = cachedRlpEncoded.getOrElse(encodeRLP[MptNode](this)) + + lazy val hash: Array[Byte] = cachedHash.getOrElse(Node.hashFn(encode)) def capped: ByteString = { val encoded = encode @@ -25,11 +31,26 @@ object Node { val hashFn: (Array[Byte]) => Array[Byte] = (input: Array[Byte]) => crypto.kec256(input) } -case class LeafNode(key: ByteString, value: ByteString) extends MptNode +case class LeafNode(key: ByteString, value: ByteString, + cachedHash: Option[Array[Byte]] = None, cachedRlpEncoded: Option[Array[Byte]] = None) extends MptNode { + def withCachedHash(cachedHash: Array[Byte]): MptNode = copy(cachedHash = Some(cachedHash)) + + def withCachedRlpEncoded(cachedEncode: Array[Byte]): MptNode = copy(cachedRlpEncoded = Some(cachedEncode)) +} + +case class ExtensionNode(sharedKey: ByteString, next: Either[ByteString, MptNode], + cachedHash: Option[Array[Byte]] = None, cachedRlpEncoded: Option[Array[Byte]] = None) extends MptNode { + def withCachedHash(cachedHash: Array[Byte]): MptNode = copy(cachedHash = Some(cachedHash)) + + def withCachedRlpEncoded(cachedEncode: Array[Byte]): MptNode = copy(cachedRlpEncoded = Some(cachedEncode)) +} + +case class BranchNode(children: Seq[Option[Either[ByteString, MptNode]]], terminator: Option[ByteString], + cachedHash: Option[Array[Byte]] = None, cachedRlpEncoded: Option[Array[Byte]] = None) extends MptNode { + def withCachedHash(cachedHash: Array[Byte]): MptNode = copy(cachedHash = Some(cachedHash)) -case class ExtensionNode(sharedKey: ByteString, next: Either[ByteString, MptNode]) extends MptNode + def withCachedRlpEncoded(cachedEncode: Array[Byte]): MptNode = copy(cachedRlpEncoded = Some(cachedEncode)) -case class BranchNode(children: Seq[Option[Either[ByteString, MptNode]]], terminator: Option[ByteString]) extends MptNode { require(children.length == 16, "MptBranch childHashes length have to be 16") /** From ca5dbc034c501fe443f8069b80c4f816eea6e509 Mon Sep 17 00:00:00 2001 From: Adam Smolarek Date: Thu, 20 Jul 2017 15:17:10 +0200 Subject: [PATCH 07/20] config --- .../iohk/ethereum/txExecTest/ForksTest.scala | 1 + src/main/resources/application.conf | 4 + .../daoFork/DaoForkConfiguration.scala | 129 ++++++++++++++++++ .../scala/io/iohk/ethereum/utils/Config.scala | 2 + .../validators/BlockHeaderValidator.scala | 17 ++- .../jsonrpc/PersonalServiceSpec.scala | 1 + .../ethereum/mining/BlockGeneratorSpec.scala | 2 + .../handshaker/EtcHandshakerSpec.scala | 1 + .../validators/BlockHeaderValidatorSpec.scala | 1 + src/universal/conf/blockchain.conf | 4 + src/universal/conf/morden.conf | 65 ++++----- 11 files changed, 192 insertions(+), 35 deletions(-) create mode 100644 src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala index d29cd2ec2c..3c7ca2646c 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala @@ -28,6 +28,7 @@ class ForksTest extends FlatSpec with Matchers { override val difficultyBombPauseBlockNumber: BigInt = Long.MaxValue override val difficultyBombContinueBlockNumber: BigInt = Long.MaxValue override val accountStartNonce: UInt256 = UInt256.Zero + override val proDaoFork: Boolean = false } val noErrors = a[Right[_, Seq[Receipt]]] diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index c42f674187..701ed18a63 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -228,6 +228,10 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md difficulty-bomb-continue-block-number = "5000000" + # specify if this client is pro or against DAO hard fork + # if true it will accept ETH version of chain if false it will accept ETC + pro-dao-fork = false + # DAO fork block number (Ethereum HF/Classic split) # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ dao-fork-block-number = "1920000" diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala new file mode 100644 index 0000000000..6337e8eca7 --- /dev/null +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala @@ -0,0 +1,129 @@ +package io.iohk.ethereum.daoFork + +import akka.util.ByteString +import io.iohk.ethereum.domain.Address +import org.spongycastle.util.encoders.Hex + +object DaoForkConfiguration { + val blockExtraData = ByteString(Hex.decode("64616f2d686172642d666f726b")) + val range = 10 + val refundContract = Address(Hex.decode("bf4ed7b27f1d666546e30d74d50d173d20bca754")) + val drainList = Seq( + Address(Hex.decode("d4fe7bc31cedb7bfb8a345f31e668033056b2728")), + Address(Hex.decode("b3fb0e5aba0e20e5c49d252dfd30e102b171a425")), + Address(Hex.decode("2c19c7f9ae8b751e37aeb2d93a699722395ae18f")), + Address(Hex.decode("ecd135fa4f61a655311e86238c92adcd779555d2")), + Address(Hex.decode("1975bd06d486162d5dc297798dfc41edd5d160a7")), + Address(Hex.decode("a3acf3a1e16b1d7c315e23510fdd7847b48234f6")), + Address(Hex.decode("319f70bab6845585f412ec7724b744fec6095c85")), + Address(Hex.decode("06706dd3f2c9abf0a21ddcc6941d9b86f0596936")), + Address(Hex.decode("5c8536898fbb74fc7445814902fd08422eac56d0")), + Address(Hex.decode("6966ab0d485353095148a2155858910e0965b6f9")), + Address(Hex.decode("779543a0491a837ca36ce8c635d6154e3c4911a6")), + Address(Hex.decode("2a5ed960395e2a49b1c758cef4aa15213cfd874c")), + Address(Hex.decode("5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5")), + Address(Hex.decode("9c50426be05db97f5d64fc54bf89eff947f0a321")), + Address(Hex.decode("200450f06520bdd6c527622a273333384d870efb")), + Address(Hex.decode("be8539bfe837b67d1282b2b1d61c3f723966f049")), + Address(Hex.decode("6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb")), + Address(Hex.decode("f1385fb24aad0cd7432824085e42aff90886fef5")), + Address(Hex.decode("d1ac8b1ef1b69ff51d1d401a476e7e612414f091")), + Address(Hex.decode("8163e7fb499e90f8544ea62bbf80d21cd26d9efd")), + Address(Hex.decode("51e0ddd9998364a2eb38588679f0d2c42653e4a6")), + Address(Hex.decode("627a0a960c079c21c34f7612d5d230e01b4ad4c7")), + Address(Hex.decode("f0b1aa0eb660754448a7937c022e30aa692fe0c5")), + Address(Hex.decode("24c4d950dfd4dd1902bbed3508144a54542bba94")), + Address(Hex.decode("9f27daea7aca0aa0446220b98d028715e3bc803d")), + Address(Hex.decode("a5dc5acd6a7968a4554d89d65e59b7fd3bff0f90")), + Address(Hex.decode("d9aef3a1e38a39c16b31d1ace71bca8ef58d315b")), + Address(Hex.decode("63ed5a272de2f6d968408b4acb9024f4cc208ebf")), + Address(Hex.decode("6f6704e5a10332af6672e50b3d9754dc460dfa4d")), + Address(Hex.decode("77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6")), + Address(Hex.decode("492ea3bb0f3315521c31f273e565b868fc090f17")), + Address(Hex.decode("0ff30d6de14a8224aa97b78aea5388d1c51c1f00")), + Address(Hex.decode("9ea779f907f0b315b364b0cfc39a0fde5b02a416")), + Address(Hex.decode("ceaeb481747ca6c540a000c1f3641f8cef161fa7")), + Address(Hex.decode("cc34673c6c40e791051898567a1222daf90be287")), + Address(Hex.decode("579a80d909f346fbfb1189493f521d7f48d52238")), + Address(Hex.decode("e308bd1ac5fda103967359b2712dd89deffb7973")), + Address(Hex.decode("4cb31628079fb14e4bc3cd5e30c2f7489b00960c")), + Address(Hex.decode("ac1ecab32727358dba8962a0f3b261731aad9723")), + Address(Hex.decode("4fd6ace747f06ece9c49699c7cabc62d02211f75")), + Address(Hex.decode("440c59b325d2997a134c2c7c60a8c61611212bad")), + Address(Hex.decode("4486a3d68fac6967006d7a517b889fd3f98c102b")), + Address(Hex.decode("9c15b54878ba618f494b38f0ae7443db6af648ba")), + Address(Hex.decode("27b137a85656544b1ccb5a0f2e561a5703c6a68f")), + Address(Hex.decode("21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241")), + Address(Hex.decode("23b75c2f6791eef49c69684db4c6c1f93bf49a50")), + Address(Hex.decode("1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b")), + Address(Hex.decode("b9637156d330c0d605a791f1c31ba5890582fe1c")), + Address(Hex.decode("6131c42fa982e56929107413a9d526fd99405560")), + Address(Hex.decode("1591fc0f688c81fbeb17f5426a162a7024d430c2")), + Address(Hex.decode("542a9515200d14b68e934e9830d91645a980dd7a")), + Address(Hex.decode("c4bbd073882dd2add2424cf47d35213405b01324")), + Address(Hex.decode("782495b7b3355efb2833d56ecb34dc22ad7dfcc4")), + Address(Hex.decode("58b95c9a9d5d26825e70a82b6adb139d3fd829eb")), + Address(Hex.decode("3ba4d81db016dc2890c81f3acec2454bff5aada5")), + Address(Hex.decode("b52042c8ca3f8aa246fa79c3feaa3d959347c0ab")), + Address(Hex.decode("e4ae1efdfc53b73893af49113d8694a057b9c0d1")), + Address(Hex.decode("3c02a7bc0391e86d91b7d144e61c2c01a25a79c5")), + Address(Hex.decode("0737a6b837f97f46ebade41b9bc3e1c509c85c53")), + Address(Hex.decode("97f43a37f595ab5dd318fb46e7a155eae057317a")), + Address(Hex.decode("52c5317c848ba20c7504cb2c8052abd1fde29d03")), + Address(Hex.decode("4863226780fe7c0356454236d3b1c8792785748d")), + Address(Hex.decode("5d2b2e6fcbe3b11d26b525e085ff818dae332479")), + Address(Hex.decode("5f9f3392e9f62f63b8eac0beb55541fc8627f42c")), + Address(Hex.decode("057b56736d32b86616a10f619859c6cd6f59092a")), + Address(Hex.decode("9aa008f65de0b923a2a4f02012ad034a5e2e2192")), + Address(Hex.decode("304a554a310c7e546dfe434669c62820b7d83490")), + Address(Hex.decode("914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79")), + Address(Hex.decode("4deb0033bb26bc534b197e61d19e0733e5679784")), + Address(Hex.decode("07f5c1e1bc2c93e0402f23341973a0e043f7bf8a")), + Address(Hex.decode("35a051a0010aba705c9008d7a7eff6fb88f6ea7b")), + Address(Hex.decode("4fa802324e929786dbda3b8820dc7834e9134a2a")), + Address(Hex.decode("9da397b9e80755301a3b32173283a91c0ef6c87e")), + Address(Hex.decode("8d9edb3054ce5c5774a420ac37ebae0ac02343c6")), + Address(Hex.decode("0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9")), + Address(Hex.decode("5dc28b15dffed94048d73806ce4b7a4612a1d48f")), + Address(Hex.decode("bcf899e6c7d9d5a215ab1e3444c86806fa854c76")), + Address(Hex.decode("12e626b0eebfe86a56d633b9864e389b45dcb260")), + Address(Hex.decode("a2f1ccba9395d7fcb155bba8bc92db9bafaeade7")), + Address(Hex.decode("ec8e57756626fdc07c63ad2eafbd28d08e7b0ca5")), + Address(Hex.decode("d164b088bd9108b60d0ca3751da4bceb207b0782")), + Address(Hex.decode("6231b6d0d5e77fe001c2a460bd9584fee60d409b")), + Address(Hex.decode("1cba23d343a983e9b5cfd19496b9a9701ada385f")), + Address(Hex.decode("a82f360a8d3455c5c41366975bde739c37bfeb8a")), + Address(Hex.decode("9fcd2deaff372a39cc679d5c5e4de7bafb0b1339")), + Address(Hex.decode("005f5cee7a43331d5a3d3eec71305925a62f34b6")), + Address(Hex.decode("0e0da70933f4c7849fc0d203f5d1d43b9ae4532d")), + Address(Hex.decode("d131637d5275fd1a68a3200f4ad25c71a2a9522e")), + Address(Hex.decode("bc07118b9ac290e4622f5e77a0853539789effbe")), + Address(Hex.decode("47e7aa56d6bdf3f36be34619660de61275420af8")), + Address(Hex.decode("acd87e28b0c9d1254e868b81cba4cc20d9a32225")), + Address(Hex.decode("adf80daec7ba8dcf15392f1ac611fff65d94f880")), + Address(Hex.decode("5524c55fb03cf21f549444ccbecb664d0acad706")), + Address(Hex.decode("40b803a9abce16f50f36a77ba41180eb90023925")), + Address(Hex.decode("fe24cdd8648121a43a7c86d289be4dd2951ed49f")), + Address(Hex.decode("17802f43a0137c506ba92291391a8a8f207f487d")), + Address(Hex.decode("253488078a4edf4d6f42f113d1e62836a942cf1a")), + Address(Hex.decode("86af3e9626fce1957c82e88cbf04ddf3a2ed7915")), + Address(Hex.decode("b136707642a4ea12fb4bae820f03d2562ebff487")), + Address(Hex.decode("dbe9b615a3ae8709af8b93336ce9b477e4ac0940")), + Address(Hex.decode("f14c14075d6c4ed84b86798af0956deef67365b5")), + Address(Hex.decode("ca544e5c4687d109611d0f8f928b53a25af72448")), + Address(Hex.decode("aeeb8ff27288bdabc0fa5ebb731b6f409507516c")), + Address(Hex.decode("cbb9d3703e651b0d496cdefb8b92c25aeb2171f7")), + Address(Hex.decode("6d87578288b6cb5549d5076a207456a1f6a63dc0")), + Address(Hex.decode("b2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e")), + Address(Hex.decode("accc230e8a6e5be9160b8cdf2864dd2a001c28b6")), + Address(Hex.decode("2b3455ec7fedf16e646268bf88846bd7a2319bb2")), + Address(Hex.decode("4613f3bca5c44ea06337a9e439fbc6d42e501d0a")), + Address(Hex.decode("d343b217de44030afaa275f54d31a9317c7f441e")), + Address(Hex.decode("84ef4b2357079cd7a7c69fd7a37cd0609a679106")), + Address(Hex.decode("da2fef9e4a3230988ff17df2165440f37e8b1708")), + Address(Hex.decode("f4c64518ea10f995918a454158c6b61407ea345c")), + Address(Hex.decode("7602b46df5390e432ef1c307d4f2c9ff6d65cc97")), + Address(Hex.decode("bb9bc244d798123fde783fcc1c72d3bb8c189413")), + Address(Hex.decode("807640a13483f8ac783c557fcdf27be11ea4ac7a")) + ) +} \ No newline at end of file diff --git a/src/main/scala/io/iohk/ethereum/utils/Config.scala b/src/main/scala/io/iohk/ethereum/utils/Config.scala index 6d40cbbd68..7bc13f5fee 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -249,6 +249,7 @@ trait BlockchainConfig { val customGenesisFileOpt: Option[String] + val proDaoFork: Boolean val daoForkBlockNumber: BigInt val daoForkBlockHash: ByteString val accountStartNonce: UInt256 @@ -273,6 +274,7 @@ object BlockchainConfig { override val customGenesisFileOpt: Option[String] = Try(blockchainConfig.getString("custom-genesis-file")).toOption + override val proDaoFork: Boolean = blockchainConfig.getBoolean("pro-dao-fork") override val daoForkBlockNumber: BigInt = BigInt(blockchainConfig.getString("dao-fork-block-number")) override val daoForkBlockHash: ByteString = ByteString(Hex.decode(blockchainConfig.getString("dao-fork-block-hash"))) override val accountStartNonce: UInt256 = UInt256(BigInt(blockchainConfig.getString("account-start-nonce"))) diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index 6c0d303c1a..af9c8bbb85 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -2,6 +2,7 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.crypto.{kec256, kec512} +import io.iohk.ethereum.daoFork.DaoForkConfiguration import io.iohk.ethereum.domain.{BlockHeader, Blockchain, DifficultyCalculator} import io.iohk.ethereum.utils.BlockchainConfig import org.spongycastle.util.encoders.Hex @@ -58,9 +59,18 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block * @param blockHeader BlockHeader to validate. * @return BlockHeader if valid, an [[HeaderExtraDataError]] otherwise */ - private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = - if(blockHeader.extraData.length <= MaxExtraDataSize) Right(blockHeader) - else Left(HeaderExtraDataError) + private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = { + if (blockchainConfig.proDaoFork && + blockchainConfig.daoForkBlockNumber <= blockHeader.number && + blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockHeader.number && + blockHeader.extraData != DaoForkConfiguration.blockExtraData) { + Left(DaoHeaderExtraDataError) + } else if (blockHeader.extraData.length <= MaxExtraDataSize) { + Right(blockHeader) + } else { + Left(HeaderExtraDataError) + } + } /** * Validates [[io.iohk.ethereum.domain.BlockHeader.unixTimestamp]] is greater than the one of its parent @@ -168,6 +178,7 @@ sealed trait BlockHeaderError object BlockHeaderError { case object HeaderParentNotFoundError extends BlockHeaderError case object HeaderExtraDataError extends BlockHeaderError + case object DaoHeaderExtraDataError extends BlockHeaderError case object HeaderTimestampError extends BlockHeaderError case object HeaderDifficultyError extends BlockHeaderError case object HeaderGasUsedError extends BlockHeaderError diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala index 8d67a0c68c..3eacb0ddcb 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala @@ -352,6 +352,7 @@ class PersonalServiceSpec extends FlatSpec with Matchers with MockFactory with S override val daoForkBlockHash: ByteString = ByteString.empty override val accountStartNonce: UInt256 = UInt256.Zero override val monetaryPolicyConfig: MonetaryPolicyConfig = new MonetaryPolicyConfig(0, 0, 0) + override val proDaoFork: Boolean = false } val wallet = Wallet(address, prvKey) diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index e831567151..47bd39d16b 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -115,6 +115,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val eip150BlockNumber: BigInt = Long.MaxValue override val daoForkBlockHash: ByteString = ByteString("unused") override val accountStartNonce: UInt256 = UInt256.Zero + override val proDaoFork: Boolean = false } val generalTx = SignedTransaction.sign(transaction, keyPair, None) @@ -264,6 +265,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val eip150BlockNumber: BigInt = Long.MaxValue override val daoForkBlockHash: ByteString = ByteString("unused") override val accountStartNonce: UInt256 = UInt256.Zero + override val proDaoFork: Boolean = false } lazy val ledger = new LedgerImpl(VM, blockchain, blockchainConfig) diff --git a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala index bd5af709a4..016112d8d4 100644 --- a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala @@ -160,6 +160,7 @@ class EtcHandshakerSpec extends FlatSpec with Matchers { override val chainId: Byte = 0.toByte override val monetaryPolicyConfig: MonetaryPolicyConfig = null override val accountStartNonce: UInt256 = UInt256.Zero + override val proDaoFork: Boolean = false } val etcHandshakerConfigurationWithResolver = new MockEtcHandshakerConfiguration { diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index a922953c35..4a12d57ac6 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -33,6 +33,7 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck override val monetaryPolicyConfig: MonetaryPolicyConfig = null override val customGenesisFileOpt: Option[String] = None override val accountStartNonce: UInt256 = UInt256.Zero + override val proDaoFork: Boolean = false } val blockHeaderValidator = new BlockHeaderValidatorImpl(blockchainConfig) diff --git a/src/universal/conf/blockchain.conf b/src/universal/conf/blockchain.conf index 4e09f10fc6..938f765e8c 100644 --- a/src/universal/conf/blockchain.conf +++ b/src/universal/conf/blockchain.conf @@ -29,6 +29,10 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md # difficulty-bomb-continue-block-number = "5000000" + # specify if this client is pro or against DAO hard fork + # if true it will accept ETH version of chain if false it will accept ETC + # pro-dao-fork = false + # DAO fork block number (Ethereum HF/Classic split) # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ # dao-fork-block-number = "1920000" diff --git a/src/universal/conf/morden.conf b/src/universal/conf/morden.conf index 2ff4555919..ebcca54f51 100644 --- a/src/universal/conf/morden.conf +++ b/src/universal/conf/morden.conf @@ -1,32 +1,33 @@ -mantis { - network { - discovery.bootstrap-nodes = [ - "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303", - "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303", - "enode://fb28713820e718066a2f5df6250ae9d07cff22f672dbf26be6c75d088f821a9ad230138ba492c533a80407d054b1436ef18e951bb65e6901553516c8dffe8ff0@104.155.176.151:30304", - "enode://afdc6076b9bf3e7d3d01442d6841071e84c76c73a7016cb4f35c0437df219db38565766234448f1592a07ba5295a867f0ce87b359bf50311ed0b830a2361392d@104.154.136.117:30403", - "enode://21101a9597b79e933e17bc94ef3506fe99a137808907aa8fefa67eea4b789792ad11fb391f38b00087f8800a2d3dff011572b62a31232133dd1591ac2d1502c8@104.198.71.200:30403" - ] - - peer.network-id = 2 - } - - blockchain { - frontier-block-number = "0" - homestead-block-number = "494000" - eip150-block-number = "1783000" - eip155-block-number = "1915000" - eip160-block-number = "1915000" - difficulty-bomb-pause-block-number = "1915000" - difficulty-bomb-continue-block-number = "3415000" - - dao-fork-block-number = "1783000" - dao-fork-block-hash = "f376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145" - - account-start-nonce = "1048576" - - chain-id = "3e" - - custom-genesis-file = "conf/morden.json" - } -} +mantis { + network { + discovery.bootstrap-nodes = [ + "enode://e731347db0521f3476e6bbbb83375dcd7133a1601425ebd15fd10f3835fd4c304fba6282087ca5a0deeafadf0aa0d4fd56c3323331901c1f38bd181c283e3e35@128.199.55.137:30303", + "enode://ceb5c0f85eb994dbe9693bf46d99b03f6b838d17cc74e68d5eb003171ff39e5f120b17f965b267c319303f94d80b9d994b77062fb1486d76ce95d9f3d8fe1cb4@46.101.122.141:30303", + "enode://fb28713820e718066a2f5df6250ae9d07cff22f672dbf26be6c75d088f821a9ad230138ba492c533a80407d054b1436ef18e951bb65e6901553516c8dffe8ff0@104.155.176.151:30304", + "enode://afdc6076b9bf3e7d3d01442d6841071e84c76c73a7016cb4f35c0437df219db38565766234448f1592a07ba5295a867f0ce87b359bf50311ed0b830a2361392d@104.154.136.117:30403", + "enode://21101a9597b79e933e17bc94ef3506fe99a137808907aa8fefa67eea4b789792ad11fb391f38b00087f8800a2d3dff011572b62a31232133dd1591ac2d1502c8@104.198.71.200:30403" + ] + + peer.network-id = 2 + } + + blockchain { + frontier-block-number = "0" + homestead-block-number = "494000" + eip150-block-number = "1783000" + eip155-block-number = "1915000" + eip160-block-number = "1915000" + difficulty-bomb-pause-block-number = "1915000" + difficulty-bomb-continue-block-number = "3415000" + + pro-dao-fork = false + dao-fork-block-number = "1783000" + dao-fork-block-hash = "f376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145" + + account-start-nonce = "1048576" + + chain-id = "3e" + + custom-genesis-file = "conf/morden.json" + } +} From cc026a28a5aa55c01af7651b6fcaf8c4ae06e9b9 Mon Sep 17 00:00:00 2001 From: Adam Smolarek Date: Mon, 24 Jul 2017 12:14:38 +0200 Subject: [PATCH 08/20] wip --- .../daoFork/DaoForkConfiguration.scala | 2 +- .../iohk/ethereum/mining/BlockGenerator.scala | 42 +++++++++++-------- .../scala/io/iohk/ethereum/utils/Config.scala | 4 ++ .../validators/BlockHeaderValidator.scala | 22 +++++----- src/test/resources/application.conf | 4 ++ .../ethereum/jsonrpc/EthServiceSpec.scala | 1 + .../jsonrpc/JsonRpcControllerSpec.scala | 1 + .../ethereum/mining/BlockGeneratorSpec.scala | 11 +++++ .../iohk/ethereum/ommers/OmmersPoolSpec.scala | 2 + 9 files changed, 60 insertions(+), 29 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala index 6337e8eca7..ab20f0697c 100644 --- a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala @@ -126,4 +126,4 @@ object DaoForkConfiguration { Address(Hex.decode("bb9bc244d798123fde783fcc1c72d3bb8c189413")), Address(Hex.decode("807640a13483f8ac783c557fcdf27be11ea4ac7a")) ) -} \ No newline at end of file +} diff --git a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala index 158856e73f..51c8c9266d 100644 --- a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala +++ b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala @@ -20,6 +20,7 @@ import io.iohk.ethereum.validators.MptListValidator.intByteArraySerializable import io.iohk.ethereum.validators.OmmersValidator.OmmersError import io.iohk.ethereum.validators.Validators import io.iohk.ethereum.crypto._ +import io.iohk.ethereum.daoFork.DaoForkConfiguration class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, miningConfig: MiningConfig, ledger: Ledger, validators: Validators, blockTimestampProvider: BlockTimestampProvider = DefaultBlockTimestampProvider) { @@ -88,24 +89,29 @@ class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, transactionsForBlock } - private def prepareHeader(blockNumber: BigInt, ommers: Seq[BlockHeader], beneficiary: Address, parent: Block, blockTimestamp: Long) = BlockHeader( - parentHash = parent.header.hash, - ommersHash = ByteString(kec256(ommers.toBytes: Array[Byte])), - beneficiary = beneficiary.bytes, - stateRoot = ByteString.empty, - //we are not able to calculate transactionsRoot here because we do not know if they will fail - transactionsRoot = ByteString.empty, - receiptsRoot = ByteString.empty, - logsBloom = ByteString.empty, - difficulty = difficulty.calculateDifficulty(blockNumber, blockTimestamp, parent.header), - number = blockNumber, - gasLimit = calculateGasLimit(parent.header.gasLimit), - gasUsed = 0, - unixTimestamp = blockTimestamp, - extraData = ByteString("mined with etc scala"), - mixHash = ByteString.empty, - nonce = ByteString.empty - ) + private def prepareHeader(blockNumber: BigInt, ommers: Seq[BlockHeader], beneficiary: Address, parent: Block, blockTimestamp: Long) = { + val inInRange = blockchainConfig.daoForkBlockNumber <= blockNumber && + blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockNumber + + BlockHeader( + parentHash = parent.header.hash, + ommersHash = ByteString(kec256(ommers.toBytes: Array[Byte])), + beneficiary = beneficiary.bytes, + stateRoot = ByteString.empty, + //we are not able to calculate transactionsRoot here because we do not know if they will fail + transactionsRoot = ByteString.empty, + receiptsRoot = ByteString.empty, + logsBloom = ByteString.empty, + difficulty = difficulty.calculateDifficulty(blockNumber, blockTimestamp, parent.header), + number = blockNumber, + gasLimit = calculateGasLimit(parent.header.gasLimit), + gasUsed = 0, + unixTimestamp = blockTimestamp, + extraData = if (inInRange && blockchainConfig.proDaoFork) DaoForkConfiguration.blockExtraData else miningConfig.headerExtraData, + mixHash = ByteString.empty, + nonce = ByteString.empty + ) + } def getPrepared(powHeaderHash: ByteString): Option[PendingBlock] = { cache.getAndUpdate(new UnaryOperator[List[PendingBlock]] { diff --git a/src/main/scala/io/iohk/ethereum/utils/Config.scala b/src/main/scala/io/iohk/ethereum/utils/Config.scala index 7bc13f5fee..32ac15bc52 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -217,11 +217,14 @@ object TxPoolConfig { } trait MiningConfig { + //duplicated in BlockHeaderValidator + val MaxExtraDataSize: Int = 32 val ommersPoolSize: Int val blockCacheSize: Int val coinbase: Address val activeTimeout: FiniteDuration val ommerPoolQueryTimeout: FiniteDuration + val headerExtraData: ByteString } object MiningConfig { @@ -234,6 +237,7 @@ object MiningConfig { val ommersPoolSize: Int = miningConfig.getInt("ommers-pool-size") val activeTimeout: FiniteDuration = miningConfig.getDuration("active-timeout").toMillis.millis val ommerPoolQueryTimeout: FiniteDuration = miningConfig.getDuration("ommer-pool-query-timeout").toMillis.millis + override val headerExtraData: ByteString = ByteString(miningConfig.getString("header-extra-data").getBytes).take(MaxExtraDataSize) } } } diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index af9c8bbb85..786fb317c4 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -5,7 +5,6 @@ import io.iohk.ethereum.crypto.{kec256, kec512} import io.iohk.ethereum.daoFork.DaoForkConfiguration import io.iohk.ethereum.domain.{BlockHeader, Blockchain, DifficultyCalculator} import io.iohk.ethereum.utils.BlockchainConfig -import org.spongycastle.util.encoders.Hex trait BlockHeaderValidator { @@ -59,18 +58,21 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block * @param blockHeader BlockHeader to validate. * @return BlockHeader if valid, an [[HeaderExtraDataError]] otherwise */ - private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = { - if (blockchainConfig.proDaoFork && - blockchainConfig.daoForkBlockNumber <= blockHeader.number && - blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockHeader.number && - blockHeader.extraData != DaoForkConfiguration.blockExtraData) { - Left(DaoHeaderExtraDataError) - } else if (blockHeader.extraData.length <= MaxExtraDataSize) { - Right(blockHeader) + private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = + if (blockHeader.extraData.length <= MaxExtraDataSize) { + val inInRange = blockchainConfig.daoForkBlockNumber <= blockHeader.number && + blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockHeader.number + val daoForkData = blockHeader.extraData == DaoForkConfiguration.blockExtraData + + (inInRange, daoForkData, blockchainConfig.proDaoFork) match { + case (false, _, _) | (true, true, true) | (true, false, false) => + Right(blockHeader) + case _ => + Left(DaoHeaderExtraDataError) + } } else { Left(HeaderExtraDataError) } - } /** * Validates [[io.iohk.ethereum.domain.BlockHeader.unixTimestamp]] is greater than the one of its parent diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index 44534b83de..b99687fbfe 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -18,6 +18,10 @@ mantis { eip155-block-number = "3000000" } + mining { + header-extra-data = "grothendieck" + } + sync { do-fast-sync = true peers-scan-interval = 500.millis diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala index cb12ed3453..47bf21ff3a 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala @@ -777,6 +777,7 @@ class EthServiceSpec extends FlatSpec with Matchers with ScalaFutures with MockF override val ommersPoolSize: Int = 30 override val activeTimeout: FiniteDuration = Timeouts.shortTimeout override val ommerPoolQueryTimeout: FiniteDuration = Timeouts.normalTimeout + override val headerExtraData: ByteString = ByteString.empty } val filterConfig = new FilterConfig { diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala index 8b88f61de6..90db67543a 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala @@ -1308,6 +1308,7 @@ class JsonRpcControllerSpec extends FlatSpec with Matchers with PropertyChecks w override val ommersPoolSize: Int = 30 override val activeTimeout: FiniteDuration = Timeouts.normalTimeout override val ommerPoolQueryTimeout: FiniteDuration = Timeouts.normalTimeout + override val headerExtraData: ByteString = ByteString.empty } val filterConfig = new FilterConfig { diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index 47bd39d16b..1be7489802 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -18,6 +18,7 @@ import org.spongycastle.util.encoders.Hex import io.iohk.ethereum.crypto._ import io.iohk.ethereum.db.components.Storages.PruningModeComponent import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} +import io.iohk.ethereum.daoFork.DaoForkConfiguration import io.iohk.ethereum.domain.SignedTransaction.FirstByteOfAddress import io.iohk.ethereum.utils.Config.DbConfig import org.spongycastle.crypto.AsymmetricCipherKeyPair @@ -41,6 +42,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with .map(pb => pb.block.copy(header = pb.block.header.copy(nonce = minedNonce, mixHash = minedMixHash, unixTimestamp = miningTimestamp))) fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "generate correct block with transactions" in new TestSetup { @@ -56,6 +58,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with .map(pb => pb.block.copy(header = pb.block.header.copy(nonce = minedNonce, mixHash = minedMixHash, unixTimestamp = miningTimestamp))) fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "filter out failing transactions" in new TestSetup { @@ -73,6 +76,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "filter out transactions exceeding block gas limit and include correct transactions" in new TestSetup { @@ -96,6 +100,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "generate block before eip155 and filter out chain specific tx" in new TestSetup { @@ -135,6 +140,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(generalTx)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "generate block after eip155 and allow both chain specific and general transactions" in new TestSetup { @@ -154,6 +160,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction, generalTx)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "include consecutive transactions from single sender" in new TestSetup { @@ -173,6 +180,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction, nextTransaction)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "filter out failing transaction from the middle of tx list" in new TestSetup { @@ -205,6 +213,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction, nextTransaction)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "include transaction with higher gas price if nonce is the same" in new TestSetup { @@ -227,6 +236,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction)) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } trait TestSetup extends EphemBlockchainTestSetup { @@ -287,6 +297,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val ommersPoolSize: Int = 30 override val activeTimeout: FiniteDuration = Timeouts.normalTimeout override val ommerPoolQueryTimeout: FiniteDuration = Timeouts.normalTimeout + override val headerExtraData: ByteString = ByteString("mined with etc scala") } lazy val blockTimestampProvider = new FakeBlockTimestampProvider diff --git a/src/test/scala/io/iohk/ethereum/ommers/OmmersPoolSpec.scala b/src/test/scala/io/iohk/ethereum/ommers/OmmersPoolSpec.scala index 3b57b5ace2..7f1c5f79ac 100644 --- a/src/test/scala/io/iohk/ethereum/ommers/OmmersPoolSpec.scala +++ b/src/test/scala/io/iohk/ethereum/ommers/OmmersPoolSpec.scala @@ -2,6 +2,7 @@ package io.iohk.ethereum.ommers import akka.actor.ActorSystem import akka.testkit.TestProbe +import akka.util.ByteString import io.iohk.ethereum.Fixtures.Blocks.Block3125369 import io.iohk.ethereum.Timeouts import io.iohk.ethereum.domain.{Address, Blockchain, BlockchainImpl} @@ -60,6 +61,7 @@ class OmmersPoolSpec extends FlatSpec with Matchers with MockFactory { override val ommerPoolQueryTimeout: FiniteDuration = Timeouts.normalTimeout override val blockCacheSize: Int = 4 override val activeTimeout: FiniteDuration = Timeouts.normalTimeout + override val headerExtraData: ByteString = ByteString.empty } val testProbe = TestProbe() From f57650ed860eeabf27f358b6869c941f7a6a29ba Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Wed, 13 Sep 2017 21:12:23 -0300 Subject: [PATCH 09/20] [EC-236] Apply DAO hardfork in ledger --- .../iohk/ethereum/txExecTest/ForksTest.scala | 5 ++-- .../txExecTest/util/DumpChainApp.scala | 2 +- ...onfiguration.scala => DaoForkConfig.scala} | 27 +++++++++++++++---- .../io/iohk/ethereum/ledger/Ledger.scala | 22 ++++++++++++++- .../iohk/ethereum/mining/BlockGenerator.scala | 7 +++-- .../iohk/ethereum/network/ForkResolver.scala | 7 ++--- .../ethereum/nodebuilder/NodeBuilder.scala | 2 +- .../scala/io/iohk/ethereum/utils/Config.scala | 13 ++++----- .../validators/BlockHeaderValidator.scala | 11 ++++---- .../jsonrpc/PersonalServiceSpec.scala | 5 ++-- .../ethereum/mining/BlockGeneratorSpec.scala | 18 +++++-------- .../ethereum/network/EtcPeerManagerSpec.scala | 2 +- .../handshaker/EtcHandshakerSpec.scala | 12 +++++---- .../ethereum/network/p2p/PeerActorSpec.scala | 2 +- .../validators/BlockHeaderValidatorSpec.scala | 9 ++++--- 15 files changed, 91 insertions(+), 53 deletions(-) rename src/main/scala/io/iohk/ethereum/daoFork/{DaoForkConfiguration.scala => DaoForkConfig.scala} (89%) diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala index 3c7ca2646c..b7cac3e9c2 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala @@ -1,6 +1,7 @@ package io.iohk.ethereum.txExecTest import akka.util.ByteString +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256} import io.iohk.ethereum.ledger.LedgerImpl import io.iohk.ethereum.txExecTest.util.FixtureProvider @@ -23,12 +24,10 @@ class ForksTest extends FlatSpec with Matchers { // unused override val customGenesisFileOpt: Option[String] = None - override val daoForkBlockNumber: BigInt = 10000 - override val daoForkBlockHash: ByteString = ByteString("unused") override val difficultyBombPauseBlockNumber: BigInt = Long.MaxValue override val difficultyBombContinueBlockNumber: BigInt = Long.MaxValue override val accountStartNonce: UInt256 = UInt256.Zero - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = 1000, daoForkBlockHash = ByteString("unused"), proDaoFork = false) } val noErrors = a[Right[_, Seq[Receipt]]] diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala index e46959175e..0383fdabb9 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala @@ -71,7 +71,7 @@ object DumpChainApp extends App with NodeKeyBuilder with SecureRandomBuilder wit lazy val forkResolverOpt = if (blockchainConfig.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) private val handshakerConfiguration: EtcHandshakerConfiguration = new EtcHandshakerConfiguration { diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala similarity index 89% rename from src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala rename to src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala index ab20f0697c..183dd3ff8a 100644 --- a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfiguration.scala +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala @@ -4,11 +4,28 @@ import akka.util.ByteString import io.iohk.ethereum.domain.Address import org.spongycastle.util.encoders.Hex -object DaoForkConfiguration { - val blockExtraData = ByteString(Hex.decode("64616f2d686172642d666f726b")) - val range = 10 - val refundContract = Address(Hex.decode("bf4ed7b27f1d666546e30d74d50d173d20bca754")) - val drainList = Seq( +trait DaoForkConfig { + + val proDaoFork: Boolean + val daoForkBlockNumber: BigInt + val daoForkBlockHash: ByteString + val blockExtraData: ByteString + val range: Int + val refundContract: Address + val drainList: Seq[Address] + + private val extratadaBlockRange = daoForkBlockNumber to (daoForkBlockNumber + range) + + def isDaoForkBlock(blockNumber: BigInt): Boolean = proDaoFork && (daoForkBlockNumber == blockNumber) + + def requiresExtraData(blockNumber: BigInt): Boolean = proDaoFork && (extratadaBlockRange contains blockNumber) +} + +case class DefaultDaoForkConfig(proDaoFork: Boolean, daoForkBlockNumber: BigInt, daoForkBlockHash: ByteString) extends DaoForkConfig { + override val blockExtraData = ByteString(Hex.decode("64616f2d686172642d666f726b")) + override val range = 10 + override val refundContract = Address(Hex.decode("bf4ed7b27f1d666546e30d74d50d173d20bca754")) + override val drainList = Seq( Address(Hex.decode("d4fe7bc31cedb7bfb8a345f31e668033056b2728")), Address(Hex.decode("b3fb0e5aba0e20e5c49d252dfd30e102b171a425")), Address(Hex.decode("2c19c7f9ae8b751e37aeb2d93a699722395ae18f")), diff --git a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala index 4c6d4d0143..6033f9b1d3 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala @@ -1,6 +1,7 @@ package io.iohk.ethereum.ledger import akka.util.ByteString +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain._ import io.iohk.ethereum.validators._ import io.iohk.ethereum.ledger.BlockExecutionError.{StateBeforeFailure, TxsExecutionError, ValidationAfterExecError, ValidationBeforeExecError} @@ -76,8 +77,12 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai val parentStateRoot = blockchain.getBlockHeaderByHash(block.header.parentHash).map(_.stateRoot) val initialWorld = blockchain.getWorldStateProxy(block.header.number, blockchainConfig.accountStartNonce, parentStateRoot) + val inputWorld = + if(blockchainConfig.daoForkConfig isDaoForkBlock block.header.number) applyDaoFork(initialWorld, blockchainConfig.daoForkConfig) + else initialWorld + log.debug(s"About to execute ${block.body.transactionList.size} txs from block ${block.header.number} (with hash: ${block.header.hashAsHexString})") - val blockTxsExecResult = executeTransactions(block.body.transactionList, initialWorld, block.header, signedTransactionValidator) + val blockTxsExecResult = executeTransactions(block.body.transactionList, inputWorld, block.header, signedTransactionValidator) blockTxsExecResult match { case Right(_) => log.debug(s"All txs from block ${block.header.hashAsHexString} were executed successfully") case Left(error) => log.debug(s"Not all txs from block ${block.header.hashAsHexString} were executed correctly, due to ${error.reason}") @@ -371,6 +376,21 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai private[ledger] def deleteAccounts(addressesToDelete: Set[Address])(worldStateProxy: InMemoryWorldStateProxy): InMemoryWorldStateProxy = addressesToDelete.foldLeft(worldStateProxy){ case (world, address) => world.deleteAccount(address) } + /** + * This function updates worldState transfering balance from drainList accounts to refundContract address + * + * @param worldState Initial world state + * @param daoForkConfig Dao fork configuration with drainList and refundContract config + * @return Updated world state proxy + */ + private def applyDaoFork(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { + daoForkConfig.drainList.foldLeft(worldState) { (ws, address) => + ws.getAccount(address) + .map(acc => ws.transfer(from = address, to = daoForkConfig.refundContract, acc.balance)) + .getOrElse(ws) + } + } + } object Ledger { diff --git a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala index 51c8c9266d..0ad95891e5 100644 --- a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala +++ b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala @@ -20,7 +20,7 @@ import io.iohk.ethereum.validators.MptListValidator.intByteArraySerializable import io.iohk.ethereum.validators.OmmersValidator.OmmersError import io.iohk.ethereum.validators.Validators import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.DaoForkConfiguration +import io.iohk.ethereum.daoFork.DaoForkConfig class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, miningConfig: MiningConfig, ledger: Ledger, validators: Validators, blockTimestampProvider: BlockTimestampProvider = DefaultBlockTimestampProvider) { @@ -90,8 +90,7 @@ class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, } private def prepareHeader(blockNumber: BigInt, ommers: Seq[BlockHeader], beneficiary: Address, parent: Block, blockTimestamp: Long) = { - val inInRange = blockchainConfig.daoForkBlockNumber <= blockNumber && - blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockNumber + import blockchainConfig.daoForkConfig BlockHeader( parentHash = parent.header.hash, @@ -107,7 +106,7 @@ class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, gasLimit = calculateGasLimit(parent.header.gasLimit), gasUsed = 0, unixTimestamp = blockTimestamp, - extraData = if (inInRange && blockchainConfig.proDaoFork) DaoForkConfiguration.blockExtraData else miningConfig.headerExtraData, + extraData = if (daoForkConfig requiresExtraData blockNumber) daoForkConfig.blockExtraData else miningConfig.headerExtraData, mixHash = ByteString.empty, nonce = ByteString.empty ) diff --git a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala index 512f3ecd3c..8480083fe0 100644 --- a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala +++ b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala @@ -1,5 +1,6 @@ package io.iohk.ethereum.network +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.BlockHeader import io.iohk.ethereum.utils.BlockchainConfig @@ -15,15 +16,15 @@ object ForkResolver { trait Fork - class EtcForkResolver(blockchainConfig: BlockchainConfig) extends ForkResolver { + class EtcForkResolver(daoForkConfig: DaoForkConfig) extends ForkResolver { sealed trait Fork extends ForkResolver.Fork case object AcceptedFork extends Fork case object RejectedFork extends Fork - override def forkBlockNumber: BigInt = blockchainConfig.daoForkBlockNumber + override def forkBlockNumber: BigInt = daoForkConfig.daoForkBlockNumber override def recognizeFork(blockHeader: BlockHeader): Fork = { - if (blockHeader.hash == blockchainConfig.daoForkBlockHash) AcceptedFork + if (blockHeader.hash == daoForkConfig.daoForkBlockHash) AcceptedFork else RejectedFork } diff --git a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala index 67a266b8a6..48846db2aa 100644 --- a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala @@ -130,7 +130,7 @@ trait ForkResolverBuilder { lazy val forkResolverOpt = if (blockchainConfig.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) } trait HandshakerBuilder { diff --git a/src/main/scala/io/iohk/ethereum/utils/Config.scala b/src/main/scala/io/iohk/ethereum/utils/Config.scala index 32ac15bc52..bd15489a64 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -4,6 +4,7 @@ import java.net.InetSocketAddress import akka.util.ByteString import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig} +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.db.dataSource.LevelDbConfig import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, BasicPruning, PruningMode} import io.iohk.ethereum.domain.{Address, UInt256} @@ -253,9 +254,8 @@ trait BlockchainConfig { val customGenesisFileOpt: Option[String] - val proDaoFork: Boolean - val daoForkBlockNumber: BigInt - val daoForkBlockHash: ByteString + val daoForkConfig: DaoForkConfig + val accountStartNonce: UInt256 val chainId: Byte @@ -278,9 +278,10 @@ object BlockchainConfig { override val customGenesisFileOpt: Option[String] = Try(blockchainConfig.getString("custom-genesis-file")).toOption - override val proDaoFork: Boolean = blockchainConfig.getBoolean("pro-dao-fork") - override val daoForkBlockNumber: BigInt = BigInt(blockchainConfig.getString("dao-fork-block-number")) - override val daoForkBlockHash: ByteString = ByteString(Hex.decode(blockchainConfig.getString("dao-fork-block-hash"))) + val proDaoFork: Boolean = blockchainConfig.getBoolean("pro-dao-fork") + val daoForkBlockNumber: BigInt = BigInt(blockchainConfig.getString("dao-fork-block-number")) + val daoForkBlockHash: ByteString = ByteString(Hex.decode(blockchainConfig.getString("dao-fork-block-hash"))) + override val daoForkConfig = DefaultDaoForkConfig(proDaoFork, daoForkBlockNumber, daoForkBlockHash) override val accountStartNonce: UInt256 = UInt256(BigInt(blockchainConfig.getString("account-start-nonce"))) override val chainId: Byte = { diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index 786fb317c4..436dd793a7 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -2,7 +2,7 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.crypto.{kec256, kec512} -import io.iohk.ethereum.daoFork.DaoForkConfiguration +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.{BlockHeader, Blockchain, DifficultyCalculator} import io.iohk.ethereum.utils.BlockchainConfig @@ -60,11 +60,12 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block */ private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = if (blockHeader.extraData.length <= MaxExtraDataSize) { - val inInRange = blockchainConfig.daoForkBlockNumber <= blockHeader.number && - blockchainConfig.daoForkBlockNumber + DaoForkConfiguration.range > blockHeader.number - val daoForkData = blockHeader.extraData == DaoForkConfiguration.blockExtraData + import blockchainConfig._ - (inInRange, daoForkData, blockchainConfig.proDaoFork) match { + val inInRange = daoForkConfig requiresExtraData blockHeader.number + val daoForkData = blockHeader.extraData == daoForkConfig.blockExtraData + + (inInRange, daoForkData, daoForkConfig.proDaoFork) match { case (false, _, _) | (true, true, true) | (true, false, false) => Right(blockHeader) case _ => diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala index 3eacb0ddcb..73d1c0a450 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala @@ -4,6 +4,7 @@ import akka.actor.ActorSystem import akka.testkit.TestProbe import akka.util.ByteString import io.iohk.ethereum.crypto.ECDSASignature +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.db.storage.AppStateStorage import io.iohk.ethereum.domain.{UInt256, _} import io.iohk.ethereum.jsonrpc.JsonRpcErrors._ @@ -348,11 +349,9 @@ class PersonalServiceSpec extends FlatSpec with Matchers with MockFactory with S override val difficultyBombPauseBlockNumber: BigInt = 0 override val difficultyBombContinueBlockNumber: BigInt = 0 override val customGenesisFileOpt: Option[String] = None - override val daoForkBlockNumber: BigInt = 0 - override val daoForkBlockHash: ByteString = ByteString.empty override val accountStartNonce: UInt256 = UInt256.Zero override val monetaryPolicyConfig: MonetaryPolicyConfig = new MonetaryPolicyConfig(0, 0, 0) - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = 0, daoForkBlockHash = ByteString.empty, proDaoFork = false) } val wallet = Wallet(address, prvKey) diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index 1be7489802..06273393d6 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -6,7 +6,6 @@ import akka.util.ByteString import io.iohk.ethereum.{Timeouts, crypto} import io.iohk.ethereum.blockchain.data.GenesisDataLoader import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup -import io.iohk.ethereum.db.components.{SharedEphemDataSources, Storages} import io.iohk.ethereum.domain._ import io.iohk.ethereum.ledger.{BlockPreparationError, LedgerImpl} import io.iohk.ethereum.utils.{BlockchainConfig, Logger, MiningConfig, MonetaryPolicyConfig} @@ -16,16 +15,13 @@ import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.db.components.Storages.PruningModeComponent -import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} -import io.iohk.ethereum.daoFork.DaoForkConfiguration +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.domain.SignedTransaction.FirstByteOfAddress import io.iohk.ethereum.utils.Config.DbConfig import org.spongycastle.crypto.AsymmetricCipherKeyPair import org.spongycastle.crypto.params.ECPublicKeyParameters import scala.concurrent.duration.FiniteDuration -import scala.concurrent.duration._ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with Logger { @@ -115,12 +111,14 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, BigInt("5000000000000000000")) // unused - override val daoForkBlockNumber: BigInt = Long.MaxValue override val eip160BlockNumber: BigInt = Long.MaxValue override val eip150BlockNumber: BigInt = Long.MaxValue - override val daoForkBlockHash: ByteString = ByteString("unused") override val accountStartNonce: UInt256 = UInt256.Zero - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( + daoForkBlockNumber = Long.MaxValue, + daoForkBlockHash = ByteString("unused"), + proDaoFork = false + ) } val generalTx = SignedTransaction.sign(transaction, keyPair, None) @@ -270,12 +268,10 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, BigInt("5000000000000000000")) // unused - override val daoForkBlockNumber: BigInt = Long.MaxValue override val eip160BlockNumber: BigInt = Long.MaxValue override val eip150BlockNumber: BigInt = Long.MaxValue - override val daoForkBlockHash: ByteString = ByteString("unused") override val accountStartNonce: UInt256 = UInt256.Zero - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = Long.MaxValue, daoForkBlockHash = ByteString("unused"), proDaoFork = false) } lazy val ledger = new LedgerImpl(VM, blockchain, blockchainConfig) diff --git a/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala b/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala index 1682c76bd0..feeb5633aa 100644 --- a/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala @@ -254,7 +254,7 @@ class EtcPeerManagerSpec extends FlatSpec with Matchers { blockchain.save(Fixtures.Blocks.Genesis.header) val blockchainConfig = BlockchainConfig(Config.config) - val forkResolver = new ForkResolver.EtcForkResolver(blockchainConfig) + val forkResolver = new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig) val peerStatus = Status( protocolVersion = Versions.PV63, diff --git a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala index 016112d8d4..c1cc5daced 100644 --- a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala @@ -5,6 +5,7 @@ import akka.util.ByteString import io.iohk.ethereum.Fixtures import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto.generateKeyPair +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.db.components.Storages.PruningModeComponent import io.iohk.ethereum.db.components.{SharedEphemDataSources, Storages} import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} @@ -145,9 +146,6 @@ class EtcHandshakerSpec extends FlatSpec with Matchers { } val blockchainConfig = new BlockchainConfig { - override val daoForkBlockHash: ByteString = forkBlockHeader.hash - override val daoForkBlockNumber: BigInt = forkBlockHeader.number - //unused override val frontierBlockNumber: BigInt = 0 override val homesteadBlockNumber: BigInt = 0 @@ -160,11 +158,15 @@ class EtcHandshakerSpec extends FlatSpec with Matchers { override val chainId: Byte = 0.toByte override val monetaryPolicyConfig: MonetaryPolicyConfig = null override val accountStartNonce: UInt256 = UInt256.Zero - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( + daoForkBlockNumber = forkBlockHeader.number, + daoForkBlockHash = forkBlockHeader.hash, + proDaoFork = false + ) } val etcHandshakerConfigurationWithResolver = new MockEtcHandshakerConfiguration { - override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) } val initHandshakerWithoutResolver = EtcHandshaker(new MockEtcHandshakerConfiguration) diff --git a/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala b/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala index 0cedb24fe6..67429b7948 100644 --- a/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala @@ -399,7 +399,7 @@ class PeerActorSpec extends FlatSpec with Matchers { trait HandshakerSetup extends NodeStatusSetup { val handshakerConfiguration = new EtcHandshakerConfiguration { - override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) override val nodeStatusHolder: Agent[NodeStatus] = HandshakerSetup.this.nodeStatusHolder override val peerConfiguration: PeerConfiguration = HandshakerSetup.this.peerConf override val blockchain: Blockchain = HandshakerSetup.this.blockchain diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index 4a12d57ac6..6bb1981ceb 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -3,6 +3,7 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.ObjectGenerators import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.domain.{UInt256, _} import io.iohk.ethereum.utils.{BlockchainConfig, Config, MonetaryPolicyConfig} import io.iohk.ethereum.validators.BlockHeaderError._ @@ -24,16 +25,18 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck override val difficultyBombContinueBlockNumber: BigInt = 5000000 // unused - override val daoForkBlockNumber: BigInt = Long.MaxValue override val eip155BlockNumber: BigInt = Long.MaxValue override val eip160BlockNumber: BigInt = Long.MaxValue override val eip150BlockNumber: BigInt = Long.MaxValue override val chainId: Byte = 0x3d.toByte - override val daoForkBlockHash: ByteString = ByteString("unused") override val monetaryPolicyConfig: MonetaryPolicyConfig = null override val customGenesisFileOpt: Option[String] = None override val accountStartNonce: UInt256 = UInt256.Zero - override val proDaoFork: Boolean = false + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( + daoForkBlockNumber = Long.MaxValue, + daoForkBlockHash = ByteString("unused"), + proDaoFork = false + ) } val blockHeaderValidator = new BlockHeaderValidatorImpl(blockchainConfig) From 03df69f2c264a399f5337e6990370a979775b356 Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Thu, 14 Sep 2017 12:40:40 -0300 Subject: [PATCH 10/20] [EC-236] BlockHeaderValidator tests --- .../iohk/ethereum/daoFork/DaoForkConfig.scala | 4 +- .../validators/BlockHeaderValidator.scala | 8 +- .../scala/io/iohk/ethereum/Fixtures.scala | 52 ++++++++ .../validators/BlockHeaderValidatorSpec.scala | 123 +++++++++++++++--- 4 files changed, 160 insertions(+), 27 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala index 183dd3ff8a..a6a8f0145c 100644 --- a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala @@ -14,7 +14,7 @@ trait DaoForkConfig { val refundContract: Address val drainList: Seq[Address] - private val extratadaBlockRange = daoForkBlockNumber to (daoForkBlockNumber + range) + private val extratadaBlockRange = daoForkBlockNumber until (daoForkBlockNumber + range) def isDaoForkBlock(blockNumber: BigInt): Boolean = proDaoFork && (daoForkBlockNumber == blockNumber) @@ -23,7 +23,7 @@ trait DaoForkConfig { case class DefaultDaoForkConfig(proDaoFork: Boolean, daoForkBlockNumber: BigInt, daoForkBlockHash: ByteString) extends DaoForkConfig { override val blockExtraData = ByteString(Hex.decode("64616f2d686172642d666f726b")) - override val range = 10 + override lazy val range = 10 override val refundContract = Address(Hex.decode("bf4ed7b27f1d666546e30d74d50d173d20bca754")) override val drainList = Seq( Address(Hex.decode("d4fe7bc31cedb7bfb8a345f31e668033056b2728")), diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index 436dd793a7..5abd0704f4 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -62,11 +62,11 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block if (blockHeader.extraData.length <= MaxExtraDataSize) { import blockchainConfig._ - val inInRange = daoForkConfig requiresExtraData blockHeader.number - val daoForkData = blockHeader.extraData == daoForkConfig.blockExtraData + val requiresExtraData = daoForkConfig requiresExtraData blockHeader.number + val isDaoForkData = blockHeader.extraData == daoForkConfig.blockExtraData - (inInRange, daoForkData, daoForkConfig.proDaoFork) match { - case (false, _, _) | (true, true, true) | (true, false, false) => + (requiresExtraData, isDaoForkData) match { + case (false, _) | (true, true) => Right(blockHeader) case _ => Left(DaoHeaderExtraDataError) diff --git a/src/test/scala/io/iohk/ethereum/Fixtures.scala b/src/test/scala/io/iohk/ethereum/Fixtures.scala index 0b439a3de8..e28ea9082d 100644 --- a/src/test/scala/io/iohk/ethereum/Fixtures.scala +++ b/src/test/scala/io/iohk/ethereum/Fixtures.scala @@ -230,6 +230,58 @@ object Fixtures { override val size: Long = 978L } + object ProDaoForkBlock extends FixtureBlock { + override val header: BlockHeader = BlockHeader( + parentHash = ByteString(Hex.decode("a218e2c611f21232d857e3c8cecdcdf1f65f25a4477f98f6f47e4063807f2308")), + ommersHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + beneficiary = ByteString(Hex.decode("bcdfc35b86bedf72f0cda046a3c16829a2ef41d1 ")), + stateRoot = ByteString(Hex.decode("c5e389416116e3696cce82ec4533cce33efccb24ce245ae9546a4b8f0d5e9a75")), + transactionsRoot = ByteString(Hex.decode("7701df8e07169452554d14aadd7bfa256d4a1d0355c1d174ab373e3e2d0a3743")), + receiptsRoot = ByteString(Hex.decode("26cf9d9422e9dd95aedc7914db690b92bab6902f5221d62694a2fa5d065f534b")), + logsBloom = ByteString(Hex.decode("0" * 512)), + difficulty = BigInt("62413376722602"), + number = 1920000, + gasLimit = 4712384, + gasUsed = 84000, + unixTimestamp = 1469020840, + extraData = ByteString(Hex.decode("64616f2d686172642d666f726b")), + mixHash = ByteString(Hex.decode("5b5acbf4bf305f948bd7be176047b20623e1417f75597341a059729165b92397")), + nonce = ByteString(Hex.decode("bede87201de42426")) + ) + override lazy val body: BlockBody = ??? + + override val transactionHashes: Seq[ByteString] = Seq( + ByteString(Hex.decode("4677a93807b73a0875d3a292eacb450d0af0d6f0eec6f283f8ad927ec539a17b")), + ByteString(Hex.decode("6f75b64d9364b71b43cde81a889f95df72e6be004b28477f9083ed0ee471a7f9")), + ByteString(Hex.decode("50d8156ee48d01b56cb17b6cb2ac8f29e1bf565be0e604b2d8ffb2fb50a0f611")), + ByteString(Hex.decode("2a5177e6d6cea40594c7d4b0115dcd087443be3ec2fa81db3c21946a5e51cea9")) + ) + override val size: Long = 976 + } + + object DaoParentBlock extends FixtureBlock { + override val header: BlockHeader = BlockHeader( + parentHash = ByteString(Hex.decode("505ffd21f4cbf2c5c34fa84cd8c92525f3a719b7ad18852bffddad601035f5f4")), + ommersHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + beneficiary = ByteString(Hex.decode("2a65aca4d5fc5b5c859090a6c34d164135398226")), + stateRoot = ByteString(Hex.decode("fdf2fc04580b95ca15defc639080b902e93892dcce288be0c1f7a7bbc778248b")), + transactionsRoot = ByteString(Hex.decode("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), + receiptsRoot = ByteString(Hex.decode("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), + logsBloom = ByteString(Hex.decode("00" * 256)), + difficulty = BigInt("62382916183238"), + number = 1919999, + gasLimit = 4707788, + gasUsed = 0, + unixTimestamp = 1469020838, + extraData = ByteString(Hex.decode("4477617266506f6f6c")), + mixHash = ByteString(Hex.decode("7f9ac1ddeafff0f926ed9887b8cf7d50c3f919d902e618b957022c46c8b404a6")), + nonce = ByteString(Hex.decode("60832709c8979daa")) + ) + override lazy val body: BlockBody = ??? + override lazy val transactionHashes: Seq[ByteString] = ??? + override lazy val size: Long = ??? + } + } } diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index 6bb1981ceb..7b37a111ff 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -1,7 +1,7 @@ package io.iohk.ethereum.validators import akka.util.ByteString -import io.iohk.ethereum.ObjectGenerators +import io.iohk.ethereum.{Fixtures, ObjectGenerators} import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.domain.{UInt256, _} @@ -12,32 +12,14 @@ import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyChecks with ObjectGenerators { + import Fixtures.Blocks._ val ExtraDataSizeLimit = 20 //BlockHeader member's lengths obtained from Yellow paper val NonceLength = 8 //64bit val MixHashLength = 32 //256bit - val blockchainConfig = new BlockchainConfig { - override val frontierBlockNumber: BigInt = 0 - override val homesteadBlockNumber: BigInt = 1150000 - override val difficultyBombPauseBlockNumber: BigInt = 3000000 - override val difficultyBombContinueBlockNumber: BigInt = 5000000 - - // unused - override val eip155BlockNumber: BigInt = Long.MaxValue - override val eip160BlockNumber: BigInt = Long.MaxValue - override val eip150BlockNumber: BigInt = Long.MaxValue - override val chainId: Byte = 0x3d.toByte - override val monetaryPolicyConfig: MonetaryPolicyConfig = null - override val customGenesisFileOpt: Option[String] = None - override val accountStartNonce: UInt256 = UInt256.Zero - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( - daoForkBlockNumber = Long.MaxValue, - daoForkBlockHash = ByteString("unused"), - proDaoFork = false - ) - } + val blockchainConfig = createBlockchainConfig() val blockHeaderValidator = new BlockHeaderValidatorImpl(blockchainConfig) val difficultyCalculator = new DifficultyCalculator(blockchainConfig) @@ -59,6 +41,31 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck } } + it should "validate DAO block (extra data)" in { + import Fixtures.Blocks._ + val cases = Table( + ("Block", "Parent Block", "Pro Dao Fork", "Valid"), + (DaoForkBlock.header, DaoParentBlock.header, false, true), + (DaoForkBlock.header, DaoParentBlock.header, true, false), + (ProDaoForkBlock.header, DaoParentBlock.header, true, true), + (ProDaoForkBlock.header, DaoParentBlock.header, false, true), // We don't care for extra data if no pro dao + (ProDaoForkBlock.header.copy(extraData = ByteString("Wrond DAO Extra")), DaoParentBlock.header, true, false), + // We need to check extradata up to 10 blocks after + (ProDaoBlock1920009Header, ProDaoBlock1920008Header, true, true), + (ProDaoBlock1920009Header.copy(extraData = ByteString("Wrond DAO Extra")), ProDaoBlock1920008Header, true, false), + (ProDaoBlock1920010Header, ProDaoBlock1920009Header, true, true) + ) + + forAll(cases) { (block, parentBlock, proDaoFork, valid ) => + val blockHeaderValidator = new BlockHeaderValidatorImpl(createBlockchainConfig(proDaoFork)) + blockHeaderValidator.validate(block, parentBlock) match { + case Right(_) => assert(valid) + case Left(DaoHeaderExtraDataError) => assert(!valid) + case _ => fail() + } + } + } + it should "return a failure if created based on invalid timestamp" in { forAll(longGen) { timestamp => val blockHeader = validBlockHeader.copy(unixTimestamp = timestamp) @@ -232,4 +239,78 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck nonce = ByteString(Hex.decode("3fc7bc671f7cee70")) ) + def createBlockchainConfig(proDaoFork: Boolean = false): BlockchainConfig = + new BlockchainConfig { + override val frontierBlockNumber: BigInt = 0 + override val homesteadBlockNumber: BigInt = 1150000 + override val difficultyBombPauseBlockNumber: BigInt = 3000000 + override val difficultyBombContinueBlockNumber: BigInt = 5000000 + + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(proDaoFork, DaoForkBlock.header.number, DaoForkBlock.header.hash) + + // unused + override val eip155BlockNumber: BigInt = Long.MaxValue + override val eip160BlockNumber: BigInt = Long.MaxValue + override val eip150BlockNumber: BigInt = Long.MaxValue + override val chainId: Byte = 0x3d.toByte + override val monetaryPolicyConfig: MonetaryPolicyConfig = null + override val customGenesisFileOpt: Option[String] = None + override val accountStartNonce: UInt256 = UInt256.Zero + } + + val ProDaoBlock1920008Header = BlockHeader( + parentHash = ByteString(Hex.decode("05c45c9671ee31736b9f37ee98faa72c89e314059ecff3257206e6ab498eb9d1")), + ommersHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + beneficiary = ByteString(Hex.decode("2a65aca4d5fc5b5c859090a6c34d164135398226")), + stateRoot = ByteString(Hex.decode("fa8d3b3cbd37caba2faf09d5e472ae6c47a58d846751bc72306166a71d0fa4fa")), + transactionsRoot = ByteString(Hex.decode("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), + receiptsRoot = ByteString(Hex.decode("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")), + logsBloom = ByteString(Hex.decode("00" * 256)), + difficulty = BigInt("62230570926948"), + number = 1920008, + gasLimit = 4707788, + gasUsed = 0, + unixTimestamp = 1469021025, + extraData = ByteString(Hex.decode("64616f2d686172642d666f726b")), + mixHash = ByteString(Hex.decode("e73421390c1b084a9806754b238715ec333cdccc8d09b90cb6e38a9d1e247d6f")), + nonce = ByteString(Hex.decode("c207c8381305bef2")) + ) + + val ProDaoBlock1920009Header = BlockHeader( + parentHash = ByteString(Hex.decode("41254723e12eb736ddef151371e4c3d614233e6cad95f2d9017de2ab8b469a18")), + ommersHash = ByteString(Hex.decode("808d06176049aecfd504197dde49f46c3dd75f1af055e417d100228162eefdd8")), + beneficiary = ByteString(Hex.decode("ea674fdde714fd979de3edf0f56aa9716b898ec8")), + stateRoot = ByteString(Hex.decode("49eb333152713b78d920440ef065ed7f681611e0c2e6933d657d6f4a7f1936ee")), + transactionsRoot = ByteString(Hex.decode("a8060f1391fd4cbde4b03d83b32a1bda445578cd6ec6b7982db20c499ed3682b")), + receiptsRoot = ByteString(Hex.decode("ab66b1986e713eaf5621059e79f04ba9c528187c1b9da969f46442c3f915c120")), + logsBloom = ByteString(Hex.decode("00000000000000020000000000020000000000000008000000000000000000000000000000000000000000000000400000000000000000000000000000202010000000000000000000000008000000000000000000000000400000000000000000000800000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000001001000020000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000004000000000000000000000000000000010000000000000000000000000000000100000000000000000000000000000")), + difficulty = BigInt("62230571058020"), + number = 1920009, + gasLimit = 4712384, + gasUsed = 109952, + unixTimestamp = 1469021040, + extraData = ByteString(Hex.decode("64616f2d686172642d666f726b")), + mixHash = ByteString(Hex.decode("5bde79f4dc5be28af2d956e748a0d6ebc1f8eb5c1397e76729269e730611cb99")), + nonce = ByteString(Hex.decode("2b4b464c0a4da82a")) + ) + + val ProDaoBlock1920010Header = BlockHeader( + parentHash = ByteString(Hex.decode("69d04aec94ad69d7d190d3b51d24cd42dded0c4767598a1d30480363509acbef")), + ommersHash = ByteString(Hex.decode("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")), + beneficiary = ByteString(Hex.decode("4bb96091ee9d802ed039c4d1a5f6216f90f81b01")), + stateRoot = ByteString(Hex.decode("6ee63abee7416d3a671bcbefa01aa5d4ea427e246d548e15c5f3d9a108e738fd")), + transactionsRoot = ByteString(Hex.decode("0c6d4a643ed081f92e384a5853f14d7f5ff5d68b65d0c90b46159584a80effe0")), + receiptsRoot = ByteString(Hex.decode("a7d1ddb80060d4b77c07007e9a9f0b83413bd2c5de71501683ba4764982eef4b")), + logsBloom = ByteString(Hex.decode("00000000000000020000000000020000001000000000000000000000000000000008000000000000000000000000400000000000000000000000000000202000000000000800000000000008000000000000000000000000400000000008000000000000000000000000000000000000000000000000000000000010000000000000000000000000000221000000000000000000080400000000000000011000020000000200001000000000000000000000000000000000400000000000000000000002000000000100000000000000000000000040000000000000000000000010000000000000000000000000000000000000000000000000000000000000")), + difficulty = BigInt("62230571189092"), + number = 1920010, + gasLimit = 4712388, + gasUsed = 114754, + unixTimestamp = 1469021050, + extraData = ByteString(Hex.decode("657468706f6f6c2e6f7267202855533129")), + mixHash = ByteString(Hex.decode("8f86617d6422c26a89b8b349b160973ca44f90326e758f1ef669c4046741dd06")), + nonce = ByteString(Hex.decode("c7de19e00a8c3e32")) + ) + + } From 36acfe0460387b53a78daa54b7b0162b987898bc Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Thu, 14 Sep 2017 16:28:29 -0300 Subject: [PATCH 11/20] [EC-236] Ledger apply dao fork tests --- .../ledger/InMemoryWorldStateProxy.scala | 2 +- .../io/iohk/ethereum/ledger/Ledger.scala | 6 +- .../scala/io/iohk/ethereum/Fixtures.scala | 59 +++++++++++- .../io/iohk/ethereum/ledger/LedgerSpec.scala | 89 +++++++++++++++++-- 4 files changed, 144 insertions(+), 12 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/ledger/InMemoryWorldStateProxy.scala b/src/main/scala/io/iohk/ethereum/ledger/InMemoryWorldStateProxy.scala index 0f52263170..8678c78040 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/InMemoryWorldStateProxy.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/InMemoryWorldStateProxy.scala @@ -125,7 +125,7 @@ class InMemoryWorldStateProxyStorage(val wrapped: InMemorySimpleMapProxy[UInt256 override def load(addr: UInt256): UInt256 = wrapped.get(addr).getOrElse(UInt256.Zero) } -class InMemoryWorldStateProxy private( +class InMemoryWorldStateProxy private[ledger]( // State MPT proxied nodes storage needed to construct the storage MPT when calling [[getStorage]]. // Accounts state and accounts storage states are saved within the same storage val stateStorage: NodesKeyValueStorage, diff --git a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala index 6033f9b1d3..5d59d2688c 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala @@ -78,7 +78,7 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai val initialWorld = blockchain.getWorldStateProxy(block.header.number, blockchainConfig.accountStartNonce, parentStateRoot) val inputWorld = - if(blockchainConfig.daoForkConfig isDaoForkBlock block.header.number) applyDaoFork(initialWorld, blockchainConfig.daoForkConfig) + if(blockchainConfig.daoForkConfig isDaoForkBlock block.header.number) drainDaoForkAccounts(initialWorld, blockchainConfig.daoForkConfig) else initialWorld log.debug(s"About to execute ${block.body.transactionList.size} txs from block ${block.header.number} (with hash: ${block.header.hashAsHexString})") @@ -377,13 +377,13 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai addressesToDelete.foldLeft(worldStateProxy){ case (world, address) => world.deleteAccount(address) } /** - * This function updates worldState transfering balance from drainList accounts to refundContract address + * This function updates worldState transferring balance from drainList accounts to refundContract address * * @param worldState Initial world state * @param daoForkConfig Dao fork configuration with drainList and refundContract config * @return Updated world state proxy */ - private def applyDaoFork(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { + private def drainDaoForkAccounts(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { daoForkConfig.drainList.foldLeft(worldState) { (ws, address) => ws.getAccount(address) .map(acc => ws.transfer(from = address, to = daoForkConfig.refundContract, acc.balance)) diff --git a/src/test/scala/io/iohk/ethereum/Fixtures.scala b/src/test/scala/io/iohk/ethereum/Fixtures.scala index e28ea9082d..99b24b77b6 100644 --- a/src/test/scala/io/iohk/ethereum/Fixtures.scala +++ b/src/test/scala/io/iohk/ethereum/Fixtures.scala @@ -248,7 +248,64 @@ object Fixtures { mixHash = ByteString(Hex.decode("5b5acbf4bf305f948bd7be176047b20623e1417f75597341a059729165b92397")), nonce = ByteString(Hex.decode("bede87201de42426")) ) - override lazy val body: BlockBody = ??? + override lazy val body: BlockBody = BlockBody( + transactionList = Seq[SignedTransaction]( + SignedTransaction( + tx = Transaction( + nonce = BigInt("1"), + gasPrice = BigInt("20000000000"), + gasLimit = BigInt("21000"), + receivingAddress = Address(ByteString(Hex.decode("53d284357ec70ce289d6d64134dfac8e511c8a3d"))), + value = BigInt("1502561962583879700"), + payload = ByteString.empty + ), + pointSign = 0x1b.toByte, + signatureRandom = ByteString(Hex.decode("fdbbc462a8a60ac3d8b13ee236b45af9b7991cf4f0f556d3af46aa5aeca242ab")), + signature = ByteString(Hex.decode("5de5dc03fdcb6cf6d14609dbe6f5ba4300b8ff917c7d190325d9ea2144a7a2fb")), + chainId = 0x01.toByte + ).get,SignedTransaction( + tx = Transaction( + nonce = BigInt("1"), + gasPrice = BigInt("20000000000"), + gasLimit = BigInt("21000"), + receivingAddress = Address(ByteString(Hex.decode("53d284357ec70ce289d6d64134dfac8e511c8a3d"))), + value = BigInt("10046680000000000000"), + payload = ByteString.empty + ), + pointSign = 0x1b.toByte, + signatureRandom = ByteString(Hex.decode("8d94a55c7ac7adbfa2285ef7f4b0c955ae1a02647452cd4ead03ee6f449675c6")), + signature = ByteString(Hex.decode("67149821b74208176d78fc4dffbe37c8b64eecfd47532406b9727c4ae8eb7c9a")), + chainId = 0x01.toByte + ).get,SignedTransaction( + tx = Transaction( + nonce = BigInt("1"), + gasPrice = BigInt("20000000000"), + gasLimit = BigInt("21000"), + receivingAddress = Address(ByteString(Hex.decode("53d284357ec70ce289d6d64134dfac8e511c8a3d"))), + value = BigInt("20093780000000000000"), + payload = ByteString.empty + ), + pointSign = 0x1c.toByte, + signatureRandom = ByteString(Hex.decode("6d31e3d59bfea97a34103d8ce767a8fe7a79b8e2f30af1e918df53f9e78e69ab")), + signature = ByteString(Hex.decode("098e5b80e1cc436421aa54eb17e96b08fe80d28a2fbd46451b56f2bca7a321e7")), + chainId = 0x01.toByte + ).get,SignedTransaction( + tx = Transaction( + nonce = BigInt("1"), + gasPrice = BigInt("20000000000"), + gasLimit = BigInt("21000"), + receivingAddress = Address(ByteString(Hex.decode("53d284357ec70ce289d6d64134dfac8e511c8a3d"))), + value = BigInt("1022338440000000000"), + payload = ByteString.empty + ), + pointSign = 0x1b.toByte, + signatureRandom = ByteString(Hex.decode("bafb9f71cef873b9e0395b9ed89aac4f2a752e2a4b88ba3c9b6c1fea254eae73")), + signature = ByteString(Hex.decode("1cef688f6718932f7705d9c1f0dd5a8aad9ddb196b826775f6e5703fdb997706")), + chainId = 0x01.toByte + ).get + ), + uncleNodesList = Seq[BlockHeader]() + ) override val transactionHashes: Seq[ByteString] = Seq( ByteString(Hex.decode("4677a93807b73a0875d3a292eacb450d0af0d6f0eec6f283f8ad927ec539a17b")), diff --git a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala index a059cf45e2..123813f32a 100644 --- a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala @@ -6,18 +6,16 @@ import akka.util.ByteString.{empty => bEmpty} import io.iohk.ethereum.Mocks.MockVM import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.db.components.Storages.PruningModeComponent -import io.iohk.ethereum.db.components.{SharedEphemDataSources, Storages} -import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} +import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} import io.iohk.ethereum.domain._ import io.iohk.ethereum.ledger.BlockExecutionError.{ValidationAfterExecError, ValidationBeforeExecError} -import io.iohk.ethereum.{Mocks, rlp} +import io.iohk.ethereum.{Fixtures, Mocks, rlp} import io.iohk.ethereum.rlp.RLPList -import io.iohk.ethereum.utils.{BlockchainConfig, Config} +import io.iohk.ethereum.utils.{BlockchainConfig, Config, MonetaryPolicyConfig} import io.iohk.ethereum.ledger.Ledger.{BlockResult, PC, PR} import io.iohk.ethereum.network.p2p.messages.PV62.BlockBody import io.iohk.ethereum.nodebuilder.SecureRandomBuilder -import io.iohk.ethereum.vm.{_} +import io.iohk.ethereum.vm._ import org.scalatest.{FlatSpec, Matchers} import org.scalatest.prop.PropertyChecks import org.spongycastle.crypto.params.ECPublicKeyParameters @@ -26,11 +24,12 @@ import io.iohk.ethereum.rlp.RLPImplicits._ import io.iohk.ethereum.validators.BlockValidator.BlockTransactionsHashError import io.iohk.ethereum.validators.SignedTransactionError.TransactionSignatureError import io.iohk.ethereum.validators._ +import org.scalamock.scalatest.MockFactory import org.spongycastle.crypto.AsymmetricCipherKeyPair import org.spongycastle.util.encoders.Hex // scalastyle:off file.size.limit -class LedgerSpec extends FlatSpec with PropertyChecks with Matchers { +class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFactory { val blockchainConfig = BlockchainConfig(Config.config) @@ -756,6 +755,52 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers { result match { case (_, executedTxs) => executedTxs shouldBe Seq.empty } } + it should "drain DAO accounts and send the funds to refund address if it's Pro DAO Fork configured" in new DaoForkTestSetup { + + // Check we drain all the accounts and send the balance to refund contract + proDaoBlockchainConfig.daoForkConfig.drainList.foreach { addr => + val daoAccountsFakeBalance = UInt256(1000) + (worldState.getAccount _).expects(addr).returning(Some(Account(nonce = 1, balance = daoAccountsFakeBalance))) + (worldState.transfer _).expects(addr, proDaoBlockchainConfig.daoForkConfig.refundContract, daoAccountsFakeBalance).returning(worldState) + } + + val ledger = new LedgerImpl(new MockVM(c => createResult( + context = c, + gasUsed = UInt256(10), + gasLimit = UInt256(10), + gasRefund = UInt256.Zero, + logs = Seq.empty, + addressesToDelete = Set.empty + )), testBlockchain, proDaoBlockchainConfig) + + ledger.executeBlockTransactions( + proDaoBlock.copy(body = proDaoBlock.body.copy(transactionList = Seq.empty)), // We don't care about block txs in this test + (new Mocks.MockValidatorsAlwaysSucceed).signedTransactionValidator + ) + } + + it should "not drain DAO accounts and send the funds to refund address if it not Pro DAO Fork configured" in new DaoForkTestSetup { + // Check we drain all the accounts and send the balance to refund contract + proDaoBlockchainConfig.daoForkConfig.drainList.foreach { addr => + val daoAccountsFakeBalance = UInt256(1000) + (worldState.transfer _).expects(addr, proDaoBlockchainConfig.daoForkConfig.refundContract, *).never() + } + + val ledger = new LedgerImpl(new MockVM(c => createResult( + context = c, + gasUsed = UInt256(10), + gasLimit = UInt256(10), + gasRefund = UInt256.Zero, + logs = Seq.empty, + addressesToDelete = Set.empty + )), testBlockchain, blockchainConfig) + + ledger.executeBlockTransactions( + proDaoBlock.copy(body = proDaoBlock.body.copy(transactionList = Seq.empty)), // We don't care about block txs in this test + (new Mocks.MockValidatorsAlwaysSucceed).signedTransactionValidator + ) + } + trait TestSetup extends SecureRandomBuilder with EphemBlockchainTestSetup { val originKeyPair: AsymmetricCipherKeyPair = generateKeyPair(secureRandom) val receiverKeyPair: AsymmetricCipherKeyPair = generateKeyPair(secureRandom) @@ -849,4 +894,34 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers { val validStxSignedByOrigin: SignedTransaction = SignedTransaction.sign(validTx, originKeyPair, Some(blockchainConfig.chainId)) } + trait DaoForkTestSetup extends TestSetup { + val proDaoBlockchainConfig = new BlockchainConfig { + override val frontierBlockNumber: BigInt = blockchainConfig.frontierBlockNumber + override val accountStartNonce: UInt256 = blockchainConfig.accountStartNonce + override val homesteadBlockNumber: BigInt = blockchainConfig.homesteadBlockNumber + override val difficultyBombPauseBlockNumber: BigInt = blockchainConfig.difficultyBombPauseBlockNumber + override val eip155BlockNumber: BigInt = blockchainConfig.eip155BlockNumber + override val monetaryPolicyConfig: MonetaryPolicyConfig = blockchainConfig.monetaryPolicyConfig + override val eip160BlockNumber: BigInt = blockchainConfig.eip160BlockNumber + override val eip150BlockNumber: BigInt = blockchainConfig.eip150BlockNumber + override val chainId: Byte = 0x01.toByte + override val difficultyBombContinueBlockNumber: BigInt = blockchainConfig.difficultyBombContinueBlockNumber + override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(true, 1920000, ByteString("")) + override val customGenesisFileOpt: Option[String] = None + } + val testBlockchain = mock[BlockchainImpl] + val worldState = mock[InMemoryWorldStateProxy] + val proDaoBlock = Fixtures.Blocks.ProDaoForkBlock.block + + (testBlockchain.getBlockHeaderByHash _).expects(proDaoBlock.header.parentHash).returning(Some(Fixtures.Blocks.DaoParentBlock.header)) + (testBlockchain.getWorldStateProxy _) + .expects(proDaoBlock.header.number, proDaoBlockchainConfig.accountStartNonce, Some(Fixtures.Blocks.DaoParentBlock.header.stateRoot)) + .returning(worldState) + + (worldState.getAccount _) + .expects(proDaoBlockchainConfig.daoForkConfig.refundContract) + .anyNumberOfTimes() + .returning(Some(Account(nonce = 1, balance = UInt256.Zero))) + } + } From ae91eb3e062c2a52c6b77875af89d3f2b9d955cb Mon Sep 17 00:00:00 2001 From: Nicolas Tallar Date: Fri, 15 Sep 2017 16:10:17 -0300 Subject: [PATCH 12/20] [EC-298] Moved benchmark tests from test to benchmark task --- build.sbt | 5 +- circle.yml | 3 + .../mpt/MerklePatriciaTreeSpeedSpec.scala | 90 +++++++++++++++++++ .../io/iohk/ethereum/rlp/RLPSpeedSuite.scala | 0 .../MerklePatriciaTreeIntegrationSuite.scala | 60 +------------ 5 files changed, 100 insertions(+), 58 deletions(-) create mode 100644 src/benchmark/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeSpeedSpec.scala rename src/{it => benchmark}/scala/io/iohk/ethereum/rlp/RLPSpeedSuite.scala (100%) diff --git a/build.sbt b/build.sbt index ff902fde21..26040b6db5 100644 --- a/build.sbt +++ b/build.sbt @@ -45,15 +45,18 @@ val dep = { val Integration = config("it") extend Test +val Benchmark = config("benchmark") extend Test + val Evm = config("evm") extend Test val Ets = config("ets") extend Test val root = project.in(file(".")) - .configs(Integration, Evm, Ets) + .configs(Integration, Benchmark, Evm, Ets) .settings(commonSettings: _*) .settings(libraryDependencies ++= dep) .settings(inConfig(Integration)(Defaults.testSettings) : _*) + .settings(inConfig(Benchmark)(Defaults.testSettings) : _*) .settings(inConfig(Evm)(Defaults.testSettings) : _*) .settings(inConfig(Ets)(Defaults.testSettings) : _*) diff --git a/circle.yml b/circle.yml index 77784a6d65..4b22f1e6dd 100644 --- a/circle.yml +++ b/circle.yml @@ -24,6 +24,9 @@ test: git submodule update; sbt "ets:testOnly *VMSuite -- -Dexg=vmPerf*"; fi + + # As the benchmark tests don't test functionality and should be manually ran, having them compile is enough + - sbt benchmark:compile post: - mkdir -p $CIRCLE_ARTIFACTS/scala-2.12 - mv target/scala-2.12/coverage-report $CIRCLE_ARTIFACTS/scala-2.12/coverage-report diff --git a/src/benchmark/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeSpeedSpec.scala b/src/benchmark/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeSpeedSpec.scala new file mode 100644 index 0000000000..87780db85d --- /dev/null +++ b/src/benchmark/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeSpeedSpec.scala @@ -0,0 +1,90 @@ +package io.iohk.ethereum.mpt + +import java.io.File +import java.nio.file.Files + +import io.iohk.ethereum.db.dataSource.{EphemDataSource, LevelDBDataSource, LevelDbConfig} +import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, NodeStorage} +import io.iohk.ethereum.mpt.MerklePatriciaTrie.defaultByteArraySerializable +import io.iohk.ethereum.utils.Logger +import io.iohk.ethereum.{ObjectGenerators, crypto} +import org.scalatest.FunSuite +import org.scalatest.prop.PropertyChecks +import org.spongycastle.util.encoders.Hex + +class MerklePatriciaTreeSpeedSpec extends FunSuite + with PropertyChecks + with ObjectGenerators + with Logger + with PersistentStorage { + + test("Performance test (From: https://github.com/ethereum/wiki/wiki/Benchmarks)") { + val Rounds = 1000 + val Symmetric = true + + val start: Long = System.currentTimeMillis + val emptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](new ArchiveNodeStorage(new NodeStorage(EphemDataSource()))) + var seed: Array[Byte] = Array.fill(32)(0.toByte) + + val trieResult = (0 until Rounds).foldLeft(emptyTrie) { case (recTrie, i) => + seed = Node.hashFn(seed) + if (!Symmetric) recTrie.put(seed, seed) + else { + val mykey = seed + seed = Node.hashFn(seed) + val myval = if ((seed(0) & 0xFF) % 2 == 1) Array[Byte](seed.last) else seed + recTrie.put(mykey, myval) + } + } + val rootHash = Hex.toHexString(trieResult.getRootHash) + + log.debug("Time taken(ms): " + (System.currentTimeMillis - start)) + log.debug("Root hash obtained: " + rootHash) + + if (Symmetric) assert(rootHash.take(4) == "36f6" && rootHash.drop(rootHash.length - 4) == "93a3") + else assert(rootHash.take(4) == "da8a" && rootHash.drop(rootHash.length - 4) == "0ca4") + } + + test("MPT benchmark") { + withNodeStorage { ns => + val hashFn = crypto.kec256(_: Array[Byte]) + + val defaultByteArraySer = MerklePatriciaTrie.defaultByteArraySerializable + val EmptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](ns)(defaultByteArraySer, defaultByteArraySer) + + var t = System.currentTimeMillis() + (1 to 20000000).foldLeft(EmptyTrie){case (trie, i) => + val k = hashFn(("hello" + i).getBytes) + val v = hashFn(("world" + i).getBytes) + + if (i % 100000 == 0) { + val newT = System.currentTimeMillis() + val delta = (newT - t) / 1000.0 + t = newT + log.debug(s"=== $i elements put, time for batch is: $delta sec") + } + trie.put(k, v) + } + } + } + +} + +trait PersistentStorage { + def withNodeStorage(testCode: NodesKeyValueStorage => Unit): Unit = { + val dbPath = Files.createTempDirectory("testdb").toAbsolutePath.toString + val dataSource = LevelDBDataSource(new LevelDbConfig { + override val verifyChecksums: Boolean = true + override val paranoidChecks: Boolean = true + override val createIfMissing: Boolean = true + override val path: String = dbPath + }) + + try { + testCode(new ArchiveNodeStorage(new NodeStorage(dataSource))) + } finally { + val dir = new File(dbPath) + !dir.exists() || dir.delete() + } + } +} diff --git a/src/it/scala/io/iohk/ethereum/rlp/RLPSpeedSuite.scala b/src/benchmark/scala/io/iohk/ethereum/rlp/RLPSpeedSuite.scala similarity index 100% rename from src/it/scala/io/iohk/ethereum/rlp/RLPSpeedSuite.scala rename to src/benchmark/scala/io/iohk/ethereum/rlp/RLPSpeedSuite.scala diff --git a/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala b/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala index 90ede57674..dbc061e004 100644 --- a/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala +++ b/src/it/scala/io/iohk/ethereum/mpt/MerklePatriciaTreeIntegrationSuite.scala @@ -5,10 +5,9 @@ import java.nio.ByteBuffer import java.nio.file.Files import java.security.MessageDigest -import io.iohk.ethereum.{ObjectGenerators, crypto} -import io.iohk.ethereum.crypto.kec256 -import io.iohk.ethereum.db.dataSource.{EphemDataSource, LevelDBDataSource, LevelDbConfig} -import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, NodeStorage, ReferenceCountNodeStorage} +import io.iohk.ethereum.ObjectGenerators +import io.iohk.ethereum.db.dataSource.{LevelDBDataSource, LevelDbConfig} +import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, NodeStorage} import io.iohk.ethereum.mpt.MerklePatriciaTrie.defaultByteArraySerializable import io.iohk.ethereum.utils.Logger import org.scalatest.FunSuite @@ -117,59 +116,6 @@ class MerklePatriciaTreeIntegrationSuite extends FunSuite } } - /* Performance test */ - test("Performance test (From: https://github.com/ethereum/wiki/wiki/Benchmarks)") { - withNodeStorage { ns => - val EmptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](ns) - val Rounds = 1000 - val Symmetric = true - - val start: Long = System.currentTimeMillis - val emptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](new ArchiveNodeStorage(new NodeStorage(EphemDataSource()))) - var seed: Array[Byte] = Array.fill(32)(0.toByte) - - val trieResult = (0 until Rounds).foldLeft(emptyTrie) { case (recTrie, i) => - seed = Node.hashFn(seed) - if (!Symmetric) recTrie.put(seed, seed) - else { - val mykey = seed - seed = Node.hashFn(seed) - val myval = if ((seed(0) & 0xFF) % 2 == 1) Array[Byte](seed.last) else seed - recTrie.put(mykey, myval) - } - } - val rootHash = Hex.toHexString(trieResult.getRootHash) - - log.debug("Time taken(ms): " + (System.currentTimeMillis - start)) - log.debug("Root hash obtained: " + rootHash) - - if (Symmetric) assert(rootHash.take(4) == "36f6" && rootHash.drop(rootHash.length - 4) == "93a3") - else assert(rootHash.take(4) == "da8a" && rootHash.drop(rootHash.length - 4) == "0ca4") - } - } - - ignore("MPT benchmark") { - withNodeStorage { ns => - val hashFn = crypto.kec256(_: Array[Byte]) - - val defaultByteArraySer = MerklePatriciaTrie.defaultByteArraySerializable - val EmptyTrie = MerklePatriciaTrie[Array[Byte], Array[Byte]](ns)(defaultByteArraySer, defaultByteArraySer) - - var t = System.currentTimeMillis() - (1 to 20000000).foldLeft(EmptyTrie){case (trie, i) => - val k = hashFn(("hello" + i).getBytes) - val v = hashFn(("world" + i).getBytes) - - if (i % 100000 == 0) { - val newT = System.currentTimeMillis() - val delta = (newT - t) / 1000.0 - t = newT - log.debug(s"=== $i elements put, time for batch is: $delta sec") - } - trie.put(k, v) - } - } - } } trait PersistentStorage { From 23a1ba41907413adba47ceaabf47b40314b4dae5 Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Fri, 15 Sep 2017 14:46:02 -0300 Subject: [PATCH 13/20] [EC-236] DaoForkConfig refactor --- .../iohk/ethereum/txExecTest/ForksTest.scala | 6 +- .../txExecTest/util/DumpChainApp.scala | 4 +- src/main/resources/application.conf | 30 +++- .../iohk/ethereum/daoFork/DaoForkConfig.scala | 142 ++--------------- .../io/iohk/ethereum/ledger/Ledger.scala | 17 ++- .../iohk/ethereum/mining/BlockGenerator.scala | 2 +- .../iohk/ethereum/network/ForkResolver.scala | 5 +- .../ethereum/nodebuilder/NodeBuilder.scala | 4 +- .../scala/io/iohk/ethereum/utils/Config.scala | 28 +++- .../validators/BlockHeaderValidator.scala | 22 ++- src/test/resources/application.conf | 24 +++ .../jsonrpc/PersonalServiceSpec.scala | 4 +- .../io/iohk/ethereum/ledger/LedgerSpec.scala | 61 ++++---- .../ethereum/mining/BlockGeneratorSpec.scala | 12 +- .../ethereum/network/EtcPeerManagerSpec.scala | 2 +- .../handshaker/EtcHandshakerSpec.scala | 20 ++- .../ethereum/network/p2p/PeerActorSpec.scala | 2 +- .../validators/BlockHeaderValidatorSpec.scala | 23 ++- src/universal/conf/blockchain.conf | 23 +++ src/universal/conf/ethereumHF.conf | 144 +++++++++++++++++- src/universal/conf/morden.conf | 4 +- 21 files changed, 344 insertions(+), 235 deletions(-) diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala index b7cac3e9c2..eb339e9439 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala @@ -1,8 +1,8 @@ package io.iohk.ethereum.txExecTest import akka.util.ByteString -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} -import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256} +import io.iohk.ethereum.daoFork.DaoForkConfig +import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256} import io.iohk.ethereum.ledger.LedgerImpl import io.iohk.ethereum.txExecTest.util.FixtureProvider import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig} @@ -27,7 +27,7 @@ class ForksTest extends FlatSpec with Matchers { override val difficultyBombPauseBlockNumber: BigInt = Long.MaxValue override val difficultyBombContinueBlockNumber: BigInt = Long.MaxValue override val accountStartNonce: UInt256 = UInt256.Zero - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = 1000, daoForkBlockHash = ByteString("unused"), proDaoFork = false) + override val daoForkConfig: Option[DaoForkConfig] = None } val noErrors = a[Right[_, Seq[Receipt]]] diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala index 0383fdabb9..ead7fd265e 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala @@ -70,8 +70,8 @@ object DumpChainApp extends App with NodeKeyBuilder with SecureRandomBuilder wit lazy val nodeStatusHolder = Agent(nodeStatus) lazy val forkResolverOpt = - if (blockchainConfig.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) + if (blockchainConfig.daoForkConfig.isEmpty) None + else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) private val handshakerConfiguration: EtcHandshakerConfiguration = new EtcHandshakerConfiguration { diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 701ed18a63..f6e8507f9f 100644 --- a/src/main/resources/application.conf +++ b/src/main/resources/application.conf @@ -196,6 +196,9 @@ mantis { active-timeout = 5 seconds ommer-pool-query-timeout = 5.seconds + + # Extra data to add to mined blocks + header-extra-data = "mantis" } blockchain { @@ -228,16 +231,27 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md difficulty-bomb-continue-block-number = "5000000" - # specify if this client is pro or against DAO hard fork - # if true it will accept ETH version of chain if false it will accept ETC - pro-dao-fork = false - - # DAO fork block number (Ethereum HF/Classic split) + # DAO fork configuration (Ethereum HF/Classic split) # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ - dao-fork-block-number = "1920000" + dao { + # DAO fork block number + fork-block-number = "1920000" + + # The hash of the accepted DAO fork block + fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + + # Extra data to be put in fork block headers + block-extra-data = null - # The hash of the accepted DAO fork block - dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + # number of blocks to place extra data after fork + block-extra-data-range = 10 + + # Address to send funds when draining + refund-contract-address = null + + # List of accounts to be drained + drain-list = null + } # Starting nonce a an empty account. Some networks (like Morden) use different values. account-start-nonce = "0" diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala index a6a8f0145c..9e8d3caa40 100644 --- a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala @@ -2,145 +2,23 @@ package io.iohk.ethereum.daoFork import akka.util.ByteString import io.iohk.ethereum.domain.Address -import org.spongycastle.util.encoders.Hex trait DaoForkConfig { - val proDaoFork: Boolean - val daoForkBlockNumber: BigInt - val daoForkBlockHash: ByteString - val blockExtraData: ByteString + val forkBlockNumber: BigInt + val forkBlockHash: ByteString + val blockExtraData: Option[ByteString] val range: Int - val refundContract: Address + val refundContract: Option[Address] val drainList: Seq[Address] - private val extratadaBlockRange = daoForkBlockNumber until (daoForkBlockNumber + range) + private lazy val extratadaBlockRange = forkBlockNumber until(forkBlockNumber + range) - def isDaoForkBlock(blockNumber: BigInt): Boolean = proDaoFork && (daoForkBlockNumber == blockNumber) + def isDaoForkBlock(blockNumber: BigInt): Boolean = forkBlockNumber == blockNumber - def requiresExtraData(blockNumber: BigInt): Boolean = proDaoFork && (extratadaBlockRange contains blockNumber) -} + def requiresExtraData(blockNumber: BigInt): Boolean = blockExtraData.isDefined && (extratadaBlockRange contains blockNumber) -case class DefaultDaoForkConfig(proDaoFork: Boolean, daoForkBlockNumber: BigInt, daoForkBlockHash: ByteString) extends DaoForkConfig { - override val blockExtraData = ByteString(Hex.decode("64616f2d686172642d666f726b")) - override lazy val range = 10 - override val refundContract = Address(Hex.decode("bf4ed7b27f1d666546e30d74d50d173d20bca754")) - override val drainList = Seq( - Address(Hex.decode("d4fe7bc31cedb7bfb8a345f31e668033056b2728")), - Address(Hex.decode("b3fb0e5aba0e20e5c49d252dfd30e102b171a425")), - Address(Hex.decode("2c19c7f9ae8b751e37aeb2d93a699722395ae18f")), - Address(Hex.decode("ecd135fa4f61a655311e86238c92adcd779555d2")), - Address(Hex.decode("1975bd06d486162d5dc297798dfc41edd5d160a7")), - Address(Hex.decode("a3acf3a1e16b1d7c315e23510fdd7847b48234f6")), - Address(Hex.decode("319f70bab6845585f412ec7724b744fec6095c85")), - Address(Hex.decode("06706dd3f2c9abf0a21ddcc6941d9b86f0596936")), - Address(Hex.decode("5c8536898fbb74fc7445814902fd08422eac56d0")), - Address(Hex.decode("6966ab0d485353095148a2155858910e0965b6f9")), - Address(Hex.decode("779543a0491a837ca36ce8c635d6154e3c4911a6")), - Address(Hex.decode("2a5ed960395e2a49b1c758cef4aa15213cfd874c")), - Address(Hex.decode("5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5")), - Address(Hex.decode("9c50426be05db97f5d64fc54bf89eff947f0a321")), - Address(Hex.decode("200450f06520bdd6c527622a273333384d870efb")), - Address(Hex.decode("be8539bfe837b67d1282b2b1d61c3f723966f049")), - Address(Hex.decode("6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb")), - Address(Hex.decode("f1385fb24aad0cd7432824085e42aff90886fef5")), - Address(Hex.decode("d1ac8b1ef1b69ff51d1d401a476e7e612414f091")), - Address(Hex.decode("8163e7fb499e90f8544ea62bbf80d21cd26d9efd")), - Address(Hex.decode("51e0ddd9998364a2eb38588679f0d2c42653e4a6")), - Address(Hex.decode("627a0a960c079c21c34f7612d5d230e01b4ad4c7")), - Address(Hex.decode("f0b1aa0eb660754448a7937c022e30aa692fe0c5")), - Address(Hex.decode("24c4d950dfd4dd1902bbed3508144a54542bba94")), - Address(Hex.decode("9f27daea7aca0aa0446220b98d028715e3bc803d")), - Address(Hex.decode("a5dc5acd6a7968a4554d89d65e59b7fd3bff0f90")), - Address(Hex.decode("d9aef3a1e38a39c16b31d1ace71bca8ef58d315b")), - Address(Hex.decode("63ed5a272de2f6d968408b4acb9024f4cc208ebf")), - Address(Hex.decode("6f6704e5a10332af6672e50b3d9754dc460dfa4d")), - Address(Hex.decode("77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6")), - Address(Hex.decode("492ea3bb0f3315521c31f273e565b868fc090f17")), - Address(Hex.decode("0ff30d6de14a8224aa97b78aea5388d1c51c1f00")), - Address(Hex.decode("9ea779f907f0b315b364b0cfc39a0fde5b02a416")), - Address(Hex.decode("ceaeb481747ca6c540a000c1f3641f8cef161fa7")), - Address(Hex.decode("cc34673c6c40e791051898567a1222daf90be287")), - Address(Hex.decode("579a80d909f346fbfb1189493f521d7f48d52238")), - Address(Hex.decode("e308bd1ac5fda103967359b2712dd89deffb7973")), - Address(Hex.decode("4cb31628079fb14e4bc3cd5e30c2f7489b00960c")), - Address(Hex.decode("ac1ecab32727358dba8962a0f3b261731aad9723")), - Address(Hex.decode("4fd6ace747f06ece9c49699c7cabc62d02211f75")), - Address(Hex.decode("440c59b325d2997a134c2c7c60a8c61611212bad")), - Address(Hex.decode("4486a3d68fac6967006d7a517b889fd3f98c102b")), - Address(Hex.decode("9c15b54878ba618f494b38f0ae7443db6af648ba")), - Address(Hex.decode("27b137a85656544b1ccb5a0f2e561a5703c6a68f")), - Address(Hex.decode("21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241")), - Address(Hex.decode("23b75c2f6791eef49c69684db4c6c1f93bf49a50")), - Address(Hex.decode("1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b")), - Address(Hex.decode("b9637156d330c0d605a791f1c31ba5890582fe1c")), - Address(Hex.decode("6131c42fa982e56929107413a9d526fd99405560")), - Address(Hex.decode("1591fc0f688c81fbeb17f5426a162a7024d430c2")), - Address(Hex.decode("542a9515200d14b68e934e9830d91645a980dd7a")), - Address(Hex.decode("c4bbd073882dd2add2424cf47d35213405b01324")), - Address(Hex.decode("782495b7b3355efb2833d56ecb34dc22ad7dfcc4")), - Address(Hex.decode("58b95c9a9d5d26825e70a82b6adb139d3fd829eb")), - Address(Hex.decode("3ba4d81db016dc2890c81f3acec2454bff5aada5")), - Address(Hex.decode("b52042c8ca3f8aa246fa79c3feaa3d959347c0ab")), - Address(Hex.decode("e4ae1efdfc53b73893af49113d8694a057b9c0d1")), - Address(Hex.decode("3c02a7bc0391e86d91b7d144e61c2c01a25a79c5")), - Address(Hex.decode("0737a6b837f97f46ebade41b9bc3e1c509c85c53")), - Address(Hex.decode("97f43a37f595ab5dd318fb46e7a155eae057317a")), - Address(Hex.decode("52c5317c848ba20c7504cb2c8052abd1fde29d03")), - Address(Hex.decode("4863226780fe7c0356454236d3b1c8792785748d")), - Address(Hex.decode("5d2b2e6fcbe3b11d26b525e085ff818dae332479")), - Address(Hex.decode("5f9f3392e9f62f63b8eac0beb55541fc8627f42c")), - Address(Hex.decode("057b56736d32b86616a10f619859c6cd6f59092a")), - Address(Hex.decode("9aa008f65de0b923a2a4f02012ad034a5e2e2192")), - Address(Hex.decode("304a554a310c7e546dfe434669c62820b7d83490")), - Address(Hex.decode("914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79")), - Address(Hex.decode("4deb0033bb26bc534b197e61d19e0733e5679784")), - Address(Hex.decode("07f5c1e1bc2c93e0402f23341973a0e043f7bf8a")), - Address(Hex.decode("35a051a0010aba705c9008d7a7eff6fb88f6ea7b")), - Address(Hex.decode("4fa802324e929786dbda3b8820dc7834e9134a2a")), - Address(Hex.decode("9da397b9e80755301a3b32173283a91c0ef6c87e")), - Address(Hex.decode("8d9edb3054ce5c5774a420ac37ebae0ac02343c6")), - Address(Hex.decode("0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9")), - Address(Hex.decode("5dc28b15dffed94048d73806ce4b7a4612a1d48f")), - Address(Hex.decode("bcf899e6c7d9d5a215ab1e3444c86806fa854c76")), - Address(Hex.decode("12e626b0eebfe86a56d633b9864e389b45dcb260")), - Address(Hex.decode("a2f1ccba9395d7fcb155bba8bc92db9bafaeade7")), - Address(Hex.decode("ec8e57756626fdc07c63ad2eafbd28d08e7b0ca5")), - Address(Hex.decode("d164b088bd9108b60d0ca3751da4bceb207b0782")), - Address(Hex.decode("6231b6d0d5e77fe001c2a460bd9584fee60d409b")), - Address(Hex.decode("1cba23d343a983e9b5cfd19496b9a9701ada385f")), - Address(Hex.decode("a82f360a8d3455c5c41366975bde739c37bfeb8a")), - Address(Hex.decode("9fcd2deaff372a39cc679d5c5e4de7bafb0b1339")), - Address(Hex.decode("005f5cee7a43331d5a3d3eec71305925a62f34b6")), - Address(Hex.decode("0e0da70933f4c7849fc0d203f5d1d43b9ae4532d")), - Address(Hex.decode("d131637d5275fd1a68a3200f4ad25c71a2a9522e")), - Address(Hex.decode("bc07118b9ac290e4622f5e77a0853539789effbe")), - Address(Hex.decode("47e7aa56d6bdf3f36be34619660de61275420af8")), - Address(Hex.decode("acd87e28b0c9d1254e868b81cba4cc20d9a32225")), - Address(Hex.decode("adf80daec7ba8dcf15392f1ac611fff65d94f880")), - Address(Hex.decode("5524c55fb03cf21f549444ccbecb664d0acad706")), - Address(Hex.decode("40b803a9abce16f50f36a77ba41180eb90023925")), - Address(Hex.decode("fe24cdd8648121a43a7c86d289be4dd2951ed49f")), - Address(Hex.decode("17802f43a0137c506ba92291391a8a8f207f487d")), - Address(Hex.decode("253488078a4edf4d6f42f113d1e62836a942cf1a")), - Address(Hex.decode("86af3e9626fce1957c82e88cbf04ddf3a2ed7915")), - Address(Hex.decode("b136707642a4ea12fb4bae820f03d2562ebff487")), - Address(Hex.decode("dbe9b615a3ae8709af8b93336ce9b477e4ac0940")), - Address(Hex.decode("f14c14075d6c4ed84b86798af0956deef67365b5")), - Address(Hex.decode("ca544e5c4687d109611d0f8f928b53a25af72448")), - Address(Hex.decode("aeeb8ff27288bdabc0fa5ebb731b6f409507516c")), - Address(Hex.decode("cbb9d3703e651b0d496cdefb8b92c25aeb2171f7")), - Address(Hex.decode("6d87578288b6cb5549d5076a207456a1f6a63dc0")), - Address(Hex.decode("b2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e")), - Address(Hex.decode("accc230e8a6e5be9160b8cdf2864dd2a001c28b6")), - Address(Hex.decode("2b3455ec7fedf16e646268bf88846bd7a2319bb2")), - Address(Hex.decode("4613f3bca5c44ea06337a9e439fbc6d42e501d0a")), - Address(Hex.decode("d343b217de44030afaa275f54d31a9317c7f441e")), - Address(Hex.decode("84ef4b2357079cd7a7c69fd7a37cd0609a679106")), - Address(Hex.decode("da2fef9e4a3230988ff17df2165440f37e8b1708")), - Address(Hex.decode("f4c64518ea10f995918a454158c6b61407ea345c")), - Address(Hex.decode("7602b46df5390e432ef1c307d4f2c9ff6d65cc97")), - Address(Hex.decode("bb9bc244d798123fde783fcc1c72d3bb8c189413")), - Address(Hex.decode("807640a13483f8ac783c557fcdf27be11ea4ac7a")) - ) + def getExtraData(blockNumber: BigInt): Option[ByteString] = + if(requiresExtraData(blockNumber)) blockExtraData + else None } diff --git a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala index 5d59d2688c..69f6bf8162 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala @@ -77,9 +77,10 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai val parentStateRoot = blockchain.getBlockHeaderByHash(block.header.parentHash).map(_.stateRoot) val initialWorld = blockchain.getWorldStateProxy(block.header.number, blockchainConfig.accountStartNonce, parentStateRoot) - val inputWorld = - if(blockchainConfig.daoForkConfig isDaoForkBlock block.header.number) drainDaoForkAccounts(initialWorld, blockchainConfig.daoForkConfig) - else initialWorld + val inputWorld = blockchainConfig.daoForkConfig match { + case Some(daoForkConfig) if daoForkConfig.isDaoForkBlock(block.header.number) => drainDaoForkAccounts(initialWorld, daoForkConfig) + case _ => initialWorld + } log.debug(s"About to execute ${block.body.transactionList.size} txs from block ${block.header.number} (with hash: ${block.header.hashAsHexString})") val blockTxsExecResult = executeTransactions(block.body.transactionList, inputWorld, block.header, signedTransactionValidator) @@ -385,9 +386,13 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai */ private def drainDaoForkAccounts(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { daoForkConfig.drainList.foldLeft(worldState) { (ws, address) => - ws.getAccount(address) - .map(acc => ws.transfer(from = address, to = daoForkConfig.refundContract, acc.balance)) - .getOrElse(ws) + val afterDrainWS = for { + account <- ws.getAccount(address) + refundContractAddress <- daoForkConfig.refundContract + afterDrainingAddress = ws.transfer(from = address, to = refundContractAddress, account.balance) + } yield afterDrainingAddress + + afterDrainWS.getOrElse(ws) } } diff --git a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala index 0ad95891e5..c86ef1e929 100644 --- a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala +++ b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala @@ -106,7 +106,7 @@ class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, gasLimit = calculateGasLimit(parent.header.gasLimit), gasUsed = 0, unixTimestamp = blockTimestamp, - extraData = if (daoForkConfig requiresExtraData blockNumber) daoForkConfig.blockExtraData else miningConfig.headerExtraData, + extraData = daoForkConfig.flatMap(daoForkConfig => daoForkConfig.getExtraData(blockNumber)).getOrElse(miningConfig.headerExtraData), mixHash = ByteString.empty, nonce = ByteString.empty ) diff --git a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala index 8480083fe0..824cc06093 100644 --- a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala +++ b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala @@ -1,5 +1,6 @@ package io.iohk.ethereum.network +import akka.util.ByteString import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.BlockHeader import io.iohk.ethereum.utils.BlockchainConfig @@ -21,10 +22,10 @@ object ForkResolver { case object AcceptedFork extends Fork case object RejectedFork extends Fork - override def forkBlockNumber: BigInt = daoForkConfig.daoForkBlockNumber + override def forkBlockNumber: BigInt = daoForkConfig.forkBlockNumber override def recognizeFork(blockHeader: BlockHeader): Fork = { - if (blockHeader.hash == daoForkConfig.daoForkBlockHash) AcceptedFork + if (blockHeader.hash == daoForkConfig.forkBlockHash) AcceptedFork else RejectedFork } diff --git a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala index 48846db2aa..e11997cf94 100644 --- a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala @@ -129,8 +129,8 @@ trait ForkResolverBuilder { self: BlockchainConfigBuilder => lazy val forkResolverOpt = - if (blockchainConfig.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) + if (blockchainConfig.daoForkConfig.isEmpty) None + else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) } trait HandshakerBuilder { diff --git a/src/main/scala/io/iohk/ethereum/utils/Config.scala b/src/main/scala/io/iohk/ethereum/utils/Config.scala index bd15489a64..4fd3a32069 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -4,7 +4,7 @@ import java.net.InetSocketAddress import akka.util.ByteString import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig} -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.db.dataSource.LevelDbConfig import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, BasicPruning, PruningMode} import io.iohk.ethereum.domain.{Address, UInt256} @@ -15,6 +15,7 @@ import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration import io.iohk.ethereum.utils.NumericUtils._ import org.spongycastle.util.encoders.Hex +import scala.collection.JavaConverters._ import scala.concurrent.duration._ import scala.util.Try @@ -254,7 +255,7 @@ trait BlockchainConfig { val customGenesisFileOpt: Option[String] - val daoForkConfig: DaoForkConfig + val daoForkConfig: Option[DaoForkConfig] val accountStartNonce: UInt256 @@ -263,7 +264,25 @@ trait BlockchainConfig { val monetaryPolicyConfig: MonetaryPolicyConfig } + object BlockchainConfig { + + def createDaoForkConfig(daoConfig: TypesafeConfig): DaoForkConfig = { + + val theForkBlockNumber = BigInt(daoConfig.getString("fork-block-number")) + + val theForkBlockHash = ByteString(Hex.decode(daoConfig.getString("fork-block-hash"))) + + new DaoForkConfig { + override val forkBlockNumber = theForkBlockNumber + override val forkBlockHash = theForkBlockHash + override val blockExtraData = Try(daoConfig.getString("block-extra-data")).toOption.map(ByteString(_)) + override val range = Try(daoConfig.getInt("block-extra-data-range")).toOption.getOrElse(0) + override val refundContract = Try(daoConfig.getString("refund-contract-address")).toOption.map(Address(_)) + override val drainList = Try(daoConfig.getStringList("drain-list").asScala.toList).toOption.getOrElse(List.empty).map(Address(_)) + } + } + def apply(etcClientConfig: TypesafeConfig): BlockchainConfig = { val blockchainConfig = etcClientConfig.getConfig("blockchain") @@ -278,10 +297,7 @@ object BlockchainConfig { override val customGenesisFileOpt: Option[String] = Try(blockchainConfig.getString("custom-genesis-file")).toOption - val proDaoFork: Boolean = blockchainConfig.getBoolean("pro-dao-fork") - val daoForkBlockNumber: BigInt = BigInt(blockchainConfig.getString("dao-fork-block-number")) - val daoForkBlockHash: ByteString = ByteString(Hex.decode(blockchainConfig.getString("dao-fork-block-hash"))) - override val daoForkConfig = DefaultDaoForkConfig(proDaoFork, daoForkBlockNumber, daoForkBlockHash) + override val daoForkConfig = Try(blockchainConfig.getConfig("dao")).toOption.map(createDaoForkConfig) override val accountStartNonce: UInt256 = UInt256(BigInt(blockchainConfig.getString("account-start-nonce"))) override val chainId: Byte = { diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index 5abd0704f4..e3929417b7 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -58,22 +58,28 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block * @param blockHeader BlockHeader to validate. * @return BlockHeader if valid, an [[HeaderExtraDataError]] otherwise */ - private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = - if (blockHeader.extraData.length <= MaxExtraDataSize) { - import blockchainConfig._ - - val requiresExtraData = daoForkConfig requiresExtraData blockHeader.number - val isDaoForkData = blockHeader.extraData == daoForkConfig.blockExtraData + private def validateExtraData(blockHeader: BlockHeader): Either[BlockHeaderError, BlockHeader] = { - (requiresExtraData, isDaoForkData) match { - case (false, _) | (true, true) => + def validateDaoForkExtraData(blockHeader: BlockHeader, daoForkConfig: DaoForkConfig): Either[BlockHeaderError, BlockHeader] = + (daoForkConfig requiresExtraData blockHeader.number, daoForkConfig.blockExtraData) match { + case (false, _) => + Right(blockHeader) + case (true, Some(forkExtraData)) if blockHeader.extraData == forkExtraData => Right(blockHeader) case _ => Left(DaoHeaderExtraDataError) } + + if (blockHeader.extraData.length <= MaxExtraDataSize) { + import blockchainConfig._ + + if(daoForkConfig.isEmpty) Right(blockHeader) + else validateDaoForkExtraData(blockHeader, daoForkConfig.get) + } else { Left(HeaderExtraDataError) } + } /** * Validates [[io.iohk.ethereum.domain.BlockHeader.unixTimestamp]] is greater than the one of its parent diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf index b99687fbfe..38f05a0067 100644 --- a/src/test/resources/application.conf +++ b/src/test/resources/application.conf @@ -1,4 +1,7 @@ mantis { + + client-id = "mantis" + datadir = "/tmp/mantis-test/" secure-random-algo = "NativePRNGNonBlocking" @@ -16,6 +19,27 @@ mantis { blockchain { eip155-block-number = "3000000" + + dao { + # DAO fork block number (Ethereum HF/Classic split) + # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ + fork-block-number = "1920000" + + # The hash of the accepted DAO fork block + fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + + # Extra data to be put in fork block headers + block-extra-data = null + + # number of blocks to place extra data after fork + block-extra-data-range = 10 + + # Address to send funds when draining + refund-contract-address = null + + # List of accounts to be drained + drain-list = null + } } mining { diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala index 73d1c0a450..2fcb83cc1e 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala @@ -4,7 +4,7 @@ import akka.actor.ActorSystem import akka.testkit.TestProbe import akka.util.ByteString import io.iohk.ethereum.crypto.ECDSASignature -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.db.storage.AppStateStorage import io.iohk.ethereum.domain.{UInt256, _} import io.iohk.ethereum.jsonrpc.JsonRpcErrors._ @@ -351,7 +351,7 @@ class PersonalServiceSpec extends FlatSpec with Matchers with MockFactory with S override val customGenesisFileOpt: Option[String] = None override val accountStartNonce: UInt256 = UInt256.Zero override val monetaryPolicyConfig: MonetaryPolicyConfig = new MonetaryPolicyConfig(0, 0, 0) - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = 0, daoForkBlockHash = ByteString.empty, proDaoFork = false) + override val daoForkConfig: Option[DaoForkConfig] = None } val wallet = Wallet(address, prvKey) diff --git a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala index 123813f32a..66a42dd321 100644 --- a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala @@ -6,7 +6,7 @@ import akka.util.ByteString.{empty => bEmpty} import io.iohk.ethereum.Mocks.MockVM import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain._ import io.iohk.ethereum.ledger.BlockExecutionError.{ValidationAfterExecError, ValidationBeforeExecError} import io.iohk.ethereum.{Fixtures, Mocks, rlp} @@ -755,23 +755,21 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFac result match { case (_, executedTxs) => executedTxs shouldBe Seq.empty } } - it should "drain DAO accounts and send the funds to refund address if it's Pro DAO Fork configured" in new DaoForkTestSetup { + it should "drain DAO accounts and send the funds to refund address if Pro DAO Fork was configured" in new DaoForkTestSetup { + + (worldState.getAccount _) + .expects(supportDaoForkConfig.refundContract.get) + .anyNumberOfTimes() + .returning(Some(Account(nonce = 1, balance = UInt256.Zero))) // Check we drain all the accounts and send the balance to refund contract - proDaoBlockchainConfig.daoForkConfig.drainList.foreach { addr => + supportDaoForkConfig.drainList.foreach { addr => val daoAccountsFakeBalance = UInt256(1000) (worldState.getAccount _).expects(addr).returning(Some(Account(nonce = 1, balance = daoAccountsFakeBalance))) - (worldState.transfer _).expects(addr, proDaoBlockchainConfig.daoForkConfig.refundContract, daoAccountsFakeBalance).returning(worldState) + (worldState.transfer _).expects(addr, supportDaoForkConfig.refundContract.get, daoAccountsFakeBalance).returning(worldState) } - val ledger = new LedgerImpl(new MockVM(c => createResult( - context = c, - gasUsed = UInt256(10), - gasLimit = UInt256(10), - gasRefund = UInt256.Zero, - logs = Seq.empty, - addressesToDelete = Set.empty - )), testBlockchain, proDaoBlockchainConfig) + val ledger = new LedgerImpl(new MockVM(), testBlockchain, proDaoBlockchainConfig) ledger.executeBlockTransactions( proDaoBlock.copy(body = proDaoBlock.body.copy(transactionList = Seq.empty)), // We don't care about block txs in this test @@ -779,21 +777,14 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFac ) } - it should "not drain DAO accounts and send the funds to refund address if it not Pro DAO Fork configured" in new DaoForkTestSetup { + it should "neither drain DAO accounts nor send the funds to refund address if Pro DAO Fork was not configured" in new DaoForkTestSetup { // Check we drain all the accounts and send the balance to refund contract - proDaoBlockchainConfig.daoForkConfig.drainList.foreach { addr => + supportDaoForkConfig.drainList.foreach { addr => val daoAccountsFakeBalance = UInt256(1000) - (worldState.transfer _).expects(addr, proDaoBlockchainConfig.daoForkConfig.refundContract, *).never() + (worldState.transfer _).expects(*, *, *).never() } - val ledger = new LedgerImpl(new MockVM(c => createResult( - context = c, - gasUsed = UInt256(10), - gasLimit = UInt256(10), - gasRefund = UInt256.Zero, - logs = Seq.empty, - addressesToDelete = Set.empty - )), testBlockchain, blockchainConfig) + val ledger = new LedgerImpl(new MockVM(), testBlockchain, blockchainConfig) ledger.executeBlockTransactions( proDaoBlock.copy(body = proDaoBlock.body.copy(transactionList = Seq.empty)), // We don't care about block txs in this test @@ -895,6 +886,20 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFac } trait DaoForkTestSetup extends TestSetup { + + val testBlockchain = mock[BlockchainImpl] + val worldState = mock[InMemoryWorldStateProxy] + val proDaoBlock = Fixtures.Blocks.ProDaoForkBlock.block + + val supportDaoForkConfig = new DaoForkConfig { + override val blockExtraData: Option[ByteString] = Some(ByteString("refund extra data")) + override val range: Int = 10 + override val drainList: Seq[Address] = Seq(Address(1), Address(2), Address(3)) + override val forkBlockHash: ByteString = proDaoBlock.header.hash + override val forkBlockNumber: BigInt = proDaoBlock.header.number + override val refundContract: Option[Address] = Some(Address(4)) + } + val proDaoBlockchainConfig = new BlockchainConfig { override val frontierBlockNumber: BigInt = blockchainConfig.frontierBlockNumber override val accountStartNonce: UInt256 = blockchainConfig.accountStartNonce @@ -906,22 +911,14 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFac override val eip150BlockNumber: BigInt = blockchainConfig.eip150BlockNumber override val chainId: Byte = 0x01.toByte override val difficultyBombContinueBlockNumber: BigInt = blockchainConfig.difficultyBombContinueBlockNumber - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(true, 1920000, ByteString("")) + override val daoForkConfig: Option[DaoForkConfig] = Some(supportDaoForkConfig) override val customGenesisFileOpt: Option[String] = None } - val testBlockchain = mock[BlockchainImpl] - val worldState = mock[InMemoryWorldStateProxy] - val proDaoBlock = Fixtures.Blocks.ProDaoForkBlock.block (testBlockchain.getBlockHeaderByHash _).expects(proDaoBlock.header.parentHash).returning(Some(Fixtures.Blocks.DaoParentBlock.header)) (testBlockchain.getWorldStateProxy _) .expects(proDaoBlock.header.number, proDaoBlockchainConfig.accountStartNonce, Some(Fixtures.Blocks.DaoParentBlock.header.stateRoot)) .returning(worldState) - - (worldState.getAccount _) - .expects(proDaoBlockchainConfig.daoForkConfig.refundContract) - .anyNumberOfTimes() - .returning(Some(Account(nonce = 1, balance = UInt256.Zero))) } } diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index 06273393d6..ba51f145e1 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -15,7 +15,7 @@ import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.SignedTransaction.FirstByteOfAddress import io.iohk.ethereum.utils.Config.DbConfig import org.spongycastle.crypto.AsymmetricCipherKeyPair @@ -72,7 +72,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with fullBlock.right.foreach(b => validators.blockHeaderValidator.validate(b.header, blockchain) shouldBe Right(b.header)) fullBlock.right.foreach(b => ledger.executeBlock(b, validators) shouldBe a[Right[_, Seq[Receipt]]]) fullBlock.right.foreach(b => b.body.transactionList shouldBe Seq(signedTransaction)) - fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) + fullBlock.right.foreach(b => b.header.extraData shouldBe miningConfig.headerExtraData) } it should "filter out transactions exceeding block gas limit and include correct transactions" in new TestSetup { @@ -114,11 +114,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val eip160BlockNumber: BigInt = Long.MaxValue override val eip150BlockNumber: BigInt = Long.MaxValue override val accountStartNonce: UInt256 = UInt256.Zero - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( - daoForkBlockNumber = Long.MaxValue, - daoForkBlockHash = ByteString("unused"), - proDaoFork = false - ) + override val daoForkConfig: Option[DaoForkConfig] = None } val generalTx = SignedTransaction.sign(transaction, keyPair, None) @@ -271,7 +267,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with override val eip160BlockNumber: BigInt = Long.MaxValue override val eip150BlockNumber: BigInt = Long.MaxValue override val accountStartNonce: UInt256 = UInt256.Zero - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(daoForkBlockNumber = Long.MaxValue, daoForkBlockHash = ByteString("unused"), proDaoFork = false) + override val daoForkConfig: Option[DaoForkConfig] = None } lazy val ledger = new LedgerImpl(VM, blockchain, blockchainConfig) diff --git a/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala b/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala index feeb5633aa..58205ba222 100644 --- a/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala @@ -254,7 +254,7 @@ class EtcPeerManagerSpec extends FlatSpec with Matchers { blockchain.save(Fixtures.Blocks.Genesis.header) val blockchainConfig = BlockchainConfig(Config.config) - val forkResolver = new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig) + val forkResolver = new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get) val peerStatus = Status( protocolVersion = Versions.PV63, diff --git a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala index c1cc5daced..ae0bb63b38 100644 --- a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala @@ -5,12 +5,12 @@ import akka.util.ByteString import io.iohk.ethereum.Fixtures import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto.generateKeyPair -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.db.components.Storages.PruningModeComponent import io.iohk.ethereum.db.components.{SharedEphemDataSources, Storages} import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} import io.iohk.ethereum.db.storage.AppStateStorage -import io.iohk.ethereum.domain.{Block, Blockchain, BlockchainImpl, UInt256} +import io.iohk.ethereum.domain._ import io.iohk.ethereum.network.ForkResolver import io.iohk.ethereum.network.PeerManagerActor.PeerConfiguration import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo @@ -25,6 +25,7 @@ import io.iohk.ethereum.network.p2p.messages.WireProtocol.{Capability, Disconnec import io.iohk.ethereum.nodebuilder.SecureRandomBuilder import io.iohk.ethereum.utils._ import org.scalatest.{FlatSpec, Matchers} +import org.spongycastle.util.encoders.Hex import scala.concurrent.ExecutionContext.Implicits.global @@ -158,15 +159,18 @@ class EtcHandshakerSpec extends FlatSpec with Matchers { override val chainId: Byte = 0.toByte override val monetaryPolicyConfig: MonetaryPolicyConfig = null override val accountStartNonce: UInt256 = UInt256.Zero - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig( - daoForkBlockNumber = forkBlockHeader.number, - daoForkBlockHash = forkBlockHeader.hash, - proDaoFork = false - ) + override val daoForkConfig: Option[DaoForkConfig] = Some(new DaoForkConfig { + override val blockExtraData: Option[ByteString] = None + override val range: Int = 10 + override val drainList: Seq[Address] = Nil + override val forkBlockHash: ByteString = ByteString(Hex.decode("94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f")) + override val forkBlockNumber: BigInt = 1920000 + override val refundContract: Option[Address] = None + }) } val etcHandshakerConfigurationWithResolver = new MockEtcHandshakerConfiguration { - override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) + override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) } val initHandshakerWithoutResolver = EtcHandshaker(new MockEtcHandshakerConfiguration) diff --git a/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala b/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala index 67429b7948..4637eb0a13 100644 --- a/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/p2p/PeerActorSpec.scala @@ -399,7 +399,7 @@ class PeerActorSpec extends FlatSpec with Matchers { trait HandshakerSetup extends NodeStatusSetup { val handshakerConfiguration = new EtcHandshakerConfiguration { - override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig)) + override val forkResolverOpt: Option[ForkResolver] = Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) override val nodeStatusHolder: Agent[NodeStatus] = HandshakerSetup.this.nodeStatusHolder override val peerConfiguration: PeerConfiguration = HandshakerSetup.this.peerConf override val blockchain: Blockchain = HandshakerSetup.this.blockchain diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index 7b37a111ff..c1b2d0ff37 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -3,7 +3,7 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.{Fixtures, ObjectGenerators} import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup -import io.iohk.ethereum.daoFork.{DaoForkConfig, DefaultDaoForkConfig} +import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.{UInt256, _} import io.iohk.ethereum.utils.{BlockchainConfig, Config, MonetaryPolicyConfig} import io.iohk.ethereum.validators.BlockHeaderError._ @@ -12,7 +12,6 @@ import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyChecks with ObjectGenerators { - import Fixtures.Blocks._ val ExtraDataSizeLimit = 20 //BlockHeader member's lengths obtained from Yellow paper @@ -44,7 +43,7 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck it should "validate DAO block (extra data)" in { import Fixtures.Blocks._ val cases = Table( - ("Block", "Parent Block", "Pro Dao Fork", "Valid"), + ("Block", "Parent Block", "Supports Dao Fork", "Valid"), (DaoForkBlock.header, DaoParentBlock.header, false, true), (DaoForkBlock.header, DaoParentBlock.header, true, false), (ProDaoForkBlock.header, DaoParentBlock.header, true, true), @@ -56,8 +55,8 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck (ProDaoBlock1920010Header, ProDaoBlock1920009Header, true, true) ) - forAll(cases) { (block, parentBlock, proDaoFork, valid ) => - val blockHeaderValidator = new BlockHeaderValidatorImpl(createBlockchainConfig(proDaoFork)) + forAll(cases) { (block, parentBlock, supportsDaoFork, valid ) => + val blockHeaderValidator = new BlockHeaderValidatorImpl(createBlockchainConfig(supportsDaoFork)) blockHeaderValidator.validate(block, parentBlock) match { case Right(_) => assert(valid) case Left(DaoHeaderExtraDataError) => assert(!valid) @@ -239,14 +238,24 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck nonce = ByteString(Hex.decode("3fc7bc671f7cee70")) ) - def createBlockchainConfig(proDaoFork: Boolean = false): BlockchainConfig = + def createBlockchainConfig(supportsDaoFork: Boolean = false): BlockchainConfig = new BlockchainConfig { + + import Fixtures.Blocks._ + override val frontierBlockNumber: BigInt = 0 override val homesteadBlockNumber: BigInt = 1150000 override val difficultyBombPauseBlockNumber: BigInt = 3000000 override val difficultyBombContinueBlockNumber: BigInt = 5000000 - override val daoForkConfig: DaoForkConfig = DefaultDaoForkConfig(proDaoFork, DaoForkBlock.header.number, DaoForkBlock.header.hash) + override val daoForkConfig: Option[DaoForkConfig] = Some(new DaoForkConfig { + override val blockExtraData: Option[ByteString] = if(supportsDaoFork) Some(ProDaoForkBlock.header.extraData) else None + override val range: Int = 10 + override val drainList: Seq[Address] = Nil + override val forkBlockHash: ByteString = if(supportsDaoFork) ProDaoForkBlock.header.hash else DaoForkBlock.header.hash + override val forkBlockNumber: BigInt = DaoForkBlock.header.number + override val refundContract: Option[Address] = None + }) // unused override val eip155BlockNumber: BigInt = Long.MaxValue diff --git a/src/universal/conf/blockchain.conf b/src/universal/conf/blockchain.conf index 938f765e8c..3bc8e7bf4e 100644 --- a/src/universal/conf/blockchain.conf +++ b/src/universal/conf/blockchain.conf @@ -40,6 +40,29 @@ mantis { # The hash of the accepted DAO fork block # dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + # DAO fork configuration (Ethereum HF/Classic split) + # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ + # dao { + + # DAO fork block number + # fork-block-number = "1920000" + + # The hash of the accepted DAO fork block + # fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + + # Extra data to be put in fork block headers + # block-extra-data = null + + # number of blocks to place extra data after fork + #block-extra-data-range = 10 + + # Address to send funds when draining + # refund-contract-address = null + + # List of accounts addresses to be drained. For example ["d4fe7bc31cedb7bfb8a345f31e668033056b2728", "b3fb0e5aba0e20e5c49d252dfd30e102b171a425"] + # drain-list = [] + #} + # Starting nonce a an empty account. Some networks (like Morden) use different values. # account-start-nonce = "0" diff --git a/src/universal/conf/ethereumHF.conf b/src/universal/conf/ethereumHF.conf index 502003d72e..7b1db4101e 100644 --- a/src/universal/conf/ethereumHF.conf +++ b/src/universal/conf/ethereumHF.conf @@ -25,9 +25,6 @@ mantis { difficulty-bomb-pause-block-number = "0" difficulty-bomb-continue-block-number = "0" - dao-fork-block-number = "1920000" - dao-fork-block-hash = "4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb" - account-start-nonce = "0" chain-id = "1" @@ -39,5 +36,146 @@ mantis { era-duration = 5000000 reward-reduction-rate = 0 } + + # DAO fork configuration (Ethereum HF/Classic split) + # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ + dao { + + # DAO fork block number + fork-block-number = "1920000" + + # The hash of the accepted DAO fork block + fork-block-hash = "4985f5ca3d2afbec36529aa96f74de3cc10a2a4a6c44f2157a57d2c6059a11bb" + + # Extra data to be put in fork block headers + block-extra-data = "64616f2d686172642d666f726b" + + # number of blocks to place extra data after fork + block-extra-data-range = 10 + + # Address to send funds when draining + refund-contract-address = "bf4ed7b27f1d666546e30d74d50d173d20bca754" + + # List of accounts to be drained + drain-list = [ + "d4fe7bc31cedb7bfb8a345f31e668033056b2728", + "b3fb0e5aba0e20e5c49d252dfd30e102b171a425", + "2c19c7f9ae8b751e37aeb2d93a699722395ae18f", + "ecd135fa4f61a655311e86238c92adcd779555d2", + "1975bd06d486162d5dc297798dfc41edd5d160a7", + "a3acf3a1e16b1d7c315e23510fdd7847b48234f6", + "319f70bab6845585f412ec7724b744fec6095c85", + "06706dd3f2c9abf0a21ddcc6941d9b86f0596936", + "5c8536898fbb74fc7445814902fd08422eac56d0", + "6966ab0d485353095148a2155858910e0965b6f9", + "779543a0491a837ca36ce8c635d6154e3c4911a6", + "2a5ed960395e2a49b1c758cef4aa15213cfd874c", + "5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5", + "9c50426be05db97f5d64fc54bf89eff947f0a321", + "200450f06520bdd6c527622a273333384d870efb", + "be8539bfe837b67d1282b2b1d61c3f723966f049", + "6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb", + "f1385fb24aad0cd7432824085e42aff90886fef5", + "d1ac8b1ef1b69ff51d1d401a476e7e612414f091", + "8163e7fb499e90f8544ea62bbf80d21cd26d9efd", + "51e0ddd9998364a2eb38588679f0d2c42653e4a6", + "627a0a960c079c21c34f7612d5d230e01b4ad4c7", + "f0b1aa0eb660754448a7937c022e30aa692fe0c5", + "24c4d950dfd4dd1902bbed3508144a54542bba94", + "9f27daea7aca0aa0446220b98d028715e3bc803d", + "a5dc5acd6a7968a4554d89d65e59b7fd3bff0f90", + "d9aef3a1e38a39c16b31d1ace71bca8ef58d315b", + "63ed5a272de2f6d968408b4acb9024f4cc208ebf", + "6f6704e5a10332af6672e50b3d9754dc460dfa4d", + "77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6", + "492ea3bb0f3315521c31f273e565b868fc090f17", + "0ff30d6de14a8224aa97b78aea5388d1c51c1f00", + "9ea779f907f0b315b364b0cfc39a0fde5b02a416", + "ceaeb481747ca6c540a000c1f3641f8cef161fa7", + "cc34673c6c40e791051898567a1222daf90be287", + "579a80d909f346fbfb1189493f521d7f48d52238", + "e308bd1ac5fda103967359b2712dd89deffb7973", + "4cb31628079fb14e4bc3cd5e30c2f7489b00960c", + "ac1ecab32727358dba8962a0f3b261731aad9723", + "4fd6ace747f06ece9c49699c7cabc62d02211f75", + "440c59b325d2997a134c2c7c60a8c61611212bad", + "4486a3d68fac6967006d7a517b889fd3f98c102b", + "9c15b54878ba618f494b38f0ae7443db6af648ba", + "27b137a85656544b1ccb5a0f2e561a5703c6a68f", + "21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241", + "23b75c2f6791eef49c69684db4c6c1f93bf49a50", + "1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b", + "b9637156d330c0d605a791f1c31ba5890582fe1c", + "6131c42fa982e56929107413a9d526fd99405560", + "1591fc0f688c81fbeb17f5426a162a7024d430c2", + "542a9515200d14b68e934e9830d91645a980dd7a", + "c4bbd073882dd2add2424cf47d35213405b01324", + "782495b7b3355efb2833d56ecb34dc22ad7dfcc4", + "58b95c9a9d5d26825e70a82b6adb139d3fd829eb", + "3ba4d81db016dc2890c81f3acec2454bff5aada5", + "b52042c8ca3f8aa246fa79c3feaa3d959347c0ab", + "e4ae1efdfc53b73893af49113d8694a057b9c0d1", + "3c02a7bc0391e86d91b7d144e61c2c01a25a79c5", + "0737a6b837f97f46ebade41b9bc3e1c509c85c53", + "97f43a37f595ab5dd318fb46e7a155eae057317a", + "52c5317c848ba20c7504cb2c8052abd1fde29d03", + "4863226780fe7c0356454236d3b1c8792785748d", + "5d2b2e6fcbe3b11d26b525e085ff818dae332479", + "5f9f3392e9f62f63b8eac0beb55541fc8627f42c", + "057b56736d32b86616a10f619859c6cd6f59092a", + "9aa008f65de0b923a2a4f02012ad034a5e2e2192", + "304a554a310c7e546dfe434669c62820b7d83490", + "914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79", + "4deb0033bb26bc534b197e61d19e0733e5679784", + "07f5c1e1bc2c93e0402f23341973a0e043f7bf8a", + "35a051a0010aba705c9008d7a7eff6fb88f6ea7b", + "4fa802324e929786dbda3b8820dc7834e9134a2a", + "9da397b9e80755301a3b32173283a91c0ef6c87e", + "8d9edb3054ce5c5774a420ac37ebae0ac02343c6", + "0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9", + "5dc28b15dffed94048d73806ce4b7a4612a1d48f", + "bcf899e6c7d9d5a215ab1e3444c86806fa854c76", + "12e626b0eebfe86a56d633b9864e389b45dcb260", + "a2f1ccba9395d7fcb155bba8bc92db9bafaeade7", + "ec8e57756626fdc07c63ad2eafbd28d08e7b0ca5", + "d164b088bd9108b60d0ca3751da4bceb207b0782", + "6231b6d0d5e77fe001c2a460bd9584fee60d409b", + "1cba23d343a983e9b5cfd19496b9a9701ada385f", + "a82f360a8d3455c5c41366975bde739c37bfeb8a", + "9fcd2deaff372a39cc679d5c5e4de7bafb0b1339", + "005f5cee7a43331d5a3d3eec71305925a62f34b6", + "0e0da70933f4c7849fc0d203f5d1d43b9ae4532d", + "d131637d5275fd1a68a3200f4ad25c71a2a9522e", + "bc07118b9ac290e4622f5e77a0853539789effbe", + "47e7aa56d6bdf3f36be34619660de61275420af8", + "acd87e28b0c9d1254e868b81cba4cc20d9a32225", + "adf80daec7ba8dcf15392f1ac611fff65d94f880", + "5524c55fb03cf21f549444ccbecb664d0acad706", + "40b803a9abce16f50f36a77ba41180eb90023925", + "fe24cdd8648121a43a7c86d289be4dd2951ed49f", + "17802f43a0137c506ba92291391a8a8f207f487d", + "253488078a4edf4d6f42f113d1e62836a942cf1a", + "86af3e9626fce1957c82e88cbf04ddf3a2ed7915", + "b136707642a4ea12fb4bae820f03d2562ebff487", + "dbe9b615a3ae8709af8b93336ce9b477e4ac0940", + "f14c14075d6c4ed84b86798af0956deef67365b5", + "ca544e5c4687d109611d0f8f928b53a25af72448", + "aeeb8ff27288bdabc0fa5ebb731b6f409507516c", + "cbb9d3703e651b0d496cdefb8b92c25aeb2171f7", + "6d87578288b6cb5549d5076a207456a1f6a63dc0", + "b2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e", + "accc230e8a6e5be9160b8cdf2864dd2a001c28b6", + "2b3455ec7fedf16e646268bf88846bd7a2319bb2", + "4613f3bca5c44ea06337a9e439fbc6d42e501d0a", + "d343b217de44030afaa275f54d31a9317c7f441e", + "84ef4b2357079cd7a7c69fd7a37cd0609a679106", + "da2fef9e4a3230988ff17df2165440f37e8b1708", + "f4c64518ea10f995918a454158c6b61407ea345c", + "7602b46df5390e432ef1c307d4f2c9ff6d65cc97", + "bb9bc244d798123fde783fcc1c72d3bb8c189413", + "807640a13483f8ac783c557fcdf27be11ea4ac7a" + ] + } + } } diff --git a/src/universal/conf/morden.conf b/src/universal/conf/morden.conf index ebcca54f51..2cc9420611 100644 --- a/src/universal/conf/morden.conf +++ b/src/universal/conf/morden.conf @@ -20,9 +20,7 @@ mantis { difficulty-bomb-pause-block-number = "1915000" difficulty-bomb-continue-block-number = "3415000" - pro-dao-fork = false - dao-fork-block-number = "1783000" - dao-fork-block-hash = "f376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145" + dao = null account-start-nonce = "1048576" From 7f287f76dfdaf2e2b1fd351e03f2b9af467cb5cc Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Tue, 19 Sep 2017 10:10:15 -0300 Subject: [PATCH 14/20] [EC-236] Addressed PR comments --- .../iohk/ethereum/txExecTest/ForksTest.scala | 6 +- .../iohk/ethereum/daoFork/DaoForkConfig.scala | 18 ----- .../io/iohk/ethereum/ledger/Ledger.scala | 22 ++++--- .../iohk/ethereum/mining/BlockGenerator.scala | 1 - .../iohk/ethereum/network/ForkResolver.scala | 4 +- .../ethereum/nodebuilder/NodeBuilder.scala | 5 +- .../scala/io/iohk/ethereum/utils/Config.scala | 66 +++++++++++++------ .../validators/BlockHeaderValidator.scala | 21 +++--- .../jsonrpc/PersonalServiceSpec.scala | 3 +- .../io/iohk/ethereum/ledger/LedgerSpec.scala | 3 +- .../ethereum/mining/BlockGeneratorSpec.scala | 3 +- .../handshaker/EtcHandshakerSpec.scala | 4 -- .../validators/BlockHeaderValidatorSpec.scala | 16 +++-- src/universal/conf/blockchain.conf | 11 ---- 14 files changed, 84 insertions(+), 99 deletions(-) diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala index a7477f6149..19bb53d4ae 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala @@ -1,11 +1,9 @@ package io.iohk.ethereum.txExecTest -import akka.util.ByteString -import io.iohk.ethereum.daoFork.DaoForkConfig -import io.iohk.ethereum.domain.{Address, BlockchainImpl, Receipt, UInt256} +import io.iohk.ethereum.domain.{BlockchainImpl, Receipt, UInt256} import io.iohk.ethereum.ledger.LedgerImpl import io.iohk.ethereum.txExecTest.util.FixtureProvider -import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig} +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig, MonetaryPolicyConfig} import io.iohk.ethereum.validators._ import io.iohk.ethereum.vm.VM import org.scalatest.{FlatSpec, Matchers} diff --git a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala index 9e8d3caa40..d4dc76b421 100644 --- a/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala +++ b/src/main/scala/io/iohk/ethereum/daoFork/DaoForkConfig.scala @@ -3,22 +3,4 @@ package io.iohk.ethereum.daoFork import akka.util.ByteString import io.iohk.ethereum.domain.Address -trait DaoForkConfig { - val forkBlockNumber: BigInt - val forkBlockHash: ByteString - val blockExtraData: Option[ByteString] - val range: Int - val refundContract: Option[Address] - val drainList: Seq[Address] - - private lazy val extratadaBlockRange = forkBlockNumber until(forkBlockNumber + range) - - def isDaoForkBlock(blockNumber: BigInt): Boolean = forkBlockNumber == blockNumber - - def requiresExtraData(blockNumber: BigInt): Boolean = blockExtraData.isDefined && (extratadaBlockRange contains blockNumber) - - def getExtraData(blockNumber: BigInt): Option[ByteString] = - if(requiresExtraData(blockNumber)) blockExtraData - else None -} diff --git a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala index 69f6bf8162..831e6d07ce 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala @@ -1,12 +1,11 @@ package io.iohk.ethereum.ledger import akka.util.ByteString -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain._ import io.iohk.ethereum.validators._ import io.iohk.ethereum.ledger.BlockExecutionError.{StateBeforeFailure, TxsExecutionError, ValidationAfterExecError, ValidationBeforeExecError} import io.iohk.ethereum.ledger.Ledger.{BlockPreparationResult, BlockResult, PC, PR, TxResult} -import io.iohk.ethereum.utils.{BlockchainConfig, Logger} +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig, Logger} import io.iohk.ethereum.validators.{BlockValidator, SignedTransactionValidator} import io.iohk.ethereum.domain.UInt256._ import io.iohk.ethereum.vm._ @@ -385,15 +384,18 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai * @return Updated world state proxy */ private def drainDaoForkAccounts(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { - daoForkConfig.drainList.foldLeft(worldState) { (ws, address) => - val afterDrainWS = for { - account <- ws.getAccount(address) - refundContractAddress <- daoForkConfig.refundContract - afterDrainingAddress = ws.transfer(from = address, to = refundContractAddress, account.balance) - } yield afterDrainingAddress - - afterDrainWS.getOrElse(ws) + + daoForkConfig.refundContract match { + case Some(refundContractAddress) => + daoForkConfig.drainList.foldLeft(worldState) { (ws, address) => + ws.getAccount(address) + .map(account => ws.transfer(from = address, to = refundContractAddress, account.balance)) + .getOrElse(ws) + } + case None => worldState } + + } } diff --git a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala index c86ef1e929..8c89d2b255 100644 --- a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala +++ b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala @@ -20,7 +20,6 @@ import io.iohk.ethereum.validators.MptListValidator.intByteArraySerializable import io.iohk.ethereum.validators.OmmersValidator.OmmersError import io.iohk.ethereum.validators.Validators import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.DaoForkConfig class BlockGenerator(blockchain: Blockchain, blockchainConfig: BlockchainConfig, miningConfig: MiningConfig, ledger: Ledger, validators: Validators, blockTimestampProvider: BlockTimestampProvider = DefaultBlockTimestampProvider) { diff --git a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala index 824cc06093..fcc828d3b0 100644 --- a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala +++ b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala @@ -1,9 +1,7 @@ package io.iohk.ethereum.network -import akka.util.ByteString -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.BlockHeader -import io.iohk.ethereum.utils.BlockchainConfig +import io.iohk.ethereum.utils.DaoForkConfig trait ForkResolver { type Fork <: ForkResolver.Fork diff --git a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala index e11997cf94..d90e451d54 100644 --- a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala @@ -128,9 +128,8 @@ trait BlockChainBuilder { trait ForkResolverBuilder { self: BlockchainConfigBuilder => - lazy val forkResolverOpt = - if (blockchainConfig.daoForkConfig.isEmpty) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) + lazy val forkResolverOpt = blockchainConfig.daoForkConfig.map(new ForkResolver.EtcForkResolver(_)) + } trait HandshakerBuilder { diff --git a/src/main/scala/io/iohk/ethereum/utils/Config.scala b/src/main/scala/io/iohk/ethereum/utils/Config.scala index 78ca533219..e3d0148a10 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -4,7 +4,6 @@ import java.net.InetSocketAddress import akka.util.ByteString import com.typesafe.config.{ConfigFactory, Config => TypesafeConfig} -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.db.dataSource.LevelDbConfig import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, BasicPruning, PruningMode} import io.iohk.ethereum.domain.{Address, UInt256} @@ -13,6 +12,7 @@ import io.iohk.ethereum.jsonrpc.server.JsonRpcServer.JsonRpcServerConfig import io.iohk.ethereum.network.PeerManagerActor.{FastSyncHostConfiguration, PeerConfiguration} import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration import io.iohk.ethereum.utils.NumericUtils._ +import io.iohk.ethereum.validators.BlockHeaderValidatorImpl import org.spongycastle.util.encoders.Hex import scala.collection.JavaConverters._ @@ -219,8 +219,6 @@ object TxPoolConfig { } trait MiningConfig { - //duplicated in BlockHeaderValidator - val MaxExtraDataSize: Int = 32 val ommersPoolSize: Int val blockCacheSize: Int val coinbase: Address @@ -239,11 +237,53 @@ object MiningConfig { val ommersPoolSize: Int = miningConfig.getInt("ommers-pool-size") val activeTimeout: FiniteDuration = miningConfig.getDuration("active-timeout").toMillis.millis val ommerPoolQueryTimeout: FiniteDuration = miningConfig.getDuration("ommer-pool-query-timeout").toMillis.millis - override val headerExtraData: ByteString = ByteString(miningConfig.getString("header-extra-data").getBytes).take(MaxExtraDataSize) + override val headerExtraData: ByteString = + ByteString(miningConfig + .getString("header-extra-data").getBytes) + .take(BlockHeaderValidatorImpl.MaxExtraDataSize) } } } +trait DaoForkConfig { + + val forkBlockNumber: BigInt + val forkBlockHash: ByteString + val blockExtraData: Option[ByteString] + val range: Int + val refundContract: Option[Address] + val drainList: Seq[Address] + + private lazy val extratadaBlockRange = forkBlockNumber until(forkBlockNumber + range) + + def isDaoForkBlock(blockNumber: BigInt): Boolean = forkBlockNumber == blockNumber + + def requiresExtraData(blockNumber: BigInt): Boolean = blockExtraData.isDefined && (extratadaBlockRange contains blockNumber) + + def getExtraData(blockNumber: BigInt): Option[ByteString] = + if(requiresExtraData(blockNumber)) blockExtraData + else None +} + +object DaoForkConfig { + def apply(daoConfig: TypesafeConfig): DaoForkConfig = { + + val theForkBlockNumber = BigInt(daoConfig.getString("fork-block-number")) + + val theForkBlockHash = ByteString(Hex.decode(daoConfig.getString("fork-block-hash"))) + + new DaoForkConfig { + override val forkBlockNumber: BigInt = theForkBlockNumber + override val forkBlockHash: ByteString = theForkBlockHash + override val blockExtraData: Option[ByteString] = Try(daoConfig.getString("block-extra-data")).toOption.map(ByteString(_)) + override val range: Int = Try(daoConfig.getInt("block-extra-data-range")).toOption.getOrElse(0) + override val refundContract: Option[Address] = Try(daoConfig.getString("refund-contract-address")).toOption.map(Address(_)) + override val drainList: List[Address] = Try(daoConfig.getStringList("drain-list").asScala.toList).toOption.getOrElse(List.empty).map(Address(_)) + } + } +} + + trait BlockchainConfig { val frontierBlockNumber: BigInt val homesteadBlockNumber: BigInt @@ -268,22 +308,6 @@ trait BlockchainConfig { object BlockchainConfig { - def createDaoForkConfig(daoConfig: TypesafeConfig): DaoForkConfig = { - - val theForkBlockNumber = BigInt(daoConfig.getString("fork-block-number")) - - val theForkBlockHash = ByteString(Hex.decode(daoConfig.getString("fork-block-hash"))) - - new DaoForkConfig { - override val forkBlockNumber = theForkBlockNumber - override val forkBlockHash = theForkBlockHash - override val blockExtraData = Try(daoConfig.getString("block-extra-data")).toOption.map(ByteString(_)) - override val range = Try(daoConfig.getInt("block-extra-data-range")).toOption.getOrElse(0) - override val refundContract = Try(daoConfig.getString("refund-contract-address")).toOption.map(Address(_)) - override val drainList = Try(daoConfig.getStringList("drain-list").asScala.toList).toOption.getOrElse(List.empty).map(Address(_)) - } - } - def apply(etcClientConfig: TypesafeConfig): BlockchainConfig = { val blockchainConfig = etcClientConfig.getConfig("blockchain") @@ -298,7 +322,7 @@ object BlockchainConfig { override val customGenesisFileOpt: Option[String] = Try(blockchainConfig.getString("custom-genesis-file")).toOption - override val daoForkConfig = Try(blockchainConfig.getConfig("dao")).toOption.map(createDaoForkConfig) + override val daoForkConfig = Try(blockchainConfig.getConfig("dao")).toOption.map(DaoForkConfig(_)) override val accountStartNonce: UInt256 = UInt256(BigInt(blockchainConfig.getString("account-start-nonce"))) override val chainId: Byte = { diff --git a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala index 5ad4a2c898..62f46308e0 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -2,25 +2,27 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.crypto.{kec256, kec512} -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.{BlockHeader, Blockchain, DifficultyCalculator} -import io.iohk.ethereum.utils.BlockchainConfig +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig} trait BlockHeaderValidator { - def validate(blockHeader: BlockHeader, blockchain: Blockchain): Either[BlockHeaderError, BlockHeader] - } -class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends BlockHeaderValidator { - +object BlockHeaderValidatorImpl { val MaxExtraDataSize: Int = 32 val GasLimitBoundDivisor: Int = 1024 val MinGasLimit: BigInt = 5000 //Although the paper states this value is 125000, on the different clients 5000 is used - val difficulty = new DifficultyCalculator(blockchainConfig) val MaxGasLimit = Long.MaxValue // max gasLimit is equal 2^63-1 according to EIP106 +} + +class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends BlockHeaderValidator { + + import BlockHeaderValidatorImpl._ import BlockHeaderError._ + val difficulty = new DifficultyCalculator(blockchainConfig) + /** This method allows validate a BlockHeader (stated on * section 4.4.4 of http://paper.gavwood.com/). * @@ -73,10 +75,7 @@ class BlockHeaderValidatorImpl(blockchainConfig: BlockchainConfig) extends Block if (blockHeader.extraData.length <= MaxExtraDataSize) { import blockchainConfig._ - - if(daoForkConfig.isEmpty) Right(blockHeader) - else validateDaoForkExtraData(blockHeader, daoForkConfig.get) - + daoForkConfig.map(c => validateDaoForkExtraData(blockHeader, c)).getOrElse(Right(blockHeader)) } else { Left(HeaderExtraDataError) } diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala index 68fa906621..09b281cc8b 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala @@ -4,7 +4,6 @@ import akka.actor.ActorSystem import akka.testkit.TestProbe import akka.util.ByteString import io.iohk.ethereum.crypto.ECDSASignature -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.db.storage.AppStateStorage import io.iohk.ethereum.domain.{UInt256, _} import io.iohk.ethereum.jsonrpc.JsonRpcErrors._ @@ -12,7 +11,7 @@ import io.iohk.ethereum.jsonrpc.PersonalService._ import io.iohk.ethereum.keystore.KeyStore.{DecryptionFailed, IOError} import io.iohk.ethereum.keystore.{KeyStore, Wallet} import io.iohk.ethereum.transactions.PendingTransactionsManager.{AddOrOverrideTransaction, GetPendingTransactions, PendingTransaction, PendingTransactionsResponse} -import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig, TxPoolConfig} +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig, MonetaryPolicyConfig, TxPoolConfig} import io.iohk.ethereum.{Fixtures, NormalPatience, Timeouts} import org.scalamock.matchers.Matcher import org.scalamock.scalatest.MockFactory diff --git a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala index 7c54436da7..934443c486 100644 --- a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala @@ -6,12 +6,11 @@ import akka.util.ByteString.{empty => bEmpty} import io.iohk.ethereum.Mocks.MockVM import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain._ import io.iohk.ethereum.ledger.BlockExecutionError.{ValidationAfterExecError, ValidationBeforeExecError} import io.iohk.ethereum.{Fixtures, Mocks, rlp} import io.iohk.ethereum.rlp.RLPList -import io.iohk.ethereum.utils.{BlockchainConfig, Config, MonetaryPolicyConfig} +import io.iohk.ethereum.utils.{BlockchainConfig, Config, DaoForkConfig, MonetaryPolicyConfig} import io.iohk.ethereum.ledger.Ledger.{BlockResult, PC, PR} import io.iohk.ethereum.network.p2p.messages.PV62.BlockBody import io.iohk.ethereum.nodebuilder.SecureRandomBuilder diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index 0fe2d10164..fc50dedcf9 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -8,14 +8,13 @@ import io.iohk.ethereum.blockchain.data.GenesisDataLoader import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.domain._ import io.iohk.ethereum.ledger.{BlockPreparationError, LedgerImpl} -import io.iohk.ethereum.utils.{BlockchainConfig, Logger, MiningConfig, MonetaryPolicyConfig} +import io.iohk.ethereum.utils._ import io.iohk.ethereum.validators._ import io.iohk.ethereum.vm.VM import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex import io.iohk.ethereum.crypto._ -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.SignedTransaction.FirstByteOfAddress import io.iohk.ethereum.utils.Config.DbConfig import org.spongycastle.crypto.AsymmetricCipherKeyPair diff --git a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala index b10fd0c173..c2d157bf9d 100644 --- a/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/network/handshaker/EtcHandshakerSpec.scala @@ -5,10 +5,6 @@ import akka.util.ByteString import io.iohk.ethereum.Fixtures import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup import io.iohk.ethereum.crypto.generateKeyPair -import io.iohk.ethereum.daoFork.DaoForkConfig -import io.iohk.ethereum.db.components.Storages.PruningModeComponent -import io.iohk.ethereum.db.components.{SharedEphemDataSources, Storages} -import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} import io.iohk.ethereum.db.storage.AppStateStorage import io.iohk.ethereum.domain._ import io.iohk.ethereum.network.ForkResolver diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index a09824dc5c..88798c9532 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -3,15 +3,17 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.{Fixtures, ObjectGenerators} import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup -import io.iohk.ethereum.daoFork.DaoForkConfig import io.iohk.ethereum.domain.{UInt256, _} -import io.iohk.ethereum.utils.{BlockchainConfig, Config, MonetaryPolicyConfig} +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig, MonetaryPolicyConfig} import io.iohk.ethereum.validators.BlockHeaderError._ import org.scalatest.prop.PropertyChecks import org.scalatest.{FlatSpec, Matchers} import org.spongycastle.util.encoders.Hex +import io.iohk.ethereum.validators.BlockHeaderValidatorImpl._ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyChecks with ObjectGenerators { + + val ExtraDataSizeLimit = 20 //BlockHeader member's lengths obtained from Yellow paper @@ -32,8 +34,8 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck it should "return a failure if created based on invalid extra data" in { forAll(randomSizeByteStringGen( - blockHeaderValidator.MaxExtraDataSize + 1, - blockHeaderValidator.MaxExtraDataSize + ExtraDataSizeLimit) + MaxExtraDataSize + 1, + MaxExtraDataSize + ExtraDataSizeLimit) ) { wrongExtraData => val invalidBlockHeader = validBlockHeader.copy(extraData = wrongExtraData) assert(blockHeaderValidator.validate(invalidBlockHeader, validBlockParent) == Left(HeaderExtraDataError)) @@ -96,9 +98,9 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck } it should "return a failure if created based on invalid gas limit" in { - val LowerGasLimit = blockHeaderValidator.MinGasLimit.max( - validBlockParent.gasLimit - validBlockParent.gasLimit / blockHeaderValidator.GasLimitBoundDivisor + 1) - val UpperGasLimit = validBlockParent.gasLimit + validBlockParent.gasLimit / blockHeaderValidator.GasLimitBoundDivisor - 1 + val LowerGasLimit = MinGasLimit.max( + validBlockParent.gasLimit - validBlockParent.gasLimit / GasLimitBoundDivisor + 1) + val UpperGasLimit = validBlockParent.gasLimit + validBlockParent.gasLimit / GasLimitBoundDivisor - 1 forAll(bigIntGen) { gasLimit => val blockHeader = validBlockHeader.copy(gasLimit = gasLimit) diff --git a/src/universal/conf/blockchain.conf b/src/universal/conf/blockchain.conf index 35dbf81a67..1ad198e40f 100644 --- a/src/universal/conf/blockchain.conf +++ b/src/universal/conf/blockchain.conf @@ -33,17 +33,6 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md # difficulty-bomb-continue-block-number = "5000000" - # specify if this client is pro or against DAO hard fork - # if true it will accept ETH version of chain if false it will accept ETC - # pro-dao-fork = false - - # DAO fork block number (Ethereum HF/Classic split) - # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ - # dao-fork-block-number = "1920000" - - # The hash of the accepted DAO fork block - # dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" - # DAO fork configuration (Ethereum HF/Classic split) # https://blog.ethereum.org/2016/07/20/hard-fork-completed/ # dao { From a6a22748c92b5c893409cce80b5e649a1872d310 Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Tue, 19 Sep 2017 21:35:46 +0200 Subject: [PATCH 15/20] snappy: post-merge compilation fixes --- .../ethereum/nodebuilder/NodeBuilder.scala | 28 +++++++++---------- .../iohk/ethereum/snappy/Prerequisites.scala | 25 ++++++++--------- .../io/iohk/ethereum/snappy/SnappyTest.scala | 5 ++-- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala index a3d53a949e..1174e53521 100644 --- a/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala +++ b/src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala @@ -119,7 +119,7 @@ trait NodeStatusBuilder { lazy val nodeStatusHolder = Agent(nodeStatus) } -trait BlockChainBuilder { +trait BlockchainBuilder { self: StorageBuilder => lazy val blockchain: BlockchainImpl = BlockchainImpl(storagesInstance.storages) @@ -134,7 +134,7 @@ trait ForkResolverBuilder { } trait HandshakerBuilder { - self: BlockChainBuilder + self: BlockchainBuilder with NodeStatusBuilder with StorageBuilder with PeerManagerActorBuilder @@ -203,7 +203,7 @@ trait EtcPeerManagerActorBuilder { trait BlockchainHostBuilder { self: ActorSystemBuilder - with BlockChainBuilder + with BlockchainBuilder with PeerManagerActorBuilder with EtcPeerManagerActorBuilder with PeerEventBusBuilder => @@ -217,7 +217,7 @@ trait ServerActorBuilder { self: ActorSystemBuilder with NodeStatusBuilder - with BlockChainBuilder + with BlockchainBuilder with PeerManagerActorBuilder => lazy val networkConfig = Config.Network @@ -251,7 +251,7 @@ trait PendingTransactionsManagerBuilder { trait FilterManagerBuilder { self: ActorSystemBuilder - with BlockChainBuilder + with BlockchainBuilder with BlockGeneratorBuilder with StorageBuilder with KeyStoreBuilder @@ -276,14 +276,14 @@ trait BlockGeneratorBuilder { ValidatorsBuilder with LedgerBuilder with MiningConfigBuilder with - BlockChainBuilder => + BlockchainBuilder => lazy val blockGenerator = new BlockGenerator(blockchain, blockchainConfig, miningConfig, ledger, validators) } trait EthServiceBuilder { self: StorageBuilder with - BlockChainBuilder with + BlockchainBuilder with BlockGeneratorBuilder with BlockchainConfigBuilder with PendingTransactionsManagerBuilder with @@ -303,7 +303,7 @@ trait EthServiceBuilder { trait PersonalServiceBuilder { self: KeyStoreBuilder with - BlockChainBuilder with + BlockchainBuilder with BlockchainConfigBuilder with PendingTransactionsManagerBuilder with StorageBuilder with @@ -326,7 +326,7 @@ trait JSONRpcControllerBuilder { trait JSONRpcHttpServerBuilder { - self: ActorSystemBuilder with BlockChainBuilder with JSONRpcControllerBuilder with SecureRandomBuilder => + self: ActorSystemBuilder with BlockchainBuilder with JSONRpcControllerBuilder with SecureRandomBuilder => lazy val jsonRpcServerConfig: JsonRpcServerConfig = Config.Network.Rpc @@ -335,7 +335,7 @@ trait JSONRpcHttpServerBuilder { trait OmmersPoolBuilder { self: ActorSystemBuilder with - BlockChainBuilder with + BlockchainBuilder with MiningConfigBuilder => lazy val ommersPool: ActorRef = actorSystem.actorOf(OmmersPool.props(blockchain, miningConfig)) @@ -354,7 +354,7 @@ trait ValidatorsBuilder { trait LedgerBuilder { self: BlockchainConfigBuilder - with BlockChainBuilder => + with BlockchainBuilder => lazy val ledger: Ledger = new LedgerImpl(VM, blockchain, blockchainConfig) } @@ -363,7 +363,7 @@ trait SyncControllerBuilder { self: ActorSystemBuilder with ServerActorBuilder with - BlockChainBuilder with + BlockchainBuilder with NodeStatusBuilder with StorageBuilder with BlockchainConfigBuilder with @@ -406,7 +406,7 @@ trait ShutdownHookBuilder { } trait GenesisDataLoaderBuilder { - self: BlockChainBuilder + self: BlockchainBuilder with StorageBuilder with BlockchainConfigBuilder => @@ -421,7 +421,7 @@ trait SecureRandomBuilder { trait Node extends NodeKeyBuilder with ActorSystemBuilder with StorageBuilder - with BlockChainBuilder + with BlockchainBuilder with NodeStatusBuilder with ForkResolverBuilder with HandshakerBuilder diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala index 2949054bd3..96b1469f06 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala @@ -6,12 +6,13 @@ import io.iohk.ethereum.db.components.{SharedLevelDBDataSources, Storages} import io.iohk.ethereum.db.dataSource.{LevelDBDataSource, LevelDbConfig} import io.iohk.ethereum.db.storage.pruning.ArchivePruning import io.iohk.ethereum.domain.{Blockchain, BlockchainImpl} -import io.iohk.ethereum.ledger.Ledger -import io.iohk.ethereum.nodebuilder.{BlockchainConfigBuilder, LedgerBuilder, ValidatorsBuilder} +import io.iohk.ethereum.ledger.{Ledger, LedgerImpl} +import io.iohk.ethereum.nodebuilder.{BlockchainConfigBuilder, ValidatorsBuilder} import io.iohk.ethereum.snappy.Config.{DualDB, SingleDB} import io.iohk.ethereum.snappy.Prerequisites._ import io.iohk.ethereum.utils.Config.DbConfig import io.iohk.ethereum.validators.Validators +import io.iohk.ethereum.vm.VM object Prerequisites { @@ -47,24 +48,22 @@ class Prerequisites(config: Config) { case SingleDB => None } - val sourceBlockchain: Blockchain = BlockchainImpl(sourceStorages.storages) - val targetBlockchain: Option[Blockchain] = targetStorages.map(ts => BlockchainImpl(ts.storages)) + val sourceBlockchain = BlockchainImpl(sourceStorages.storages) + val targetBlockchain = targetStorages.map(ts => BlockchainImpl(ts.storages)) - private val components = - new LedgerBuilder - with ValidatorsBuilder - with BlockchainConfigBuilder + val components = new ValidatorsBuilder with BlockchainConfigBuilder - val ledger: Ledger = components.ledger + val ledger: Ledger = targetBlockchain match { + case Some(tb) => new LedgerImpl(VM, tb, components.blockchainConfig) + case None => new LedgerImpl(VM, sourceBlockchain, components.blockchainConfig) + } val validators: Validators = components.validators - targetStorages.foreach { ts => + targetBlockchain.foreach { blockchain => val genesisLoader = new GenesisDataLoader( - ts.dataSource, - BlockchainImpl(ts.storages), - ArchivePruning, + blockchain, components.blockchainConfig, new DbConfig { val batchSize: Int = 1000 diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index 30affa05e9..e294f2a4f8 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -40,6 +40,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { case Right(receipts) => if (receipts == expectedReceipts) { targetBlockchain.foreach(_.save(block)) + targetBlockchain.foreach(_.save(block.header.hash, receipts)) } else { fail(s"Block $n did not execute correctly.\n$receipts did not equal $expectedReceipts") } @@ -52,11 +53,11 @@ class SnappyTest extends FreeSpec with Matchers with Logger { private def executeBlock(block: Block): Either[Any, Seq[Receipt]] = targetStorages match { case Some(storages) => - ledger.executeBlock(block, storages.storages, validators) + ledger.executeBlock(block, validators) case None => // this seems to discard failures, for better errors messages we might want to implement a different method (simulateBlock?) - val result = ledger.prepareBlock(block, sourceStorages.storages, validators) + val result = ledger.prepareBlock(block, validators) Right(result.blockResult.receipts) } From c751eca051553c53e03ba7f55e689b36672a18ce Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Wed, 20 Sep 2017 14:48:54 -0300 Subject: [PATCH 16/20] [EC-236] Ethereum tests integration --- .../ets/blockchain/BlockChainTestConfig.scala | 138 +++++++++++++++++- .../ets/blockchain/BlockchainSuite.scala | 4 +- .../ets/blockchain/ScenarioSetup.scala | 1 + 3 files changed, 137 insertions(+), 6 deletions(-) diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala index 3b8620be48..a095880812 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala @@ -1,8 +1,9 @@ package io.iohk.ethereum.ets.blockchain import akka.util.ByteString -import io.iohk.ethereum.domain.UInt256 -import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig} +import io.iohk.ethereum.domain.{Address, UInt256} +import io.iohk.ethereum.utils.{BlockchainConfig, DaoForkConfig, MonetaryPolicyConfig} +import org.spongycastle.util.encoders.Hex trait BlockChainTestConfig extends BlockchainConfig { @@ -19,8 +20,7 @@ trait BlockChainTestConfig extends BlockchainConfig { override val chainId: Byte = 0x3d.toByte override val customGenesisFileOpt: Option[String] = Some("test-genesis.json") override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, BigInt("5000000000000000000")) - override val daoForkBlockNumber: BigInt = Long.MaxValue - override val daoForkBlockHash: ByteString = ByteString("unused") + override val daoForkConfig: Option[DaoForkConfig] = None override val accountStartNonce: UInt256 = UInt256.Zero } @@ -45,4 +45,134 @@ class HomesteadToEIP150At5 extends BlockChainTestConfig { override val eip150BlockNumber = 5 override val homesteadBlockNumber = 0 } +class HomesteadToDaoAt5 extends BlockChainTestConfig { + override val homesteadBlockNumber = 0 + override val daoForkConfig: Option[DaoForkConfig] = Some( + new DaoForkConfig { + override val forkBlockNumber = 5 + override val forkBlockHash = ByteString(Hex.decode("f6d7ef1087b5fd94eada533cf8a563f78c3944a2f8ae850e80935d20dc3b7315")) + override val blockExtraData = Some(ByteString(Hex.decode("64616f2d686172642d666f726b"))) + override val range = 10 + override val refundContract = Some(Address("bf4ed7b27f1d666546e30d74d50d173d20bca754")) + override val drainList = Seq( + Address("d4fe7bc31cedb7bfb8a345f31e668033056b2728"), + Address("b3fb0e5aba0e20e5c49d252dfd30e102b171a425"), + Address("2c19c7f9ae8b751e37aeb2d93a699722395ae18f"), + Address("ecd135fa4f61a655311e86238c92adcd779555d2"), + Address("1975bd06d486162d5dc297798dfc41edd5d160a7"), + Address("a3acf3a1e16b1d7c315e23510fdd7847b48234f6"), + Address("319f70bab6845585f412ec7724b744fec6095c85"), + Address("06706dd3f2c9abf0a21ddcc6941d9b86f0596936"), + Address("5c8536898fbb74fc7445814902fd08422eac56d0"), + Address("6966ab0d485353095148a2155858910e0965b6f9"), + Address("779543a0491a837ca36ce8c635d6154e3c4911a6"), + Address("2a5ed960395e2a49b1c758cef4aa15213cfd874c"), + Address("5c6e67ccd5849c0d29219c4f95f1a7a93b3f5dc5"), + Address("9c50426be05db97f5d64fc54bf89eff947f0a321"), + Address("200450f06520bdd6c527622a273333384d870efb"), + Address("be8539bfe837b67d1282b2b1d61c3f723966f049"), + Address("6b0c4d41ba9ab8d8cfb5d379c69a612f2ced8ecb"), + Address("f1385fb24aad0cd7432824085e42aff90886fef5"), + Address("d1ac8b1ef1b69ff51d1d401a476e7e612414f091"), + Address("8163e7fb499e90f8544ea62bbf80d21cd26d9efd"), + Address("51e0ddd9998364a2eb38588679f0d2c42653e4a6"), + Address("627a0a960c079c21c34f7612d5d230e01b4ad4c7"), + Address("f0b1aa0eb660754448a7937c022e30aa692fe0c5"), + Address("24c4d950dfd4dd1902bbed3508144a54542bba94"), + Address("9f27daea7aca0aa0446220b98d028715e3bc803d"), + Address("a5dc5acd6a7968a4554d89d65e59b7fd3bff0f90"), + Address("d9aef3a1e38a39c16b31d1ace71bca8ef58d315b"), + Address("63ed5a272de2f6d968408b4acb9024f4cc208ebf"), + Address("6f6704e5a10332af6672e50b3d9754dc460dfa4d"), + Address("77ca7b50b6cd7e2f3fa008e24ab793fd56cb15f6"), + Address("492ea3bb0f3315521c31f273e565b868fc090f17"), + Address("0ff30d6de14a8224aa97b78aea5388d1c51c1f00"), + Address("9ea779f907f0b315b364b0cfc39a0fde5b02a416"), + Address("ceaeb481747ca6c540a000c1f3641f8cef161fa7"), + Address("cc34673c6c40e791051898567a1222daf90be287"), + Address("579a80d909f346fbfb1189493f521d7f48d52238"), + Address("e308bd1ac5fda103967359b2712dd89deffb7973"), + Address("4cb31628079fb14e4bc3cd5e30c2f7489b00960c"), + Address("ac1ecab32727358dba8962a0f3b261731aad9723"), + Address("4fd6ace747f06ece9c49699c7cabc62d02211f75"), + Address("440c59b325d2997a134c2c7c60a8c61611212bad"), + Address("4486a3d68fac6967006d7a517b889fd3f98c102b"), + Address("9c15b54878ba618f494b38f0ae7443db6af648ba"), + Address("27b137a85656544b1ccb5a0f2e561a5703c6a68f"), + Address("21c7fdb9ed8d291d79ffd82eb2c4356ec0d81241"), + Address("23b75c2f6791eef49c69684db4c6c1f93bf49a50"), + Address("1ca6abd14d30affe533b24d7a21bff4c2d5e1f3b"), + Address("b9637156d330c0d605a791f1c31ba5890582fe1c"), + Address("6131c42fa982e56929107413a9d526fd99405560"), + Address("1591fc0f688c81fbeb17f5426a162a7024d430c2"), + Address("542a9515200d14b68e934e9830d91645a980dd7a"), + Address("c4bbd073882dd2add2424cf47d35213405b01324"), + Address("782495b7b3355efb2833d56ecb34dc22ad7dfcc4"), + Address("58b95c9a9d5d26825e70a82b6adb139d3fd829eb"), + Address("3ba4d81db016dc2890c81f3acec2454bff5aada5"), + Address("b52042c8ca3f8aa246fa79c3feaa3d959347c0ab"), + Address("e4ae1efdfc53b73893af49113d8694a057b9c0d1"), + Address("3c02a7bc0391e86d91b7d144e61c2c01a25a79c5"), + Address("0737a6b837f97f46ebade41b9bc3e1c509c85c53"), + Address("97f43a37f595ab5dd318fb46e7a155eae057317a"), + Address("52c5317c848ba20c7504cb2c8052abd1fde29d03"), + Address("4863226780fe7c0356454236d3b1c8792785748d"), + Address("5d2b2e6fcbe3b11d26b525e085ff818dae332479"), + Address("5f9f3392e9f62f63b8eac0beb55541fc8627f42c"), + Address("057b56736d32b86616a10f619859c6cd6f59092a"), + Address("9aa008f65de0b923a2a4f02012ad034a5e2e2192"), + Address("304a554a310c7e546dfe434669c62820b7d83490"), + Address("914d1b8b43e92723e64fd0a06f5bdb8dd9b10c79"), + Address("4deb0033bb26bc534b197e61d19e0733e5679784"), + Address("07f5c1e1bc2c93e0402f23341973a0e043f7bf8a"), + Address("35a051a0010aba705c9008d7a7eff6fb88f6ea7b"), + Address("4fa802324e929786dbda3b8820dc7834e9134a2a"), + Address("9da397b9e80755301a3b32173283a91c0ef6c87e"), + Address("8d9edb3054ce5c5774a420ac37ebae0ac02343c6"), + Address("0101f3be8ebb4bbd39a2e3b9a3639d4259832fd9"), + Address("5dc28b15dffed94048d73806ce4b7a4612a1d48f"), + Address("bcf899e6c7d9d5a215ab1e3444c86806fa854c76"), + Address("12e626b0eebfe86a56d633b9864e389b45dcb260"), + Address("a2f1ccba9395d7fcb155bba8bc92db9bafaeade7"), + Address("ec8e57756626fdc07c63ad2eafbd28d08e7b0ca5"), + Address("d164b088bd9108b60d0ca3751da4bceb207b0782"), + Address("6231b6d0d5e77fe001c2a460bd9584fee60d409b"), + Address("1cba23d343a983e9b5cfd19496b9a9701ada385f"), + Address("a82f360a8d3455c5c41366975bde739c37bfeb8a"), + Address("9fcd2deaff372a39cc679d5c5e4de7bafb0b1339"), + Address("005f5cee7a43331d5a3d3eec71305925a62f34b6"), + Address("0e0da70933f4c7849fc0d203f5d1d43b9ae4532d"), + Address("d131637d5275fd1a68a3200f4ad25c71a2a9522e"), + Address("bc07118b9ac290e4622f5e77a0853539789effbe"), + Address("47e7aa56d6bdf3f36be34619660de61275420af8"), + Address("acd87e28b0c9d1254e868b81cba4cc20d9a32225"), + Address("adf80daec7ba8dcf15392f1ac611fff65d94f880"), + Address("5524c55fb03cf21f549444ccbecb664d0acad706"), + Address("40b803a9abce16f50f36a77ba41180eb90023925"), + Address("fe24cdd8648121a43a7c86d289be4dd2951ed49f"), + Address("17802f43a0137c506ba92291391a8a8f207f487d"), + Address("253488078a4edf4d6f42f113d1e62836a942cf1a"), + Address("86af3e9626fce1957c82e88cbf04ddf3a2ed7915"), + Address("b136707642a4ea12fb4bae820f03d2562ebff487"), + Address("dbe9b615a3ae8709af8b93336ce9b477e4ac0940"), + Address("f14c14075d6c4ed84b86798af0956deef67365b5"), + Address("ca544e5c4687d109611d0f8f928b53a25af72448"), + Address("aeeb8ff27288bdabc0fa5ebb731b6f409507516c"), + Address("cbb9d3703e651b0d496cdefb8b92c25aeb2171f7"), + Address("6d87578288b6cb5549d5076a207456a1f6a63dc0"), + Address("b2c6f0dfbb716ac562e2d85d6cb2f8d5ee87603e"), + Address("accc230e8a6e5be9160b8cdf2864dd2a001c28b6"), + Address("2b3455ec7fedf16e646268bf88846bd7a2319bb2"), + Address("4613f3bca5c44ea06337a9e439fbc6d42e501d0a"), + Address("d343b217de44030afaa275f54d31a9317c7f441e"), + Address("84ef4b2357079cd7a7c69fd7a37cd0609a679106"), + Address("da2fef9e4a3230988ff17df2165440f37e8b1708"), + Address("f4c64518ea10f995918a454158c6b61407ea345c"), + Address("7602b46df5390e432ef1c307d4f2c9ff6d65cc97"), + Address("bb9bc244d798123fde783fcc1c72d3bb8c189413"), + Address("807640a13483f8ac783c557fcdf27be11ea4ac7a") + ) + } + ) +} diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainSuite.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainSuite.scala index f980e45037..9ff857d90d 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainSuite.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainSuite.scala @@ -9,8 +9,8 @@ import org.scalatest._ class BlockchainSuite extends FreeSpec with Matchers with Logger { - val unsupportedNetworks = Set("Byzantium","Constantinople", "EIP158", "EIP158ToByzantiumAt5", "HomesteadToDaoAt5") - val supportedNetworks = Set("EIP150", "Frontier", "FrontierToHomesteadAt5", "Homestead", "HomesteadToEIP150At5") + val unsupportedNetworks = Set("Byzantium","Constantinople", "EIP158", "EIP158ToByzantiumAt5") + val supportedNetworks = Set("EIP150", "Frontier", "FrontierToHomesteadAt5", "Homestead", "HomesteadToEIP150At5", "HomesteadToDaoAt5") //Map of ignored tests, empty set of ignored names means cancellation of whole group val ignoredTests: Map[String, Set[String]] = Map( diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/ScenarioSetup.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/ScenarioSetup.scala index dc4a9748b6..e8dcf7186d 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/ScenarioSetup.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/ScenarioSetup.scala @@ -69,6 +69,7 @@ abstract class ScenarioSetup(scenario: BlockchainScenario) case "Homestead" => new HomesteadConfig case "FrontierToHomesteadAt5" => new FrontierToHomesteadAt5 case "HomesteadToEIP150At5" => new HomesteadToEIP150At5 + case "HomesteadToDaoAt5" => new HomesteadToDaoAt5 // Some default config, test will fail or be canceled case _ => new FrontierConfig From d8a691dac8e25f7d3f099b3620032ac7c162048c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20G=C4=85sior?= Date: Thu, 21 Sep 2017 11:21:25 +0200 Subject: [PATCH 17/20] Update LevelDB version --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index c3563c1620..da0e9af2d7 100644 --- a/build.sbt +++ b/build.sbt @@ -27,8 +27,8 @@ val dep = { "io.suzaku" %% "boopickle" % "1.2.6", "org.consensusresearch" %% "scrypto" % "1.2.0-RC3", "com.madgag.spongycastle" % "core" % "1.56.0.0", - "org.iq80.leveldb" % "leveldb" % "0.11", - "org.iq80.leveldb" % "leveldb-api" % "0.11", + "org.iq80.leveldb" % "leveldb" % "0.12", + "org.iq80.leveldb" % "leveldb-api" % "0.12", "org.scorexfoundation" %% "iodb" % "0.3.0", "ch.qos.logback" % "logback-classic" % "1.1.9", "org.scalatest" %% "scalatest" % "3.0.1" % "it,test", From 54875bcd34fac21f8fe4985abac8d9255a1a3779 Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Thu, 21 Sep 2017 08:26:01 -0300 Subject: [PATCH 18/20] [EC-236] Fix remove isDefined call --- .../scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala index ead7fd265e..d8e65392a3 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala @@ -69,9 +69,7 @@ object DumpChainApp extends App with NodeKeyBuilder with SecureRandomBuilder wit lazy val nodeStatusHolder = Agent(nodeStatus) - lazy val forkResolverOpt = - if (blockchainConfig.daoForkConfig.isEmpty) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig.daoForkConfig.get)) + lazy val forkResolverOpt = blockchainConfig.daoForkConfig.map(new ForkResolver.EtcForkResolver(_)) private val handshakerConfiguration: EtcHandshakerConfiguration = new EtcHandshakerConfiguration { From 75cf8764ca517516626170719ed7b59dcc19cd8d Mon Sep 17 00:00:00 2001 From: Radek Tkaczyk Date: Thu, 21 Sep 2017 15:55:59 +0200 Subject: [PATCH 19/20] sanppy test: review comments; readme; compilation in circle --- circle.yml | 3 +++ ...inTestConfig.scala => BlockchainTestConfig.scala} | 12 ++++++------ .../io/iohk/ethereum/blockchain/sync/FastSync.scala | 6 +++--- src/snappy/README.md | 9 +++++++++ src/snappy/resources/example.conf | 2 +- .../io/iohk/ethereum/snappy/Prerequisites.scala | 6 +++--- .../scala/io/iohk/ethereum/snappy/SnappyTest.scala | 5 +++-- 7 files changed, 28 insertions(+), 15 deletions(-) rename src/ets/scala/io/iohk/ethereum/ets/blockchain/{BlockChainTestConfig.scala => BlockchainTestConfig.scala} (82%) create mode 100644 src/snappy/README.md diff --git a/circle.yml b/circle.yml index 9022ec09a4..2f08e5aaa7 100644 --- a/circle.yml +++ b/circle.yml @@ -29,6 +29,9 @@ test: fi fi + # snappy test is not run on Circle - this is just to prevent compilation regression + - sbt snappy:compile + post: - mkdir -p $CIRCLE_ARTIFACTS/scala-2.12 - mv target/scala-2.12/coverage-report $CIRCLE_ARTIFACTS/scala-2.12/coverage-report diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala similarity index 82% rename from src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala rename to src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala index 3b8620be48..725a41c0c7 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockChainTestConfig.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala @@ -4,7 +4,7 @@ import akka.util.ByteString import io.iohk.ethereum.domain.UInt256 import io.iohk.ethereum.utils.{BlockchainConfig, MonetaryPolicyConfig} -trait BlockChainTestConfig extends BlockchainConfig { +trait BlockchainTestConfig extends BlockchainConfig { val frontierBlockNumber: BigInt = Long.MaxValue val eip160BlockNumber: BigInt = Long.MaxValue @@ -24,24 +24,24 @@ trait BlockChainTestConfig extends BlockchainConfig { override val accountStartNonce: UInt256 = UInt256.Zero } -class FrontierConfig extends BlockChainTestConfig { +class FrontierConfig extends BlockchainTestConfig { override val frontierBlockNumber = 0 } -class HomesteadConfig extends BlockChainTestConfig { +class HomesteadConfig extends BlockchainTestConfig { override val homesteadBlockNumber = 0 } -class Eip150Config extends BlockChainTestConfig { +class Eip150Config extends BlockchainTestConfig { // To keep difficulty calculation relevant, we need to have network order // frontier >> homestead >> eip150 override val homesteadBlockNumber: BigInt = -1 override val eip150BlockNumber = 0 } -class FrontierToHomesteadAt5 extends BlockChainTestConfig { +class FrontierToHomesteadAt5 extends BlockchainTestConfig { override val frontierBlockNumber = 0 override val homesteadBlockNumber = 5 } -class HomesteadToEIP150At5 extends BlockChainTestConfig { +class HomesteadToEIP150At5 extends BlockchainTestConfig { override val eip150BlockNumber = 5 override val homesteadBlockNumber = 0 } diff --git a/src/main/scala/io/iohk/ethereum/blockchain/sync/FastSync.scala b/src/main/scala/io/iohk/ethereum/blockchain/sync/FastSync.scala index 42f6c8f9e0..3ad599b5ea 100644 --- a/src/main/scala/io/iohk/ethereum/blockchain/sync/FastSync.scala +++ b/src/main/scala/io/iohk/ethereum/blockchain/sync/FastSync.scala @@ -454,7 +454,7 @@ class FastSync( .intersect(blockChainPeers) .take(maxConcurrentRequests - assignedHandlers.size) .toSeq.sortBy(_.ref.toString()) - .foreach(assignBlockChainWork) + .foreach(assignBlockchainWork) } } @@ -462,11 +462,11 @@ class FastSync( if (syncState.nonMptNodesQueue.nonEmpty || syncState.mptNodesQueue.nonEmpty) { requestNodes(peer) } else { - assignBlockChainWork(peer) + assignBlockchainWork(peer) } } - def assignBlockChainWork(peer: Peer): Unit = { + def assignBlockchainWork(peer: Peer): Unit = { if (syncState.receiptsQueue.nonEmpty) { requestReceipts(peer) } else if (syncState.blockBodiesQueue.nonEmpty) { diff --git a/src/snappy/README.md b/src/snappy/README.md new file mode 100644 index 0000000000..39c0475a3d --- /dev/null +++ b/src/snappy/README.md @@ -0,0 +1,9 @@ +`Ledger`/`EVM` regression test based on a DB snapshot from an actual syncing client. Goes through all the blocks, replaying the transactions and comparing results. + +To run: + +``` +sbt -Dconfig.file=path/to/config snappy:test +``` + +An example configuration file can be found at: `src/snappy/resources/example.conf`. Additionally a genesis JSON file may need to be provided (in `blockchain` config section). diff --git a/src/snappy/resources/example.conf b/src/snappy/resources/example.conf index a7656f24de..d7a93c9949 100644 --- a/src/snappy/resources/example.conf +++ b/src/snappy/resources/example.conf @@ -42,7 +42,7 @@ mantis.blockchain { dao-fork-block-number = "1920000" dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" account-start-nonce = "0" - chain-id = "3d" + chain-id = "0x3d" custom-genesis-file = null monetary-policy { first-era-block-reward = "5000000000000000000" diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala index 96b1469f06..216446811a 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/Prerequisites.scala @@ -35,11 +35,11 @@ class Prerequisites(config: Config) { } ) - val sourceStorages: Storages = new Storages { + private val sourceStorages: Storages = new Storages { override lazy val dataSource = levelDb(config.sourceDbPath) } - val targetStorages: Option[Storages] = config.mode match { + private val targetStorages: Option[Storages] = config.mode match { case DualDB => Some(new Storages { override lazy val dataSource = levelDb(config.targetDbPath) @@ -51,7 +51,7 @@ class Prerequisites(config: Config) { val sourceBlockchain = BlockchainImpl(sourceStorages.storages) val targetBlockchain = targetStorages.map(ts => BlockchainImpl(ts.storages)) - val components = new ValidatorsBuilder with BlockchainConfigBuilder + private val components = new ValidatorsBuilder with BlockchainConfigBuilder val ledger: Ledger = targetBlockchain match { diff --git a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala index e294f2a4f8..4d00b42d4b 100644 --- a/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala +++ b/src/snappy/scala/io/iohk/ethereum/snappy/SnappyTest.scala @@ -51,8 +51,8 @@ class SnappyTest extends FreeSpec with Matchers with Logger { } private def executeBlock(block: Block): Either[Any, Seq[Receipt]] = - targetStorages match { - case Some(storages) => + targetBlockchain match { + case Some(_) => ledger.executeBlock(block, validators) case None => @@ -61,6 +61,7 @@ class SnappyTest extends FreeSpec with Matchers with Logger { Right(result.blockResult.receipts) } + // TODO: replace with blockchain.getBestBlockNumber() - not implemented yet private def findHighestBlockNumber(blockchain: Blockchain, n: BigInt = 1000000, bottom: BigInt = 0, top: BigInt = -1): BigInt = { if (top - bottom == 1) n From d5fb4d70bf28d2120a88638f81ab3adba4bae062 Mon Sep 17 00:00:00 2001 From: Alan Verbner Date: Fri, 22 Sep 2017 09:02:59 -0300 Subject: [PATCH 20/20] [EC-236] Fix ets compilation issue --- .../io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala index bcb14d5911..a3a5649889 100644 --- a/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala +++ b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala @@ -45,7 +45,7 @@ class HomesteadToEIP150At5 extends BlockchainTestConfig { override val eip150BlockNumber = 5 override val homesteadBlockNumber = 0 } -class HomesteadToDaoAt5 extends BlockChainTestConfig { +class HomesteadToDaoAt5 extends BlockchainTestConfig { override val homesteadBlockNumber = 0 override val daoForkConfig: Option[DaoForkConfig] = Some( new DaoForkConfig {