Skip to content

Commit

Permalink
[ETCM-518] Refactor EthService by adding EthBlocksSevice - service th…
Browse files Browse the repository at this point in the history
…at handles RPC calls related to blocks
  • Loading branch information
Leonor Boga authored and Jaap van der Plas committed Jan 20, 2021
1 parent b0116d5 commit 1628997
Show file tree
Hide file tree
Showing 17 changed files with 1,005 additions and 836 deletions.
Expand Up @@ -29,8 +29,7 @@ class EthashMiner(
blockchain: Blockchain,
blockCreator: EthashBlockCreator,
syncController: ActorRef,
ethService: EthService,
miningService: EthMiningService
ethMiningService: EthMiningService
) extends Actor
with ActorLogging {

Expand Down Expand Up @@ -72,7 +71,7 @@ class EthashMiner(
val time = System.nanoTime() - startTime
//FIXME: consider not reporting hash rate when time delta is zero
val hashRate = if (time > 0) (mineResult.triedHashes.toLong * 1000000000) / time else Long.MaxValue
miningService.submitHashRate(SubmitHashRateRequest(hashRate, ByteString("mantis-miner")))
ethMiningService.submitHashRate(SubmitHashRateRequest(hashRate, ByteString("mantis-miner")))
mineResult match {
case MiningSuccessful(_, pow, nonce) =>
log.info(
Expand Down Expand Up @@ -202,10 +201,10 @@ object EthashMiner {
blockCreator: EthashBlockCreator,
syncController: ActorRef,
ethService: EthService,
miningService: EthMiningService
ethMiningService: EthMiningService
): Props =
Props(
new EthashMiner(blockchain, blockCreator, syncController, ethService, miningService)
new EthashMiner(blockchain, blockCreator, syncController, ethMiningService)
).withDispatcher(BlockForgerDispatcherId)

def apply(node: Node): ActorRef = {
Expand All @@ -222,7 +221,7 @@ object EthashMiner {
blockCreator = blockCreator,
syncController = node.syncController,
ethService = node.ethService,
miningService = node.miningService
ethMiningService = node.ethMiningService
)
node.system.actorOf(minerProps)
case consensus =>
Expand Down
200 changes: 200 additions & 0 deletions src/main/scala/io/iohk/ethereum/jsonrpc/EthBlocksService.scala
@@ -0,0 +1,200 @@
package io.iohk.ethereum.jsonrpc

import akka.util.ByteString
import io.iohk.ethereum.domain.Blockchain
import io.iohk.ethereum.jsonrpc.EthService.{BlockParam, ResolvedBlock}
import io.iohk.ethereum.ledger.Ledger
import io.iohk.ethereum.utils.{BlockchainConfig, Logger}
import monix.eval.Task
import org.bouncycastle.util.encoders.Hex

//eth_call -> ethService.call. // not moved

object EthBlocksService {
case class ChainIdRequest()
case class ChainIdResponse(value: Byte)

case class BestBlockNumberRequest()
case class BestBlockNumberResponse(bestBlockNumber: BigInt)

case class TxCountByBlockHashRequest(blockHash: ByteString)
case class TxCountByBlockHashResponse(txsQuantity: Option[Int])

case class BlockByBlockHashRequest(blockHash: ByteString, fullTxs: Boolean)
case class BlockByBlockHashResponse(blockResponse: Option[BlockResponse])

case class BlockByNumberRequest(block: BlockParam, fullTxs: Boolean)
case class BlockByNumberResponse(blockResponse: Option[BlockResponse])

case class GetBlockTransactionCountByNumberRequest(block: BlockParam)
case class GetBlockTransactionCountByNumberResponse(result: BigInt)

case class UncleByBlockHashAndIndexRequest(blockHash: ByteString, uncleIndex: BigInt)
case class UncleByBlockHashAndIndexResponse(uncleBlockResponse: Option[BlockResponse])

case class UncleByBlockNumberAndIndexRequest(block: BlockParam, uncleIndex: BigInt)
case class UncleByBlockNumberAndIndexResponse(uncleBlockResponse: Option[BlockResponse])

case class GetUncleCountByBlockNumberRequest(block: BlockParam)
case class GetUncleCountByBlockNumberResponse(result: BigInt)

case class GetUncleCountByBlockHashRequest(blockHash: ByteString)
case class GetUncleCountByBlockHashResponse(result: BigInt)
}

class EthBlocksService(blockchain: Blockchain, ledger: Ledger, blockchainConfig: BlockchainConfig) extends Logger {
import EthBlocksService._

private[jsonrpc] def consensus = ledger.consensus
private[jsonrpc] def blockGenerator = consensus.blockGenerator

def chainId(req: ChainIdRequest): ServiceResponse[ChainIdResponse] =
Task.now(Right(ChainIdResponse(blockchainConfig.chainId)))

/**
* eth_blockNumber that returns the number of most recent block.
*
* @return Current block number the client is on.
*/
def bestBlockNumber(req: BestBlockNumberRequest): ServiceResponse[BestBlockNumberResponse] = Task {
Right(BestBlockNumberResponse(blockchain.getBestBlockNumber()))
}

/**
* Implements the eth_getBlockTransactionCountByHash method that fetches the number of txs that a certain block has.
*
* @param request with the hash of the block requested
* @return the number of txs that the block has or None if the client doesn't have the block requested
*/
def getBlockTransactionCountByHash(request: TxCountByBlockHashRequest): ServiceResponse[TxCountByBlockHashResponse] =
Task {
val txsCount = blockchain.getBlockBodyByHash(request.blockHash).map(_.transactionList.size)
Right(TxCountByBlockHashResponse(txsCount))
}

/**
* Implements the eth_getBlockByHash method that fetches a requested block.
*
* @param request with the hash of the block requested
* @return the block requested or None if the client doesn't have the block
*/
def getByBlockHash(request: BlockByBlockHashRequest): ServiceResponse[BlockByBlockHashResponse] = Task {
val BlockByBlockHashRequest(blockHash, fullTxs) = request
val blockOpt = blockchain.getBlockByHash(blockHash)
val weight = blockchain.getChainWeightByHash(blockHash)

val blockResponseOpt = blockOpt.map(block => BlockResponse(block, weight, fullTxs = fullTxs))
Right(BlockByBlockHashResponse(blockResponseOpt))
}

/**
* Implements the eth_getBlockByNumber method that fetches a requested block.
*
* @param request with the block requested (by it's number or by tag)
* @return the block requested or None if the client doesn't have the block
*/
def getBlockByNumber(request: BlockByNumberRequest): ServiceResponse[BlockByNumberResponse] = Task {
val BlockByNumberRequest(blockParam, fullTxs) = request
val blockResponseOpt = EthService.resolveBlock(blockParam, blockchain, blockGenerator).toOption.map {
case ResolvedBlock(block, pending) =>
val weight = blockchain.getChainWeightByHash(block.header.hash)
BlockResponse(block, weight, fullTxs = fullTxs, pendingBlock = pending.isDefined)
}
Right(BlockByNumberResponse(blockResponseOpt))
}

def getBlockTransactionCountByNumber(
req: GetBlockTransactionCountByNumberRequest
): ServiceResponse[GetBlockTransactionCountByNumberResponse] = {
Task {
EthService.resolveBlock(req.block, blockchain, blockGenerator).map { case ResolvedBlock(block, _) =>
GetBlockTransactionCountByNumberResponse(block.body.transactionList.size)
}
}
}

/**
* Implements the eth_getUncleByBlockHashAndIndex method that fetches an uncle from a certain index in a requested block.
*
* @param request with the hash of the block and the index of the uncle requested
* @return the uncle that the block has at the given index or None if the client doesn't have the block or if there's no uncle in that index
*/
def getUncleByBlockHashAndIndex(
request: UncleByBlockHashAndIndexRequest
): ServiceResponse[UncleByBlockHashAndIndexResponse] = Task {
val UncleByBlockHashAndIndexRequest(blockHash, uncleIndex) = request
val uncleHeaderOpt = blockchain
.getBlockBodyByHash(blockHash)
.flatMap { body =>
if (uncleIndex >= 0 && uncleIndex < body.uncleNodesList.size)
Some(body.uncleNodesList.apply(uncleIndex.toInt))
else
None
}
val weight = uncleHeaderOpt.flatMap(uncleHeader => blockchain.getChainWeightByHash(uncleHeader.hash))

//The block in the response will not have any txs or uncles
val uncleBlockResponseOpt = uncleHeaderOpt.map { uncleHeader =>
BlockResponse(blockHeader = uncleHeader, weight = weight, pendingBlock = false)
}
Right(UncleByBlockHashAndIndexResponse(uncleBlockResponseOpt))
}

/**
* Implements the eth_getUncleByBlockNumberAndIndex method that fetches an uncle from a certain index in a requested block.
*
* @param request with the number/tag of the block and the index of the uncle requested
* @return the uncle that the block has at the given index or None if the client doesn't have the block or if there's no uncle in that index
*/
def getUncleByBlockNumberAndIndex(
request: UncleByBlockNumberAndIndexRequest
): ServiceResponse[UncleByBlockNumberAndIndexResponse] = Task {
val UncleByBlockNumberAndIndexRequest(blockParam, uncleIndex) = request
val uncleBlockResponseOpt = EthService
.resolveBlock(blockParam, blockchain, blockGenerator)
.toOption
.flatMap { case ResolvedBlock(block, pending) =>
if (uncleIndex >= 0 && uncleIndex < block.body.uncleNodesList.size) {
val uncleHeader = block.body.uncleNodesList.apply(uncleIndex.toInt)
val weight = blockchain.getChainWeightByHash(uncleHeader.hash)

//The block in the response will not have any txs or uncles
Some(
BlockResponse(
blockHeader = uncleHeader,
weight = weight,
pendingBlock = pending.isDefined
)
)
} else
None
}

Right(UncleByBlockNumberAndIndexResponse(uncleBlockResponseOpt))
}

def getUncleCountByBlockNumber(
req: GetUncleCountByBlockNumberRequest
): ServiceResponse[GetUncleCountByBlockNumberResponse] = {
Task {
EthService.resolveBlock(req.block, blockchain, blockGenerator).map { case ResolvedBlock(block, _) =>
GetUncleCountByBlockNumberResponse(block.body.uncleNodesList.size)
}
}
}

def getUncleCountByBlockHash(
req: GetUncleCountByBlockHashRequest
): ServiceResponse[GetUncleCountByBlockHashResponse] = {
Task {
blockchain.getBlockBodyByHash(req.blockHash) match {
case Some(blockBody) =>
Right(GetUncleCountByBlockHashResponse(blockBody.uncleNodesList.size))
case None =>
Left(
JsonRpcError.InvalidParams(s"Block with hash ${Hex.toHexString(req.blockHash.toArray[Byte])} not found")
)
}
}
}
}
@@ -1,6 +1,7 @@
package io.iohk.ethereum.jsonrpc

import akka.util.ByteString
import io.iohk.ethereum.jsonrpc.EthBlocksService._
import io.iohk.ethereum.jsonrpc.EthService._
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
import io.iohk.ethereum.jsonrpc.EthMiningService._
Expand Down

0 comments on commit 1628997

Please sign in to comment.