Skip to content

[ECTM-212] Fix block preparation (taking into account EIP-161) #741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 59 additions & 40 deletions src/main/scala/io/iohk/ethereum/domain/Blockchain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,21 @@ trait Blockchain {

def genesisBlock: Block = getBlockByNumber(0).get

def getWorldStateProxy(blockNumber: BigInt,
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean): WS

def getReadOnlyWorldStateProxy(blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean): WS
def getWorldStateProxy(
blockNumber: BigInt,
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): WS

def getReadOnlyWorldStateProxy(
blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): WS

def getStateStorage: StateStorage
}
Expand Down Expand Up @@ -259,7 +263,11 @@ class BlockchainImpl(
mpt.get(address)
}

override def getAccountStorageAt(rootHash: ByteString, position: BigInt, ethCompatibleStorage: Boolean): ByteString = {
override def getAccountStorageAt(
rootHash: ByteString,
position: BigInt,
ethCompatibleStorage: Boolean
): ByteString = {
val storage = stateStorage.getBackingStorage(0)
val mpt =
if (ethCompatibleStorage) domain.EthereumUInt256Mpt.storageMpt(rootHash, storage)
Expand All @@ -268,7 +276,8 @@ class BlockchainImpl(
}

private def persistBestBlocksData(): Unit = {
appStateStorage.putBestBlockNumber(getBestBlockNumber())
appStateStorage
.putBestBlockNumber(getBestBlockNumber())
.and(appStateStorage.putLatestCheckpointBlockNumber(getLatestCheckpointBlockNumber()))
.commit()
}
Expand Down Expand Up @@ -298,7 +307,8 @@ class BlockchainImpl(
override def getMptNodeByHash(hash: ByteString): Option[MptNode] =
stateStorage.getNode(hash)

override def getTransactionLocation(txHash: ByteString): Option[TransactionLocation] = transactionMappingStorage.get(txHash)
override def getTransactionLocation(txHash: ByteString): Option[TransactionLocation] =
transactionMappingStorage.get(txHash)

override def storeBlockBody(blockHash: ByteString, blockBody: BlockBody): DataSourceBatchUpdate = {
blockBodiesStorage.put(blockHash, blockBody).and(saveTxsLocations(blockHash, blockBody))
Expand Down Expand Up @@ -351,14 +361,15 @@ class BlockchainImpl(
val bestBlocks = bestKnownBlockAndLatestCheckpoint.get()
// as we are decreasing block numbers in memory more often than in storage,
// we can't use here getBestBlockNumber / getLatestCheckpointBlockNumber
val bestBlockNumber = if(bestBlocks.bestBlockNumber != 0) bestBlocks.bestBlockNumber else appStateStorage.getBestBlockNumber()
val bestBlockNumber =
if (bestBlocks.bestBlockNumber != 0) bestBlocks.bestBlockNumber else appStateStorage.getBestBlockNumber()
val latestCheckpointNumber = {
if(bestBlocks.latestCheckpointNumber != 0) bestBlocks.latestCheckpointNumber
if (bestBlocks.latestCheckpointNumber != 0) bestBlocks.latestCheckpointNumber
else appStateStorage.getLatestCheckpointBlockNumber()
}

val blockNumberMappingUpdates = {
maybeBlockHeader.fold(blockNumberMappingStorage.emptyBatchUpdate)( h =>
maybeBlockHeader.fold(blockNumberMappingStorage.emptyBatchUpdate)(h =>
if (getHashByBlockNumber(h.number).contains(blockHash))
removeBlockNumberMapping(h.number)
else blockNumberMappingStorage.emptyBatchUpdate
Expand All @@ -369,19 +380,22 @@ class BlockchainImpl(
case Some(header) =>
if (header.hasCheckpoint && header.number == latestCheckpointNumber) {
val prev = findPreviousCheckpointBlockNumber(header.number, header.number)
prev.map { num =>
(appStateStorage.putLatestCheckpointBlockNumber(num), Some(num))
}.getOrElse {
(appStateStorage.removeLatestCheckpointBlockNumber(), Some(0))
}
prev
.map { num =>
(appStateStorage.putLatestCheckpointBlockNumber(num), Some(num))
}
.getOrElse {
(appStateStorage.removeLatestCheckpointBlockNumber(), Some(0))
}
} else (appStateStorage.emptyBatchUpdate, None)
case None =>
(appStateStorage.emptyBatchUpdate, None)
}

val newBestBlockNumber: BigInt = if(bestBlockNumber >= 1) bestBlockNumber - 1 else 0
val newBestBlockNumber: BigInt = if (bestBlockNumber >= 1) bestBlockNumber - 1 else 0

blockHeadersStorage.remove(blockHash)
blockHeadersStorage
.remove(blockHash)
.and(blockBodiesStorage.remove(blockHash))
.and(totalDifficultyStorage.remove(blockHash))
.and(receiptStorage.remove(blockHash))
Expand All @@ -394,7 +408,8 @@ class BlockchainImpl(

maybeBlockHeader.foreach { h =>
if (withState) {
val bestBlocksUpdates = appStateStorage.putBestBlockNumber(newBestBlockNumber)
val bestBlocksUpdates = appStateStorage
.putBestBlockNumber(newBestBlockNumber)
.and(checkpointUpdates)
stateStorage.onBlockRollback(h.number, bestBlockNumber)(() => bestBlocksUpdates.commit())
}
Expand All @@ -408,8 +423,8 @@ class BlockchainImpl(
*/
@tailrec
private def findPreviousCheckpointBlockNumber(
blockNumberToCheck: BigInt,
latestCheckpointBlockNumber: BigInt
blockNumberToCheck: BigInt,
latestCheckpointBlockNumber: BigInt
): Option[BigInt] = {
if (blockNumberToCheck > 0) {
val maybePreviousCheckpointBlockNumber = for {
Expand All @@ -432,19 +447,21 @@ class BlockchainImpl(
}

private def removeTxsLocations(stxs: Seq[SignedTransaction]): DataSourceBatchUpdate = {
stxs.map(_.hash).foldLeft(transactionMappingStorage.emptyBatchUpdate) {
case (updates, hash) => updates.and(transactionMappingStorage.remove(hash))
stxs.map(_.hash).foldLeft(transactionMappingStorage.emptyBatchUpdate) { case (updates, hash) =>
updates.and(transactionMappingStorage.remove(hash))
}
}

override type S = InMemoryWorldStateProxyStorage
override type WS = InMemoryWorldStateProxy

override def getWorldStateProxy(blockNumber: BigInt,
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean): InMemoryWorldStateProxy =
override def getWorldStateProxy(
blockNumber: BigInt,
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): InMemoryWorldStateProxy =
InMemoryWorldStateProxy(
evmCodeStorage,
stateStorage.getBackingStorage(blockNumber),
Expand All @@ -456,18 +473,20 @@ class BlockchainImpl(
)

//FIXME Maybe we can use this one in regular execution too and persist underlying storage when block execution is successful
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe create ticket for this FIXME ? as it seems having two separate methods to retrieve execution world was a bit error prone

override def getReadOnlyWorldStateProxy(blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean): InMemoryWorldStateProxy =
override def getReadOnlyWorldStateProxy(
blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: Option[ByteString],
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): InMemoryWorldStateProxy =
InMemoryWorldStateProxy(
evmCodeStorage,
stateStorage.getReadOnlyStorage,
accountStartNonce,
(number: BigInt) => getBlockHeaderByNumber(number).map(_.hash),
stateRootHash,
noEmptyAccounts = false,
noEmptyAccounts = noEmptyAccounts,
ethCompatibleStorage = ethCompatibleStorage
)

Expand Down
Loading