Skip to content

Commit f144be7

Browse files
author
Aurélien Richez
authored
[ETCM-846] [ETCM-868] ETS: fix test genesis storage (#992)
make ETS stBadOpcode, stSpecialTest and other tests pass * [ETCM-846] Add storage into genesis state * [ETCM-846] Make GenesisDataLoader compute the storage hash instead of taking it as argument. * [ETCM-846] Delete test persistence before running in test mode. * [ETCM-846] simplify storage functions * [ETCM-846] increase timeout * [ETCM-868] fix storeGenesisAccountStorageData and stSpecialTest
1 parent 9d44cf8 commit f144be7

File tree

3 files changed

+44
-40
lines changed

3 files changed

+44
-40
lines changed

src/main/scala/io/iohk/ethereum/blockchain/data/GenesisDataLoader.scala

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import io.iohk.ethereum.blockchain.data.GenesisDataLoader.JsonSerializers.{
66
ByteStringJsonSerializer,
77
UInt256JsonSerializer
88
}
9-
import io.iohk.ethereum.db.storage.MptStorage
9+
import io.iohk.ethereum.db.dataSource.EphemDataSource
10+
import io.iohk.ethereum.db.storage.{ArchiveNodeStorage, MptStorage, NodeStorage, SerializingMptStorage}
1011
import io.iohk.ethereum.db.storage.StateStorage.GenesisDataLoad
1112
import io.iohk.ethereum.rlp.RLPList
1213
import io.iohk.ethereum.utils.BlockchainConfig
@@ -128,21 +129,37 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
128129
genesisData.alloc.zipWithIndex.foldLeft(initalRootHash) { case (rootHash, ((address, genesisAccount), _)) =>
129130
val mpt = MerklePatriciaTrie[Array[Byte], Account](rootHash, storage)
130131
val paddedAddress = address.reverse.padTo(addressLength, "0").reverse.mkString
132+
131133
val stateRoot = mpt
132134
.put(
133135
crypto.kec256(Hex.decode(paddedAddress)),
134136
Account(
135137
nonce = genesisAccount.nonce
136138
.getOrElse(blockchainConfig.accountStartNonce),
137139
balance = genesisAccount.balance,
138-
codeHash = genesisAccount.code.map(codeValue => crypto.kec256(codeValue)).getOrElse(Account.EmptyCodeHash)
140+
codeHash = genesisAccount.code.fold(Account.EmptyCodeHash)(codeValue => crypto.kec256(codeValue)),
141+
storageRoot = genesisAccount.storage.fold(Account.EmptyStorageRootHash)(computeStorageRootHash)
139142
)
140143
)
141144
.getRootHash
142145
stateRoot
143146
}
144147
}
145148

