Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
75335b0
[ETCM-126] implement JSON-RPC eth_getRawTransactionByHash
Oct 2, 2020
1471d1a
[ETCM-126] add eth_getRawTransactionByBlockHashAndIndex
Oct 2, 2020
adde5eb
[ETCM-126] add TODO for tests getRawTransactionByHash, getRawTransact…
Oct 2, 2020
5ac3f7a
[ETCM-126] add tests for eth_getRawTransactionByBlockHashAndIndex ret…
Oct 5, 2020
158e117
[ETCM-126] add unit tests for eth_getRawTransactionByHash
Oct 5, 2020
64f34b4
[ETCM-126] implement JSON-RPC eth_getRawTransactionByBlockHashAndIndex
Oct 5, 2020
f99019e
[ETCM-126] extract common utils for handle RawTransaction
Oct 5, 2020
f0e2b72
[ETCM-126] extract drop unused JsonRpcController specs
Oct 5, 2020
dd10538
[ETCM-126] extract common part: getTransactionDataByBlockNumberAndInd…
Oct 6, 2020
74ce39e
[ETCM-126] extract common part: getTransactionByBlockHashAndIndexRequest
Oct 6, 2020
bd63640
[ETCM-126] review: remove redundant XXXRequest in get transaction fun…
Oct 7, 2020
e1d8d75
[ETCM-126] review: remove redundant XXXRequest in get transaction fun…
Oct 7, 2020
1bbf078
[ETCM-126] review: unify GetRawXXX responses into RawTransactionResponse
Oct 8, 2020
3061304
[ETCM-126] review: reuse GetTransactionXXX requests to handle GetRawT…
Oct 8, 2020
d208983
[ETCM-126] review: remove rawTransactionFromBlock and move encoding t…
Oct 8, 2020
c88e90b
[ETCM-126] review: add JsonRpcController tests for eth_getRawTransact…
Oct 8, 2020
255aa3c
[ETCM-126] add JSON RPC test for eth_getRawTransactionByBlockHashAndI…
Oct 8, 2020
6912d15
[ETCM-126] add JSON RPC test for eth_getRawTransactionByHash
Oct 8, 2020
7bb0d21
[ETCM-126] review: use custom matchers from JRCMatchers
Oct 9, 2020
f5fd099
Merge branch 'develop' into ETCM-126-get-raw-transaction
lemastero Oct 9, 2020
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
111 changes: 111 additions & 0 deletions insomnia_workspace.json
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,117 @@
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_056848524b07432f9f0167540b54903b",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
"modified": 1602080919748,
"created": 1602080239083,
"url": "{{ node_url }}",
"name": "eth_getRawTransactionByHash",
"description": "Returns raw transaction data of a transaction with the given hash",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByHash\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
},
"parameters": [],
"headers": [
{
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
"name": "Content-Type",
"value": "application/json"
},
{
"id": "pair_088edc31f5e04f20a16b465a673871bb",
"name": "Cache-Control",
"value": "no-cache"
}
],
"authentication": {},
"metaSortKey": -1552732410741.25,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_e9008e2f2aa14acca2f3e6eca0c30677",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
"modified": 1602080922028,
"created": 1602080826211,
"url": "{{ node_url }}",
"name": "eth_getRawTransactionByBlockHashAndIndex",
"description": "Returns raw transaction data of a transaction with the block hash and index of which it was mined",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByBlockHashAndIndex\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
},
"parameters": [],
"headers": [
{
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
"name": "Content-Type",
"value": "application/json"
},
{
"id": "pair_088edc31f5e04f20a16b465a673871bb",
"name": "Cache-Control",
"value": "no-cache"
}
],
"authentication": {},
"metaSortKey": -1552732410728.75,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_37fea96b211241f495fc5d09081a5045",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
"modified": 1602080924128,
"created": 1602080883045,
"url": "{{ node_url }}",
"name": "eth_getRawTransactionByBlockNumberAndIndex",
"description": "Returns raw transaction data of a transaction with the block number and index of which it was mined",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByBlockNumberAndIndex\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
},
"parameters": [],
"headers": [
{
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
"name": "Content-Type",
"value": "application/json"
},
{
"id": "pair_088edc31f5e04f20a16b465a673871bb",
"name": "Cache-Control",
"value": "no-cache"
}
],
"authentication": {},
"metaSortKey": -1552732410722.5,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_71950018809a482da79fc927070de862",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,14 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
Extraction.decompose(t.txResponse)
}

