Skip to content

Commit d1d70c2

Browse files
grale88Jaap van der Plasdzajkowski
authored
ETS integration RPC endpoints (#966)
Co-authored-by: Jaap van der Plas <[email protected]> Co-authored-by: Dominik Zajkowski <[email protected]>
1 parent a534127 commit d1d70c2

20 files changed

+817
-223
lines changed

src/main/resources/conf/base.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ mantis {
332332
testnet-internal-nomad {include required("chains/testnet-internal-nomad-chain.conf")}
333333

334334
testnet-internal-gac {include required("chains/testnet-internal-gac-chain.conf")}
335-
335+
336336
pottery {include required("chains/pottery-chain.conf")}
337337
}
338338

src/main/resources/conf/testmode.conf

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,41 @@ mantis {
1414

1515
blockchains {
1616
network = "test"
17+
18+
test {
19+
chain-id = 1
20+
21+
frontier-block-number = 0
22+
23+
homestead-block-number = 0
24+
25+
eip106-block-number = 0
26+
27+
eip150-block-number = 0
28+
29+
eip155-block-number = 0
30+
31+
eip160-block-number = 0
32+
33+
eip161-block-number = 0
34+
35+
byzantium-block-number = 0
36+
37+
constantinople-block-number = 0
38+
39+
istanbul-block-number = 0
40+
41+
atlantis-block-number = 0
42+
43+
agharta-block-number = 0
44+
45+
phoenix-block-number = 0
46+
47+
petersburg-block-number = 0
48+
}
1749
}
1850

1951
network.rpc {
2052
apis = "eth,web3,net,personal,mantis,test,iele,debug,qa,checkpointing"
2153
}
22-
2354
}

src/main/scala/io/iohk/ethereum/blockchain/data/GenesisDataLoader.scala

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
package io.iohk.ethereum.blockchain.data
22

33
import java.io.FileNotFoundException
4-
54
import akka.util.ByteString
6-
import io.iohk.ethereum.blockchain.data.GenesisDataLoader.JsonSerializers.ByteStringJsonSerializer
5+
import io.iohk.ethereum.blockchain.data.GenesisDataLoader.JsonSerializers.{
6+
ByteStringJsonSerializer,
7+
UInt256JsonSerializer
8+
}
9+
import io.iohk.ethereum.db.storage.MptStorage
710
import io.iohk.ethereum.db.storage.StateStorage.GenesisDataLoad
811
import io.iohk.ethereum.rlp.RLPList
912
import io.iohk.ethereum.utils.BlockchainConfig
1013
import io.iohk.ethereum.utils.Logger
1114
import io.iohk.ethereum.{crypto, rlp}
1215
import io.iohk.ethereum.domain._
16+
import io.iohk.ethereum.jsonrpc.JsonMethodsImplicits
1317
import io.iohk.ethereum.mpt.MerklePatriciaTrie
1418
import io.iohk.ethereum.rlp.RLPImplicits._
1519
import org.json4s.{CustomSerializer, DefaultFormats, Formats, JString, JValue}
@@ -75,7 +79,7 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
7579

7680
private def loadGenesisData(genesisJson: String): Try[Unit] = {
7781
import org.json4s.native.JsonMethods.parse
78-
implicit val formats: Formats = DefaultFormats + ByteStringJsonSerializer
82+
implicit val formats: Formats = DefaultFormats + ByteStringJsonSerializer + UInt256JsonSerializer
7983
for {
8084
genesisData <- Try(parse(genesisJson).extract[GenesisData])
8185
_ <- loadGenesisData(genesisData)
@@ -89,19 +93,7 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
8993
val storage = stateStorage.getReadOnlyStorage
9094
val initalRootHash = MerklePatriciaTrie.EmptyRootHash
9195

92-
val stateMptRootHash = genesisData.alloc.zipWithIndex.foldLeft(initalRootHash) {
93-
case (rootHash, (((address, AllocAccount(balance)), idx))) =>
94-
val mpt = MerklePatriciaTrie[Array[Byte], Account](rootHash, storage)
95-
val paddedAddress = address.reverse.padTo(addressLength, "0").reverse.mkString
96-
val stateRoot = mpt
97-
.put(
98-
crypto.kec256(Hex.decode(paddedAddress)),
99-
Account(blockchainConfig.accountStartNonce, UInt256(BigInt(balance)), emptyTrieRootHash, emptyEvmHash)
100-
)
101-
.getRootHash
102-
stateRoot
103-
}
104-
96+
val stateMptRootHash = getGenesisStateRoot(genesisData, initalRootHash, storage)
10597
val header: BlockHeader = prepareHeader(genesisData, stateMptRootHash)
10698

10799
log.debug(s"Prepared genesis header: $header")
@@ -130,6 +122,27 @@ class GenesisDataLoader(blockchain: Blockchain, blockchainConfig: BlockchainConf
130122
}
131123
}
132124

125+
private def getGenesisStateRoot(genesisData: GenesisData, initalRootHash: Array[Byte], storage: MptStorage) = {
126+
import MerklePatriciaTrie.defaultByteArraySerializable
127+
128+
genesisData.alloc.zipWithIndex.foldLeft(initalRootHash) { case (rootHash, ((address, genesisAccount), _)) =>
129+
val mpt = MerklePatriciaTrie[Array[Byte], Account](rootHash, storage)
130+
val paddedAddress = address.reverse.padTo(addressLength, "0").reverse.mkString
131+
val stateRoot = mpt
132+
.put(
133+
crypto.kec256(Hex.decode(paddedAddress)),
134+
Account(
135+
nonce = genesisAccount.nonce
136+
.getOrElse(blockchainConfig.accountStartNonce),
137+
balance = genesisAccount.balance,
138+
codeHash = genesisAccount.code.map(codeValue => crypto.kec256(codeValue)).getOrElse(Account.EmptyCodeHash)
139+
)
140+
)
141+
.getRootHash
142+
stateRoot
143+
}
144+
}
145+
133146
private def prepareHeader(genesisData: GenesisData, stateMptRootHash: Array[Byte]) =
134147
BlockHeader(
135148
parentHash = zeros(hashLength),
@@ -178,5 +191,18 @@ object GenesisDataLoader {
178191
)
179192
)
180193

194+
def deserializeUint256String(jv: JValue): UInt256 = jv match {
195+
case JString(s) =>
196+
Try(UInt256(BigInt(s))) match {
197+
case Failure(_) => throw new RuntimeException("Cannot parse hex string: " + s)
198+
case Success(value) => value
199+
}
200+
case other => throw new RuntimeException("Expected hex string, but got: " + other)
201+
}
202+
203+
object UInt256JsonSerializer
204+
extends CustomSerializer[UInt256](_ => ({ case jv => deserializeUint256String(jv) }, PartialFunction.empty))
181205
}
182206
}
207+
208+
object Implicits extends JsonMethodsImplicits
Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
package io.iohk.ethereum.blockchain.data
22

