Skip to content

ETS integration RPC endpoints #966

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7e06e1b
ETCM-657 move config files to resources to enable usage during develo…
Mar 23, 2021
d276b87
fix test API for retesteth WIP
Mar 23, 2021
922b205
ETCM-697: Added initial import raw block test method
Mar 25, 2021
9c52217
ETCM-697: Fixed issue related to genesis data loading
Mar 26, 2021
00cb050
ETCM-697: Added TestEthBlockServiceWrapper to accommodate response ac…
Mar 26, 2021
34b3ac5
ETCM-697: Fixed code hash parsing bug. Added debug account range meth…
Mar 30, 2021
cf5e5f4
add some logging, wait for importBlock before continuing, don't omit …
Mar 29, 2021
f779c60
ETCM-697: Added initial account range implementation
Apr 1, 2021
28e9342
ETCM-697: Added separate transaction response class that includes sig…
Apr 5, 2021
f395370
ETCM-697: Fixed state root calculation when block mining in ets. Adde…
Apr 8, 2021
7625b42
ETCM-697: Transaction get log hash implemented
Apr 9, 2021
c9c2b14
ETCM-697: Added zero fork blocks to testmode config
Apr 15, 2021
ebaab0c
ETCM-697: Fixed failing tests
Apr 15, 2021
5a34ad1
ETCM-697: Fixed scalastyle issues
Apr 15, 2021
9dcde25
fix import
Apr 16, 2021
5532229
[ETCM-697] Cleanup TestJsonMethodsImplicits
dzajkowski Apr 21, 2021
a8141a1
[ETCM-697] Cleanup EthBlockResponse usage
dzajkowski Apr 22, 2021
53b53ff
[ETCM-697] Capture tech debt in 'handleTestRequest'
dzajkowski Apr 22, 2021
addfc2f
[ETCM-697] Cleanup EthTransactionResponse usage
dzajkowski Apr 22, 2021
bc3b779
[ETCM-697] Align responses in TestService
dzajkowski Apr 22, 2021
78687e2
[ETCM-697] Reorganise BlockPreparator testmode condition
dzajkowski Apr 22, 2021
f39864b
[ETCM-697] Cleanup TestEthBlockServiceWrapper
dzajkowski Apr 22, 2021
7998259
[ETCM-697] Cleanup TestService
dzajkowski Apr 22, 2021
85b04a2
[ETCM-697] Apply scalafmt
dzajkowski Apr 22, 2021
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
2 changes: 1 addition & 1 deletion src/main/resources/conf/base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ mantis {
testnet-internal-nomad {include required("chains/testnet-internal-nomad-chain.conf")}

testnet-internal-gac {include required("chains/testnet-internal-gac-chain.conf")}

pottery {include required("chains/pottery-chain.conf")}
}

Expand Down
33 changes: 32 additions & 1 deletion src/main/resources/conf/testmode.conf
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,41 @@ mantis {

blockchains {
network = "test"

test {
chain-id = 1

frontier-block-number = 0

homestead-block-number = 0

eip106-block-number = 0

eip150-block-number = 0

eip155-block-number = 0

eip160-block-number = 0

eip161-block-number = 0

byzantium-block-number = 0

constantinople-block-number = 0

istanbul-block-number = 0

atlantis-block-number = 0

agharta-block-number = 0

phoenix-block-number = 0

petersburg-block-number = 0
}
}

network.rpc {
apis = "eth,web3,net,personal,mantis,test,iele,debug,qa,checkpointing"
}

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
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.MptStorage
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 +79,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 @@ -89,19 +93,7 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
val storage = stateStorage.getReadOnlyStorage
val initalRootHash = MerklePatriciaTrie.EmptyRootHash

val stateMptRootHash = genesisData.alloc.zipWithIndex.foldLeft(initalRootHash) {
case (rootHash, (((address, AllocAccount(balance)), idx))) =>
val mpt = MerklePatriciaTrie[Array[Byte], Account](rootHash, storage)
val paddedAddress = address.reverse.padTo(addressLength, "0").reverse.mkString
val stateRoot = mpt
.put(
crypto.kec256(Hex.decode(paddedAddress)),
Account(blockchainConfig.accountStartNonce, UInt256(BigInt(balance)), emptyTrieRootHash, emptyEvmHash)
)
.getRootHash
stateRoot
}

val stateMptRootHash = getGenesisStateRoot(genesisData, initalRootHash, storage)
val header: BlockHeader = prepareHeader(genesisData, stateMptRootHash)

log.debug(s"Prepared genesis header: $header")
Expand Down Expand Up @@ -130,6 +122,27 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
}
}