implicit val eth_getTransactionByBlockHashAndIndex =
new JsonDecoder[GetTransactionByBlockHashAndIndexRequest]
with JsonEncoder[GetTransactionByBlockHashAndIndexResponse] {
implicit val GetTransactionByBlockHashAndIndexResponseEncoder =
new JsonEncoder[GetTransactionByBlockHashAndIndexResponse] {
override def encodeJson(t: GetTransactionByBlockHashAndIndexResponse): JValue =
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
}

implicit val GetTransactionByBlockHashAndIndexRequestDecoder =
new JsonDecoder[GetTransactionByBlockHashAndIndexRequest] {
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionByBlockHashAndIndexRequest] =
params match {
case Some(JArray(JString(blockHash) :: transactionIndex :: Nil)) =>
Expand All @@ -167,27 +172,31 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
} yield GetTransactionByBlockHashAndIndexRequest(parsedBlockHash, parsedTransactionIndex)
case _ => Left(InvalidParams())
}
}

override def encodeJson(t: GetTransactionByBlockHashAndIndexResponse): JValue =
implicit val GetTransactionByBlockNumberAndIndexResponseEncoder =
new JsonEncoder[GetTransactionByBlockNumberAndIndexResponse] {
override def encodeJson(t: GetTransactionByBlockNumberAndIndexResponse): JValue =
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
}

