From ef2e58626d2103713d62bbc9696693752af6425f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Al=C4=85ski?= Date: Tue, 22 Sep 2020 12:15:01 +0200 Subject: [PATCH] etcm-74 added checkpoint block number methods to appStateStorage --- .../ethereum/db/storage/AppStateStorage.scala | 48 +++++++--- .../db/storage/AppStateStorageSpec.scala | 95 +++++++++++++++++++ 2 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 src/test/scala/io/iohk/ethereum/db/storage/AppStateStorageSpec.scala diff --git a/src/main/scala/io/iohk/ethereum/db/storage/AppStateStorage.scala b/src/main/scala/io/iohk/ethereum/db/storage/AppStateStorage.scala index a05d6fba16..d0f5ac444e 100644 --- a/src/main/scala/io/iohk/ethereum/db/storage/AppStateStorage.scala +++ b/src/main/scala/io/iohk/ethereum/db/storage/AppStateStorage.scala @@ -1,5 +1,7 @@ package io.iohk.ethereum.db.storage +import java.math.BigInteger + import io.iohk.ethereum.db.dataSource.{DataSource, DataSourceBatchUpdate} import io.iohk.ethereum.db.storage.AppStateStorage._ @@ -8,18 +10,15 @@ import io.iohk.ethereum.db.storage.AppStateStorage._ * Key: see AppStateStorage.Keys * Value: stored string value */ -class AppStateStorage(val dataSource: DataSource) extends TransactionalKeyValueStorage[Key, Value]{ - type T = AppStateStorage +class AppStateStorage(val dataSource: DataSource) extends TransactionalKeyValueStorage[Key, Value] { val namespace: IndexedSeq[Byte] = Namespaces.AppStateNamespace - def keySerializer: Key => IndexedSeq[Byte] = _.name.getBytes + def keySerializer: Key => IndexedSeq[Byte] = _.getBytes def valueSerializer: String => IndexedSeq[Byte] = _.getBytes def valueDeserializer: IndexedSeq[Byte] => String = (valueBytes: IndexedSeq[Byte]) => new String(valueBytes.toArray) - protected def apply(dataSource: DataSource): AppStateStorage = new AppStateStorage(dataSource) - def getBestBlockNumber(): BigInt = - BigInt(get(Keys.BestBlockNumber).getOrElse("0")) + getBigInt(Keys.BestBlockNumber) def putBestBlockNumber(bestBlockNumber: BigInt): DataSourceBatchUpdate = put(Keys.BestBlockNumber, bestBlockNumber.toString) @@ -31,27 +30,48 @@ class AppStateStorage(val dataSource: DataSource) extends TransactionalKeyValueS put(Keys.FastSyncDone, true.toString) def getEstimatedHighestBlock(): BigInt = - BigInt(get(Keys.EstimatedHighestBlock).getOrElse("0")) + getBigInt(Keys.EstimatedHighestBlock) def putEstimatedHighestBlock(n: BigInt): DataSourceBatchUpdate = put(Keys.EstimatedHighestBlock, n.toString) def getSyncStartingBlock(): BigInt = - BigInt(get(Keys.SyncStartingBlock).getOrElse("0")) + getBigInt(Keys.SyncStartingBlock) def putSyncStartingBlock(n: BigInt): DataSourceBatchUpdate = put(Keys.SyncStartingBlock, n.toString) + + private def getBigInt(key: Key): BigInt = { + get(key).map(BigInt(_)).getOrElse(BigInt(BigInteger.ZERO)) + } + + /** + * It is safe to return zero in case of not having any checkpoint block, + * because we assume that genesis block is a kinda stable checkpoint block (without real checkpoint) + * + * @return Latest CheckpointBlock Number + */ + def getLatestCheckpointBlockNumber(): BigInt = + getBigInt(Keys.LatestCheckpointBlockNumber) + + def removeLatestCheckpointBlockNumber(): DataSourceBatchUpdate = { + update(toRemove = Seq(Keys.LatestCheckpointBlockNumber), toUpsert = Nil) + } + + def putLatestCheckpointBlockNumber(latestCheckpointBlockNumber: BigInt): DataSourceBatchUpdate = { + update(Nil, Seq(Keys.LatestCheckpointBlockNumber -> latestCheckpointBlockNumber.toString)) + } } object AppStateStorage { + type Key = String type Value = String - case class Key private (name: String) - object Keys { - val BestBlockNumber = Key("BestBlockNumber") - val FastSyncDone = Key("FastSyncDone") - val EstimatedHighestBlock = Key("EstimatedHighestBlock") - val SyncStartingBlock = Key("SyncStartingBlock") + val BestBlockNumber = "BestBlockNumber" + val FastSyncDone = "FastSyncDone" + val EstimatedHighestBlock = "EstimatedHighestBlock" + val SyncStartingBlock = "SyncStartingBlock" + val LatestCheckpointBlockNumber = "LatestCheckpointBlockNumber" } } diff --git a/src/test/scala/io/iohk/ethereum/db/storage/AppStateStorageSpec.scala b/src/test/scala/io/iohk/ethereum/db/storage/AppStateStorageSpec.scala new file mode 100644 index 0000000000..0575f7a2a7 --- /dev/null +++ b/src/test/scala/io/iohk/ethereum/db/storage/AppStateStorageSpec.scala @@ -0,0 +1,95 @@ +package io.iohk.ethereum.db.storage + +import io.iohk.ethereum.ObjectGenerators +import io.iohk.ethereum.db.dataSource.EphemDataSource +import org.scalatest.WordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +class AppStateStorageSpec + extends WordSpec + with ScalaCheckPropertyChecks + with ObjectGenerators { + + "AppStateStorage" should { + + "insert and get best block number properly" in new Fixtures { + forAll(ObjectGenerators.bigIntGen) { bestBlockNumber => + val storage = newAppStateStorage() + storage.putBestBlockNumber(bestBlockNumber).commit() + + assert(storage.getBestBlockNumber() == bestBlockNumber) + } + } + + "get zero as best block number when storage is empty" in new Fixtures { + assert(newAppStateStorage().getBestBlockNumber() == 0) + } + + "insert and get fast sync done properly" in new Fixtures { + val storage = newAppStateStorage() + storage.fastSyncDone().commit() + + assert(storage.isFastSyncDone()) + } + + "get fast sync done false when storage is empty" in new Fixtures { + assert(!newAppStateStorage().isFastSyncDone()) + } + + "insert and get estimated highest block properly" in new Fixtures { + forAll(ObjectGenerators.bigIntGen) { estimatedHighestBlock => + val storage = newAppStateStorage() + storage.putEstimatedHighestBlock(estimatedHighestBlock).commit() + + assert(storage.getEstimatedHighestBlock() == estimatedHighestBlock) + } + } + + "get zero as estimated highest block when storage is empty" in new Fixtures { + assert(newAppStateStorage().getEstimatedHighestBlock() == 0) + } + + "insert and get sync starting block properly" in new Fixtures { + forAll(ObjectGenerators.bigIntGen) { syncStartingBlock => + val storage = newAppStateStorage() + storage.putSyncStartingBlock(syncStartingBlock).commit() + + assert(storage.getSyncStartingBlock() == syncStartingBlock) + } + } + + "get zero as sync starting block when storage is empty" in new Fixtures { + assert(newAppStateStorage().getSyncStartingBlock() == 0) + } + + "update and remove latest checkpoint block number properly" in new Fixtures { + forAll(ObjectGenerators.bigIntGen) { latestCheckpointBlockNumber => + val storage = newAppStateStorage() + + storage.putLatestCheckpointBlockNumber(latestCheckpointBlockNumber).commit() + assert(storage.getLatestCheckpointBlockNumber() == latestCheckpointBlockNumber) + + storage.removeLatestCheckpointBlockNumber().commit() + assert(storage.getLatestCheckpointBlockNumber() == 0) + } + } + + "update checkpoint block number and get it properly" in new Fixtures { + forAll(ObjectGenerators.bigIntGen) { latestCheckpointBlockNumber => + val storage = newAppStateStorage() + storage.putLatestCheckpointBlockNumber(latestCheckpointBlockNumber).commit() + + assert(storage.getLatestCheckpointBlockNumber() == latestCheckpointBlockNumber) + } + } + + "get zero as checkpoint block number when storage is empty" in new Fixtures { + assert(newAppStateStorage().getBestBlockNumber() == 0) + } + } + + trait Fixtures { + def newAppStateStorage(): AppStateStorage = new AppStateStorage(EphemDataSource()) + } + +}