Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ETCM-126] add eth_pendingTransactions #726

Merged
merged 14 commits into from
Oct 15, 2020
Merged
10 changes: 10 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,13 @@ addCommandAlias(
|;benchmark:compile
|""".stripMargin
)

// prepare PR
lemastero marked this conversation as resolved.
Show resolved Hide resolved
addCommandAlias(
"pp",
""";compile-all
|;test
|;scalastyle
|;test:scalastyle
|""".stripMargin
)
74 changes: 37 additions & 37 deletions insomnia_workspace.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,43 +139,6 @@
"metaSortKey": -1600249374160,
"_type": "request_group"
},
{
"_id": "req_6197fefa1e1448a89f30712ec12295f8",
"parentId": "fld_2b54cbb84e244284b3ef752c5f805376",
"modified": 1600249571669,
"created": 1600249491560,
"url": "{{ node_url }}",
"name": "qa_getPendingTransactions",
"description": "",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"qa_getPendingTransactions\", \n \"params\": [],\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": -1552939150156.832,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_dd6e5c718f974407bb79fe3c953b7106",
"parentId": "fld_72829b866f0441e184e0d1a2030f8220",
Expand Down Expand Up @@ -961,6 +924,43 @@
"settingFollowRedirects": "global",
"_type": "request"
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove qa_getPendingTransactions from insomnia also

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And remember to notify QA once this gets merged :)

"_id": "req_4c1135a4a69644fe9850292131197b47",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
"modified": 1602234543563,
"created": 1602234438767,
"url": "{{ node_url }}",
"name": "eth_pendingTransactions",
"description": "Returns the transactions that are pending in the transaction pool and have a from address that is one of the accounts this node manages",
"method": "POST",
"body": {
"mimeType": "application/json",
"text": "{\n\t\"jsonrpc\": \"2.0\",\n \"method\": \"eth_pendingTransactions\", \n\t\"params\": [],\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": -1552732410719.375,
"isPrivate": false,
"settingStoreCookies": true,
"settingSendCookies": true,
"settingDisableRenderRequestBody": false,
"settingEncodeUrl": true,
"settingRebuildPath": true,
"settingFollowRedirects": "global",
"_type": "request"
},
{
"_id": "req_579a127b434b4ac7aa51ee6c93f04630",
"parentId": "fld_a06eb77e183c4727800eb7dc43ceabe1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
Extraction.decompose(t.blockResponse)
}

implicit val eth_pendingTransactions = new NoParamsDecoder(EthPendingTransactionsRequest())
with JsonEncoder[EthPendingTransactionsResponse] {

override def encodeJson(t: EthPendingTransactionsResponse): JValue =
JArray(t.pendingTransactions.toList.map { pendingTx =>
encodeAsHex(pendingTx.stx.tx.hash)
})
}

implicit val eth_getTransactionByHash =
new JsonDecoder[GetTransactionByHashRequest] with JsonEncoder[GetTransactionByHashResponse] {
override def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetTransactionByHashRequest] =
Expand Down
19 changes: 16 additions & 3 deletions src/main/scala/io/iohk/ethereum/jsonrpc/EthService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import io.iohk.ethereum.rlp.RLPImplicits._
import io.iohk.ethereum.rlp.RLPList
import io.iohk.ethereum.rlp.UInt256RLPImplicits._
import io.iohk.ethereum.transactions.PendingTransactionsManager
import io.iohk.ethereum.transactions.PendingTransactionsManager.PendingTransactionsResponse
import io.iohk.ethereum.transactions.PendingTransactionsManager.{PendingTransaction, PendingTransactionsResponse}
import io.iohk.ethereum.utils._
import org.bouncycastle.util.encoders.Hex

Expand Down Expand Up @@ -191,6 +191,9 @@ object EthService {

case class GetStorageRootRequest(address: Address, block: BlockParam)
case class GetStorageRootResponse(storageRoot: ByteString)

case class EthPendingTransactionsRequest()
case class EthPendingTransactionsResponse(pendingTransactions: Seq[PendingTransaction])
}

class EthService(
Expand Down Expand Up @@ -573,8 +576,8 @@ class EthService(
}
})(Future.successful(OmmersPool.Ommers(Nil))) // NOTE If not Ethash consensus, ommers do not make sense, so => Nil

// TODO This seems to be re-implemented elsewhere, probably move to a better place? Also generalize the error message.
private def getTransactionsFromPool: Future[PendingTransactionsResponse] = {
// TODO This seems to be re-implemented in TransactionPicker, probably move to a better place? Also generalize the error message.
private[jsonrpc] def getTransactionsFromPool(): Future[PendingTransactionsResponse] = {
implicit val timeout: Timeout = Timeout(getTransactionFromPoolTimeout)

(pendingTransactionsManager ? PendingTransactionsManager.GetPendingTransactions)
Expand Down Expand Up @@ -972,4 +975,14 @@ class EthService(
}
}

/**
* Returns the transactions that are pending in the transaction pool and have a from address that is one of the accounts this node manages.
*
* @param req request
* @return pending transactions
*/
def ethPendingTransactions(req: EthPendingTransactionsRequest): ServiceResponse[EthPendingTransactionsResponse] =
getTransactionsFromPool().map { resp =>
Right(EthPendingTransactionsResponse(resp.pendingTransactions))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import org.json4s.JsonDSL._
import com.typesafe.config.{Config => TypesafeConfig}
import io.iohk.ethereum.jsonrpc.DebugService.{ListPeersInfoRequest, ListPeersInfoResponse}
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
import io.iohk.ethereum.jsonrpc.QAService.{GetPendingTransactionsRequest, GetPendingTransactionsResponse}
import io.iohk.ethereum.jsonrpc.TestService._
import io.iohk.ethereum.jsonrpc.server.http.JsonRpcHttpServer.JsonRpcHttpServerConfig
import io.iohk.ethereum.jsonrpc.server.ipc.JsonRpcIpcServer.JsonRpcIpcServerConfig
Expand Down Expand Up @@ -278,6 +277,8 @@ class JsonRpcController(
ethService.getRawTransactionByBlockNumberAndIndex,
req
)
case req @ JsonRpcRequest(_, "eth_pendingTransactions", _, _) =>
handle[EthPendingTransactionsRequest, EthPendingTransactionsResponse](ethService.ethPendingTransactions, req)
}

private def handleDebugRequest: PartialFunction[JsonRpcRequest, Future[JsonRpcResponse]] = {
Expand Down Expand Up @@ -349,9 +350,6 @@ class JsonRpcController(
private def handleQARequest: PartialFunction[JsonRpcRequest, Future[JsonRpcResponse]] = {
case req @ JsonRpcRequest(_, "qa_mineBlocks", _, _) =>
handle[QAService.MineBlocksRequest, QAService.MineBlocksResponse](qaService.mineBlocks, req)

case req @ JsonRpcRequest(_, "qa_getPendingTransactions", _, _) =>
handle[GetPendingTransactionsRequest, GetPendingTransactionsResponse](qaService.getPendingTransactions, req)
}

private def handleCheckpointingRequest: PartialFunction[JsonRpcRequest, Future[JsonRpcResponse]] = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
package io.iohk.ethereum.jsonrpc

import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonDecoder.NoParamsDecoder
import io.iohk.ethereum.jsonrpc.JsonRpcController.{Codec, JsonEncoder}
import io.iohk.ethereum.jsonrpc.JsonRpcController.Codec
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
import io.iohk.ethereum.jsonrpc.QAService.{
GetPendingTransactionsRequest,
GetPendingTransactionsResponse,
MineBlocksRequest,
MineBlocksResponse
}
import io.iohk.ethereum.transactions.PendingTransactionsManager.PendingTransaction
import org.json4s.JsonAST._

object QAJsonMethodsImplicits extends JsonMethodsImplicits {
Expand All @@ -36,12 +32,4 @@ object QAJsonMethodsImplicits extends JsonMethodsImplicits {
"message" -> t.message.fold[JValue](JNull)(JString)
)
}

implicit val qa_getPendingTransactions: Codec[GetPendingTransactionsRequest, GetPendingTransactionsResponse] =
new NoParamsDecoder(GetPendingTransactionsRequest()) with JsonEncoder[GetPendingTransactionsResponse] {
def encodeJson(t: GetPendingTransactionsResponse): JValue =
JArray(t.pendingTransactions.toList.map { pendingTx: PendingTransaction =>
encodeAsHex(pendingTx.stx.tx.hash)
})
}
}
27 changes: 3 additions & 24 deletions src/main/scala/io/iohk/ethereum/jsonrpc/QAService.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,24 @@
package io.iohk.ethereum.jsonrpc

import akka.actor.ActorRef
import akka.util.ByteString
lemastero marked this conversation as resolved.
Show resolved Hide resolved
import cats.implicits._
import enumeratum._
import io.iohk.ethereum.consensus._
import io.iohk.ethereum.consensus.ethash.MinerResponses._
import io.iohk.ethereum.consensus.ethash.MockedMinerProtocol.MineBlocks
import io.iohk.ethereum.consensus.ethash.{MinerResponse, MinerResponses, TransactionPicker}
import io.iohk.ethereum.consensus.ethash.{MinerResponse, MinerResponses}
import io.iohk.ethereum.jsonrpc.QAService.MineBlocksResponse.MinerResponseType
import io.iohk.ethereum.jsonrpc.QAService.{
GetPendingTransactionsRequest,
GetPendingTransactionsResponse,
MineBlocksRequest,
MineBlocksResponse
}
import io.iohk.ethereum.transactions.PendingTransactionsManager.PendingTransaction
import io.iohk.ethereum.utils.Logger
import monix.execution.Scheduler.Implicits.global
import mouse.all._
import scala.concurrent.duration.FiniteDuration

class QAService(
consensus: Consensus,
val pendingTransactionsManager: ActorRef,
val getTransactionFromPoolTimeout: FiniteDuration
) extends Logger
with TransactionPicker {
consensus: Consensus
) extends Logger {

/**
* qa_mineBlocks that instructs mocked miner to mine given number of blocks
Expand All @@ -44,16 +36,6 @@ class QAService(
Left(JsonRpcErrors.InternalError)
}
}

/**
* qa_getPendingTransactions that returns all pending transactions from the mempool
*
* @return all pending transactions from the mempool
*/
def getPendingTransactions(req: GetPendingTransactionsRequest): ServiceResponse[GetPendingTransactionsResponse] =
getTransactionsFromPool.map { resp =>
Right(GetPendingTransactionsResponse(resp.pendingTransactions))
}
}

object QAService {
Expand Down Expand Up @@ -88,7 +70,4 @@ object QAService {
}
}
}

case class GetPendingTransactionsRequest()
case class GetPendingTransactionsResponse(pendingTransactions: Seq[PendingTransaction])
}
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,7 @@ trait PersonalServiceBuilder {
trait QaServiceBuilder {
self: ConsensusBuilder with PendingTransactionsManagerBuilder with TxPoolConfigBuilder =>

lazy val qaService =
new QAService(
consensus,
pendingTransactionsManager,
txPoolConfig.getTransactionFromPoolTimeout
)
lazy val qaService = new QAService(consensus)
}

trait CheckpointingServiceBuilder {
Expand Down
48 changes: 47 additions & 1 deletion src/test/scala/io/iohk/ethereum/jsonrpc/EthServiceSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup
import io.iohk.ethereum.consensus._
import io.iohk.ethereum.consensus.blocks.{PendingBlock, PendingBlockAndState}
import io.iohk.ethereum.consensus.ethash.blocks.EthashBlockGenerator
import io.iohk.ethereum.crypto.ECDSASignature
import io.iohk.ethereum.db.storage.AppStateStorage
import io.iohk.ethereum.domain.{Address, Block, BlockHeader, BlockchainImpl, UInt256, _}
import io.iohk.ethereum.jsonrpc.EthService.{ProtocolVersionRequest, _}
Expand All @@ -20,7 +21,7 @@ import io.iohk.ethereum.ledger.{Ledger, StxLedger}
import io.iohk.ethereum.mpt.{ByteArrayEncoder, ByteArraySerializable, MerklePatriciaTrie}
import io.iohk.ethereum.ommers.OmmersPool
import io.iohk.ethereum.transactions.PendingTransactionsManager
import io.iohk.ethereum.transactions.PendingTransactionsManager.{PendingTransaction, PendingTransactionsResponse}
import io.iohk.ethereum.transactions.PendingTransactionsManager.{GetPendingTransactions, PendingTransaction, PendingTransactionsResponse}
import io.iohk.ethereum.utils._
import io.iohk.ethereum.{Fixtures, NormalPatience, Timeouts, crypto}
import org.bouncycastle.util.encoders.Hex
Expand Down Expand Up @@ -1100,6 +1101,51 @@ class EthServiceSpec
response.futureValue shouldEqual Right(GetAccountTransactionsResponse(expectedSent))
}

it should "send message to pendingTransactionsManager and return an empty GetPendingTransactionsResponse" in new TestSetup {
val res = ethService.getTransactionsFromPool()

pendingTransactionsManager.expectMsg(GetPendingTransactions)
pendingTransactionsManager.reply(PendingTransactionsResponse(Nil))

res.futureValue shouldBe PendingTransactionsResponse(Nil)
}

it should "send message to pendingTransactionsManager and return GetPendingTransactionsResponse with two transactions" in new TestSetup {
val transactions = (0 to 1)
.map(_ => {
val fakeTransaction = SignedTransactionWithSender(
Transaction(
nonce = 0,
gasPrice = 123,
gasLimit = 123,
receivingAddress = Address("0x1234"),
value = 0,
payload = ByteString()
),
signature = ECDSASignature(0, 0, 0.toByte),
sender = Address("0x1234")
)
PendingTransaction(fakeTransaction, System.currentTimeMillis)
})
.toList

val res = ethService.getTransactionsFromPool()

pendingTransactionsManager.expectMsg(GetPendingTransactions)
pendingTransactionsManager.reply(PendingTransactionsResponse(transactions))

res.futureValue shouldBe PendingTransactionsResponse(transactions)
}

it should "send message to pendingTransactionsManager and return an empty GetPendingTransactionsResponse in case of error" in new TestSetup {
val res = ethService.getTransactionsFromPool()

pendingTransactionsManager.expectMsg(GetPendingTransactions)
pendingTransactionsManager.reply(new ClassCastException("error"))

res.futureValue shouldBe PendingTransactionsResponse(Nil)
}

// NOTE TestSetup uses Ethash consensus; check `consensusConfig`.
trait TestSetup extends MockFactory with EphemBlockchainTestSetup {
val blockGenerator = mock[EthashBlockGenerator]
Expand Down
Loading