implicit val eth_getTransactionByBlockNumberAndIndex =
new JsonDecoder[GetTransactionByBlockNumberAndIndexRequest]
with JsonEncoder[GetTransactionByBlockNumberAndIndexResponse] {
override def decodeJson(
params: Option[JArray]
): Either[JsonRpcError, GetTransactionByBlockNumberAndIndexRequest] = params match {
case Some(JArray(blockParam :: transactionIndex :: Nil)) =>
for {
blockParam <- extractBlockParam(blockParam)
parsedTransactionIndex <- extractQuantity(transactionIndex)
} yield GetTransactionByBlockNumberAndIndexRequest(blockParam, parsedTransactionIndex)
case _ => Left(InvalidParams())
implicit val GetTransactionByBlockNumberAndIndexRequestDecoder =
new JsonDecoder[GetTransactionByBlockNumberAndIndexRequest] {
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionByBlockNumberAndIndexRequest] =
params match {
case Some(JArray(blockParam :: transactionIndex :: Nil)) =>
for {
blockParam <- extractBlockParam(blockParam)
parsedTransactionIndex <- extractQuantity(transactionIndex)
} yield GetTransactionByBlockNumberAndIndexRequest(blockParam, parsedTransactionIndex)
case _ => Left(InvalidParams())
}
}

override def encodeJson(t: GetTransactionByBlockNumberAndIndexResponse): JValue =
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
implicit val RawTransactionResponseJsonEncoder: JsonEncoder[RawTransactionResponse] =
new JsonEncoder[RawTransactionResponse] {
override def encodeJson(t: RawTransactionResponse): JValue =
t.transactionResponse.map(RawTransactionCodec.asRawTransaction _ andThen encodeAsHex)
}

implicit val eth_getUncleByBlockHashAndIndex = new JsonDecoder[UncleByBlockHashAndIndexRequest]
Expand Down Expand Up @@ -581,5 +590,4 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {

def encodeJson(t: GetStorageRootResponse): JValue = encodeAsHex(t.storageRoot)
}

}
128 changes: 91 additions & 37 deletions src/main/scala/io/iohk/ethereum/jsonrpc/EthService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ object EthService {
case class GetTransactionByBlockNumberAndIndexRequest(block: BlockParam, transactionIndex: BigInt)
case class GetTransactionByBlockNumberAndIndexResponse(transactionResponse: Option[TransactionResponse])

case class RawTransactionResponse(transactionResponse: Option[SignedTransaction])

case class GetHashRateRequest()
case class GetHashRateResponse(hashRate: BigInt)

Expand Down Expand Up @@ -281,6 +283,21 @@ class EthService(
Right(BlockByNumberResponse(blockResponseOpt))
}

/**
* Implements the eth_getRawTransactionByHash - fetch raw transaction data of a transaction with the given hash.
*
* The tx requested will be fetched from the pending tx pool or from the already executed txs (depending on the tx state)
*
* @param req with the tx requested (by it's hash)
* @return the raw transaction hask or None if the client doesn't have the tx
*/
def getRawTransactionByHash(req: GetTransactionByHashRequest): ServiceResponse[RawTransactionResponse] = {
getTransactionDataByHash(req.txHash).map(asRawTransactionResponse)
}

private def asRawTransactionResponse(txResponse: Option[TransactionData]): Right[Nothing, RawTransactionResponse] =
Right(RawTransactionResponse(txResponse.map(_.stx)))

/**
* Implements the eth_getTransactionByHash method that fetches a requested tx.
* The tx requested will be fetched from the pending tx pool or from the already executed txs (depending on the tx state)
Expand All @@ -289,23 +306,24 @@ class EthService(
* @return the tx requested or None if the client doesn't have the tx
*/
def getTransactionByHash(req: GetTransactionByHashRequest): ServiceResponse[GetTransactionByHashResponse] = {
val maybeTxPendingResponse: Future[Option[TransactionResponse]] = getTransactionsFromPool.map {
_.pendingTransactions.map(_.stx.tx).find(_.hash == req.txHash).map(TransactionResponse(_))
val eventualMaybeData = getTransactionDataByHash(req.txHash)
eventualMaybeData.map(txResponse => Right(GetTransactionByHashResponse(txResponse.map(TransactionResponse(_)))))
}

def getTransactionDataByHash(txHash: ByteString): Future[Option[TransactionData]] = {
val maybeTxPendingResponse: Future[Option[TransactionData]] = getTransactionsFromPool.map {
_.pendingTransactions.map(_.stx.tx).find(_.hash == txHash).map(TransactionData(_))
}

val maybeTxResponse: Future[Option[TransactionResponse]] = maybeTxPendingResponse.flatMap { txPending =>
Future {
txPending.orElse {
for {
TransactionLocation(blockHash, txIndex) <- blockchain.getTransactionLocation(req.txHash)
Block(header, body) <- blockchain.getBlockByHash(blockHash)
stx <- body.transactionList.lift(txIndex)
} yield TransactionResponse(stx, Some(header), Some(txIndex))
}
maybeTxPendingResponse.map { txPending =>
txPending.orElse {
for {
TransactionLocation(blockHash, txIndex) <- blockchain.getTransactionLocation(txHash)
Block(header, body) <- blockchain.getBlockByHash(blockHash)
stx <- body.transactionList.lift(txIndex)
} yield TransactionData(stx, Some(header), Some(txIndex))
}
}

maybeTxResponse.map(txResponse => Right(GetTransactionByHashResponse(txResponse)))
}

def getTransactionReceipt(req: GetTransactionReceiptRequest): ServiceResponse[GetTransactionReceiptResponse] =
Expand Down Expand Up @@ -361,20 +379,32 @@ class EthService(
*
* @return the tx requested or None if the client doesn't have the block or if there's no tx in the that index
*/
def getTransactionByBlockHashAndIndexRequest(
def getTransactionByBlockHashAndIndex(
req: GetTransactionByBlockHashAndIndexRequest
): ServiceResponse[GetTransactionByBlockHashAndIndexResponse] = Future {
import req._
val maybeTransactionResponse = blockchain.getBlockByHash(blockHash).flatMap { blockWithTx =>
val blockTxs = blockWithTx.body.transactionList
if (transactionIndex >= 0 && transactionIndex < blockTxs.size)
Some(
TransactionResponse(blockTxs(transactionIndex.toInt), Some(blockWithTx.header), Some(transactionIndex.toInt))
)
else None
): ServiceResponse[GetTransactionByBlockHashAndIndexResponse] =
getTransactionByBlockHashAndIndex(req.blockHash, req.transactionIndex)
.map(td => Right(GetTransactionByBlockHashAndIndexResponse(td.map(TransactionResponse(_)))))

/**
* eth_getRawTransactionByBlockHashAndIndex returns raw transaction data of a transaction with the block hash and index of which it was mined
*
* @return the tx requested or None if the client doesn't have the block or if there's no tx in the that index
*/
def getRawTransactionByBlockHashAndIndex(
req: GetTransactionByBlockHashAndIndexRequest
): ServiceResponse[RawTransactionResponse] =
getTransactionByBlockHashAndIndex(req.blockHash, req.transactionIndex)
.map(asRawTransactionResponse)


private def getTransactionByBlockHashAndIndex(blockHash: ByteString, transactionIndex: BigInt) =
Future {
for {
blockWithTx <- blockchain.getBlockByHash(blockHash)
blockTxs = blockWithTx.body.transactionList if transactionIndex >= 0 && transactionIndex < blockTxs.size
transaction <- blockTxs.lift(transactionIndex.toInt)
} yield TransactionData(transaction, Some(blockWithTx.header), Some(transactionIndex.toInt))
}
Right(GetTransactionByBlockHashAndIndexResponse(maybeTransactionResponse))
}

/**
* Implements the eth_getUncleByBlockHashAndIndex method that fetches an uncle from a certain index in a requested block.
Expand Down Expand Up @@ -697,28 +727,52 @@ class EthService(
}
}

def getTransactionByBlockNumberAndIndexRequest(
/**
* eth_getTransactionByBlockNumberAndIndex Returns the information about a transaction with
* the block number and index of which it was mined.
*
* @param req block number and index
* @return transaction
*/
def getTransactionByBlockNumberAndIndex(
req: GetTransactionByBlockNumberAndIndexRequest
): ServiceResponse[GetTransactionByBlockNumberAndIndexResponse] = Future {
import req._
getTransactionDataByBlockNumberAndIndex(req.block, req.transactionIndex)
.map(_.map(TransactionResponse(_)))
.map(GetTransactionByBlockNumberAndIndexResponse)
}

/**
* eth_getRawTransactionByBlockNumberAndIndex Returns raw transaction data of a transaction
* with the block number and index of which it was mined.
*
* @param req block number and ordering in which a transaction is mined within its block
* @return raw transaction data
*/
def getRawTransactionByBlockNumberAndIndex(
req: GetTransactionByBlockNumberAndIndexRequest
): ServiceResponse[RawTransactionResponse] = Future {
getTransactionDataByBlockNumberAndIndex(req.block, req.transactionIndex)
.map(x => x.map(_.stx))
.map(RawTransactionResponse)
}

private def getTransactionDataByBlockNumberAndIndex(block: BlockParam, transactionIndex: BigInt) = {
resolveBlock(block)
.map { blockWithTx =>
val blockTxs = blockWithTx.block.body.transactionList
if (transactionIndex >= 0 && transactionIndex < blockTxs.size)
GetTransactionByBlockNumberAndIndexResponse(
Some(
TransactionResponse(
blockTxs(transactionIndex.toInt),
Some(blockWithTx.block.header),
Some(transactionIndex.toInt)
)
Some(
TransactionData(
blockTxs(transactionIndex.toInt),
Some(blockWithTx.block.header),
Some(transactionIndex.toInt)
)
)
else
GetTransactionByBlockNumberAndIndexResponse(None)
else None
}
.left
.flatMap(_ => Right(GetTransactionByBlockNumberAndIndexResponse(None)))
.flatMap(_ => Right(None))
}

def getBalance(req: GetBalanceRequest): ServiceResponse[GetBalanceResponse] = {
Expand Down
Loading