-
Notifications
You must be signed in to change notification settings - Fork 76
/
Blockchain.scala
638 lines (536 loc) · 23.7 KB
/
Blockchain.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
package io.iohk.ethereum.domain
import java.util.concurrent.atomic.AtomicReference
import akka.util.ByteString
import cats.syntax.flatMap._
import cats.instances.option._
import io.iohk.ethereum.db.dataSource.DataSourceBatchUpdate
import io.iohk.ethereum.db.dataSource.RocksDbDataSource.IterationError
import io.iohk.ethereum.db.storage.NodeStorage.{NodeEncoded, NodeHash}
import io.iohk.ethereum.db.storage.TransactionMappingStorage.TransactionLocation
import io.iohk.ethereum.db.storage._
import io.iohk.ethereum.db.storage.pruning.PruningMode
import io.iohk.ethereum.domain
import io.iohk.ethereum.domain.BlockchainImpl.BestBlockLatestCheckpointNumbers
import io.iohk.ethereum.jsonrpc.ProofService.StorageProof
import io.iohk.ethereum.ledger.{InMemoryWorldStateProxy, InMemoryWorldStateProxyStorage}
import io.iohk.ethereum.mpt.{MerklePatriciaTrie, MptNode}
import io.iohk.ethereum.utils.{ByteStringUtils, Logger}
import io.iohk.ethereum.vm.{Storage, WorldStateProxy}
import monix.reactive.Observable
import scala.annotation.tailrec
/**
* Entity to be used to persist and query Blockchain related objects (blocks, transactions, ommers)
*/
// scalastyle:off number.of.methods
trait Blockchain {
type S <: Storage[S]
type WS <: WorldStateProxy[WS, S]
/**
* Allows to query a blockHeader by block hash
*
* @param hash of the block that's being searched
* @return [[BlockHeader]] if found
*/
def getBlockHeaderByHash(hash: ByteString): Option[BlockHeader]
def getBlockHeaderByNumber(number: BigInt): Option[BlockHeader] = {
for {
hash <- getHashByBlockNumber(number)
header <- getBlockHeaderByHash(hash)
} yield header
}
/**
* Allows to query a blockBody by block hash
*
* @param hash of the block that's being searched
* @return [[io.iohk.ethereum.domain.BlockBody]] if found
*/
def getBlockBodyByHash(hash: ByteString): Option[BlockBody]
/**
* Allows to query for a block based on it's hash
*
* @param hash of the block that's being searched
* @return Block if found
*/
def getBlockByHash(hash: ByteString): Option[Block] =
for {
header <- getBlockHeaderByHash(hash)
body <- getBlockBodyByHash(hash)
} yield Block(header, body)
/**
* Allows to query for a block based on it's number
*
* @param number Block number
* @return Block if it exists
*/
def getBlockByNumber(number: BigInt): Option[Block] =
for {
hash <- getHashByBlockNumber(number)
block <- getBlockByHash(hash)
} yield block
/**
* Get an account for an address and a block number
*
* @param address address of the account
* @param blockNumber the block that determines the state of the account
*/
def getAccount(address: Address, blockNumber: BigInt): Option[Account]
def getAccountProof(address: Address, blockNumber: BigInt): Option[Vector[MptNode]]
/**
* Get account storage at given position
*
* @param rootHash storage root hash
* @param position storage position
*/
def getAccountStorageAt(rootHash: ByteString, position: BigInt, ethCompatibleStorage: Boolean): ByteString
/**
* Get a storage-value and its proof being the path from the root node until the last matching node.
*
* @param rootHash storage root hash
* @param position storage position
*/
def getStorageProofAt(
rootHash: ByteString,
position: BigInt,
ethCompatibleStorage: Boolean
): StorageProof
/**
* Returns the receipts based on a block hash
* @param blockhash
* @return Receipts if found
*/
def getReceiptsByHash(blockhash: ByteString): Option[Seq[Receipt]]
/**
* Returns EVM code searched by it's hash
* @param hash Code Hash
* @return EVM code if found
*/
def getEvmCodeByHash(hash: ByteString): Option[ByteString]
/**
* Returns MPT node searched by it's hash
* @param hash Node Hash
* @return MPT node
*/
def getMptNodeByHash(hash: ByteString): Option[MptNode]
/**
* Looks up ChainWeight for a given chain
* @param blockhash Hash of top block in the chain
* @return ChainWeight if found
*/
def getChainWeightByHash(blockhash: ByteString): Option[ChainWeight]
def getChainWeightByNumber(blockNumber: BigInt): Option[ChainWeight] =
getHashByBlockNumber(blockNumber).flatMap(getChainWeightByHash)
def getTransactionLocation(txHash: ByteString): Option[TransactionLocation]
def getBestBlockNumber(): BigInt
def getBestBlock(): Option[Block]
def getLatestCheckpointBlockNumber(): BigInt
/**
* Persists full block along with receipts and chain weight
* @param saveAsBestBlock - whether to save the block's number as current best block
*/
def save(block: Block, receipts: Seq[Receipt], chainWeight: ChainWeight, saveAsBestBlock: Boolean): Unit
/**
* Persists a block in the underlying Blockchain Database
* Note: all store* do not update the database immediately, rather they create
* a [[io.iohk.ethereum.db.dataSource.DataSourceBatchUpdate]] which then has to be committed (atomic operation)
*
* @param block Block to be saved
*/
def storeBlock(block: Block): DataSourceBatchUpdate = {
storeBlockHeader(block.header).and(storeBlockBody(block.header.hash, block.body))
}
def removeBlock(hash: ByteString, withState: Boolean): Unit
/**
* Persists a block header in the underlying Blockchain Database
*
* @param blockHeader Block to be saved
*/
def storeBlockHeader(blockHeader: BlockHeader): DataSourceBatchUpdate
def storeBlockBody(blockHash: ByteString, blockBody: BlockBody): DataSourceBatchUpdate
def storeReceipts(blockHash: ByteString, receipts: Seq[Receipt]): DataSourceBatchUpdate
def storeEvmCode(hash: ByteString, evmCode: ByteString): DataSourceBatchUpdate
def storeChainWeight(blockhash: ByteString, weight: ChainWeight): DataSourceBatchUpdate
def saveBestKnownBlocks(bestBlockNumber: BigInt, latestCheckpointNumber: Option[BigInt] = None): Unit
def saveNode(nodeHash: NodeHash, nodeEncoded: NodeEncoded, blockNumber: BigInt): Unit
/**
* Returns a block hash given a block number
*
* @param number Number of the searchead block
* @return Block hash if found
*/
protected def getHashByBlockNumber(number: BigInt): Option[ByteString]
def genesisHeader: BlockHeader = getBlockHeaderByNumber(0).get
def genesisBlock: Block = getBlockByNumber(0).get
def getWorldStateProxy(
blockNumber: BigInt,
accountStartNonce: UInt256,
stateRootHash: ByteString,
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): WS
def getReadOnlyWorldStateProxy(
blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: ByteString,
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): WS
def getStateStorage: StateStorage
def mptStateSavedKeys(): Observable[Either[IterationError, ByteString]]
/**
* Strict check if given block hash is in chain
* Using any of getXXXByHash is not always accurate - after restart the best block is often lower than before restart
* The result of that is returning data of blocks which we don't consider as a part of the chain anymore
* @param hash block hash
*/
def isInChain(hash: ByteString): Boolean = {
(for {
header <- getBlockHeaderByHash(hash) if header.number <= getBestBlockNumber()
hash <- getHashByBlockNumber(header.number)
} yield header.hash == hash).getOrElse(false)
}
}
// scalastyle:on
class BlockchainImpl(
protected val blockHeadersStorage: BlockHeadersStorage,
protected val blockBodiesStorage: BlockBodiesStorage,
protected val blockNumberMappingStorage: BlockNumberMappingStorage,
protected val receiptStorage: ReceiptStorage,
protected val evmCodeStorage: EvmCodeStorage,
protected val pruningMode: PruningMode,
protected val nodeStorage: NodeStorage,
protected val cachedNodeStorage: CachedNodeStorage,
protected val chainWeightStorage: ChainWeightStorage,
protected val transactionMappingStorage: TransactionMappingStorage,
protected val appStateStorage: AppStateStorage,
protected val stateStorage: StateStorage
) extends Blockchain
with Logger {
override def getStateStorage: StateStorage = stateStorage
// There is always only one writer thread (ensured by actor), but can by many readers (api calls)
// to ensure visibility of writes, needs to be volatile or atomic ref
// Laziness required for mocking BlockchainImpl on tests
private lazy val bestKnownBlockAndLatestCheckpoint: AtomicReference[BestBlockLatestCheckpointNumbers] =
new AtomicReference(
BestBlockLatestCheckpointNumbers(
appStateStorage.getBestBlockNumber(),
appStateStorage.getLatestCheckpointBlockNumber()
)
)
override def getBlockHeaderByHash(hash: ByteString): Option[BlockHeader] =
blockHeadersStorage.get(hash)
override def getBlockBodyByHash(hash: ByteString): Option[BlockBody] =
blockBodiesStorage.get(hash)
override def getReceiptsByHash(blockhash: ByteString): Option[Seq[Receipt]] = receiptStorage.get(blockhash)
override def getEvmCodeByHash(hash: ByteString): Option[ByteString] = evmCodeStorage.get(hash)
override def getChainWeightByHash(blockhash: ByteString): Option[ChainWeight] = chainWeightStorage.get(blockhash)
override def getBestBlockNumber(): BigInt = {
val bestSavedBlockNumber = appStateStorage.getBestBlockNumber()
val bestKnownBlockNumber = bestKnownBlockAndLatestCheckpoint.get().bestBlockNumber
log.debug(
"Current best saved block number {}. Current best known block number {}",
bestSavedBlockNumber,
bestKnownBlockNumber
)
// The cached best block number should always be more up-to-date than the one on disk, we are keeping access to disk
// above only for logging purposes
bestKnownBlockNumber
}
override def getLatestCheckpointBlockNumber(): BigInt =
bestKnownBlockAndLatestCheckpoint.get().latestCheckpointNumber
//returns the best known block if it's available in the storage, otherwise the best stored block
override def getBestBlock(): Option[Block] = {
val bestBlockNumber = getBestBlockNumber()
log.debug("Trying to get best block with number {}", bestBlockNumber)
getBlockByNumber(bestBlockNumber) orElse getBlockByNumber(appStateStorage.getBestBlockNumber())
}
override def getAccount(address: Address, blockNumber: BigInt): Option[Account] =
getAccountMpt(blockNumber) >>= (_.get(address))
override def getAccountProof(address: Address, blockNumber: BigInt): Option[Vector[MptNode]] =
getAccountMpt(blockNumber) >>= (_.getProof(address))
private def getAccountMpt(blockNumber: BigInt): Option[MerklePatriciaTrie[Address, Account]] =
getBlockHeaderByNumber(blockNumber).map { bh =>
val storage = stateStorage.getBackingStorage(blockNumber)
MerklePatriciaTrie[Address, Account](
rootHash = bh.stateRoot.toArray,
source = storage
)
}
override def getAccountStorageAt(
rootHash: ByteString,
position: BigInt,
ethCompatibleStorage: Boolean
): ByteString = {
val storage = stateStorage.getBackingStorage(0)
val mpt =
if (ethCompatibleStorage) domain.EthereumUInt256Mpt.storageMpt(rootHash, storage)
else domain.ArbitraryIntegerMpt.storageMpt(rootHash, storage)
val bigIntValue = mpt.get(position).getOrElse(BigInt(0))
val byteArrayValue = bigIntValue.toByteArray
// BigInt.toArray actually might return one more byte than necessary because it adds a sign bit, which in our case
// will always be 0. This would add unwanted 0 bytes and might cause the value to be 33 byte long while an EVM
// word is 32 byte long.
if (bigIntValue != 0)
ByteString(byteArrayValue.dropWhile(_ == 0))
else
ByteString(byteArrayValue)
}
override def getStorageProofAt(
rootHash: ByteString,
position: BigInt,
ethCompatibleStorage: Boolean
): StorageProof = {
val storage: MptStorage = stateStorage.getBackingStorage(0)
val mpt: MerklePatriciaTrie[BigInt, BigInt] = {
if (ethCompatibleStorage) domain.EthereumUInt256Mpt.storageMpt(rootHash, storage)
else domain.ArbitraryIntegerMpt.storageMpt(rootHash, storage)
}
val value: Option[BigInt] = mpt.get(position)
val proof: Option[Vector[MptNode]] = mpt.getProof(position)
StorageProof(position, value, proof)
}
private def persistBestBlocksData(): Unit = {
val currentBestBlockNumber = getBestBlockNumber()
val currentBestCheckpointNumber = getLatestCheckpointBlockNumber()
log.debug(
"Persisting app info data into database. Persisted block number is {}. " +
"Persisted checkpoint number is {}",
currentBestBlockNumber,
currentBestCheckpointNumber
)
appStateStorage
.putBestBlockNumber(currentBestBlockNumber)
.and(appStateStorage.putLatestCheckpointBlockNumber(currentBestCheckpointNumber))
.commit()
}
def save(block: Block, receipts: Seq[Receipt], weight: ChainWeight, saveAsBestBlock: Boolean): Unit = {
if (saveAsBestBlock && block.hasCheckpoint) {
log.debug(
"New best known block block number - {}, new best checkpoint number - {}",
block.header.number,
block.header.number
)
saveBestKnownBlockAndLatestCheckpointNumber(block.header.number, block.header.number)
} else if (saveAsBestBlock) {
log.debug(
"New best known block block number - {}",
block.header.number
)
saveBestKnownBlock(block.header.number)
}
log.debug("Saving new block block {} to database", block.idTag)
storeBlock(block)
.and(storeReceipts(block.header.hash, receipts))
.and(storeChainWeight(block.header.hash, weight))
.commit()
// not transactional part
// the best blocks data will be persisted only when the cache will be persisted
stateStorage.onBlockSave(block.header.number, appStateStorage.getBestBlockNumber())(persistBestBlocksData)
}
override def storeBlockHeader(blockHeader: BlockHeader): DataSourceBatchUpdate = {
val hash = blockHeader.hash
blockHeadersStorage.put(hash, blockHeader).and(saveBlockNumberMapping(blockHeader.number, hash))
}
override def getMptNodeByHash(hash: ByteString): Option[MptNode] =
stateStorage.getNode(hash)
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))
}
override def storeReceipts(blockHash: ByteString, receipts: Seq[Receipt]): DataSourceBatchUpdate =
receiptStorage.put(blockHash, receipts)
override def storeEvmCode(hash: ByteString, evmCode: ByteString): DataSourceBatchUpdate =
evmCodeStorage.put(hash, evmCode)
override def saveBestKnownBlocks(bestBlockNumber: BigInt, latestCheckpointNumber: Option[BigInt] = None): Unit = {
latestCheckpointNumber match {
case Some(number) =>
saveBestKnownBlockAndLatestCheckpointNumber(bestBlockNumber, number)
case None =>
saveBestKnownBlock(bestBlockNumber)
}
}
private def saveBestKnownBlock(bestBlockNumber: BigInt): Unit =
bestKnownBlockAndLatestCheckpoint.updateAndGet(_.copy(bestBlockNumber = bestBlockNumber))
private def saveBestKnownBlockAndLatestCheckpointNumber(number: BigInt, latestCheckpointNumber: BigInt): Unit =
bestKnownBlockAndLatestCheckpoint.set(BestBlockLatestCheckpointNumbers(number, latestCheckpointNumber))
def storeChainWeight(blockhash: ByteString, weight: ChainWeight): DataSourceBatchUpdate =
chainWeightStorage.put(blockhash, weight)
def saveNode(nodeHash: NodeHash, nodeEncoded: NodeEncoded, blockNumber: BigInt): Unit =
stateStorage.saveNode(nodeHash, nodeEncoded, blockNumber)
override protected def getHashByBlockNumber(number: BigInt): Option[ByteString] =
blockNumberMappingStorage.get(number)
private def saveBlockNumberMapping(number: BigInt, hash: ByteString): DataSourceBatchUpdate =
blockNumberMappingStorage.put(number, hash)
private def removeBlockNumberMapping(number: BigInt): DataSourceBatchUpdate = {
blockNumberMappingStorage.remove(number)
}
override def removeBlock(blockHash: ByteString, withState: Boolean): Unit = {
val maybeBlock = getBlockByHash(blockHash)
maybeBlock match {
case Some(block) => removeBlock(block, withState)
case None =>
log.warn(s"Attempted removing block with hash ${ByteStringUtils.hash2string(blockHash)} that we don't have")
}
}
// scalastyle:off method.length
private def removeBlock(block: Block, withState: Boolean): Unit = {
val blockHash = block.hash
log.debug(s"Trying to remove block ${block.idTag}")
val txList = block.body.transactionList
val bestBlockNumber = getBestBlockNumber()
val latestCheckpointNumber = getLatestCheckpointBlockNumber()
val blockNumberMappingUpdates =
if (getHashByBlockNumber(block.number).contains(blockHash))
removeBlockNumberMapping(block.number)
else blockNumberMappingStorage.emptyBatchUpdate
val newBestBlockNumber: BigInt = (bestBlockNumber - 1).max(0)
val newLatestCheckpointNumber: BigInt =
if (block.hasCheckpoint && block.number == latestCheckpointNumber) {
findPreviousCheckpointBlockNumber(block.number, block.number)
} else latestCheckpointNumber
/*
This two below updates are an exception to the rule of only updating the best blocks when persisting the node
cache.
They are required in case we are removing a block that's marked on db as the best (or as the last checkpoint),
to keep it's consistency, as it will no longer be the best block (nor the last checkpoint).
This updates can't be done if the conditions are false as we might not have the associated mpt nodes, so falling
into the case of having an incomplete best block and so an inconsistent db
*/
val bestBlockNumberUpdates =
if (appStateStorage.getBestBlockNumber() > newBestBlockNumber)
appStateStorage.putBestBlockNumber(newBestBlockNumber)
else appStateStorage.emptyBatchUpdate
val latestCheckpointNumberUpdates =
if (appStateStorage.getLatestCheckpointBlockNumber() > newLatestCheckpointNumber)
appStateStorage.putLatestCheckpointBlockNumber(newLatestCheckpointNumber)
else appStateStorage.emptyBatchUpdate
log.debug(
"Persisting app info data into database. Persisted block number is {}. Persisted checkpoint number is {}",
newBestBlockNumber,
newLatestCheckpointNumber
)
blockHeadersStorage
.remove(blockHash)
.and(blockBodiesStorage.remove(blockHash))
.and(chainWeightStorage.remove(blockHash))
.and(receiptStorage.remove(blockHash))
.and(removeTxsLocations(txList))
.and(blockNumberMappingUpdates)
.and(bestBlockNumberUpdates)
.and(latestCheckpointNumberUpdates)
.commit()
saveBestKnownBlocks(newBestBlockNumber, Some(newLatestCheckpointNumber))
log.debug(
"Removed block with hash {}. New best block number - {}, new best checkpoint block number - {}",
ByteStringUtils.hash2string(blockHash),
newBestBlockNumber,
newLatestCheckpointNumber
)
// not transactional part
if (withState)
stateStorage.onBlockRollback(block.number, bestBlockNumber) { () => persistBestBlocksData() }
}
// scalastyle:on method.length
def mptStateSavedKeys(): Observable[Either[IterationError, ByteString]] = {
(nodeStorage.storageContent.map(c => c.map(_._1)) ++ evmCodeStorage.storageContent.map(c => c.map(_._1)))
.takeWhileInclusive(_.isRight)
}
/**
* Recursive function which try to find the previous checkpoint by traversing blocks from top to the bottom.
* In case of finding the checkpoint block number, the function will finish the job and return result
*/
@tailrec
private def findPreviousCheckpointBlockNumber(
blockNumberToCheck: BigInt,
latestCheckpointBlockNumber: BigInt
): BigInt = {
if (blockNumberToCheck > 0) {
val maybePreviousCheckpointBlockNumber = for {
currentBlock <- getBlockByNumber(blockNumberToCheck)
if currentBlock.hasCheckpoint &&
currentBlock.number < latestCheckpointBlockNumber
} yield currentBlock.number
maybePreviousCheckpointBlockNumber match {
case Some(previousCheckpointBlockNumber) => previousCheckpointBlockNumber
case None => findPreviousCheckpointBlockNumber(blockNumberToCheck - 1, latestCheckpointBlockNumber)
}
} else 0
}
private def saveTxsLocations(blockHash: ByteString, blockBody: BlockBody): DataSourceBatchUpdate =
blockBody.transactionList.zipWithIndex.foldLeft(transactionMappingStorage.emptyBatchUpdate) {
case (updates, (tx, index)) =>
updates.and(transactionMappingStorage.put(tx.hash, TransactionLocation(blockHash, index)))
}
private def removeTxsLocations(stxs: Seq[SignedTransaction]): DataSourceBatchUpdate = {
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: ByteString,
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): InMemoryWorldStateProxy =
InMemoryWorldStateProxy(
evmCodeStorage,
stateStorage.getBackingStorage(blockNumber),
accountStartNonce,
(number: BigInt) => getBlockHeaderByNumber(number).map(_.hash),
stateRootHash,
noEmptyAccounts,
ethCompatibleStorage
)
//FIXME Maybe we can use this one in regular execution too and persist underlying storage when block execution is successful
override def getReadOnlyWorldStateProxy(
blockNumber: Option[BigInt],
accountStartNonce: UInt256,
stateRootHash: ByteString,
noEmptyAccounts: Boolean,
ethCompatibleStorage: Boolean
): InMemoryWorldStateProxy =
InMemoryWorldStateProxy(
evmCodeStorage,
stateStorage.getReadOnlyStorage,
accountStartNonce,
(number: BigInt) => getBlockHeaderByNumber(number).map(_.hash),
stateRootHash,
noEmptyAccounts = noEmptyAccounts,
ethCompatibleStorage = ethCompatibleStorage
)
}
trait BlockchainStorages {
val blockHeadersStorage: BlockHeadersStorage
val blockBodiesStorage: BlockBodiesStorage
val blockNumberMappingStorage: BlockNumberMappingStorage
val receiptStorage: ReceiptStorage
val evmCodeStorage: EvmCodeStorage
val chainWeightStorage: ChainWeightStorage
val transactionMappingStorage: TransactionMappingStorage
val nodeStorage: NodeStorage
val pruningMode: PruningMode
val appStateStorage: AppStateStorage
val cachedNodeStorage: CachedNodeStorage
val stateStorage: StateStorage
}
object BlockchainImpl {
def apply(storages: BlockchainStorages): BlockchainImpl =
new BlockchainImpl(
blockHeadersStorage = storages.blockHeadersStorage,
blockBodiesStorage = storages.blockBodiesStorage,
blockNumberMappingStorage = storages.blockNumberMappingStorage,
receiptStorage = storages.receiptStorage,
evmCodeStorage = storages.evmCodeStorage,
pruningMode = storages.pruningMode,
nodeStorage = storages.nodeStorage,
cachedNodeStorage = storages.cachedNodeStorage,
chainWeightStorage = storages.chainWeightStorage,
transactionMappingStorage = storages.transactionMappingStorage,
appStateStorage = storages.appStateStorage,
stateStorage = storages.stateStorage
)
private case class BestBlockLatestCheckpointNumbers(bestBlockNumber: BigInt, latestCheckpointNumber: BigInt)
}