Skip to content

Commit 8a18c76

Browse files
authored
[ETCM-126] getRawTransactionBy* (#720)
* [ETCM-126] add JSON-RPC eth_getRawTransactionByHash, eth_getRawTransactionByBlockHashAndIndex, getTransactionDataByBlockNumberAndIndexRequest
1 parent 11755e2 commit 8a18c76

File tree

11 files changed

+657
-322
lines changed

11 files changed

+657
-322
lines changed

insomnia_workspace.json

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,117 @@
737737
"settingFollowRedirects": "global",
738738
"_type": "request"
739739
},
740+
{
741+
"_id": "req_056848524b07432f9f0167540b54903b",
742+
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
743+
"modified": 1602080919748,
744+
"created": 1602080239083,
745+
"url": "{{ node_url }}",
746+
"name": "eth_getRawTransactionByHash",
747+
"description": "Returns raw transaction data of a transaction with the given hash",
748+
"method": "POST",
749+
"body": {
750+
"mimeType": "application/json",
751+
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByHash\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
752+
},
753+
"parameters": [],
754+
"headers": [
755+
{
756+
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
757+
"name": "Content-Type",
758+
"value": "application/json"
759+
},
760+
{
761+
"id": "pair_088edc31f5e04f20a16b465a673871bb",
762+
"name": "Cache-Control",
763+
"value": "no-cache"
764+
}
765+
],
766+
"authentication": {},
767+
"metaSortKey": -1552732410741.25,
768+
"isPrivate": false,
769+
"settingStoreCookies": true,
770+
"settingSendCookies": true,
771+
"settingDisableRenderRequestBody": false,
772+
"settingEncodeUrl": true,
773+
"settingRebuildPath": true,
774+
"settingFollowRedirects": "global",
775+
"_type": "request"
776+
},
777+
{
778+
"_id": "req_e9008e2f2aa14acca2f3e6eca0c30677",
779+
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
780+
"modified": 1602080922028,
781+
"created": 1602080826211,
782+
"url": "{{ node_url }}",
783+
"name": "eth_getRawTransactionByBlockHashAndIndex",
784+
"description": "Returns raw transaction data of a transaction with the block hash and index of which it was mined",
785+
"method": "POST",
786+
"body": {
787+
"mimeType": "application/json",
788+
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByBlockHashAndIndex\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
789+
},
790+
"parameters": [],
791+
"headers": [
792+
{
793+
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
794+
"name": "Content-Type",
795+
"value": "application/json"
796+
},
797+
{
798+
"id": "pair_088edc31f5e04f20a16b465a673871bb",
799+
"name": "Cache-Control",
800+
"value": "no-cache"
801+
}
802+
],
803+
"authentication": {},
804+
"metaSortKey": -1552732410728.75,
805+
"isPrivate": false,
806+
"settingStoreCookies": true,
807+
"settingSendCookies": true,
808+
"settingDisableRenderRequestBody": false,
809+
"settingEncodeUrl": true,
810+
"settingRebuildPath": true,
811+
"settingFollowRedirects": "global",
812+
"_type": "request"
813+
},
814+
{
815+
"_id": "req_37fea96b211241f495fc5d09081a5045",
816+
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
817+
"modified": 1602080924128,
818+
"created": 1602080883045,
819+
"url": "{{ node_url }}",
820+
"name": "eth_getRawTransactionByBlockNumberAndIndex",
821+
"description": "Returns raw transaction data of a transaction with the block number and index of which it was mined",
822+
"method": "POST",
823+
"body": {
824+
"mimeType": "application/json",
825+
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_getRawTransactionByBlockNumberAndIndex\", \n\t\"params\": [\"0x926db397ed35bedee660fe5bf6879679fa71f1fe8c27823f7f6a1e5d96a49b94\"],\n \"id\": 1\n}"
826+
},
827+
"parameters": [],
828+
"headers": [
829+
{
830+
"id": "pair_9f4d6a9dde554cd384487e04fa3b21aa",
831+
"name": "Content-Type",
832+
"value": "application/json"
833+
},
834+
{
835+
"id": "pair_088edc31f5e04f20a16b465a673871bb",
836+
"name": "Cache-Control",
837+
"value": "no-cache"
838+
}
839+
],
840+
"authentication": {},
841+
"metaSortKey": -1552732410722.5,
842+
"isPrivate": false,
843+
"settingStoreCookies": true,
844+
"settingSendCookies": true,
845+
"settingDisableRenderRequestBody": false,
846+
"settingEncodeUrl": true,
847+
"settingRebuildPath": true,
848+
"settingFollowRedirects": "global",
849+
"_type": "request"
850+
},
740851
{
741852
"_id": "req_71950018809a482da79fc927070de862",
742853
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",

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

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,14 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
155155
Extraction.decompose(t.txResponse)
156156
}
157157

