Skip to content

Commit

Permalink
ETCM-697: Added TestEthBlockServiceWrapper to accommodate response ac…
Browse files Browse the repository at this point in the history
…cording to eth spec for retesteth. Fixed genesis loading for regular node
  • Loading branch information
Igor Grahovac committed Apr 15, 2021
1 parent e597217 commit 8f71d32
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 29 deletions.
@@ -1,15 +1,18 @@
package io.iohk.ethereum.blockchain.data

import java.io.FileNotFoundException

import akka.util.ByteString
import io.iohk.ethereum.blockchain.data.GenesisDataLoader.JsonSerializers.ByteStringJsonSerializer
import io.iohk.ethereum.blockchain.data.GenesisDataLoader.JsonSerializers.{
ByteStringJsonSerializer,
UInt256JsonSerializer
}
import io.iohk.ethereum.db.storage.StateStorage.GenesisDataLoad
import io.iohk.ethereum.rlp.RLPList
import io.iohk.ethereum.utils.BlockchainConfig
import io.iohk.ethereum.utils.Logger
import io.iohk.ethereum.{crypto, rlp}
import io.iohk.ethereum.domain._
import io.iohk.ethereum.jsonrpc.JsonMethodsImplicits
import io.iohk.ethereum.mpt.MerklePatriciaTrie
import io.iohk.ethereum.rlp.RLPImplicits._
import org.json4s.{CustomSerializer, DefaultFormats, Formats, JString, JValue}
Expand Down Expand Up @@ -75,7 +78,7 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf

private def loadGenesisData(genesisJson: String): Try[Unit] = {
import org.json4s.native.JsonMethods.parse
implicit val formats: Formats = DefaultFormats + ByteStringJsonSerializer
implicit val formats: Formats = DefaultFormats + ByteStringJsonSerializer + UInt256JsonSerializer
for {
genesisData <- Try(parse(genesisJson).extract[GenesisData])
_ <- loadGenesisData(genesisData)
Expand All @@ -96,7 +99,11 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
val stateRoot = mpt
.put(
crypto.kec256(Hex.decode(paddedAddress)),
Account(nonce = genesisAccount.nonce, balance = genesisAccount.balance)
Account(
nonce = genesisAccount.nonce
.getOrElse(blockchainConfig.accountStartNonce),
balance = genesisAccount.balance
)
)
.getRootHash
stateRoot
Expand Down Expand Up @@ -178,5 +185,23 @@ object GenesisDataLoader {
)
)

def deserializeUint256String(jv: JValue): UInt256 = jv match {
case JString(s) =>
Try(UInt256(BigInt(1, Implicits.decode(s)))) match {
case Failure(_) => throw new RuntimeException("Cannot parse hex string: " + s)
case Success(value) => value
}
case other => throw new RuntimeException("Expected hex string, but got: " + other)
}

object UInt256JsonSerializer
extends CustomSerializer[UInt256](formats =>
(
{ case jv => deserializeUint256String(jv) },
PartialFunction.empty
)
)
}
}

object Implicits extends JsonMethodsImplicits
6 changes: 3 additions & 3 deletions src/main/scala/io/iohk/ethereum/blockchain/data/genesis.scala
Expand Up @@ -8,9 +8,9 @@ case class PrecompiledAccountConfig(name: String)
case class GenesisAccount(
precompiled: Option[PrecompiledAccountConfig],
balance: UInt256,
code: ByteString,
nonce: UInt256,
storage: Map[UInt256, UInt256]
code: Option[ByteString],
nonce: Option[UInt256],
storage: Option[Map[UInt256, UInt256]]
)

