Skip to content

Commit

Permalink
move user-related calls from EthService to its own class
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaap van der Plas committed Jan 14, 2021
1 parent 2561bb8 commit cda21c9
Show file tree
Hide file tree
Showing 21 changed files with 534 additions and 384 deletions.
Expand Up @@ -13,10 +13,10 @@ object ByteStringUtils {
def string2hash(hash: String): ByteString =
ByteString(Hex.decode(hash))


implicit class Padding(val bs: ByteString) extends AnyVal {
def padToByteString(length: Int, b: Byte): ByteString = {
if (length <= bs.length) bs else {
if (length <= bs.length) bs
else {
val len = Math.max(bs.length, length)
val result = new Array[Byte](len)
bs.copyToArray(result, 0)
Expand All @@ -30,7 +30,6 @@ object ByteStringUtils {
}
}


sealed trait ByteStringElement {
def len: Int
def asByteArray: Array[Byte]
Expand Down
2 changes: 1 addition & 1 deletion bytes/src/main/scala/io/iohk/ethereum/utils/Hex.scala
Expand Up @@ -5,5 +5,5 @@ object Hex {
bytes.map("%02x".format(_)).mkString

def decode(hex: String): Array[Byte] =
hex.toSeq.sliding(2, 2).toArray.map(s=> Integer.parseInt(s.unwrap, 16).toByte)
hex.toSeq.sliding(2, 2).toArray.map(s => Integer.parseInt(s.unwrap, 16).toByte)
}
Expand Up @@ -48,14 +48,13 @@ class ByteStringUtilsTest extends AnyWordSpec with Matchers {
val bsu = string2hash("0000FFFF")
val seq = ArraySeq.unsafeWrapArray(bsu.toArray)
bsu.padToByteString(3, 0) shouldEqual bsu // result is ByteString
bsu.padTo(3,0) shouldEqual seq // result is Seq
bsu.padTo(3, 0) shouldEqual seq // result is Seq

val longSeq = ArraySeq[Byte](0, 0, -1, -1, 1 ,1)
val longSeq = ArraySeq[Byte](0, 0, -1, -1, 1, 1)
val longBsu = string2hash("0000FFFF0101")
bsu.padToByteString(6, 1) shouldEqual longBsu
bsu.padTo(6, 1) shouldEqual longSeq
}

}
}

Expand Up @@ -64,7 +64,7 @@ object ECDSASignature {
case Some(id) if v == negativePointSign => (id * 2 + newNegativePointSign).toByte
case Some(id) if v == positivePointSign => (id * 2 + newPositivePointSign).toByte
case None => v
case _ => throw new IllegalStateException(s"Unexpected pointSign. ChainId: ${chainId}, v: ${v}")
case _ => throw new IllegalStateException(s"Unexpected pointSign. ChainId: ${chainId}, v: ${v}")
}

ECDSASignature(r, s, pointSign)
Expand Down Expand Up @@ -177,7 +177,6 @@ case class ECDSASignature(r: BigInt, s: BigInt, v: Byte) {
def bigInt2Bytes(b: BigInt) =
ByteUtils.padLeft(ByteString(b.toByteArray).takeRight(RLength), RLength, 0)


bigInt2Bytes(r) ++ bigInt2Bytes(s) ++ ByteString(v)
}
}
Expand Up @@ -214,20 +214,6 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {

}

implicit val eth_getCode = new JsonMethodDecoder[GetCodeRequest] with JsonEncoder[GetCodeResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetCodeRequest] =
params match {
case Some(JArray((address: JString) :: (blockValue: JValue) :: Nil)) =>
for {
addr <- extractAddress(address)
block <- extractBlockParam(blockValue)
} yield GetCodeRequest(addr, block)
case _ => Left(InvalidParams())
}

def encodeJson(t: GetCodeResponse): JValue = encodeAsHex(t.result)
}

implicit val eth_getUncleCountByBlockNumber = new JsonMethodDecoder[GetUncleCountByBlockNumberRequest]
with JsonEncoder[GetUncleCountByBlockNumberResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetUncleCountByBlockNumberRequest] =
Expand Down Expand Up @@ -270,51 +256,6 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
def encodeJson(t: GetBlockTransactionCountByNumberResponse): JValue = encodeAsHex(t.result)
}

