Skip to content

[etcm-74] Latest Checkpoint Block Number in AppStateStorage #687

New issue

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

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

Already on GitHub? # to your account

Merged
merged 1 commit into from
Sep 23, 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
48 changes: 34 additions & 14 deletions src/main/scala/io/iohk/ethereum/db/storage/AppStateStorage.scala
Original file line number Diff line number Diff line change
@@ -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._

Expand All @@ -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)
Expand All @@ -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"
}
}
Original file line number Diff line number Diff line change
@@ -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())
}

}