case class GenesisData(
Expand Down
48 changes: 47 additions & 1 deletion src/main/scala/io/iohk/ethereum/jsonrpc/BlockResponse.scala
Expand Up @@ -9,6 +9,52 @@ import io.iohk.ethereum.utils.ByteStringUtils

case class CheckpointResponse(signatures: Seq[ECDSASignature], signers: Seq[ByteString])

trait BaseBlockResponse {
def number: BigInt
def hash: Option[ByteString]
def mixHash: Option[ByteString]
def parentHash: ByteString
def nonce: Option[ByteString]
def sha3Uncles: ByteString
def logsBloom: ByteString
def transactionsRoot: ByteString
def stateRoot: ByteString
def receiptsRoot: ByteString
def miner: Option[ByteString]
def difficulty: BigInt
def totalDifficulty: Option[BigInt]
def extraData: ByteString
def size: BigInt
def gasLimit: BigInt
def gasUsed: BigInt
def timestamp: BigInt
def transactions: Either[Seq[ByteString], Seq[TransactionResponse]]
def uncles: Seq[ByteString]
}

case class EthBlockResponse(
number: BigInt,
hash: Option[ByteString],
mixHash: Option[ByteString],
parentHash: ByteString,
nonce: Option[ByteString],
sha3Uncles: ByteString,
logsBloom: ByteString,
transactionsRoot: ByteString,
stateRoot: ByteString,
receiptsRoot: ByteString,
miner: Option[ByteString],
difficulty: BigInt,
totalDifficulty: Option[BigInt],
extraData: ByteString,
size: BigInt,
gasLimit: BigInt,
gasUsed: BigInt,
timestamp: BigInt,
transactions: Either[Seq[ByteString], Seq[TransactionResponse]],
uncles: Seq[ByteString]
) extends BaseBlockResponse

//scalastyle:off method.length
case class BlockResponse(
number: BigInt,
Expand Down Expand Up @@ -36,7 +82,7 @@ case class BlockResponse(
uncles: Seq[ByteString],
signature: String,
signer: String
) {
) extends BaseBlockResponse {
val chainWeight: Option[ChainWeight] = for {
lcn <- lastCheckpointNumber
td <- totalDifficulty
Expand Down
Expand Up @@ -15,19 +15,19 @@ object EthBlocksService {
case class TxCountByBlockHashResponse(txsQuantity: Option[Int])

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

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

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 UncleByBlockHashAndIndexResponse(uncleBlockResponse: Option[BaseBlockResponse])

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

case class GetUncleCountByBlockNumberRequest(block: BlockParam)
case class GetUncleCountByBlockNumberResponse(result: BigInt)
Expand Down
Expand Up @@ -21,22 +21,22 @@ object TestJsonMethodsImplicits extends JsonMethodsImplicits {
private def extractAccount(accountJson: JValue): Either[JsonRpcError, GenesisAccount] =
for {
storageObject <- Try((accountJson \ "storage").extract[JObject]).toEither.left.map(e =>
InvalidParams(e.toString())
InvalidParams(e.toString)
)
storage <- storageObject.obj.traverse {
case (key, JString(value)) =>
Try(UInt256(decode(key)) -> UInt256(decode(value))).toEither.left.map(e => InvalidParams(e.toString()))
Try(UInt256(decode(key)) -> UInt256(decode(value))).toEither.left.map(e => InvalidParams(e.toString))
case _ => Left(InvalidParams())
}
balance = UInt256(decode((accountJson \ "balance").extract[String]))
code = ByteString(decode((accountJson \ "code").extract[String]))
nonce = UInt256(decode((accountJson \ "nonce").extract[String]))
code = Some(ByteString(decode((accountJson \ "code").extract[String])))
nonce = Some(UInt256(decode((accountJson \ "nonce").extract[String])))
} yield GenesisAccount(
None,
balance,
code,
nonce,
storage.toMap
Some(storage.toMap)
)

private def extractAccounts(accountsJson: JValue): Either[JsonRpcError, Map[ByteString, GenesisAccount]] =
Expand Down
Expand Up @@ -28,7 +28,7 @@ import io.iohk.ethereum.network.p2p.EthereumMessageDecoder
import io.iohk.ethereum.network.rlpx.AuthHandshaker
import io.iohk.ethereum.network.{PeerManagerActor, ServerActor, _}
import io.iohk.ethereum.ommers.OmmersPool
import io.iohk.ethereum.testmode.{TestLedgerBuilder, TestmodeConsensusBuilder}
import io.iohk.ethereum.testmode.{TestEthBlockServiceWrapper, TestLedgerBuilder, TestmodeConsensusBuilder}
import io.iohk.ethereum.transactions.{PendingTransactionsManager, TransactionHistoryService}
import io.iohk.ethereum.utils.Config.SyncConfig
import io.iohk.ethereum.utils._
Expand Down Expand Up @@ -367,6 +367,11 @@ trait TestServiceBuilder {
new TestService(blockchain, pendingTransactionsManager, consensusConfig, consensus, testLedgerWrapper)(scheduler)
}

trait TestEthBlockServiceBuilder extends EthBlocksServiceBuilder {
self: BlockchainBuilder with TestLedgerBuilder =>
override lazy val ethBlocksService = new TestEthBlockServiceWrapper(blockchain, ledger)
}

trait EthProofServiceBuilder {
self: StorageBuilder with BlockchainBuilder with BlockchainConfigBuilder with ConsensusBuilder =>

Expand Down
7 changes: 6 additions & 1 deletion src/main/scala/io/iohk/ethereum/nodebuilder/StdNode.scala
Expand Up @@ -104,4 +104,9 @@ abstract class BaseNode extends Node {
}

class StdNode extends BaseNode with StdLedgerBuilder with StdConsensusBuilder
class TestNode extends BaseNode with TestLedgerBuilder with TestmodeConsensusBuilder with TestServiceBuilder
class TestNode
extends BaseNode
with TestLedgerBuilder
with TestmodeConsensusBuilder
with TestServiceBuilder
with TestEthBlockServiceBuilder
@@ -0,0 +1,71 @@
package io.iohk.ethereum.testmode

import io.iohk.ethereum.domain.Blockchain
import io.iohk.ethereum.jsonrpc.EthBlocksService.{BlockByBlockHashResponse, BlockByNumberResponse}
import io.iohk.ethereum.jsonrpc.{BaseBlockResponse, EthBlockResponse, EthBlocksService, ServiceResponse}
import io.iohk.ethereum.ledger.Ledger
import io.iohk.ethereum.utils.Logger

class TestEthBlockServiceWrapper(blockchain: Blockchain, ledger: Ledger)
extends EthBlocksService(blockchain, ledger)
with Logger {

/**
* 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
*/
override def getByBlockHash(
request: EthBlocksService.BlockByBlockHashRequest
): ServiceResponse[EthBlocksService.BlockByBlockHashResponse] = super
.getByBlockHash(request)
.map(
_.map(blockByBlockResponse =>
BlockByBlockHashResponse(
blockByBlockResponse.blockResponse.map(response => toEthResponse(response))
)
)
)

/**
* 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
*/
override def getBlockByNumber(
request: EthBlocksService.BlockByNumberRequest
): ServiceResponse[EthBlocksService.BlockByNumberResponse] = super
.getBlockByNumber(request)
.map(
_.map(blockByBlockResponse =>
BlockByNumberResponse(
blockByBlockResponse.blockResponse.map(response => toEthResponse(response))
)
)
)

private def toEthResponse(response: BaseBlockResponse) = EthBlockResponse(
response.number,
response.hash,
response.mixHash,
response.parentHash,
response.nonce,
response.sha3Uncles,
response.logsBloom,
response.transactionsRoot,
response.stateRoot,
response.receiptsRoot,
response.miner,
response.difficulty,
response.totalDifficulty,
response.extraData,
response.size,
response.gasLimit,
response.gasUsed,
response.timestamp,
response.transactions,
response.uncles
)
}
20 changes: 10 additions & 10 deletions src/test/scala/io/iohk/ethereum/jsonrpc/EthBlocksServiceSpec.scala
Expand Up @@ -83,7 +83,7 @@ class EthBlocksServiceSpec
response.blockResponse shouldBe Some(
BlockResponse(blockToRequest, fullTxs = true, weight = Some(blockWeight))
)
response.blockResponse.get.chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.transactions.toOption shouldBe Some(stxResponses)
}

Expand All @@ -98,7 +98,7 @@ class EthBlocksServiceSpec
}

response.blockResponse shouldBe Some(BlockResponse(blockToRequest, fullTxs = true))
response.blockResponse.get.chainWeight shouldBe None
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe None
response.blockResponse.get.transactions.toOption shouldBe Some(stxResponses)
}

Expand All @@ -115,7 +115,7 @@ class EthBlocksServiceSpec
response.blockResponse shouldBe Some(
BlockResponse(blockToRequest, fullTxs = false, weight = Some(blockWeight))
)
response.blockResponse.get.chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.transactions.left.toOption shouldBe Some(blockToRequest.body.transactionList.map(_.hash))
}

Expand Down Expand Up @@ -178,7 +178,7 @@ class EthBlocksServiceSpec
response.blockResponse shouldBe Some(
BlockResponse(blockToRequest, fullTxs = true, weight = Some(blockWeight))
)
response.blockResponse.get.chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.transactions.toOption shouldBe Some(stxResponses)
}

Expand All @@ -194,7 +194,7 @@ class EthBlocksServiceSpec
}

response.blockResponse shouldBe Some(BlockResponse(blockToRequest, fullTxs = true))
response.blockResponse.get.chainWeight shouldBe None
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe None
response.blockResponse.get.transactions.toOption shouldBe Some(stxResponses)
}