158-
implicit val eth_getTransactionByBlockHashAndIndex =
159-
new JsonDecoder[GetTransactionByBlockHashAndIndexRequest]
160-
with JsonEncoder[GetTransactionByBlockHashAndIndexResponse] {
158+
implicit val GetTransactionByBlockHashAndIndexResponseEncoder =
159+
new JsonEncoder[GetTransactionByBlockHashAndIndexResponse] {
160+
override def encodeJson(t: GetTransactionByBlockHashAndIndexResponse): JValue =
161+
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
162+
}
163+
164+
implicit val GetTransactionByBlockHashAndIndexRequestDecoder =
165+
new JsonDecoder[GetTransactionByBlockHashAndIndexRequest] {
161166
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionByBlockHashAndIndexRequest] =
162167
params match {
163168
case Some(JArray(JString(blockHash) :: transactionIndex :: Nil)) =>
@@ -167,27 +172,31 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
167172
} yield GetTransactionByBlockHashAndIndexRequest(parsedBlockHash, parsedTransactionIndex)
168173
case _ => Left(InvalidParams())
169174
}
175+
}
170176

171-
override def encodeJson(t: GetTransactionByBlockHashAndIndexResponse): JValue =
177+
implicit val GetTransactionByBlockNumberAndIndexResponseEncoder =
178+
new JsonEncoder[GetTransactionByBlockNumberAndIndexResponse] {
179+
override def encodeJson(t: GetTransactionByBlockNumberAndIndexResponse): JValue =
172180
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
173181
}
174182

175-
implicit val eth_getTransactionByBlockNumberAndIndex =
176-
new JsonDecoder[GetTransactionByBlockNumberAndIndexRequest]
177-
with JsonEncoder[GetTransactionByBlockNumberAndIndexResponse] {
178-
override def decodeJson(
179-
params: Option[JArray]
180-
): Either[JsonRpcError, GetTransactionByBlockNumberAndIndexRequest] = params match {
181-
case Some(JArray(blockParam :: transactionIndex :: Nil)) =>
182-
for {
183-
blockParam <- extractBlockParam(blockParam)
184-
parsedTransactionIndex <- extractQuantity(transactionIndex)
185-
} yield GetTransactionByBlockNumberAndIndexRequest(blockParam, parsedTransactionIndex)
186-
case _ => Left(InvalidParams())
183+
implicit val GetTransactionByBlockNumberAndIndexRequestDecoder =
184+
new JsonDecoder[GetTransactionByBlockNumberAndIndexRequest] {
185+
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionByBlockNumberAndIndexRequest] =
186+
params match {
187+
case Some(JArray(blockParam :: transactionIndex :: Nil)) =>
188+
for {
189+
blockParam <- extractBlockParam(blockParam)
190+
parsedTransactionIndex <- extractQuantity(transactionIndex)
191+
} yield GetTransactionByBlockNumberAndIndexRequest(blockParam, parsedTransactionIndex)
192+
case _ => Left(InvalidParams())
187193
}
194+
}
188195

189-
override def encodeJson(t: GetTransactionByBlockNumberAndIndexResponse): JValue =
190-
t.transactionResponse.map(Extraction.decompose).getOrElse(JNull)
196+
implicit val RawTransactionResponseJsonEncoder: JsonEncoder[RawTransactionResponse] =
197+
new JsonEncoder[RawTransactionResponse] {
198+
override def encodeJson(t: RawTransactionResponse): JValue =
199+
t.transactionResponse.map(RawTransactionCodec.asRawTransaction _ andThen encodeAsHex)
191200
}
192201

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

582591
def encodeJson(t: GetStorageRootResponse): JValue = encodeAsHex(t.storageRoot)
583592
}
584-
585593
}

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

