Skip to content

Commit 1d73880

Browse files
committed
[ETCM-139] Report precise information on rpc when account data is missing
1 parent c340728 commit 1d73880

19 files changed

+123
-69
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import akka.util.ByteString
44
import io.iohk.ethereum.crypto.ECDSASignature
55
import io.iohk.ethereum.jsonrpc.CheckpointingService._
66
import io.iohk.ethereum.jsonrpc.JsonRpcController.Codec
7-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
7+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
88
import io.iohk.ethereum.jsonrpc.JsonSerializers.QuantitiesSerializer
99
import org.json4s.JsonAST._
1010
import org.json4s.{Extraction, JsonAST}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import akka.util.ByteString
44
import io.iohk.ethereum.jsonrpc.EthService._
55
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonDecoder.NoParamsDecoder
66
import io.iohk.ethereum.jsonrpc.JsonRpcController.{Codec, JsonDecoder, JsonEncoder}
7-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
7+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
88
import io.iohk.ethereum.jsonrpc.PersonalService.{SendTransactionRequest, SendTransactionResponse, SignRequest}
99
import org.json4s.{Extraction, JsonAST}
1010
import org.json4s.JsonAST.{JArray, JBool, JString, JValue, _}

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

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import io.iohk.ethereum.jsonrpc.FilterManager.{FilterChanges, FilterLogs, LogFil
1919
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonRpcConfig
2020
import io.iohk.ethereum.keystore.KeyStore
2121
import io.iohk.ethereum.ledger.{InMemoryWorldStateProxy, Ledger, StxLedger}
22+
import io.iohk.ethereum.mpt.MerklePatriciaTrie.MissingNodeException
2223
import io.iohk.ethereum.ommers.OmmersPool
2324
import io.iohk.ethereum.rlp
2425
import io.iohk.ethereum.rlp.RLPImplicitConversions._
@@ -234,7 +235,7 @@ class EthService(
234235

235236
private[this] def ifEthash[Req, Res](req: Req)(f: Req => Res): ServiceResponse[Res] = {
236237
@inline def F[A](x: A): Future[A] = Future.successful(x)
237-
consensus.ifEthash[ServiceResponse[Res]](_ => F(Right(f(req))))(F(Left(JsonRpcErrors.ConsensusIsNotEthash)))
238+
consensus.ifEthash[ServiceResponse[Res]](_ => F(Right(f(req))))(F(Left(JsonRpcError.ConsensusIsNotEthash)))
238239
}
239240

240241
def protocolVersion(req: ProtocolVersionRequest): ServiceResponse[ProtocolVersionResponse] =
@@ -565,7 +566,7 @@ class EthService(
565566
)
566567
}
567568
response
568-
})(Future.successful(Left(JsonRpcErrors.ConsensusIsNotEthash)))
569+
})(Future.successful(Left(JsonRpcError.ConsensusIsNotEthash)))
569570

570571
private def getOmmersFromPool(parentBlockHash: ByteString): Future[OmmersPool.Ommers] =
571572
consensus.ifEthash(ethash => {
@@ -610,7 +611,7 @@ class EthService(
610611
Right(SubmitWorkResponse(false))
611612
}
612613
}
613-
})(Future.successful(Left(JsonRpcErrors.ConsensusIsNotEthash)))
614+
})(Future.successful(Left(JsonRpcError.ConsensusIsNotEthash)))
614615

