Skip to content
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
6 changes: 3 additions & 3 deletions src/it/scala/io/iohk/ethereum/sync/RegularSyncItSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
_ <- peer2.importBlocksUntil(30)(IdentityUpdate)
_ <- peer1.startRegularSync()
_ <- peer2.startRegularSync()
_ <- peer2.addCheckpointedBlock(peer2.blockchainReader.getBlockByNumber(25).get)
_ <- peer2.addCheckpointedBlock(peer2.blockchainReader.getBestBranch().getBlockByNumber(25).get)
_ <- peer2.waitForRegularSyncLoadLastBlock(length)
_ <- peer1.connectToPeers(Set(peer2.node))
_ <- peer1.waitForRegularSyncLoadLastBlock(length)
Expand Down Expand Up @@ -181,8 +181,8 @@ class RegularSyncItSpec extends FreeSpecBase with Matchers with BeforeAndAfterAl
)
)
(
peer1.blockchainReader.getBlockByNumber(blockNumer + 1),
peer2.blockchainReader.getBlockByNumber(blockNumer + 1)
peer1.blockchainReader.getBestBranch().getBlockByNumber(blockNumer + 1),
peer2.blockchainReader.getBestBranch().getBlockByNumber(blockNumer + 1)
) match {
case (Some(blockP1), Some(blockP2)) =>
assert(peer1.bl.getChainWeightByHash(blockP1.hash) == peer2.bl.getChainWeightByHash(blockP2.hash))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ object RegularSyncItSpecUtils {
Task(blockNumber match {
case Some(bNumber) =>
blockchainReader
.getBestBranch()
.getBlockByNumber(bNumber)
.getOrElse(throw new RuntimeException(s"block by number: $bNumber doesn't exist"))
case None => blockchainReader.getBestBlock().get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import io.iohk.ethereum.db.storage.pruning.PruningMode
import io.iohk.ethereum.domain.BlockHeader.HeaderExtraFields.HefEmpty
import io.iohk.ethereum.domain.Blockchain
import io.iohk.ethereum.domain._
import io.iohk.ethereum.domain.branch.Branch
import io.iohk.ethereum.jsonrpc.ProofService.EmptyStorageValueProof
import io.iohk.ethereum.jsonrpc.ProofService.StorageProof
import io.iohk.ethereum.jsonrpc.ProofService.StorageProofKey
Expand Down Expand Up @@ -101,7 +102,9 @@ object DumpChainApp

val blockchain: Blockchain = new BlockchainMock(genesisHash)
val blockchainReader: BlockchainReader = mock[BlockchainReader]
(blockchainReader.getHashByBlockNumber _).expects(*).returning(Some(genesisHash))
val bestChain: Branch = mock[Branch]
(blockchainReader.getBestBranch _).expects().anyNumberOfTimes().returning(bestChain)
(bestChain.getHashByBlockNumber _).expects(*).returning(Some(genesisHash))

val nodeStatus: NodeStatus =
NodeStatus(key = nodeKey, serverStatus = ServerStatus.NotListening, discoveryStatus = ServerStatus.NotListening)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,11 @@ trait OmmersValidator {
): Either[OmmersError, OmmersValid] = {

val getBlockHeaderByHash: ByteString => Option[BlockHeader] = blockchainReader.getBlockHeaderByHash
val bestBranch = blockchainReader.getBestBranch()
val getNBlocksBack: (ByteString, Int) => List[Block] =
(_, n) => ((blockNumber - n) until blockNumber).toList.flatMap(blockchainReader.getBlockByNumber)
(_, n) =>
((blockNumber - n) until blockNumber).toList
.flatMap(nb => bestBranch.getBlockByNumber(nb))

validate(parentHash, blockNumber, ommers, getBlockHeaderByHash, getNBlocksBack)
}
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/io/iohk/ethereum/domain/Blockchain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class BlockchainImpl(
override def isInChain(hash: ByteString): Boolean =
(for {
header <- blockchainReader.getBlockHeaderByHash(hash) if header.number <= blockchainReader.getBestBlockNumber()
hash <- blockchainReader.getHashByBlockNumber(header.number)
hash <- blockchainReader.getBestBranch().getHashByBlockNumber(header.number)
} yield header.hash == hash).getOrElse(false)

override def getChainWeightByHash(blockhash: ByteString): Option[ChainWeight] = chainWeightStorage.get(blockhash)
Expand Down Expand Up @@ -223,7 +223,7 @@ class BlockchainImpl(
val latestCheckpointNumber = getLatestCheckpointBlockNumber()

val blockNumberMappingUpdates =
if (blockchainReader.getHashByBlockNumber(block.number).contains(blockHash))
if (blockchainReader.getBestBranch().getHashByBlockNumber(block.number).contains(blockHash))
removeBlockNumberMapping(block.number)
else blockNumberMappingStorage.emptyBatchUpdate

Expand Down Expand Up @@ -292,7 +292,7 @@ class BlockchainImpl(
): BigInt =
if (blockNumberToCheck > 0) {
val maybePreviousCheckpointBlockNumber = for {
currentBlock <- blockchainReader.getBlockByNumber(blockNumberToCheck)
currentBlock <- blockchainReader.getBestBranch().getBlockByNumber(blockNumberToCheck)
if currentBlock.hasCheckpoint &&
currentBlock.number < latestCheckpointBlockNumber
} yield currentBlock.number
Expand Down
53 changes: 34 additions & 19 deletions src/main/scala/io/iohk/ethereum/domain/BlockchainReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import io.iohk.ethereum.db.storage.BlockHeadersStorage
import io.iohk.ethereum.db.storage.BlockNumberMappingStorage
import io.iohk.ethereum.db.storage.ReceiptStorage
import io.iohk.ethereum.db.storage.StateStorage
import io.iohk.ethereum.domain.branch.BestBranch
import io.iohk.ethereum.domain.branch.Branch
import io.iohk.ethereum.domain.branch.EmptyBranch$
import io.iohk.ethereum.mpt.MptNode
import io.iohk.ethereum.utils.Logger

Expand Down Expand Up @@ -48,31 +51,12 @@ class BlockchainReader(
body <- getBlockBodyByHash(hash)
} yield Block(header, body)

/** Returns a block hash given a block number
*
* @param number Number of the searchead block
* @return Block hash if found
*/
def getHashByBlockNumber(number: BigInt): Option[ByteString] =
blockNumberMappingStorage.get(number)

def getBlockHeaderByNumber(number: BigInt): Option[BlockHeader] =
for {
hash <- getHashByBlockNumber(number)
header <- getBlockHeaderByHash(hash)
} yield header

/** 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

/** Returns MPT node searched by it's hash
* @param hash Node Hash
* @return MPT node
Expand All @@ -86,6 +70,18 @@ class BlockchainReader(
*/
def getReceiptsByHash(blockhash: ByteString): Option[Seq[Receipt]] = receiptStorage.get(blockhash)

/** get the current best stored branch */
def getBestBranch(): Branch =
getBestBlock()
.map { block =>
new BestBranch(
block.header,
blockNumberMappingStorage,
this
)
}
.getOrElse(EmptyBranch$)

def getBestBlockNumber(): BigInt = {
val bestSavedBlockNumber = appStateStorage.getBestBlockNumber()
val bestKnownBlockNumber = blockchainMetadata.bestKnownBlockAndLatestCheckpoint.get().bestBlockNumber
Expand Down Expand Up @@ -116,6 +112,25 @@ class BlockchainReader(

def genesisBlock: Block =
getBlockByNumber(0).get

/** Allows to query for a block based on it's number
*
* @param number Block number
* @return Block if it exists
*/
private def getBlockByNumber(number: BigInt): Option[Block] =
for {
hash <- getHashByBlockNumber(number)
block <- getBlockByHash(hash)
} yield block

/** Returns a block hash given a block number
*
* @param number Number of the searchead block
* @return Block hash if found
*/
private def getHashByBlockNumber(number: BigInt): Option[ByteString] =
blockNumberMappingStorage.get(number)
}

object BlockchainReader {
Expand Down
35 changes: 35 additions & 0 deletions src/main/scala/io/iohk/ethereum/domain/branch/BestBranch.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.iohk.ethereum.domain.branch
import akka.util.ByteString

import io.iohk.ethereum.db.storage.BlockNumberMappingStorage
import io.iohk.ethereum.domain.Block
import io.iohk.ethereum.domain.BlockHeader
import io.iohk.ethereum.domain.BlockchainReader

/** A Branch instance which only works for the best canonical branch or a subset of this branch.
* This implementation uses the existing storage indexes to access blocks by number more efficiently.
*/
class BestBranch(
tipBlockHeader: BlockHeader,
bestChainBlockNumberMappingStorage: BlockNumberMappingStorage,
blockchainReader: BlockchainReader
) extends Branch {

/* The following assumptions are made in this class :
* - The whole branch exists in storage
* - The various metadata and index are consistent
*/

override def getBlockByNumber(number: BigInt): Option[Block] =
if (tipBlockHeader.number >= number && number >= 0) {
for {
hash <- getHashByBlockNumber(number)
block <- blockchainReader.getBlockByHash(hash)
} yield block
} else None

override def getHashByBlockNumber(number: BigInt): Option[ByteString] =
if (tipBlockHeader.number >= number && number >= 0) {
bestChainBlockNumberMappingStorage.get(number)
} else None
}
16 changes: 16 additions & 0 deletions src/main/scala/io/iohk/ethereum/domain/branch/Branch.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.iohk.ethereum.domain.branch

import akka.util.ByteString

import io.iohk.ethereum.domain.Block

/** An interface to manipulate blockchain branches */
trait Branch {

/** Returns a block inside this branch based on its number */
def getBlockByNumber(number: BigInt): Option[Block]

/** Returns a block hash for the block at the given height if any */
def getHashByBlockNumber(number: BigInt): Option[ByteString]

}
11 changes: 11 additions & 0 deletions src/main/scala/io/iohk/ethereum/domain/branch/EmptyBranch$.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.iohk.ethereum.domain.branch
import akka.util.ByteString

import io.iohk.ethereum.domain.Block

object EmptyBranch$ extends Branch {

override def getBlockByNumber(number: BigInt): Option[Block] = None

override def getHashByBlockNumber(number: BigInt): Option[ByteString] = None
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class CheckpointingService(
req.parentCheckpoint.forall(blockchainReader.getBlockHeaderByHash(_).exists(_.number < blockToReturnNum))

Task {
blockchainReader.getBlockByNumber(blockToReturnNum)
blockchainReader.getBestBranch().getBlockByNumber(blockToReturnNum)
}.flatMap {
case Some(b) if isValidParent =>
Task.now(Right(GetLatestBlockResponse(Some(BlockInfo(b.hash, b.number)))))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class EthProofService(
private def resolveBlock(blockParam: BlockParam): Either[JsonRpcError, ResolvedBlock] = {
def getBlock(number: BigInt): Either[JsonRpcError, Block] =
blockchainReader
.getBestBranch()
.getBlockByNumber(number)
.toRight(JsonRpcError.InvalidParams(s"Block $number not found"))

Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/io/iohk/ethereum/jsonrpc/EthTxService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ class EthTxService(
val bestBlock = blockchainReader.getBestBlockNumber()

Task {
val bestBranch = blockchainReader.getBestBranch()
val gasPrice = ((bestBlock - blockDifference) to bestBlock)
.flatMap(blockchainReader.getBlockByNumber)
.flatMap(nb => bestBranch.getBlockByNumber(nb))
.flatMap(_.body.transactionList)
.map(_.tx.gasPrice)
if (gasPrice.nonEmpty) {
Expand Down
1 change: 1 addition & 0 deletions src/main/scala/io/iohk/ethereum/jsonrpc/ResolveBlock.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ trait ResolveBlock {

private def getBlock(number: BigInt): Either[JsonRpcError, Block] =
blockchainReader
.getBestBranch()
.getBlockByNumber(number)
.toRight(JsonRpcError.InvalidParams(s"Block $number not found"))

Expand Down
7 changes: 5 additions & 2 deletions src/main/scala/io/iohk/ethereum/jsonrpc/TestService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class TestService(

val blockOpt = request.parameters.blockHashOrNumber
.fold(
number => blockchainReader.getBlockByNumber(number),
number => blockchainReader.getBestBranch().getBlockByNumber(number),
blockHash => blockchainReader.getBlockByHash(blockHash)
)

Expand Down Expand Up @@ -411,7 +411,10 @@ class TestService(
def storageRangeAt(request: StorageRangeRequest): ServiceResponse[StorageRangeResponse] = {

val blockOpt = request.parameters.blockHashOrNumber
.fold(number => blockchainReader.getBlockByNumber(number), hash => blockchainReader.getBlockByHash(hash))
.fold(
number => blockchainReader.getBestBranch().getBlockByNumber(number),
hash => blockchainReader.getBlockByHash(hash)
)

(for {
block <- blockOpt.toRight(StorageRangeResponse(complete = false, Map.empty, None))
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/io/iohk/ethereum/ledger/BlockImport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class BlockImport(
private def removeBlocksUntil(parent: ByteString, fromNumber: BigInt): List[BlockData] = {
@tailrec
def removeBlocksUntil(parent: ByteString, fromNumber: BigInt, acc: List[BlockData]): List[BlockData] =
blockchainReader.getBlockByNumber(fromNumber) match {
blockchainReader.getBestBranch().getBlockByNumber(fromNumber) match {
case Some(block) if block.header.hash == parent || fromNumber == 0 =>
acc

Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/io/iohk/ethereum/ledger/BlockValidation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ class BlockValidation(
val remaining = n - queuedBlocks.length - 1

val numbers = (block.header.number - remaining) until block.header.number
val blocks = (numbers.toList.flatMap(blockchainReader.getBlockByNumber) :+ block) ::: queuedBlocks
val bestBranch = blockchainReader.getBestBranch()
val blocks =
(numbers.toList.flatMap(nb => bestBranch.getBlockByNumber(nb)) :+ block) ::: queuedBlocks
blocks
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/scala/io/iohk/ethereum/ledger/BranchResolution.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,12 @@ class BranchResolution(blockchain: Blockchain, blockchainReader: BlockchainReade
}
}

private def getTopBlocksFromNumber(from: BigInt): List[Block] =
private def getTopBlocksFromNumber(from: BigInt): List[Block] = {
val bestBranch = blockchainReader.getBestBranch()
(from to blockchainReader.getBestBlockNumber())
.flatMap(blockchainReader.getBlockByNumber)
.flatMap(nb => bestBranch.getBlockByNumber(nb))
.toList
}
}

sealed trait BranchResolutionResult
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class TestEthBlockServiceWrapper(
.getBlockByNumber(request)
.map(
_.map { blockByBlockResponse =>
val fullBlock = blockchainReader.getBlockByNumber(blockByBlockResponse.blockResponse.get.number).get
val bestBranch = blockchainReader.getBestBranch()
val fullBlock = bestBranch.getBlockByNumber(blockByBlockResponse.blockResponse.get.number).get
BlockByNumberResponse(blockByBlockResponse.blockResponse.map(response => toEthResponse(fullBlock, response)))
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class TransactionHistoryService(
val getLastCheckpoint = Task(blockchain.getLatestCheckpointBlockNumber()).memoizeOnSuccess
val txnsFromBlocks = Observable
.from(fromBlocks.reverse)
.mapParallelOrdered(10)(blockNr => Task(blockchainReader.getBlockByNumber(blockNr)))(
.mapParallelOrdered(10)(blockNr => Task(blockchainReader.getBestBranch().getBlockByNumber(blockNr)))(
OverflowStrategy.Unbounded
)
.collect { case Some(block) => block }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ class StdOmmersValidatorSpec extends AnyFlatSpec with Matchers with ScalaCheckPr

it should "report failure if there is an ommer which that is not parent of an ancestor" in new BlockUtils {
val getNBlocksBack: (ByteString, Int) => List[Block] =
(_, n) => ((ommersBlockNumber - n) until ommersBlockNumber).toList.flatMap(blockchainReader.getBlockByNumber)
(_, n) =>
((ommersBlockNumber - n) until ommersBlockNumber).toList
.flatMap(nb => blockchainReader.getBestBranch().getBlockByNumber(nb))

ommersValidator.validateOmmersAncestors(
ommersBlockParentHash,
Expand Down Expand Up @@ -462,6 +464,7 @@ class StdOmmersValidatorSpec extends AnyFlatSpec with Matchers with ScalaCheckPr
.and(blockchainWriter.storeBlock(block95))
.and(blockchainWriter.storeBlock(block96))
.commit()
blockchain.saveBestKnownBlocks(block96.number)

}
}
9 changes: 6 additions & 3 deletions src/test/scala/io/iohk/ethereum/domain/BlockchainSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class BlockchainSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyCh
it should "be able to store a block and retrieve it by number" in new EphemBlockchainTestSetup {
val validBlock = Fixtures.Blocks.ValidBlock.block
blockchainWriter.storeBlock(validBlock).commit()
val block = blockchainReader.getBlockByNumber(validBlock.header.number)
blockchain.saveBestKnownBlocks(validBlock.number)
val block = blockchainReader.getBestBranch().getBlockByNumber(validBlock.header.number)
block.isDefined should ===(true)
validBlock should ===(block.get)
}
Expand All @@ -67,8 +68,10 @@ class BlockchainSpec extends AnyFlatSpec with Matchers with ScalaCheckPropertyCh
}

it should "not return a value if not stored" in new EphemBlockchainTestSetup {
blockchainReader.getBlockByNumber(Fixtures.Blocks.ValidBlock.header.number).isEmpty should ===(true)
blockchainReader.getBlockByHash(Fixtures.Blocks.ValidBlock.header.hash).isEmpty should ===(true)
blockchainReader
.getBestBranch()
.getBlockByNumber(Fixtures.Blocks.ValidBlock.header.number) shouldBe None
blockchainReader.getBlockByHash(Fixtures.Blocks.ValidBlock.header.hash) shouldBe None
}

it should "be able to store a block with checkpoint and retrieve it and checkpoint" in new EphemBlockchainTestSetup {
Expand Down
Loading