149+
private def computeStorageRootHash(storage: Map[UInt256, UInt256]): ByteString = {
150+
val emptyTrie = EthereumUInt256Mpt.storageMpt(
151+
ByteString(MerklePatriciaTrie.EmptyRootHash),
152+
new SerializingMptStorage(new ArchiveNodeStorage(new NodeStorage(EphemDataSource())))
153+
)
154+
155+
val storageTrie = storage.foldLeft(emptyTrie) {
156+
case (trie, (key, UInt256.Zero)) => trie
157+
case (trie, (key, value)) => trie.put(key, value)
158+
}
159+
160+
ByteString(storageTrie.getRootHash)
161+
}
162+
146163
private def prepareHeader(genesisData: GenesisData, stateMptRootHash: Array[Byte]) =
147164
BlockHeader(
148165
parentHash = zeros(hashLength),

src/main/scala/io/iohk/ethereum/jsonrpc/TestService.scala

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import akka.util.{ByteString, Timeout}
55
import io.iohk.ethereum.blockchain.data.{GenesisAccount, GenesisData, GenesisDataLoader}
66
import io.iohk.ethereum.consensus.ConsensusConfig
77
import io.iohk.ethereum.consensus.blocks._
8-
import io.iohk.ethereum.{crypto, rlp}
8+
import io.iohk.ethereum.crypto.kec256
9+
import io.iohk.ethereum.{crypto, domain, rlp}
910
import io.iohk.ethereum.domain.Block._
10-
import io.iohk.ethereum.domain.{Address, Block, BlockchainImpl, UInt256}
11+
import io.iohk.ethereum.domain.{Account, Address, Block, BlockchainImpl, UInt256}
1112
import io.iohk.ethereum.ledger._
1213
import io.iohk.ethereum.testmode.{TestLedgerWrapper, TestmodeConsensus}
1314
import io.iohk.ethereum.transactions.PendingTransactionsManager
1415
import io.iohk.ethereum.transactions.PendingTransactionsManager.PendingTransactionsResponse
15-
import io.iohk.ethereum.utils.{BlockchainConfig, ByteStringUtils, Logger}
16+
import io.iohk.ethereum.utils.{ByteStringUtils, Logger}
1617
import monix.eval.Task
1718
import monix.execution.Scheduler
1819
import org.bouncycastle.util.encoders.Hex
@@ -154,10 +155,10 @@ class TestService(
154155
// load the new genesis
155156
val genesisDataLoader = new GenesisDataLoader(blockchain, newBlockchainConfig)
156157
genesisDataLoader.loadGenesisData(genesisData)
157-
158158
//save account codes to world state
159-
storeGenesisAccountCodes(newBlockchainConfig, genesisData.alloc)
160-
storeGenesisAccountStorageData(newBlockchainConfig, genesisData.alloc)
159+
storeGenesisAccountCodes(genesisData.alloc)
160+
storeGenesisAccountStorageData(genesisData.alloc)
161+
161162
// update test ledger with new config
162163
testLedgerWrapper.blockchainConfig = newBlockchainConfig
163164

@@ -167,39 +168,23 @@ class TestService(
167168
SetChainParamsResponse().rightNow
168169
}
169170

170-
private def storeGenesisAccountCodes(config: BlockchainConfig, accounts: Map[String, GenesisAccount]): Unit = {
171-
val genesisBlock = blockchain.getBlockByNumber(0).get
172-
val world =
173-
blockchain.getWorldStateProxy(0, UInt256.Zero, genesisBlock.header.stateRoot, false, config.ethCompatibleStorage)
174-
175-
val accountsWithCodes = accounts.filter(pair => pair._2.code.isDefined)
176-
177-
val worldToPersist = accountsWithCodes.foldLeft(world)((world, addressAccountPair) => {
178-
world.saveCode(Address(addressAccountPair._1), addressAccountPair._2.code.get)
179-
})
180-
181-
InMemoryWorldStateProxy.persistState(worldToPersist)
182-
}
183-
184-
private def storeGenesisAccountStorageData(config: BlockchainConfig, accounts: Map[String, GenesisAccount]): Unit = {
185-
val genesisBlock = blockchain.getBlockByNumber(0).get
186-
val world =
187-
blockchain.getWorldStateProxy(0, UInt256.Zero, genesisBlock.header.stateRoot, false, config.ethCompatibleStorage)
188-
189-
val accountsWithStorageData = accounts.filter(pair => pair._2.storage.isDefined && pair._2.storage.get.nonEmpty)
171+
private def storeGenesisAccountCodes(accounts: Map[String, GenesisAccount]): Unit =
172+
accounts
173+
.collect { case (_, GenesisAccount(_, _, Some(code), _, _)) => code }
174+
.foreach { code => blockchain.storeEvmCode(kec256(code), code).commit() }
190175

191-
val worldToPersist = accountsWithStorageData.foldLeft(world)((world, addressAccountPair) => {
192-
val address = Address(addressAccountPair._1)
193-
val emptyStorage = world.getStorage(address)
194-
val updatedStorage = addressAccountPair._2.storage.get.foldLeft(emptyStorage) { case (storage, (key, value)) =>
195-
storage.store(key, value)
196-
}
197-
val updatedWorld = world.saveStorage(Address(addressAccountPair._1), updatedStorage)
198-
updatedWorld.contractStorages.values.foreach(cs => cs.inner.nodeStorage.persist())
199-
updatedWorld
200-
})
176+
private def storeGenesisAccountStorageData(accounts: Map[String, GenesisAccount]): Unit = {
177+
val emptyStorage = domain.EthereumUInt256Mpt.storageMpt(
178+
Account.EmptyStorageRootHash,
179+
blockchain.getStateStorage.getBackingStorage(0)
180+
)
181+
val storagesToPersist = accounts
182+
.flatMap(pair => pair._2.storage)
183+
.map(accountStorage => accountStorage.filterNot { case (_, v) => v.isZero })
184+
.filter(_.nonEmpty)
201185

202-
InMemoryWorldStateProxy.persistState(worldToPersist)
186+
val toBigInts: ((UInt256, UInt256)) => (BigInt, BigInt) = { case (a, b) => (a, b) }
187+
storagesToPersist.foreach(storage => emptyStorage.update(Nil, storage.toSeq.map(toBigInts)))
203188
}
204189

205190
def mineBlocks(request: MineBlocksRequest): ServiceResponse[MineBlocksResponse] = {
@@ -259,7 +244,7 @@ class TestService(
259244
}
260245

261246
private def getBlockForMining(parentBlock: Block): Task[PendingBlock] = {
262-
implicit val timeout: Timeout = Timeout(5.seconds)
247+
implicit val timeout: Timeout = Timeout(20.seconds)
263248
pendingTransactionsManager
264249
.askFor[PendingTransactionsResponse](PendingTransactionsManager.GetPendingTransactions)
265250
.timeout(timeout.duration)

test-ets.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ git submodule init
44
git submodule update
55

66
echo "booting Mantis and waiting for RPC API to be up"
7+
# deleting the state folder in case there is some remaining data from a previous run
8+
rm -rf ~/.mantis/test
79
$SBT -Dconfig.file=./src/main/resources/conf/testmode.conf run &> mantis-log.txt &
810

911
while ! nc -z localhost 8546; do

0 commit comments

Comments
 (0)