Skip to content

Commit 2047259

Browse files
author
Michał Mrożek
authored
Merge pull request #782 from input-output-hk/etcm-301-fix-block-preparation
[ETCM-301] Fix block preparation
2 parents 33a24dc + 51b967a commit 2047259

38 files changed

+365
-204
lines changed

src/ets/scala/io/iohk/ethereum/ets/blockchain/ScenarioSetup.scala

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.iohk.ethereum.ets.blockchain
22

3+
import akka.util.ByteString
34
import java.util.concurrent.Executors
4-
55
import io.iohk.ethereum.consensus.ethash.EthashConsensus
66
import io.iohk.ethereum.consensus.ethash.validators.ValidatorsExecutor
77
import io.iohk.ethereum.consensus.{ConsensusConfig, FullConsensusConfig, TestConsensus, ethash}
@@ -13,10 +13,10 @@ import io.iohk.ethereum.domain._
1313
import io.iohk.ethereum.ets.common.AccountState
1414
import io.iohk.ethereum.ledger.Ledger.VMImpl
1515
import io.iohk.ethereum.ledger._
16+
import io.iohk.ethereum.mpt.MerklePatriciaTrie
1617
import io.iohk.ethereum.utils.BigIntExtensionMethods._
1718
import io.iohk.ethereum.utils.{BlockchainConfig, Config}
1819
import org.bouncycastle.util.encoders.Hex
19-
2020
import scala.concurrent.ExecutionContext
2121
import scala.util.{Failure, Success, Try}
2222

