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/BlockchainTestConfig.scala b/src/ets/scala/io/iohk/ethereum/ets/blockchain/BlockchainTestConfig.scala index 725a41c0c7..a3a5649889 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/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 diff --git a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala index a556068128..19bb53d4ae 100644 --- a/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala +++ b/src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala @@ -1,10 +1,9 @@ package io.iohk.ethereum.txExecTest -import akka.util.ByteString 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} @@ -23,11 +22,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 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 e46959175e..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.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + lazy val forkResolverOpt = blockchainConfig.daoForkConfig.map(new ForkResolver.EtcForkResolver(_)) private val handshakerConfiguration: EtcHandshakerConfiguration = new EtcHandshakerConfiguration { diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf index 3b81a58478..dcb51528e0 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 { @@ -232,12 +235,27 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md difficulty-bomb-continue-block-number = "5000000" - # 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/ledger/InMemoryWorldStateProxy.scala b/src/main/scala/io/iohk/ethereum/ledger/InMemoryWorldStateProxy.scala index a86d9eb41a..4db1ff1328 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 92222700aa..7fe1547144 100644 --- a/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala +++ b/src/main/scala/io/iohk/ethereum/ledger/Ledger.scala @@ -5,7 +5,7 @@ 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._ @@ -76,8 +76,13 @@ 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 = 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, 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}") @@ -372,6 +377,28 @@ 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 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 drainDaoForkAccounts(worldState: InMemoryWorldStateProxy, daoForkConfig: DaoForkConfig): InMemoryWorldStateProxy = { + + 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 + } + + + } + } 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 158856e73f..8c89d2b255 100644 --- a/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala +++ b/src/main/scala/io/iohk/ethereum/mining/BlockGenerator.scala @@ -88,24 +88,28 @@ 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) = { + import blockchainConfig.daoForkConfig + + 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 = daoForkConfig.flatMap(daoForkConfig => daoForkConfig.getExtraData(blockNumber)).getOrElse(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/network/ForkResolver.scala b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala index 512f3ecd3c..fcc828d3b0 100644 --- a/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala +++ b/src/main/scala/io/iohk/ethereum/network/ForkResolver.scala @@ -1,7 +1,7 @@ package io.iohk.ethereum.network import io.iohk.ethereum.domain.BlockHeader -import io.iohk.ethereum.utils.BlockchainConfig +import io.iohk.ethereum.utils.DaoForkConfig trait ForkResolver { type Fork <: ForkResolver.Fork @@ -15,15 +15,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.forkBlockNumber override def recognizeFork(blockHeader: BlockHeader): Fork = { - if (blockHeader.hash == blockchainConfig.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 1174e53521..3af4009ecd 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.customGenesisFileOpt.isDefined) None - else Some(new ForkResolver.EtcForkResolver(blockchainConfig)) + 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 9143e76ee7..e3d0148a10 100644 --- a/src/main/scala/io/iohk/ethereum/utils/Config.scala +++ b/src/main/scala/io/iohk/ethereum/utils/Config.scala @@ -12,8 +12,10 @@ 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._ import scala.concurrent.duration._ import scala.util.Try @@ -222,6 +224,7 @@ trait MiningConfig { val coinbase: Address val activeTimeout: FiniteDuration val ommerPoolQueryTimeout: FiniteDuration + val headerExtraData: ByteString } object MiningConfig { @@ -234,10 +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(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 @@ -250,8 +296,8 @@ trait BlockchainConfig { val customGenesisFileOpt: Option[String] - val daoForkBlockNumber: BigInt - val daoForkBlockHash: ByteString + val daoForkConfig: Option[DaoForkConfig] + val accountStartNonce: UInt256 val chainId: Byte @@ -259,7 +305,9 @@ trait BlockchainConfig { val monetaryPolicyConfig: MonetaryPolicyConfig } + object BlockchainConfig { + def apply(etcClientConfig: TypesafeConfig): BlockchainConfig = { val blockchainConfig = etcClientConfig.getConfig("blockchain") @@ -274,8 +322,7 @@ object BlockchainConfig { override val customGenesisFileOpt: Option[String] = Try(blockchainConfig.getString("custom-genesis-file")).toOption - 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 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 dac670007a..62f46308e0 100644 --- a/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala +++ b/src/main/scala/io/iohk/ethereum/validators/BlockHeaderValidator.scala @@ -3,24 +3,26 @@ package io.iohk.ethereum.validators import akka.util.ByteString import io.iohk.ethereum.crypto.{kec256, kec512} import io.iohk.ethereum.domain.{BlockHeader, Blockchain, DifficultyCalculator} -import io.iohk.ethereum.utils.BlockchainConfig -import org.spongycastle.util.encoders.Hex +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/). * @@ -59,9 +61,25 @@ 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] = { + + 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._ + daoForkConfig.map(c => validateDaoForkExtraData(blockHeader, c)).getOrElse(Right(blockHeader)) + } else { + Left(HeaderExtraDataError) + } + } /** * Validates [[io.iohk.ethereum.domain.BlockHeader.unixTimestamp]] is greater than the one of its parent @@ -178,6 +196,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/resources/application.conf b/src/test/resources/application.conf index 44534b83de..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,31 @@ 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 { + header-extra-data = "grothendieck" } sync { diff --git a/src/test/scala/io/iohk/ethereum/Fixtures.scala b/src/test/scala/io/iohk/ethereum/Fixtures.scala index 0b439a3de8..99b24b77b6 100644 --- a/src/test/scala/io/iohk/ethereum/Fixtures.scala +++ b/src/test/scala/io/iohk/ethereum/Fixtures.scala @@ -230,6 +230,115 @@ 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 = 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")), + 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/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 dd0fa087df..c6636618c3 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/jsonrpc/PersonalServiceSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala index d82921392d..30c3bbf02b 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/PersonalServiceSpec.scala @@ -11,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 @@ -349,10 +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 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 a059cf45e2..934443c486 100644 --- a/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala @@ -6,18 +6,15 @@ 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.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, 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 -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 +23,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 +754,43 @@ 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 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 + supportDaoForkConfig.drainList.foreach { addr => + val daoAccountsFakeBalance = UInt256(1000) + (worldState.getAccount _).expects(addr).returning(Some(Account(nonce = 1, balance = daoAccountsFakeBalance))) + (worldState.transfer _).expects(addr, supportDaoForkConfig.refundContract.get, daoAccountsFakeBalance).returning(worldState) + } + + 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 + (new Mocks.MockValidatorsAlwaysSucceed).signedTransactionValidator + ) + } + + 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 + supportDaoForkConfig.drainList.foreach { addr => + val daoAccountsFakeBalance = UInt256(1000) + (worldState.transfer _).expects(*, *, *).never() + } + + 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 + (new Mocks.MockValidatorsAlwaysSucceed).signedTransactionValidator + ) + } + trait TestSetup extends SecureRandomBuilder with EphemBlockchainTestSetup { val originKeyPair: AsymmetricCipherKeyPair = generateKeyPair(secureRandom) val receiverKeyPair: AsymmetricCipherKeyPair = generateKeyPair(secureRandom) @@ -849,4 +884,41 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers { val validStxSignedByOrigin: SignedTransaction = SignedTransaction.sign(validTx, originKeyPair, Some(blockchainConfig.chainId)) } + 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 + 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: Option[DaoForkConfig] = Some(supportDaoForkConfig) + override val customGenesisFileOpt: Option[String] = None + override val eip106BlockNumber = Long.MaxValue + } + + (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) + } + } diff --git a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala index 61024a7383..fc50dedcf9 100644 --- a/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/mining/BlockGeneratorSpec.scala @@ -6,25 +6,21 @@ 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} +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.db.components.Storages.PruningModeComponent -import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode} 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 { @@ -41,6 +37,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 +53,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 +71,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 +95,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 { @@ -111,11 +111,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 daoForkConfig: Option[DaoForkConfig] = None } val generalTx = SignedTransaction.sign(transaction, keyPair, None) @@ -135,6 +134,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 +154,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 +174,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 +207,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 +230,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 { @@ -261,11 +265,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 daoForkConfig: Option[DaoForkConfig] = None } lazy val ledger = new LedgerImpl(VM, blockchain, blockchainConfig) @@ -287,6 +290,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/network/EtcPeerManagerSpec.scala b/src/test/scala/io/iohk/ethereum/network/EtcPeerManagerSpec.scala index 1682c76bd0..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) + 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 3b6c7e3185..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,11 +5,8 @@ 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.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 @@ -24,6 +21,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 @@ -145,9 +143,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 @@ -161,10 +156,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: 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)) + 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 0cedb24fe6..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)) + 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/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() diff --git a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala index 4e13f3b6a6..88798c9532 100644 --- a/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala +++ b/src/test/scala/io/iohk/ethereum/validators/BlockHeaderValidatorSpec.scala @@ -1,40 +1,26 @@ 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.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 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 daoForkBlockNumber: BigInt = Long.MaxValue - override val eip155BlockNumber: BigInt = Long.MaxValue - override val eip160BlockNumber: BigInt = Long.MaxValue - override val eip150BlockNumber: BigInt = Long.MaxValue - override val eip106BlockNumber: BigInt = 20 - 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 - } + val blockchainConfig = createBlockchainConfig() val blockHeaderValidator = new BlockHeaderValidatorImpl(blockchainConfig) val difficultyCalculator = new DifficultyCalculator(blockchainConfig) @@ -48,14 +34,39 @@ 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)) } } + it should "validate DAO block (extra data)" in { + import Fixtures.Blocks._ + val cases = Table( + ("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), + (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, supportsDaoFork, valid ) => + val blockHeaderValidator = new BlockHeaderValidatorImpl(createBlockchainConfig(supportsDaoFork)) + 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) @@ -87,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) @@ -235,4 +246,89 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck nonce = ByteString(Hex.decode("3fc7bc671f7cee70")) ) + 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: 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 + override val eip160BlockNumber: BigInt = Long.MaxValue + override val eip150BlockNumber: BigInt = Long.MaxValue + override val eip106BlockNumber: BigInt = 0 + 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")) + ) + + } diff --git a/src/universal/conf/blockchain.conf b/src/universal/conf/blockchain.conf index b2185f0e07..1ad198e40f 100644 --- a/src/universal/conf/blockchain.conf +++ b/src/universal/conf/blockchain.conf @@ -33,12 +33,28 @@ mantis { # Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md # difficulty-bomb-continue-block-number = "5000000" - # 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 { - # The hash of the accepted DAO fork block - # dao-fork-block-hash = "94365e3a8c0b35089c1d1195081fe7489b528a84b22199c916180db8b28ade7f" + # 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 2ff4555919..2cc9420611 100644 --- a/src/universal/conf/morden.conf +++ b/src/universal/conf/morden.conf @@ -1,32 +1,31 @@ -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" + + dao = null + + account-start-nonce = "1048576" + + chain-id = "3e" + + custom-genesis-file = "conf/morden.json" + } +}