private def getGenesisStateRoot(genesisData: GenesisData, initalRootHash: Array[Byte], storage: MptStorage) = {
import MerklePatriciaTrie.defaultByteArraySerializable

genesisData.alloc.zipWithIndex.foldLeft(initalRootHash) { case (rootHash, ((address, genesisAccount), _)) =>
val mpt = MerklePatriciaTrie[Array[Byte], Account](rootHash, storage)
val paddedAddress = address.reverse.padTo(addressLength, "0").reverse.mkString
val stateRoot = mpt
.put(
crypto.kec256(Hex.decode(paddedAddress)),
Account(
nonce = genesisAccount.nonce
.getOrElse(blockchainConfig.accountStartNonce),
balance = genesisAccount.balance,
codeHash = genesisAccount.code.map(codeValue => crypto.kec256(codeValue)).getOrElse(Account.EmptyCodeHash)
)
)
.getRootHash
stateRoot
}
}

private def prepareHeader(genesisData: GenesisData, stateMptRootHash: Array[Byte]) =
BlockHeader(
parentHash = zeros(hashLength),
Expand Down Expand Up @@ -178,5 +191,23 @@ object GenesisDataLoader {
)
)

def deserializeUint256String(jv: JValue): UInt256 = jv match {
case JString(s) =>
Try(UInt256(BigInt(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
)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatting seems off here

}
}

object Implicits extends JsonMethodsImplicits
13 changes: 11 additions & 2 deletions src/main/scala/io/iohk/ethereum/blockchain/data/genesis.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
package io.iohk.ethereum.blockchain.data

import akka.util.ByteString
import io.iohk.ethereum.domain.UInt256

case class AllocAccount(balance: String)
case class PrecompiledAccountConfig(name: String)

case class GenesisAccount(
precompiled: Option[PrecompiledAccountConfig],
balance: UInt256,
code: Option[ByteString],
nonce: Option[UInt256],
storage: Option[Map[UInt256, UInt256]]
)

case class GenesisData(
nonce: ByteString,
Expand All @@ -12,5 +21,5 @@ case class GenesisData(
gasLimit: String,
coinbase: ByteString,
timestamp: String,
alloc: Map[String, AllocAccount]
alloc: Map[String, GenesisAccount]
)
50 changes: 48 additions & 2 deletions src/main/scala/io/iohk/ethereum/jsonrpc/BlockResponse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,51 @@ 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 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[BaseTransactionResponse]]
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[BaseTransactionResponse]],
uncles: Seq[ByteString]
) extends BaseBlockResponse