@@ -60,7 +60,13 @@ abstract class ScenarioSetup(_vm: VMImpl, scenario: BlockchainScenario) {
6060
val consensus: TestConsensus = ScenarioSetup.loadEthashConsensus(_vm, blockchain, blockchainConfig, validators)
6161

6262
val emptyWorld: InMemoryWorldStateProxy =
63-
blockchain.getWorldStateProxy(-1, UInt256.Zero, None, noEmptyAccounts = false, ethCompatibleStorage = true)
63+
blockchain.getWorldStateProxy(
64+
-1,
65+
UInt256.Zero,
66+
ByteString(MerklePatriciaTrie.EmptyRootHash),
67+
noEmptyAccounts = false,
68+
ethCompatibleStorage = true
69+
)
6470

6571
val ledger =
6672
new LedgerImpl(

src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ abstract class CommonFakePeer(peerName: String, fakePeerCustomConfig: FakePeerCu
223223
bl.getWorldStateProxy(
224224
blockNumber = block.number,
225225
accountStartNonce = blockchainConfig.accountStartNonce,
226-
stateRootHash = Some(block.header.stateRoot),
226+
stateRootHash = block.header.stateRoot,
227227
noEmptyAccounts = EvmConfig.forBlock(block.number, blockchainConfig).noEmptyAccounts,
228228
ethCompatibleStorage = blockchainConfig.ethCompatibleStorage
229229
)

src/it/scala/io/iohk/ethereum/sync/util/RegularSyncItSpecUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ object RegularSyncItSpecUtils {
133133
bl.getWorldStateProxy(
134134
blockNumber = block.number,
135135
accountStartNonce = blockchainConfig.accountStartNonce,
136-
stateRootHash = Some(block.header.stateRoot),
136+
stateRootHash = block.header.stateRoot,
137137
noEmptyAccounts = EvmConfig.forBlock(block.number, blockchainConfig).noEmptyAccounts,
138138
ethCompatibleStorage = blockchainConfig.ethCompatibleStorage
139139
)

src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,15 @@ class BlockchainMock(genesisHash: ByteString) extends Blockchain {
173173
override def getWorldStateProxy(
174174
blockNumber: BigInt,
175175
accountStartNonce: UInt256,
176-
stateRootHash: Option[ByteString],
176+
stateRootHash: ByteString,
177177
noEmptyAccounts: Boolean,
178178
ethCompatibleStorage: Boolean
179179
): InMemoryWorldStateProxy = ???
180180

181181
override def getReadOnlyWorldStateProxy(
182182
blockNumber: Option[BigInt],
183183
accountStartNonce: UInt256,
184-
stateRootHash: Option[ByteString],
184+
stateRootHash: ByteString,
185185
noEmptyAccounts: Boolean,
186186
ethCompatibleStorage: Boolean
187187
): InMemoryWorldStateProxy = ???

src/main/scala/io/iohk/ethereum/consensus/blocks/BlockGenerator.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.iohk.ethereum.consensus.blocks
22

33
import io.iohk.ethereum.domain.{Address, Block, SignedTransaction}
4+
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
45

56
/**
67
* We use a `BlockGenerator` to create the next block.
@@ -39,8 +40,9 @@ trait BlockGenerator {
3940
parent: Block,
4041
transactions: Seq[SignedTransaction],
4142
beneficiary: Address,
42-
x: X
43-
): PendingBlock
43+
x: X,
44+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy]
45+
): PendingBlockAndState
4446
}
4547

4648
/**

src/main/scala/io/iohk/ethereum/consensus/blocks/BlockGeneratorSkeleton.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.iohk.ethereum.consensus.blocks
22

33
import java.util.concurrent.atomic.AtomicReference
4-
54
import akka.util.ByteString
65
import io.iohk.ethereum.consensus.ConsensusConfig
76
import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator
@@ -13,8 +12,8 @@ import io.iohk.ethereum.db.storage.StateStorage
1312
import io.iohk.ethereum.domain._
1413
import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields._
1514
import io.iohk.ethereum.consensus.ethash.blocks.OmmersSeqEnc
16-
import io.iohk.ethereum.ledger.Ledger.{BlockPreparationResult, BlockResult}
17-
import io.iohk.ethereum.ledger.{BlockPreparator, BloomFilter}
15+
import io.iohk.ethereum.ledger.Ledger.{BlockResult, PreparedBlock}
16+
import io.iohk.ethereum.ledger.{BlockPreparator, BloomFilter, InMemoryWorldStateProxy}
1817
import io.iohk.ethereum.mpt.{ByteArraySerializable, MerklePatriciaTrie}
1918
import io.iohk.ethereum.utils.BlockchainConfig
2019
import io.iohk.ethereum.utils.ByteUtils.or
@@ -90,7 +89,8 @@ abstract class BlockGeneratorSkeleton(
9089
beneficiary: Address,
9190
blockNumber: BigInt,
9291
blockPreparator: BlockPreparator,
93-
x: X
92+
x: X,
93+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy]
9494
): PendingBlockAndState = {
9595

9696
val blockTimestamp = blockTimestampProvider.getEpochSecond
@@ -99,8 +99,8 @@ abstract class BlockGeneratorSkeleton(
9999
val body = newBlockBody(transactionsForBlock, x)
100100
val block = Block(header, body)
101101

102-
val prepared = blockPreparator.prepareBlock(block) match {
103-
case BlockPreparationResult(prepareBlock, BlockResult(_, gasUsed, receipts), stateRoot, updatedWorld) =>
102+
blockPreparator.prepareBlock(block, parent.header, initialWorldStateBeforeExecution) match {
103+
case PreparedBlock(prepareBlock, BlockResult(_, gasUsed, receipts), stateRoot, updatedWorld) =>
104104
val receiptsLogs: Seq[Array[Byte]] =
105105
BloomFilter.EmptyBloomFilter.toArray +: receipts.map(_.logsBloomFilter.toArray)
106106
val bloomFilter = ByteString(or(receiptsLogs: _*))
@@ -122,7 +122,6 @@ abstract class BlockGeneratorSkeleton(
122122
updatedWorld
123123
)
124124
}
125-
prepared
126125
}
127126

128127
protected def prepareTransactions(

src/main/scala/io/iohk/ethereum/consensus/blocks/NoOmmersBlockGenerator.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.iohk.ethereum.consensus.blocks
33
import io.iohk.ethereum.consensus.ConsensusConfig
44
import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator
55
import io.iohk.ethereum.domain._
6-
import io.iohk.ethereum.ledger.BlockPreparator
6+
import io.iohk.ethereum.ledger.{BlockPreparator, InMemoryWorldStateProxy}
77
import io.iohk.ethereum.utils.BlockchainConfig
88

99
abstract class NoOmmersBlockGenerator(
@@ -42,15 +42,17 @@ abstract class NoOmmersBlockGenerator(
4242
parent: Block,
4343
transactions: Seq[SignedTransaction],
4444
beneficiary: Address,
45-
x: Nil.type
46-
): PendingBlock = {
45+
x: Nil.type,
46+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy]
47+
): PendingBlockAndState = {
4748

4849
val pHeader = parent.header
4950
val blockNumber = pHeader.number + 1
5051

51-
val prepared = prepareBlock(parent, transactions, beneficiary, blockNumber, blockPreparator, x)
52+
val prepared =
53+
prepareBlock(parent, transactions, beneficiary, blockNumber, blockPreparator, x, initialWorldStateBeforeExecution)
5254
cache.updateAndGet((t: List[PendingBlockAndState]) => (prepared :: t).take(blockCacheSize))
5355

54-
prepared.pendingBlock
56+
prepared
5557
}
5658
}

src/main/scala/io/iohk/ethereum/consensus/ethash/EthashBlockCreator.scala

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package io.iohk.ethereum.consensus.ethash
22

33
import akka.actor.ActorRef
44
import akka.pattern.ask
5-
import akka.util.{Timeout, ByteString}
6-
import io.iohk.ethereum.consensus.blocks.PendingBlock
5+
import akka.util.{ByteString, Timeout}
6+
import io.iohk.ethereum.consensus.blocks.PendingBlockAndState
77
import io.iohk.ethereum.consensus.ethash.blocks.EthashBlockGenerator
88
import io.iohk.ethereum.domain.{Address, Block}
9+
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
910
import io.iohk.ethereum.ommers.OmmersPool
1011
import io.iohk.ethereum.transactions.PendingTransactionsManager.PendingTransactionsResponse
1112
import scala.concurrent.Future
@@ -26,13 +27,21 @@ class EthashBlockCreator(
2627
private lazy val blockGenerator: EthashBlockGenerator = consensus.blockGenerator
2728
lazy val blockchainConfig = consensus.blockchainConfig
2829

29-
def getBlockForMining(parentBlock: Block, withTransactions: Boolean = true): Future[PendingBlock] = {
30+
def getBlockForMining(
31+
parentBlock: Block,
32+
withTransactions: Boolean = true,
33+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy] = None
34+
): Future[PendingBlockAndState] = {
3035
val transactions =
3136
if (withTransactions) getTransactionsFromPool else Future.successful(PendingTransactionsResponse(Nil))
32-
getOmmersFromPool(parentBlock.hash).zip(transactions).flatMap { case (ommers, pendingTxs) =>
33-
val pendingBlock = blockGenerator
34-
.generateBlock(parentBlock, pendingTxs.pendingTransactions.map(_.stx.tx), coinbase, ommers.headers)
35-
Future.successful(pendingBlock)
37+
getOmmersFromPool(parentBlock.hash).zip(transactions).map { case (ommers, pendingTxs) =>
38+
blockGenerator.generateBlock(
39+
parentBlock,
40+
pendingTxs.pendingTransactions.map(_.stx.tx),
41+
coinbase,
42+
ommers.headers,
43+
initialWorldStateBeforeExecution
44+
)
3645
}
3746
}
3847

src/main/scala/io/iohk/ethereum/consensus/ethash/EthashMiner.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ package io.iohk.ethereum.consensus
22
package ethash
33

44
import java.io.{File, FileInputStream, FileOutputStream}
5-
65
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
76
import akka.util.ByteString
87
import io.iohk.ethereum.blockchain.sync.SyncProtocol
9-
import io.iohk.ethereum.consensus.blocks.PendingBlock
8+
import io.iohk.ethereum.consensus.blocks.{PendingBlock, PendingBlockAndState}
109
import io.iohk.ethereum.consensus.ethash.EthashUtils.ProofOfWork
1110
import io.iohk.ethereum.consensus.ethash.MinerProtocol.{StartMining, StopMining}
1211
import io.iohk.ethereum.crypto
@@ -17,7 +16,6 @@ import io.iohk.ethereum.nodebuilder.Node
1716
import io.iohk.ethereum.utils.BigIntExtensionMethods._
1817
import io.iohk.ethereum.utils.{ByteStringUtils, ByteUtils}
1918
import org.bouncycastle.util.encoders.Hex
20-
2119
import scala.concurrent.ExecutionContext.Implicits.global
2220
import scala.concurrent.duration._
2321
import scala.util.{Failure, Random, Success, Try}
@@ -80,7 +78,7 @@ class EthashMiner(
8078
}
8179

8280
blockCreator.getBlockForMining(parentBlock) onComplete {
83-
case Success(PendingBlock(block, _)) =>
81+
case Success(PendingBlockAndState(PendingBlock(block, _), _)) =>
8482
val headerHash = crypto.kec256(BlockHeader.getEncodedWithoutNonce(block.header))
8583
val startTime = System.nanoTime()
8684
val mineResult =

src/main/scala/io/iohk/ethereum/consensus/ethash/MockedMiner.scala

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ package io.iohk.ethereum.consensus.ethash
33
import akka.actor.Status.Failure
44
import akka.actor.{Actor, ActorLogging, ActorRef, Props}
55
import io.iohk.ethereum.blockchain.sync.SyncProtocol
6-
import io.iohk.ethereum.consensus.blocks.PendingBlock
6+
import io.iohk.ethereum.consensus.blocks.PendingBlockAndState
77
import io.iohk.ethereum.consensus.ethash.MinerProtocol.{StartMining, StopMining}
88
import io.iohk.ethereum.consensus.ethash.MinerResponses.{MinerIsWorking, MiningError, MiningOrdered}
99
import io.iohk.ethereum.consensus.ethash.MockedMiner.MineBlock
1010
import io.iohk.ethereum.consensus.ethash.MockedMinerProtocol.MineBlocks
1111
import io.iohk.ethereum.consensus.wrongConsensusArgument
1212
import io.iohk.ethereum.domain.{Block, Blockchain}
13+
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
1314
import io.iohk.ethereum.nodebuilder.Node
1415
import io.iohk.ethereum.utils.ByteStringUtils
15-
1616
import scala.concurrent.duration._
1717

1818
class MockedMiner(
@@ -37,39 +37,41 @@ class MockedMiner(
3737
mineBlocks.parentBlock match {
3838
case Some(parentHash) =>
3939
blockchain.getBlockByHash(parentHash) match {
40-
case Some(parentBlock) =>
41-
self ! MineBlock
42-
sender() ! MiningOrdered
43-
context.become(working(mineBlocks.numBlocks, mineBlocks.withTransactions, parentBlock))
40+
case Some(parentBlock) => startMiningBlocks(mineBlocks, parentBlock)
4441
case None =>
4542
val error = s"Unable to get parent block with hash ${ByteStringUtils.hash2string(parentHash)} for mining"
4643
sender() ! MiningError(error)
4744
}
4845
case None =>
4946
val parentBlock = blockchain.getBestBlock()
50-
self ! MineBlock
51-
sender() ! MiningOrdered
52-
context.become(working(mineBlocks.numBlocks, mineBlocks.withTransactions, parentBlock))
47+
startMiningBlocks(mineBlocks, parentBlock)
5348
}
5449
}
5550

51+
private def startMiningBlocks(mineBlocks: MineBlocks, parentBlock: Block) = {
52+
self ! MineBlock
53+
sender() ! MiningOrdered
54+
context.become(working(mineBlocks.numBlocks, mineBlocks.withTransactions, parentBlock, None))
55+
}
56+
5657
def working(
5758
numBlocks: Int,
5859
withTransactions: Boolean,
59-
parentBlock: Block
60+
parentBlock: Block,
61+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy]
6062
): Receive = {
6163
case _: MineBlocks =>
6264
sender() ! MinerIsWorking
6365

6466
case MineBlock =>
6567
if (numBlocks > 0) {
66-
blockCreator.getBlockForMining(parentBlock, withTransactions) pipeTo self
68+
blockCreator.getBlockForMining(parentBlock, withTransactions, initialWorldStateBeforeExecution) pipeTo self
6769
} else {
6870
log.info(s"Mining all mocked blocks successful")
6971
context.become(waiting())
7072
}
7173

72-
case pendingBlock: PendingBlock =>
74+
case PendingBlockAndState(pendingBlock, state) =>
7375
val minedBlock = pendingBlock.block
7476
log.info(
7577
s"Mining mocked block {} successful. Included transactions: {}",
@@ -79,7 +81,7 @@ class MockedMiner(
7981
syncEventListener ! SyncProtocol.MinedBlock(minedBlock)
8082
// because of using seconds to calculate block timestamp, we can't mine blocks faster than one block per second
8183
context.system.scheduler.scheduleOnce(1.second, self, MineBlock)
82-
context.become(working(numBlocks - 1, withTransactions, minedBlock))
84+
context.become(working(numBlocks - 1, withTransactions, minedBlock, Some(state)))
8385

8486
case Failure(t) =>
8587
log.error(t, "Unable to get block for mining")

src/main/scala/io/iohk/ethereum/consensus/ethash/blocks/EthashBlockGenerator.scala

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
package io.iohk.ethereum.consensus.ethash.blocks
22

33
import java.util.function.UnaryOperator
4-
54
import akka.util.ByteString
65
import io.iohk.ethereum.consensus.ConsensusConfig
76
import io.iohk.ethereum.consensus.blocks._
87
import io.iohk.ethereum.consensus.difficulty.DifficultyCalculator
98
import io.iohk.ethereum.consensus.ethash.validators.ValidatorsExecutor
109
import io.iohk.ethereum.crypto.kec256
1110
import io.iohk.ethereum.domain._
12-
import io.iohk.ethereum.ledger.BlockPreparator
11+
import io.iohk.ethereum.ledger.{BlockPreparator, InMemoryWorldStateProxy}
1312
import io.iohk.ethereum.utils.BlockchainConfig
1413

1514
/** Internal API, used for testing (especially mocks) */
@@ -72,8 +71,9 @@ class EthashBlockGeneratorImpl(
7271
parent: Block,
7372
transactions: Seq[SignedTransaction],
7473
beneficiary: Address,
75-
x: Ommers
76-
): PendingBlock = {
74+
x: Ommers,
75+
initialWorldStateBeforeExecution: Option[InMemoryWorldStateProxy]
76+
): PendingBlockAndState = {
7777
val pHeader = parent.header
7878
val blockNumber = pHeader.number + 1
7979
val parentHash = pHeader.hash
@@ -83,13 +83,21 @@ class EthashBlockGeneratorImpl(
8383
case Right(_) => x
8484
}
8585

86-
val prepared = prepareBlock(parent, transactions, beneficiary, blockNumber, blockPreparator, ommers)
86+
val prepared = prepareBlock(
87+
parent,
88+
transactions,
89+
beneficiary,
90+
blockNumber,
91+
blockPreparator,
92+
ommers,
93+
initialWorldStateBeforeExecution
94+
)
8795

8896
cache.updateAndGet { t: List[PendingBlockAndState] =>
8997
(prepared :: t).take(blockCacheSize)
9098
}
9199

92-
prepared.pendingBlock
100+
prepared
93101
}
94102

95103
def withBlockTimestampProvider(blockTimestampProvider: BlockTimestampProvider): EthashBlockGeneratorImpl =

0 commit comments

Comments
 (0)