615616
/**
616617
* Implements the eth_syncing method that returns syncing information if the node is syncing.
@@ -649,10 +650,10 @@ class EthService(
649650
pendingTransactionsManager ! PendingTransactionsManager.AddOrOverrideTransaction(signedTransaction)
650651
Future.successful(Right(SendRawTransactionResponse(signedTransaction.hash)))
651652
} else {
652-
Future.successful(Left(JsonRpcErrors.InvalidRequest))
653+
Future.successful(Left(JsonRpcError.InvalidRequest))
653654
}
654655
case Failure(_) =>
655-
Future.successful(Left(JsonRpcErrors.InvalidRequest))
656+
Future.successful(Left(JsonRpcError.InvalidRequest))
656657
}
657658
}
658659

@@ -669,7 +670,7 @@ class EthService(
669670
val dataEither = (tx.function, tx.contractCode) match {
670671
case (Some(function), None) => Right(rlp.encode(RLPList(function, args)))
671672
case (None, Some(contractCode)) => Right(rlp.encode(RLPList(contractCode, args)))
672-
case _ => Left(JsonRpcErrors.InvalidParams("Iele transaction should contain either functionName or contractCode"))
673+
case _ => Left(JsonRpcError.InvalidParams("Iele transaction should contain either functionName or contractCode"))
673674
}
674675

675676
dataEither match {
@@ -722,7 +723,7 @@ class EthService(
722723
Right(GetUncleCountByBlockHashResponse(blockBody.uncleNodesList.size))
723724
case None =>
724725
Left(
725-
JsonRpcErrors.InvalidParams(s"Block with hash ${Hex.toHexString(req.blockHash.toArray[Byte])} not found")
726+
JsonRpcError.InvalidParams(s"Block with hash ${Hex.toHexString(req.blockHash.toArray[Byte])} not found")
726727
)
727728
}
728729
}
@@ -786,31 +787,22 @@ class EthService(
786787
.flatMap(_ => Right(None))
787788
}
788789

789-
def getBalance(req: GetBalanceRequest): ServiceResponse[GetBalanceResponse] = {
790-
Future {
791-
withAccount(req.address, req.block) { account =>
792-
GetBalanceResponse(account.balance)
793-
}
790+
def getBalance(req: GetBalanceRequest): ServiceResponse[GetBalanceResponse] =
791+
withAccount(req.address, req.block) { account =>
792+
GetBalanceResponse(account.balance)
794793
}
795-
}
796794

797-
def getStorageAt(req: GetStorageAtRequest): ServiceResponse[GetStorageAtResponse] = {
798-
Future {
799-
withAccount(req.address, req.block) { account =>
800-
GetStorageAtResponse(
801-
blockchain.getAccountStorageAt(account.storageRoot, req.position, blockchainConfig.ethCompatibleStorage)
802-
)
803-
}
795+
def getStorageAt(req: GetStorageAtRequest): ServiceResponse[GetStorageAtResponse] =
796+
withAccount(req.address, req.block) { account =>
797+
GetStorageAtResponse(
798+
blockchain.getAccountStorageAt(account.storageRoot, req.position, blockchainConfig.ethCompatibleStorage)
799+
)
804800
}
805-
}
806801

807-
def getTransactionCount(req: GetTransactionCountRequest): ServiceResponse[GetTransactionCountResponse] = {
808-
Future {
809-
withAccount(req.address, req.block) { account =>
810-
GetTransactionCountResponse(account.nonce)
811-
}
802+
def getTransactionCount(req: GetTransactionCountRequest): ServiceResponse[GetTransactionCountResponse] =
803+
withAccount(req.address, req.block) { account =>
804+
GetTransactionCountResponse(account.nonce)
812805
}
813-
}
814806

815807
def newFilter(req: NewFilterRequest): ServiceResponse[NewFilterResponse] = {
816808
implicit val timeout: Timeout = Timeout(filterConfig.filterManagerQueryTimeout)
@@ -875,20 +867,25 @@ class EthService(
875867
}
876868
}
877869

878-
private def withAccount[T](address: Address, blockParam: BlockParam)(f: Account => T): Either[JsonRpcError, T] = {
879-
resolveBlock(blockParam).map { case ResolvedBlock(block, _) =>
880-
f(
881-
blockchain.getAccount(address, block.header.number).getOrElse(Account.empty(blockchainConfig.accountStartNonce))
882-
)
870+
private def withAccount[T](address: Address, blockParam: BlockParam)(makeResponse: Account => T): ServiceResponse[T] =
871+
Future {
872+
resolveBlock(blockParam)
873+
.map { case ResolvedBlock(block, _) =>
874+
blockchain
875+
.getAccount(address, block.header.number)
876+
.getOrElse(Account.empty(blockchainConfig.accountStartNonce))
877+
}
878+
.map(makeResponse)
879+
}.recover { case _: MissingNodeException =>
880+
Left(JsonRpcError.NodeNotFound)
883881
}
884-
}
885882

886883
private def resolveBlock(blockParam: BlockParam): Either[JsonRpcError, ResolvedBlock] = {
887884
def getBlock(number: BigInt): Either[JsonRpcError, Block] = {
888885
blockchain
889886
.getBlockByNumber(number)
890887
.map(Right.apply)
891-
.getOrElse(Left(JsonRpcErrors.InvalidParams(s"Block $number not found")))
888+
.getOrElse(Left(JsonRpcError.InvalidParams(s"Block $number not found")))
892889
}
893890

894891
blockParam match {
@@ -941,7 +938,7 @@ class EthService(
941938
if (numBlocksToSearch > jsonRpcConfig.accountTransactionsMaxBlocks) {
942939
Future.successful(
943940
Left(
944-
JsonRpcErrors.InvalidParams(
941+
JsonRpcError.InvalidParams(
945942
s"""Maximum number of blocks to search is ${jsonRpcConfig.accountTransactionsMaxBlocks}, requested: $numBlocksToSearch.
946943
|See: 'network.rpc.account-transactions-max-blocks' config.""".stripMargin
947944
)
@@ -975,13 +972,10 @@ class EthService(
975972
}
976973
}
977974

978-
def getStorageRoot(req: GetStorageRootRequest): ServiceResponse[GetStorageRootResponse] = {
979-
Future {
980-
withAccount(req.address, req.block) { account =>
981-
GetStorageRootResponse(account.storageRoot)
982-
}
975+
def getStorageRoot(req: GetStorageRootRequest): ServiceResponse[GetStorageRootResponse] =
976+
withAccount(req.address, req.block) { account =>
977+
GetStorageRootResponse(account.storageRoot)
983978
}
984-
}
985979

986980
/**
987981
* Returns the transactions that are pending in the transaction pool and have a from address that is one of the accounts this node manages.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.iohk.ethereum.jsonrpc
33
import akka.util.ByteString
44
import io.iohk.ethereum.jsonrpc.EthService._
55
import io.iohk.ethereum.jsonrpc.JsonRpcController.{JsonDecoder, JsonEncoder}
6-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
6+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
77
import io.iohk.ethereum.jsonrpc.PersonalService.{InvalidAddress, SendIeleTransactionRequest}
88
import org.json4s.JsonAST.{JArray, JObject, JString, JValue}
99

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import io.iohk.ethereum.domain.Address
88
import io.iohk.ethereum.jsonrpc.EthService.BlockParam
99
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonDecoder.NoParamsDecoder
1010
import io.iohk.ethereum.jsonrpc.JsonRpcController.{Codec, JsonDecoder, JsonEncoder}
11-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
11+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
1212
import io.iohk.ethereum.jsonrpc.JsonSerializers.{
1313
AddressJsonSerializer,
1414
OptionNoneToJNullSerializer,
@@ -141,7 +141,7 @@ trait JsonMethodsImplicits {
141141
extractQuantity(other)
142142
.map(BlockParam.WithNumber)
143143
.left
144-
.map(_ => JsonRpcErrors.InvalidParams(s"Invalid default block param: $other"))
144+
.map(_ => JsonRpcError.InvalidParams(s"Invalid default block param: $other"))
145145
}
146146
}
147147

@@ -152,7 +152,7 @@ trait JsonMethodsImplicits {
152152

153153
object JsonMethodsImplicits extends JsonMethodsImplicits {
154154

155-
import JsonRpcErrors._
155+
import JsonRpcError._
156156

157157
implicit val web3_sha3 = new JsonDecoder[Sha3Request] with JsonEncoder[Sha3Response] {
158158
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, Sha3Request] =

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import org.json4s.JsonAST.{JArray, JValue}
1010
import org.json4s.JsonDSL._
1111
import com.typesafe.config.{Config => TypesafeConfig}
1212
import io.iohk.ethereum.jsonrpc.DebugService.{ListPeersInfoRequest, ListPeersInfoResponse}
13-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
13+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
1414
import io.iohk.ethereum.jsonrpc.QAService.{
1515
GenerateCheckpointRequest,
1616
GenerateCheckpointResponse,
@@ -47,6 +47,12 @@ object JsonRpcController {
4747
trait JsonEncoder[T] {
4848
def encodeJson(t: T): JValue
4949
}
50+
object JsonEncoder {
51+
def apply[T](implicit encoder: JsonEncoder[T]): JsonEncoder[T] = encoder
52+
53+
implicit def listEncoder[T](implicit itemEncoder: JsonEncoder[T]): JsonEncoder[List[T]] = list =>
54+
JArray(list.map(itemEncoder.encodeJson))
55+
}
5056

5157
trait Codec[Req, Res] extends JsonDecoder[Req] with JsonEncoder[Res]
5258
object Codec {
@@ -126,7 +132,7 @@ class JsonRpcController(
126132
import TestJsonMethodsImplicits._
127133
import IeleJsonMethodsImplicits._
128134
import JsonMethodsImplicits._
129-
import JsonRpcErrors._
135+
import JsonRpcError._
130136
import DebugJsonMethodsImplicits._
131137
import QAJsonMethodsImplicits._
132138
import CheckpointingJsonMethodsImplicits._
Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
package io.iohk.ethereum.jsonrpc
22

33
import io.iohk.ethereum.consensus.Protocol
4-
import org.json4s.JsonAST.JValue
4+
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonEncoder
5+
import org.json4s.{JInt, JString, JObject, JValue}
56

67
case class JsonRpcError(code: Int, message: String, data: Option[JValue])
78

89
// scalastyle:off magic.number
910
// scalastyle:off public.methods.have.type
10-
object JsonRpcErrors {
11+
object JsonRpcError {
12+
def apply[T: JsonEncoder](code: Int, message: String, data: T): JsonRpcError =
13+
JsonRpcError(code, message, Some(JsonEncoder[T].encodeJson(data)))
14+
1115
val ParseError = JsonRpcError(-32700, "An error occurred on the server while parsing the JSON text", None)
1216
val InvalidRequest = JsonRpcError(-32600, "The JSON sent is not a valid Request object", None)
1317
val MethodNotFound = JsonRpcError(-32601, "The method does not exist / is not available", None)
@@ -16,9 +20,26 @@ object JsonRpcErrors {
1620
def LogicError(msg: String) = JsonRpcError(-32000, msg, None)
1721
val AccountLocked = LogicError("account is locked or unknown")
1822

19-
// We use the recommendation from https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
23+
// We use the recommendation from https://eth.wiki/json-rpc/json-rpc-error-codes-improvement-proposal
2024
//
2125
// Note Error Code "2", "Action not allowed" could be a candidate here, but the description they provide
2226
// probably does not match this use-case.
2327
final val ConsensusIsNotEthash = JsonRpcError(200, s"The consensus algorithm is not ${Protocol.Names.Ethash}", None)
28+
29+
def executionError(reasons: List[EthCustomError]): JsonRpcError = JsonRpcError(3, "Execution error", reasons)
30+
val NodeNotFound = executionError(List(EthCustomError.DoesntExist("State node")))
31+
32+
// Custom errors based on proposal https://eth.wiki/json-rpc/json-rpc-error-codes-improvement-proposal
33+
sealed abstract class EthCustomError private (val code: Int, val message: String)
34+
object EthCustomError {
35+
implicit val ethCustomErrorEncoder: JsonEncoder[EthCustomError] = err =>
36+
JObject("code" -> JInt(err.code), "message" -> JString(err.message))
37+
38+
case class DoesntExist(what: String) extends EthCustomError(100, s"${what} doesn't exist")
39+
case object RequiresEther extends EthCustomError(101, "Requires ether")
40+
case object GasTooLow extends EthCustomError(102, "Gas too low")
41+
case object GasLimitExceeded extends EthCustomError(103, "Gas limit exceeded")
42+
case object Rejected extends EthCustomError(104, "Rejected")
43+
case object EtherTooLow extends EthCustomError(105, "Ether too low")
44+
}
2445
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import io.iohk.ethereum.db.storage.AppStateStorage
1111
import io.iohk.ethereum.domain.{Account, Address, Blockchain}
1212
import io.iohk.ethereum.jsonrpc.PersonalService._
1313
import io.iohk.ethereum.keystore.{KeyStore, Wallet}
14-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors._
14+
import io.iohk.ethereum.jsonrpc.JsonRpcError._
1515
import io.iohk.ethereum.rlp.RLPList
1616
import io.iohk.ethereum.transactions.PendingTransactionsManager
1717
import io.iohk.ethereum.transactions.PendingTransactionsManager.{AddOrOverrideTransaction, PendingTransactionsResponse}
@@ -178,7 +178,7 @@ class PersonalService(
178178
val dataEither = (tx.function, tx.contractCode) match {
179179
case (Some(function), None) => Right(rlp.encode(RLPList(function, args)))
180180
case (None, Some(contractCode)) => Right(rlp.encode(RLPList(contractCode, args)))
181-
case _ => Left(JsonRpcErrors.InvalidParams("Iele transaction should contain either functionName or contractCode"))
181+
case _ => Left(JsonRpcError.InvalidParams("Iele transaction should contain either functionName or contractCode"))
182182
}
183183

184184
dataEither match {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package io.iohk.ethereum.jsonrpc
33
import akka.util.ByteString
44
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonDecoder.NoParamsDecoder
55
import io.iohk.ethereum.jsonrpc.JsonRpcController.{Codec, JsonEncoder}
6-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
6+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
77
import io.iohk.ethereum.jsonrpc.QAService._
88
import org.json4s.Extraction
99
import org.json4s.JsonAST._

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class QAService(
3939
.map(_ |> (MineBlocksResponse(_)) |> (_.asRight))
4040
.recover { case t: Throwable =>
4141
log.info("Unable to mine requested blocks", t)
42-
Left(JsonRpcErrors.InternalError)
42+
Left(JsonRpcError.InternalError)
4343
}
4444
}
4545

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package io.iohk.ethereum.jsonrpc
22

33
import akka.util.ByteString
44
import io.iohk.ethereum.jsonrpc.JsonRpcController.{JsonDecoder, JsonEncoder}
5-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
5+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
66
import io.iohk.ethereum.jsonrpc.TestService._
77
import org.json4s.JsonAST._
88
import org.json4s.JsonDSL._

src/main/scala/io/iohk/ethereum/jsonrpc/server/http/JsonRpcHttpServer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ trait JsonRpcHttpServer extends Json4sSupport {
3737
.newBuilder()
3838
.handle {
3939
case _: MalformedRequestContentRejection =>
40-
complete((StatusCodes.BadRequest, JsonRpcResponse("2.0", None, Some(JsonRpcErrors.ParseError), JInt(0))))
40+
complete((StatusCodes.BadRequest, JsonRpcResponse("2.0", None, Some(JsonRpcError.ParseError), JInt(0))))
4141
case _: CorsRejection =>
4242
complete(StatusCodes.Forbidden)
4343
}

src/test/scala/io/iohk/ethereum/jsonrpc/CheckpointingJRCSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import io.iohk.ethereum.checkpointing.CheckpointingTestHelpers
44
import io.iohk.ethereum.crypto.ECDSASignature
55
import io.iohk.ethereum.jsonrpc.CheckpointingService._
66
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonRpcConfig
7-
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
7+
import io.iohk.ethereum.jsonrpc.JsonRpcError.InvalidParams
88
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
99
import io.iohk.ethereum.utils.{ByteStringUtils, Config}
1010
import io.iohk.ethereum.{Fixtures, NormalPatience, crypto}

src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,19 @@ class EthServiceSpec
899899
response.futureValue shouldEqual Right(GetBalanceResponse(123))
900900
}
901901

902+
it should "handle MissingNodeException when getting balance" in new TestSetup {
903+
val address = Address(ByteString(Hex.decode("abbb6bebfa05aa13e908eaa492bd7a8343760477")))
904+
905+
val newBlockHeader = blockToRequest.header
906+
val newblock = blockToRequest.copy(header = newBlockHeader)
907+
blockchain.storeBlock(newblock).commit()
908+
blockchain.saveBestKnownBlocks(newblock.header.number)
909+
910+
val response = ethService.getBalance(GetBalanceRequest(address, BlockParam.Latest))
911+
912+
response.futureValue shouldEqual Left(JsonRpcError.NodeNotFound)
913+
}
914+
902915
it should "handle getStorageAt request" in new TestSetup {
903916
import io.iohk.ethereum.rlp.UInt256RLPImplicits._
904917

0 commit comments

Comments
 (0)