33
import akka.util.ByteString
4+
import io.iohk.ethereum.domain.UInt256
45

5-
case class AllocAccount(balance: String)
6+
case class PrecompiledAccountConfig(name: String)
7+
8+
case class GenesisAccount(
9+
precompiled: Option[PrecompiledAccountConfig],
10+
balance: UInt256,
11+
code: Option[ByteString],
12+
nonce: Option[UInt256],
13+
storage: Option[Map[UInt256, UInt256]]
14+
)
615

716
case class GenesisData(
817
nonce: ByteString,
@@ -12,5 +21,5 @@ case class GenesisData(
1221
gasLimit: String,
1322
coinbase: ByteString,
1423
timestamp: String,
15-
alloc: Map[String, AllocAccount]
24+
alloc: Map[String, GenesisAccount]
1625
)

src/main/scala/io/iohk/ethereum/jsonrpc/BlockResponse.scala

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,32 @@ import io.iohk.ethereum.utils.ByteStringUtils
99

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

12+
/*
13+
* this trait has been introduced to deal with ETS requirements and discrepancies between mantis and the spec
14+
* it should be considered a band-aid solution and replaced with something robust and non-intrusive
15+
*/
16+
trait BaseBlockResponse {
17+
def number: BigInt
18+
def hash: Option[ByteString]
19+
def parentHash: ByteString
20+
def nonce: Option[ByteString]
21+
def sha3Uncles: ByteString
22+
def logsBloom: ByteString
23+
def transactionsRoot: ByteString
24+
def stateRoot: ByteString
25+
def receiptsRoot: ByteString
26+
def miner: Option[ByteString]
27+
def difficulty: BigInt
28+
def totalDifficulty: Option[BigInt]
29+
def extraData: ByteString
30+
def size: BigInt
31+
def gasLimit: BigInt
32+
def gasUsed: BigInt
33+
def timestamp: BigInt
34+
def transactions: Either[Seq[ByteString], Seq[BaseTransactionResponse]]
35+
def uncles: Seq[ByteString]
36+
}
37+
1238
//scalastyle:off method.length
1339
case class BlockResponse(
1440
number: BigInt,
@@ -35,7 +61,7 @@ case class BlockResponse(
3561
uncles: Seq[ByteString],
3662
signature: String,
3763
signer: String
38-
) {
64+
) extends BaseBlockResponse {
3965
val chainWeight: Option[ChainWeight] = for {
4066
lcn <- lastCheckpointNumber
4167
td <- totalDifficulty
@@ -50,7 +76,8 @@ object BlockResponse {
5076
block: Block,
5177
weight: Option[ChainWeight] = None,
5278
fullTxs: Boolean = false,
53-
pendingBlock: Boolean = false
79+
pendingBlock: Boolean = false,
80+
coinbase: Option[ByteString] = None
5481
): BlockResponse = {
5582
val transactions =
5683
if (fullTxs)

src/main/scala/io/iohk/ethereum/jsonrpc/EthBlocksService.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package io.iohk.ethereum.jsonrpc
33
import akka.util.ByteString
44
import io.iohk.ethereum.domain.Blockchain
55
import io.iohk.ethereum.ledger.Ledger
6-
import io.iohk.ethereum.utils.{BlockchainConfig, Logger}
76
import monix.eval.Task
87
import org.bouncycastle.util.encoders.Hex
98

@@ -15,19 +14,19 @@ object EthBlocksService {
1514
case class TxCountByBlockHashResponse(txsQuantity: Option[Int])
1615

1716
case class BlockByBlockHashRequest(blockHash: ByteString, fullTxs: Boolean)
18-
case class BlockByBlockHashResponse(blockResponse: Option[BlockResponse])
17+
case class BlockByBlockHashResponse(blockResponse: Option[BaseBlockResponse])
1918

2019
case class BlockByNumberRequest(block: BlockParam, fullTxs: Boolean)
21-
case class BlockByNumberResponse(blockResponse: Option[BlockResponse])
20+
case class BlockByNumberResponse(blockResponse: Option[BaseBlockResponse])
2221

2322
case class GetBlockTransactionCountByNumberRequest(block: BlockParam)
2423
case class GetBlockTransactionCountByNumberResponse(result: BigInt)
2524

2625
case class UncleByBlockHashAndIndexRequest(blockHash: ByteString, uncleIndex: BigInt)
27-
case class UncleByBlockHashAndIndexResponse(uncleBlockResponse: Option[BlockResponse])
26+
case class UncleByBlockHashAndIndexResponse(uncleBlockResponse: Option[BaseBlockResponse])
2827

2928
case class UncleByBlockNumberAndIndexRequest(block: BlockParam, uncleIndex: BigInt)
30-
case class UncleByBlockNumberAndIndexResponse(uncleBlockResponse: Option[BlockResponse])
29+
case class UncleByBlockNumberAndIndexResponse(uncleBlockResponse: Option[BaseBlockResponse])
3130

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

src/main/scala/io/iohk/ethereum/jsonrpc/JsonMethodsImplicits.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package io.iohk.ethereum.jsonrpc
22

33
import java.time.Duration
4-
54
import akka.util.ByteString
65
import io.iohk.ethereum.crypto.ECDSASignature
76
import io.iohk.ethereum.domain.Address
@@ -13,13 +12,14 @@ import io.iohk.ethereum.jsonrpc.serialization.JsonMethodDecoder.NoParamsMethodDe
1312
import io.iohk.ethereum.jsonrpc.serialization.{JsonEncoder, JsonMethodCodec, JsonMethodDecoder, JsonSerializers}
1413
import io.iohk.ethereum.utils.BigIntExtensionMethods.BigIntAsUnsigned
1514
import org.bouncycastle.util.encoders.Hex
15+
import org.json4s.Formats
1616
import org.json4s.JsonAST._
1717
import org.json4s.JsonDSL._
1818

1919
import scala.util.Try
2020

2121
trait JsonMethodsImplicits {
22-
implicit val formats = JsonSerializers.formats
22+
implicit val formats: Formats = JsonSerializers.formats
2323

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

33-
protected def decode(s: String): Array[Byte] = {
33+
def decode(s: String): Array[Byte] = {
3434
val stripped = s.replaceFirst("^0x", "")
3535
val normalized = if (stripped.length % 2 == 1) "0" + stripped else stripped
3636
Hex.decode(normalized)

src/main/scala/io/iohk/ethereum/jsonrpc/JsonRpcController.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,17 @@ case class JsonRpcController(
245245
handle[ModifyTimestampRequest, ModifyTimestampResponse](testService.modifyTimestamp, req)
246246
case req @ JsonRpcRequest(_, "test_rewindToBlock", _, _) =>
247247
handle[RewindToBlockRequest, RewindToBlockResponse](testService.rewindToBlock, req)
248+
case req @ JsonRpcRequest(_, "test_importRawBlock", _, _) =>
249+
handle[ImportRawBlockRequest, ImportRawBlockResponse](testService.importRawBlock, req)
250+
case req @ JsonRpcRequest(_, "test_getLogHash", _, _) =>
251+
handle[GetLogHashRequest, GetLogHashResponse](testService.getLogHash, req)
248252
case req @ JsonRpcRequest(_, "miner_setEtherbase", _, _) =>
249253
handle[SetEtherbaseRequest, SetEtherbaseResponse](testService.setEtherbase, req)
254+
//FIXME: 'debug_' has it's own 'handle' method, should be aligned (ETCM-806)
255+
case req @ JsonRpcRequest(_, "debug_accountRange", _, _) =>
256+
handle[AccountsInRangeRequest, AccountsInRangeResponse](testService.getAccountsInRange, req)
257+
case req @ JsonRpcRequest(_, "debug_storageRangeAt", _, _) =>
258+
handle[StorageRangeRequest, StorageRangeResponse](testService.storageRangeAt, req)
250259
}
251260

252261
private def handleIeleRequest: PartialFunction[JsonRpcRequest, Task[JsonRpcResponse]] = {

src/main/scala/io/iohk/ethereum/jsonrpc/JsonRpcRequest.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package io.iohk.ethereum.jsonrpc
22

33
import org.json4s.JsonAST.{JArray, JValue}
4+
import org.json4s.native.Serialization.write
5+
import org.json4s.DefaultFormats
6+
import org.json4s.Formats
47

58
//TODO: work on a more elegant solution
69
trait SensitiveInformationToString {
@@ -14,4 +17,10 @@ trait SensitiveInformationToString {
1417
}
1518

1619
case class JsonRpcRequest(jsonrpc: String, method: String, params: Option[JArray], id: Option[JValue])
17-
extends SensitiveInformationToString
20+
extends SensitiveInformationToString {
21+
22+
def inspect: String = {
23+
implicit val formats: Formats = DefaultFormats
24+
"JsonRpcRequest" + (jsonrpc, method, params.map(write(_)), id.map(write(_))).toString
25+
}
26+
}
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package io.iohk.ethereum.jsonrpc
22

3+
import org.json4s.native.Serialization.write
34
import org.json4s.JsonAST.JValue
5+
import org.json4s.DefaultFormats
6+
import org.json4s.Formats
47

5-
case class JsonRpcResponse(jsonrpc: String, result: Option[JValue], error: Option[JsonRpcError], id: JValue)
8+
case class JsonRpcResponse(jsonrpc: String, result: Option[JValue], error: Option[JsonRpcError], id: JValue) {
9+
def inspect: String = {
10+
implicit val formats: Formats = DefaultFormats
11+
"JsonRpcResponse" + (jsonrpc, result.map(write(_)), error.map(write(_))).toString
12+
}
13+
}

0 commit comments

Comments
 (0)