Expand All @@ -212,7 +212,7 @@ class EthBlocksServiceSpec
response.blockResponse shouldBe Some(
BlockResponse(blockToRequest, fullTxs = false, weight = Some(blockWeight))
)
response.blockResponse.get.chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(blockWeight)
response.blockResponse.get.transactions.left.toOption shouldBe Some(blockToRequest.body.transactionList.map(_.hash))
}

Expand Down Expand Up @@ -289,7 +289,7 @@ class EthBlocksServiceSpec
val response = ethBlocksService.getUncleByBlockHashAndIndex(request).runSyncUnsafe(Duration.Inf).toOption.get

response.uncleBlockResponse shouldBe Some(BlockResponse(uncle, None, pendingBlock = false))
response.uncleBlockResponse.get.chainWeight shouldBe None
response.uncleBlockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe None
response.uncleBlockResponse.get.transactions shouldBe Left(Nil)
response.uncleBlockResponse.get.uncles shouldBe Nil
}
Expand All @@ -305,7 +305,7 @@ class EthBlocksServiceSpec
val response = ethBlocksService.getUncleByBlockHashAndIndex(request).runSyncUnsafe(Duration.Inf).toOption.get

response.uncleBlockResponse shouldBe Some(BlockResponse(uncle, Some(uncleWeight), pendingBlock = false))
response.uncleBlockResponse.get.chainWeight shouldBe Some(uncleWeight)
response.uncleBlockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(uncleWeight)
response.uncleBlockResponse.get.transactions shouldBe Left(Nil)
response.uncleBlockResponse.get.uncles shouldBe Nil
}
Expand Down Expand Up @@ -363,7 +363,7 @@ class EthBlocksServiceSpec
val response = ethBlocksService.getUncleByBlockNumberAndIndex(request).runSyncUnsafe(Duration.Inf).toOption.get

response.uncleBlockResponse shouldBe Some(BlockResponse(uncle, None, pendingBlock = false))
response.uncleBlockResponse.get.chainWeight shouldBe None
response.uncleBlockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe None
response.uncleBlockResponse.get.transactions shouldBe Left(Nil)
response.uncleBlockResponse.get.uncles shouldBe Nil
}
Expand All @@ -380,7 +380,7 @@ class EthBlocksServiceSpec
val response = ethBlocksService.getUncleByBlockNumberAndIndex(request).runSyncUnsafe(Duration.Inf).toOption.get

response.uncleBlockResponse shouldBe Some(BlockResponse(uncle, Some(uncleWeight), pendingBlock = false))
response.uncleBlockResponse.get.chainWeight shouldBe Some(uncleWeight)
response.uncleBlockResponse.get.asInstanceOf[BlockResponse].chainWeight shouldBe Some(uncleWeight)
response.uncleBlockResponse.get.transactions shouldBe Left(Nil)
response.uncleBlockResponse.get.uncles shouldBe Nil
}
Expand Down

0 comments on commit 8f71d32

Please sign in to comment.