implicit val eth_getBalance = new JsonMethodDecoder[GetBalanceRequest] with JsonEncoder[GetBalanceResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetBalanceRequest] =
params match {
case Some(JArray((addressStr: JString) :: (blockValue: JValue) :: Nil)) =>
for {
address <- extractAddress(addressStr)
block <- extractBlockParam(blockValue)
} yield GetBalanceRequest(address, block)
case other =>
Left(InvalidParams())
}

def encodeJson(t: GetBalanceResponse): JValue = encodeAsHex(t.value)
}

implicit val eth_getStorageAt = new JsonMethodDecoder[GetStorageAtRequest] with JsonEncoder[GetStorageAtResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetStorageAtRequest] =
params match {
case Some(JArray((addressStr: JString) :: (positionStr: JString) :: (blockValue: JValue) :: Nil)) =>
for {
address <- extractAddress(addressStr)
position <- extractQuantity(positionStr)
block <- extractBlockParam(blockValue)
} yield GetStorageAtRequest(address, position, block)
case _ => Left(InvalidParams())
}

def encodeJson(t: GetStorageAtResponse): JValue = encodeAsHex(t.value)
}

implicit val eth_getTransactionCount = new JsonMethodDecoder[GetTransactionCountRequest]
with JsonEncoder[GetTransactionCountResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionCountRequest] =
params match {
case Some(JArray((addressStr: JString) :: (blockValue: JValue) :: Nil)) =>
for {
address <- extractAddress(addressStr)
block <- extractBlockParam(blockValue)
} yield GetTransactionCountRequest(address, block)
case _ => Left(InvalidParams())
}

def encodeJson(t: GetTransactionCountResponse): JValue = encodeAsHex(t.value)
}

implicit val newFilterResponseEnc = new JsonEncoder[NewFilterResponse] {
def encodeJson(t: NewFilterResponse): JValue = encodeAsHex(t.filterId)
}
Expand Down Expand Up @@ -483,19 +424,4 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
data = data.getOrElse(ByteString(""))
)
}

implicit val eth_getStorageRoot = new JsonMethodDecoder[GetStorageRootRequest]
with JsonEncoder[GetStorageRootResponse] {
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetStorageRootRequest] =
params match {
case Some(JArray((addressStr: JString) :: (blockValue: JValue) :: Nil)) =>
for {
address <- extractAddress(addressStr)
block <- extractBlockParam(blockValue)
} yield GetStorageRootRequest(address, block)
case _ => Left(InvalidParams())
}