//scalastyle:off method.length
case class BlockResponse(
number: BigInt,
Expand All @@ -35,7 +80,7 @@ case class BlockResponse(
uncles: Seq[ByteString],
signature: String,
signer: String
) {
) extends BaseBlockResponse {
val chainWeight: Option[ChainWeight] = for {
lcn <- lastCheckpointNumber
td <- totalDifficulty
Expand All @@ -50,7 +95,8 @@ object BlockResponse {
block: Block,
weight: Option[ChainWeight] = None,
fullTxs: Boolean = false,
pendingBlock: Boolean = false
pendingBlock: Boolean = false,
coinbase: Option[ByteString] = None
): BlockResponse = {
val transactions =
if (fullTxs)
Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.iohk.ethereum.jsonrpc

import java.time.Duration

import akka.util.ByteString
import io.iohk.ethereum.crypto.ECDSASignature
import io.iohk.ethereum.domain.Address
Expand All @@ -13,13 +12,14 @@ import io.iohk.ethereum.jsonrpc.serialization.JsonMethodDecoder.NoParamsMethodDe
import io.iohk.ethereum.jsonrpc.serialization.{JsonEncoder, JsonMethodCodec, JsonMethodDecoder, JsonSerializers}
import io.iohk.ethereum.utils.BigIntExtensionMethods.BigIntAsUnsigned
import org.bouncycastle.util.encoders.Hex
import org.json4s.Formats
import org.json4s.JsonAST._
import org.json4s.JsonDSL._

import scala.util.Try

trait JsonMethodsImplicits {
implicit val formats = JsonSerializers.formats
implicit val formats: Formats = JsonSerializers.formats

def encodeAsHex(input: ByteString): JString =
JString(s"0x${Hex.toHexString(input.toArray[Byte])}")
Expand All @@ -30,7 +30,7 @@ trait JsonMethodsImplicits {
def encodeAsHex(input: BigInt): JString =
JString(s"0x${input.toString(16)}")

protected def decode(s: String): Array[Byte] = {
def decode(s: String): Array[Byte] = {
val stripped = s.replaceFirst("^0x", "")
val normalized = if (stripped.length % 2 == 1) "0" + stripped else stripped
Hex.decode(normalized)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,16 @@ case class JsonRpcController(
handle[ModifyTimestampRequest, ModifyTimestampResponse](testService.modifyTimestamp, req)
case req @ JsonRpcRequest(_, "test_rewindToBlock", _, _) =>
handle[RewindToBlockRequest, RewindToBlockResponse](testService.rewindToBlock, req)
case req @ JsonRpcRequest(_, "test_importRawBlock", _, _) =>
handle[ImportRawBlockRequest, ImportRawBlockResponse](testService.importRawBlock, req)
case req @ JsonRpcRequest(_, "test_getLogHash", _, _) =>
handle[GetLogHashRequest, GetLogHashResponse](testService.getLogHash, req)
case req @ JsonRpcRequest(_, "miner_setEtherbase", _, _) =>
handle[SetEtherbaseRequest, SetEtherbaseResponse](testService.setEtherbase, req)
case req @ JsonRpcRequest(_, "debug_accountRange", _, _) =>
handle[AccountsInRangeRequest, AccountsInRangeResponse](testService.getAccountsInRange, req)
case req @ JsonRpcRequest(_, "debug_storageRangeAt", _, _) =>
handle[StorageRangeRequest, StorageRangeResponse](testService.storageRangeAt, req)
}

private def handleIeleRequest: PartialFunction[JsonRpcRequest, Task[JsonRpcResponse]] = {
Expand Down
11 changes: 10 additions & 1 deletion src/main/scala/io/iohk/ethereum/jsonrpc/JsonRpcRequest.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package io.iohk.ethereum.jsonrpc

import org.json4s.JsonAST.{JArray, JValue}
import org.json4s.native.Serialization.write
import org.json4s.DefaultFormats
import org.json4s.Formats

//TODO: work on a more elegant solution
trait SensitiveInformationToString {
Expand All @@ -14,4 +17,10 @@ trait SensitiveInformationToString {
}

case class JsonRpcRequest(jsonrpc: String, method: String, params: Option[JArray], id: Option[JValue])
extends SensitiveInformationToString
extends SensitiveInformationToString {

def inspect: String = {
implicit val formats: Formats = DefaultFormats
"JsonRpcRequest" + (jsonrpc, method, params.map(write(_)), id.map(write(_))).toString
}
}
10 changes: 9 additions & 1 deletion src/main/scala/io/iohk/ethereum/jsonrpc/JsonRpcResponse.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
package io.iohk.ethereum.jsonrpc

import org.json4s.native.Serialization.write
import org.json4s.JsonAST.JValue
import org.json4s.DefaultFormats
import org.json4s.Formats

case class JsonRpcResponse(jsonrpc: String, result: Option[JValue], error: Option[JsonRpcError], id: JValue)
case class JsonRpcResponse(jsonrpc: String, result: Option[JValue], error: Option[JsonRpcError], id: JValue) {
def inspect: String = {
implicit val formats: Formats = DefaultFormats
"JsonRpcResponse" + (jsonrpc, result.map(write(_)), error.map(write(_))).toString
}
}
Loading