Lines changed: 91 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ object EthService {
8282
case class GetTransactionByBlockNumberAndIndexRequest(block: BlockParam, transactionIndex: BigInt)
8383
case class GetTransactionByBlockNumberAndIndexResponse(transactionResponse: Option[TransactionResponse])
8484

85+
case class RawTransactionResponse(transactionResponse: Option[SignedTransaction])
86+
8587
case class GetHashRateRequest()
8688
case class GetHashRateResponse(hashRate: BigInt)
8789

@@ -281,6 +283,21 @@ class EthService(
281283
Right(BlockByNumberResponse(blockResponseOpt))
282284
}
283285

286+
/**
287+
* Implements the eth_getRawTransactionByHash - fetch raw transaction data of a transaction with the given hash.
288+
*
289+
* The tx requested will be fetched from the pending tx pool or from the already executed txs (depending on the tx state)
290+
*
291+
* @param req with the tx requested (by it's hash)
292+
* @return the raw transaction hask or None if the client doesn't have the tx
293+
*/
294+
def getRawTransactionByHash(req: GetTransactionByHashRequest): ServiceResponse[RawTransactionResponse] = {
295+
getTransactionDataByHash(req.txHash).map(asRawTransactionResponse)
296+
}
297+
298+
private def asRawTransactionResponse(txResponse: Option[TransactionData]): Right[Nothing, RawTransactionResponse] =
299+
Right(RawTransactionResponse(txResponse.map(_.stx)))
300+
284301
/**
285302
* Implements the eth_getTransactionByHash method that fetches a requested tx.
286303
* The tx requested will be fetched from the pending tx pool or from the already executed txs (depending on the tx state)
@@ -289,23 +306,24 @@ class EthService(
289306
* @return the tx requested or None if the client doesn't have the tx
290307
*/
291308
def getTransactionByHash(req: GetTransactionByHashRequest): ServiceResponse[GetTransactionByHashResponse] = {
292-
val maybeTxPendingResponse: Future[Option[TransactionResponse]] = getTransactionsFromPool.map {
293-
_.pendingTransactions.map(_.stx.tx).find(_.hash == req.txHash).map(TransactionResponse(_))
309+
val eventualMaybeData = getTransactionDataByHash(req.txHash)
310+
eventualMaybeData.map(txResponse => Right(GetTransactionByHashResponse(txResponse.map(TransactionResponse(_)))))
311+
}
312+
313+
def getTransactionDataByHash(txHash: ByteString): Future[Option[TransactionData]] = {
314+
val maybeTxPendingResponse: Future[Option[TransactionData]] = getTransactionsFromPool.map {
315+
_.pendingTransactions.map(_.stx.tx).find(_.hash == txHash).map(TransactionData(_))
294316
}
295317

296-
val maybeTxResponse: Future[Option[TransactionResponse]] = maybeTxPendingResponse.flatMap { txPending =>
297-
Future {
298-
txPending.orElse {
299-
for {
300-
TransactionLocation(blockHash, txIndex) <- blockchain.getTransactionLocation(req.txHash)
301-
Block(header, body) <- blockchain.getBlockByHash(blockHash)
302-
stx <- body.transactionList.lift(txIndex)
303-
} yield TransactionResponse(stx, Some(header), Some(txIndex))
304-
}
318+
maybeTxPendingResponse.map { txPending =>
319+
txPending.orElse {
320+
for {
321+
TransactionLocation(blockHash, txIndex) <- blockchain.getTransactionLocation(txHash)
322+
Block(header, body) <- blockchain.getBlockByHash(blockHash)
323+
stx <- body.transactionList.lift(txIndex)
324+
} yield TransactionData(stx, Some(header), Some(txIndex))
305325
}
306326
}
307-
308-
maybeTxResponse.map(txResponse => Right(GetTransactionByHashResponse(txResponse)))
309327
}
310328

311329
def getTransactionReceipt(req: GetTransactionReceiptRequest): ServiceResponse[GetTransactionReceiptResponse] =
@@ -361,20 +379,32 @@ class EthService(
361379
*
362380
* @return the tx requested or None if the client doesn't have the block or if there's no tx in the that index
363381
*/
364-
def getTransactionByBlockHashAndIndexRequest(
382+
def getTransactionByBlockHashAndIndex(
365383
req: GetTransactionByBlockHashAndIndexRequest
366-
): ServiceResponse[GetTransactionByBlockHashAndIndexResponse] = Future {
367-
import req._
368-
val maybeTransactionResponse = blockchain.getBlockByHash(blockHash).flatMap { blockWithTx =>
369-
val blockTxs = blockWithTx.body.transactionList
370-
if (transactionIndex >= 0 && transactionIndex < blockTxs.size)
371-
Some(
372-
TransactionResponse(blockTxs(transactionIndex.toInt), Some(blockWithTx.header), Some(transactionIndex.toInt))
373-
)
374-
else None
384+
): ServiceResponse[GetTransactionByBlockHashAndIndexResponse] =
385+
getTransactionByBlockHashAndIndex(req.blockHash, req.transactionIndex)
386+
.map(td => Right(GetTransactionByBlockHashAndIndexResponse(td.map(TransactionResponse(_)))))
387+
388+
/**
389+
* eth_getRawTransactionByBlockHashAndIndex returns raw transaction data of a transaction with the block hash and index of which it was mined
390+
*
391+
* @return the tx requested or None if the client doesn't have the block or if there's no tx in the that index
392+
*/
393+
def getRawTransactionByBlockHashAndIndex(
394+
req: GetTransactionByBlockHashAndIndexRequest
395+
): ServiceResponse[RawTransactionResponse] =
396+
getTransactionByBlockHashAndIndex(req.blockHash, req.transactionIndex)
397+
.map(asRawTransactionResponse)
398+
399+
400+
private def getTransactionByBlockHashAndIndex(blockHash: ByteString, transactionIndex: BigInt) =
401+
Future {
402+
for {
403+
blockWithTx <- blockchain.getBlockByHash(blockHash)
404+
blockTxs = blockWithTx.body.transactionList if transactionIndex >= 0 && transactionIndex < blockTxs.size
405+
transaction <- blockTxs.lift(transactionIndex.toInt)
406+
} yield TransactionData(transaction, Some(blockWithTx.header), Some(transactionIndex.toInt))
375407
}
376-
Right(GetTransactionByBlockHashAndIndexResponse(maybeTransactionResponse))
377-
}
378408

379409
/**
380410
* Implements the eth_getUncleByBlockHashAndIndex method that fetches an uncle from a certain index in a requested block.
@@ -697,28 +727,52 @@ class EthService(
697727
}
698728
}
699729

700-
def getTransactionByBlockNumberAndIndexRequest(
730+
/**
731+
* eth_getTransactionByBlockNumberAndIndex Returns the information about a transaction with
732+
* the block number and index of which it was mined.
733+
*
734+
* @param req block number and index
735+
* @return transaction
736+
*/
737+
def getTransactionByBlockNumberAndIndex(
701738
req: GetTransactionByBlockNumberAndIndexRequest
702739
): ServiceResponse[GetTransactionByBlockNumberAndIndexResponse] = Future {
703-
import req._
740+
getTransactionDataByBlockNumberAndIndex(req.block, req.transactionIndex)
741+
.map(_.map(TransactionResponse(_)))
742+
.map(GetTransactionByBlockNumberAndIndexResponse)
743+
}
744+
745+
/**
746+
* eth_getRawTransactionByBlockNumberAndIndex Returns raw transaction data of a transaction
747+
* with the block number and index of which it was mined.
748+
*
749+
* @param req block number and ordering in which a transaction is mined within its block
750+
* @return raw transaction data
751+
*/
752+
def getRawTransactionByBlockNumberAndIndex(
753+
req: GetTransactionByBlockNumberAndIndexRequest
754+
): ServiceResponse[RawTransactionResponse] = Future {
755+
getTransactionDataByBlockNumberAndIndex(req.block, req.transactionIndex)
756+
.map(x => x.map(_.stx))
757+
.map(RawTransactionResponse)
758+
}
759+
760+
private def getTransactionDataByBlockNumberAndIndex(block: BlockParam, transactionIndex: BigInt) = {
704761
resolveBlock(block)
705762
.map { blockWithTx =>
706763
val blockTxs = blockWithTx.block.body.transactionList
707764
if (transactionIndex >= 0 && transactionIndex < blockTxs.size)
708-
GetTransactionByBlockNumberAndIndexResponse(
709-
Some(
710-
TransactionResponse(
711-
blockTxs(transactionIndex.toInt),
712-
Some(blockWithTx.block.header),
713-
Some(transactionIndex.toInt)
714-
)
765+
Some(
766+
TransactionData(
767+
blockTxs(transactionIndex.toInt),
768+
Some(blockWithTx.block.header),
769+
Some(transactionIndex.toInt)
715770
)
716771
)
717-
else
718-
GetTransactionByBlockNumberAndIndexResponse(None)
772+
else None
719773
}
720774
.left
721-
.flatMap(_ => Right(GetTransactionByBlockNumberAndIndexResponse(None)))
775+
.flatMap(_ => Right(None))
722776
}
723777

724778
def getBalance(req: GetBalanceRequest): ServiceResponse[GetBalanceResponse] = {

0 commit comments

Comments
 (0)