def encodeJson(t: GetStorageRootResponse): JValue = encodeAsHex(t.storageRoot)
}
}
128 changes: 34 additions & 94 deletions src/main/scala/io/iohk/ethereum/jsonrpc/EthService.scala
Expand Up @@ -127,9 +127,6 @@ object EthService {
case class IeleCallResponse(returnData: Seq[ByteString])
case class EstimateGasResponse(gas: BigInt)

case class GetCodeRequest(address: Address, block: BlockParam)
case class GetCodeResponse(result: ByteString)

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

Expand All @@ -142,17 +139,6 @@ object EthService {
case class GetBlockTransactionCountByNumberRequest(block: BlockParam)
case class GetBlockTransactionCountByNumberResponse(result: BigInt)

case class GetBalanceRequest(address: Address, block: BlockParam)
case class GetBalanceResponse(value: BigInt)

case class GetStorageAtRequest(address: Address, position: BigInt, block: BlockParam)
case class GetStorageAtResponse(value: ByteString)

case class GetTransactionCountRequest(address: Address, block: BlockParam)
case class GetTransactionCountResponse(value: BigInt)

case class ResolvedBlock(block: Block, pendingState: Option[InMemoryWorldStateProxy])

case class NewFilterRequest(filter: Filter)
case class Filter(
fromBlock: Option[BlockParam],
Expand All @@ -178,8 +164,31 @@ object EthService {
case class GetLogsRequest(filter: Filter)
case class GetLogsResponse(filterLogs: LogFilterLogs)

case class GetStorageRootRequest(address: Address, block: BlockParam)
case class GetStorageRootResponse(storageRoot: ByteString)
case class ResolvedBlock(block: Block, pendingState: Option[InMemoryWorldStateProxy])

def resolveBlock(
blockchain: Blockchain,
ledger: Ledger,
blockParam: BlockParam
): Either[JsonRpcError, ResolvedBlock] = {
def getBlock(number: BigInt): Either[JsonRpcError, Block] = {
blockchain
.getBlockByNumber(number)
.map(Right.apply)
.getOrElse(Left(JsonRpcError.InvalidParams(s"Block $number not found")))
}

blockParam match {
case BlockParam.WithNumber(blockNumber) => getBlock(blockNumber).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Earliest => getBlock(0).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Latest => getBlock(blockchain.getBestBlockNumber()).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Pending =>
ledger.consensus.blockGenerator.getPendingBlockAndState
.map(pb => ResolvedBlock(pb.pendingBlock.block, pendingState = Some(pb.worldState)))
.map(Right.apply)
.getOrElse(resolveBlock(blockchain, ledger, BlockParam.Latest)) //Default behavior in other clients
}
}

}

Expand Down Expand Up @@ -266,9 +275,10 @@ class EthService(
*/
def getBlockByNumber(request: BlockByNumberRequest): ServiceResponse[BlockByNumberResponse] = Task {
val BlockByNumberRequest(blockParam, fullTxs) = request
val blockResponseOpt = resolveBlock(blockParam).toOption.map { case ResolvedBlock(block, pending) =>
val weight = blockchain.getChainWeightByHash(block.header.hash)
BlockResponse(block, weight, fullTxs = fullTxs, pendingBlock = pending.isDefined)
val blockResponseOpt = resolveBlock(blockchain, ledger, blockParam).toOption.map {
case ResolvedBlock(block, pending) =>
val weight = blockchain.getChainWeightByHash(block.header.hash)
BlockResponse(block, weight, fullTxs = fullTxs, pendingBlock = pending.isDefined)
}
Right(BlockByNumberResponse(blockResponseOpt))
}
Expand Down Expand Up @@ -310,7 +320,7 @@ class EthService(
request: UncleByBlockNumberAndIndexRequest
): ServiceResponse[UncleByBlockNumberAndIndexResponse] = Task {
val UncleByBlockNumberAndIndexRequest(blockParam, uncleIndex) = request
val uncleBlockResponseOpt = resolveBlock(blockParam).toOption
val uncleBlockResponseOpt = resolveBlock(blockchain, ledger, blockParam).toOption
.flatMap { case ResolvedBlock(block, pending) =>
if (uncleIndex >= 0 && uncleIndex < block.body.uncleNodesList.size) {
val uncleHeader = block.body.uncleNodesList.apply(uncleIndex.toInt)
Expand Down Expand Up @@ -491,26 +501,11 @@ class EthService(
}
}

def getCode(req: GetCodeRequest): ServiceResponse[GetCodeResponse] = {
Task {
resolveBlock(req.block).map { case ResolvedBlock(block, _) =>
val world = blockchain.getWorldStateProxy(
block.header.number,
blockchainConfig.accountStartNonce,
block.header.stateRoot,
noEmptyAccounts = false,
ethCompatibleStorage = blockchainConfig.ethCompatibleStorage
)
GetCodeResponse(world.getCode(req.address))
}
}
}

def getUncleCountByBlockNumber(
req: GetUncleCountByBlockNumberRequest
): ServiceResponse[GetUncleCountByBlockNumberResponse] = {
Task {
resolveBlock(req.block).map { case ResolvedBlock(block, _) =>
resolveBlock(blockchain, ledger, req.block).map { case ResolvedBlock(block, _) =>
GetUncleCountByBlockNumberResponse(block.body.uncleNodesList.size)
}
}
Expand All @@ -535,29 +530,12 @@ class EthService(
req: GetBlockTransactionCountByNumberRequest
): ServiceResponse[GetBlockTransactionCountByNumberResponse] = {
Task {
resolveBlock(req.block).map { case ResolvedBlock(block, _) =>
resolveBlock(blockchain, ledger, req.block).map { case ResolvedBlock(block, _) =>
GetBlockTransactionCountByNumberResponse(block.body.transactionList.size)
}
}
}

def getBalance(req: GetBalanceRequest): ServiceResponse[GetBalanceResponse] =
withAccount(req.address, req.block) { account =>
GetBalanceResponse(account.balance)
}

def getStorageAt(req: GetStorageAtRequest): ServiceResponse[GetStorageAtResponse] =
withAccount(req.address, req.block) { account =>
GetStorageAtResponse(
blockchain.getAccountStorageAt(account.storageRoot, req.position, blockchainConfig.ethCompatibleStorage)
)
}

def getTransactionCount(req: GetTransactionCountRequest): ServiceResponse[GetTransactionCountResponse] =
withAccount(req.address, req.block) { account =>
GetTransactionCountResponse(account.nonce)
}

def newFilter(req: NewFilterRequest): ServiceResponse[NewFilterResponse] = {
implicit val timeout: Timeout = Timeout(filterConfig.filterManagerQueryTimeout)

Expand Down Expand Up @@ -625,49 +603,16 @@ class EthService(
}
}

private def withAccount[T](address: Address, blockParam: BlockParam)(makeResponse: Account => T): ServiceResponse[T] =
Task {
resolveBlock(blockParam)
.map { case ResolvedBlock(block, _) =>
blockchain
.getAccount(address, block.header.number)
.getOrElse(Account.empty(blockchainConfig.accountStartNonce))
}
.map(makeResponse)
}.onErrorRecover { case _: MissingNodeException =>
Left(JsonRpcError.NodeNotFound)
}

private def resolveBlock(blockParam: BlockParam): Either[JsonRpcError, ResolvedBlock] = {
def getBlock(number: BigInt): Either[JsonRpcError, Block] = {
blockchain
.getBlockByNumber(number)
.map(Right.apply)
.getOrElse(Left(JsonRpcError.InvalidParams(s"Block $number not found")))
}

blockParam match {
case BlockParam.WithNumber(blockNumber) => getBlock(blockNumber).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Earliest => getBlock(0).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Latest => getBlock(blockchain.getBestBlockNumber()).map(ResolvedBlock(_, pendingState = None))
case BlockParam.Pending =>
blockGenerator.getPendingBlockAndState
.map(pb => ResolvedBlock(pb.pendingBlock.block, pendingState = Some(pb.worldState)))
.map(Right.apply)
.getOrElse(resolveBlock(BlockParam.Latest)) //Default behavior in other clients
}
}

private def doCall[A](req: CallRequest)(
f: (SignedTransactionWithSender, BlockHeader, Option[InMemoryWorldStateProxy]) => A
): Either[JsonRpcError, A] = for {
stx <- prepareTransaction(req)
block <- resolveBlock(req.block)
block <- resolveBlock(blockchain, ledger, req.block)
} yield f(stx, block.block.header, block.pendingState)

private def getGasLimit(req: CallRequest): Either[JsonRpcError, BigInt] =
if (req.tx.gas.isDefined) Right[JsonRpcError, BigInt](req.tx.gas.get)
else resolveBlock(BlockParam.Latest).map(r => r.block.header.gasLimit)
else resolveBlock(blockchain, ledger, BlockParam.Latest).map(r => r.block.header.gasLimit)

private def prepareTransaction(req: CallRequest): Either[JsonRpcError, SignedTransactionWithSender] = {
getGasLimit(req).map { gasLimit =>
Expand All @@ -689,9 +634,4 @@ class EthService(
}
}

def getStorageRoot(req: GetStorageRootRequest): ServiceResponse[GetStorageRootResponse] =
withAccount(req.address, req.block) { account =>
GetStorageRootResponse(account.storageRoot)
}

}

0 comments on commit cda21c9

Please sign in to comment.