diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthSpec.scala new file mode 100644 index 0000000000..f91e375c3c --- /dev/null +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthSpec.scala @@ -0,0 +1,745 @@ +package io.iohk.ethereum.jsonrpc + +import akka.util.ByteString +import io.iohk.ethereum.consensus.Consensus +import io.iohk.ethereum.consensus.blocks.PendingBlock +import io.iohk.ethereum.consensus.validators.SignedTransactionValidator +import io.iohk.ethereum.crypto.kec256 +import io.iohk.ethereum.domain._ +import io.iohk.ethereum.jsonrpc.EthService._ +import io.iohk.ethereum.jsonrpc.FilterManager.LogFilterLogs +import io.iohk.ethereum.jsonrpc.JsonSerializers.{OptionNoneToJNullSerializer, QuantitiesSerializer, UnformattedDataJsonSerializer} +import io.iohk.ethereum.jsonrpc.PersonalService._ +import io.iohk.ethereum.ommers.OmmersPool +import io.iohk.ethereum.ommers.OmmersPool.Ommers +import io.iohk.ethereum.transactions.PendingTransactionsManager +import io.iohk.ethereum.{Fixtures, LongPatience, Timeouts} +import org.bouncycastle.util.encoders.Hex +import org.json4s.JsonAST._ +import org.json4s.JsonDSL._ +import org.json4s.{DefaultFormats, Extraction, Formats} +import org.scalatest.concurrent.{Eventually, ScalaFutures} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +import scala.concurrent.Future + +// scalastyle:off magic.number +class JsonRpcControllerEthSpec + extends AnyFlatSpec + with Matchers + with JRCMatchers + with ScalaCheckPropertyChecks + with ScalaFutures + with LongPatience + with Eventually { + + implicit val formats: Formats = DefaultFormats.preservingEmptyValues + OptionNoneToJNullSerializer + + QuantitiesSerializer + UnformattedDataJsonSerializer + + it should "eth_protocolVersion" in new JsonRpcControllerFixture { + val rpcRequest = newJsonRpcRequest("eth_protocolVersion") + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult("0x3f") + } + + it should "handle eth_chainId" in new JsonRpcControllerFixture { + val request = newJsonRpcRequest("eth_chainId") + val response = jsonRpcController.handleRequest(request).futureValue + + response should haveStringResult("0x3d") + } + + it should "handle eth_blockNumber request" in new JsonRpcControllerFixture { + val bestBlockNumber = 10 + blockchain.saveBestKnownBlocks(bestBlockNumber) + + val rpcRequest = newJsonRpcRequest("eth_blockNumber") + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult(s"0xa") + } + + it should "eth_syncing" in new JsonRpcControllerFixture { + (appStateStorage.getSyncStartingBlock _).expects().returning(100) + (appStateStorage.getBestBlockNumber _).expects().returning(200) + (appStateStorage.getEstimatedHighestBlock _).expects().returning(300) + + blockchain.saveBestKnownBlocks(200) + val rpcRequest = newJsonRpcRequest("eth_syncing") + + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveObjectResult( + "startingBlock" -> "0x64", + "currentBlock" -> "0xc8", + "highestBlock" -> "0x12c" + ) + } + + it should "handle eth_getBlockByHash request" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val blockTd = blockToRequest.header.difficulty + + blockchain + .storeBlock(blockToRequest) + .and(blockchain.storeTotalDifficulty(blockToRequest.header.hash, blockTd)) + .commit() + + val request = newJsonRpcRequest( + "eth_getBlockByHash", + List(JString(s"0x${blockToRequest.header.hashAsHexString}"), JBool(false)) + ) + val response = jsonRpcController.handleRequest(request).futureValue + + val expectedBlockResponse = + Extraction.decompose(BlockResponse(blockToRequest, fullTxs = false, totalDifficulty = Some(blockTd))) + + response should haveResult(expectedBlockResponse) + } + + it should "handle eth_getBlockByNumber request" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val blockTd = blockToRequest.header.difficulty + + blockchain + .storeBlock(blockToRequest) + .and(blockchain.storeTotalDifficulty(blockToRequest.header.hash, blockTd)) + .commit() + + val request = newJsonRpcRequest( + "eth_getBlockByNumber", + List(JString(s"0x${Hex.toHexString(blockToRequest.header.number.toByteArray)}"), JBool(false)) + ) + val response = jsonRpcController.handleRequest(request).futureValue + + val expectedBlockResponse = + Extraction.decompose(BlockResponse(blockToRequest, fullTxs = false, totalDifficulty = Some(blockTd))) + + response should haveResult(expectedBlockResponse) + } + + it should "handle eth_getUncleByBlockHashAndIndex request" in new JsonRpcControllerFixture { + val uncle = Fixtures.Blocks.DaoForkBlock.header + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, BlockBody(Nil, Seq(uncle))) + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getUncleByBlockHashAndIndex", + List( + JString(s"0x${blockToRequest.header.hashAsHexString}"), + JString(s"0x${Hex.toHexString(BigInt(0).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + + val expectedUncleBlockResponse = Extraction + .decompose(BlockResponse(uncle, None, pendingBlock = false)) + .removeField { + case ("transactions", _) => true + case _ => false + } + + response should haveResult(expectedUncleBlockResponse) + } + + it should "handle eth_getUncleByBlockNumberAndIndex request" in new JsonRpcControllerFixture { + val uncle = Fixtures.Blocks.DaoForkBlock.header + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, BlockBody(Nil, Seq(uncle))) + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getUncleByBlockNumberAndIndex", + List( + JString(s"0x${Hex.toHexString(blockToRequest.header.number.toByteArray)}"), + JString(s"0x${Hex.toHexString(BigInt(0).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + + val expectedUncleBlockResponse = Extraction + .decompose(BlockResponse(uncle, None, pendingBlock = false)) + .removeField { + case ("transactions", _) => true + case _ => false + } + + response should haveResult(expectedUncleBlockResponse) + } + + it should "eth_getWork" in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val seed = s"""0x${"00" * 32}""" + val target = "0x1999999999999999999999999999999999999999999999999999999999999999" + val headerPowHash = s"0x${Hex.toHexString(kec256(BlockHeader.getEncodedWithoutNonce(blockHeader)))}" + + blockchain.save(parentBlock, Nil, parentBlock.header.difficulty, true) + (blockGenerator.generateBlock _) + .expects(parentBlock, *, *, *) + .returns(Right(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) + + val request: JsonRpcRequest = newJsonRpcRequest("eth_getWork") + + val result: Future[JsonRpcResponse] = jsonRpcController.handleRequest(request) + + pendingTransactionsManager.expectMsg(PendingTransactionsManager.GetPendingTransactions) + pendingTransactionsManager.reply(PendingTransactionsManager.PendingTransactionsResponse(Nil)) + + ommersPool.expectMsg(OmmersPool.GetOmmers(parentBlock.hash)) + ommersPool.reply(Ommers(Nil)) + + val response = result.futureValue + response should haveResult( + JArray( + List( + JString(headerPowHash), + JString(seed), + JString(target) + ) + ) + ) + } + + it should "eth_getWork when fail to get ommers and transactions" in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val seed = s"""0x${"00" * 32}""" + val target = "0x1999999999999999999999999999999999999999999999999999999999999999" + val headerPowHash = s"0x${Hex.toHexString(kec256(BlockHeader.getEncodedWithoutNonce(blockHeader)))}" + + blockchain.save(parentBlock, Nil, parentBlock.header.difficulty, true) + (blockGenerator.generateBlock _) + .expects(parentBlock, *, *, *) + .returns(Right(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) + + val request: JsonRpcRequest = newJsonRpcRequest("eth_getWork") + + val result: Future[JsonRpcResponse] = jsonRpcController.handleRequest(request) + + pendingTransactionsManager.expectMsg(PendingTransactionsManager.GetPendingTransactions) + ommersPool.expectMsg(OmmersPool.GetOmmers(parentBlock.hash)) + //on time out it should respond with empty list + + val response = result.futureValue(timeout(Timeouts.longTimeout)) + response should haveResult( + JArray( + List( + JString(headerPowHash), + JString(seed), + JString(target) + ) + ) + ) + } + + it should "eth_submitWork" in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val nonce = s"0x0000000000000001" + val mixHash = s"""0x${"01" * 32}""" + val headerPowHash = "02" * 32 + + (blockGenerator.getPrepared _) + .expects(ByteString(Hex.decode(headerPowHash))) + .returns(Some(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) + (appStateStorage.getBestBlockNumber _).expects().returns(1) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_submitWork", + List( + JString(nonce), + JString(s"0x$headerPowHash"), + JString(mixHash) + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveBooleanResult(true) + } + + it should "eth_submitHashrate" in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_submitHashrate", + List( + JString(s"0x${"0" * 61}500"), + JString(s"0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveBooleanResult(true) + } + + it should "eth_hashrate" in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val request: JsonRpcRequest = newJsonRpcRequest("eth_hashrate") + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x0") + } + + it should "eth_gasPrice" in new JsonRpcControllerFixture { + blockchain + .storeBlock(Block(Fixtures.Blocks.Block3125369.header.copy(number = 42), Fixtures.Blocks.Block3125369.body)) + .commit() + blockchain.saveBestKnownBlocks(42) + + val request: JsonRpcRequest = newJsonRpcRequest("eth_gasPrice") + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x4a817c800") + } + + it should "eth_call" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.call _).expects(*).returning(Future.successful(Right(CallResponse(ByteString("asd"))))) + + val json = List( + JObject( + "from" -> "0xabbb6bebfa05aa13e908eaa492bd7a8343760477", + "to" -> "0xda714fe079751fa7a1ad80b76571ea6ec52a446c", + "gas" -> "0x12", + "gasPrice" -> "0x123", + "value" -> "0x99", + "data" -> "0xFF44" + ), + JString("latest") + ) + val rpcRequest = newJsonRpcRequest("eth_call", json) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult("0x617364") + } + + it should "eth_estimateGas" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.estimateGas _) + .expects(*) + .anyNumberOfTimes() + .returning(Future.successful(Right(EstimateGasResponse(2310)))) + + val callObj = JObject( + "from" -> "0xabbb6bebfa05aa13e908eaa492bd7a8343760477", + "to" -> "0xda714fe079751fa7a1ad80b76571ea6ec52a446c", + "gas" -> "0x12", + "gasPrice" -> "0x123", + "value" -> "0x99", + "data" -> "0xFF44" + ) + val callObjWithoutData = callObj.replace(List("data"), "") + + val table = Table( + "Requests", + List(callObj, JString("latest")), + List(callObj), + List(callObjWithoutData) + ) + + forAll(table) { json => + val rpcRequest = newJsonRpcRequest("eth_estimateGas", json) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult("0x906") + } + + } + + it should "eth_getCode" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getCode _) + .expects(*) + .returning(Future.successful(Right(GetCodeResponse(ByteString(Hex.decode("FFAA22")))))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getCode", + List( + JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), + JString(s"latest") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0xffaa22") + } + + it should "eth_getUncleCountByBlockNumber" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getUncleCountByBlockNumber _) + .expects(*) + .returning(Future.successful(Right(GetUncleCountByBlockNumberResponse(2)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getUncleCountByBlockNumber", + List( + JString(s"0x12") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x2") + } + + it should "eth_getUncleCountByBlockHash " in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getUncleCountByBlockHash _) + .expects(*) + .returning(Future.successful(Right(GetUncleCountByBlockHashResponse(3)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getUncleCountByBlockHash", + List( + JString(s"0x7dc64cb9d8a95763e288d71088fe3116e10dbff317c09f7a9bd5dd6974d27d20") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x3") + } + + it should "eth_coinbase " in new JsonRpcControllerFixture { + // Just record the fact that this is going to be called, we do not care about the returned value + (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) + .expects() + .returns(null) + .anyNumberOfTimes() + + (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + + val request: JsonRpcRequest = newJsonRpcRequest("eth_coinbase") + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x000000000000000000000000000000000000002a") + } + + it should "eth_getBalance" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getBalance _) + .expects(*) + .returning(Future.successful(Right(GetBalanceResponse(17)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getBalance", + List( + JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), + JString(s"latest") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x11") + } + + it should "eth_getStorageAt" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getStorageAt _) + .expects(*) + .returning(Future.successful(Right(GetStorageAtResponse(ByteString("response"))))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getStorageAt", + List( + JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), + JString(s"0x01"), + JString(s"latest") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult(JString("0x" + Hex.toHexString(ByteString("response").toArray[Byte]))) + } + + it should "eth_sign" in new JsonRpcControllerFixture { + + (personalService.sign _) + .expects( + SignRequest( + ByteString(Hex.decode("deadbeaf")), + Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))), + None + ) + ) + .returns(Future.successful(Right(SignResponse(sig)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_sign", + List( + JString(s"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83"), + JString(s"0xdeadbeaf") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult( + "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" + ) + } + + it should "eth_newFilter" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.newFilter _) + .expects(*) + .returning(Future.successful(Right(NewFilterResponse(123)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_newFilter", + List( + JObject( + "fromBlock" -> "0x0", + "toBlock" -> "latest", + "address" -> "0x2B5A350698C91E684EB08c10F7e462f761C0e681", + "topics" -> JArray(List(JNull, "0x00000000000000000000000000000000000000000000000000000000000001c8")) + ) + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x7b") + } + + it should "eth_newBlockFilter" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.newBlockFilter _) + .expects(*) + .returning(Future.successful(Right(NewFilterResponse(999)))) + + val request: JsonRpcRequest = JsonRpcRequest( + "2.0", + "eth_newBlockFilter", + Some(JArray(List())), + Some(JInt(1)) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x3e7") + } + + it should "eth_newPendingTransactionFilter" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.newPendingTransactionFilter _) + .expects(*) + .returning(Future.successful(Right(NewFilterResponse(2)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_newPendingTransactionFilter", + Nil + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x2") + } + + it should "eth_uninstallFilter" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.uninstallFilter _) + .expects(*) + .returning(Future.successful(Right(UninstallFilterResponse(true)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_uninstallFilter", + List(JString("0x1")) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveBooleanResult(true) + } + + it should "eth_getFilterChanges" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getFilterChanges _) + .expects(*) + .returning( + Future.successful( + Right( + GetFilterChangesResponse( + FilterManager.LogFilterChanges( + Seq( + FilterManager.TxLog( + logIndex = 0, + transactionIndex = 0, + transactionHash = ByteString(Hex.decode("123ffa")), + blockHash = ByteString(Hex.decode("123eeaa22a")), + blockNumber = 99, + address = Address("0x123456"), + data = ByteString(Hex.decode("ff33")), + topics = Seq(ByteString(Hex.decode("33")), ByteString(Hex.decode("55"))) + ) + ) + ) + ) + ) + ) + ) + + val request: JsonRpcRequest = + newJsonRpcRequest("eth_getFilterChanges", List(JString("0x1"))) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult( + JArray( + List( + JObject( + "logIndex" -> JString("0x0"), + "transactionIndex" -> JString("0x0"), + "transactionHash" -> JString("0x123ffa"), + "blockHash" -> JString("0x123eeaa22a"), + "blockNumber" -> JString("0x63"), + "address" -> JString("0x0000000000000000000000000000000000123456"), + "data" -> JString("0xff33"), + "topics" -> JArray(List(JString("0x33"), JString("0x55"))) + ) + ) + ) + ) + } + + it should "eth_getFilterLogs" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getFilterLogs _) + .expects(*) + .returning( + Future.successful( + Right( + GetFilterLogsResponse( + FilterManager.BlockFilterLogs( + Seq( + ByteString(Hex.decode("1234")), + ByteString(Hex.decode("4567")), + ByteString(Hex.decode("7890")) + ) + ) + ) + ) + ) + ) + + val request: JsonRpcRequest = + newJsonRpcRequest("eth_getFilterLogs", List(JString("0x1"))) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult(JArray(List(JString("0x1234"), JString("0x4567"), JString("0x7890")))) + } + + it should "eth_getLogs" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getLogs _) + .expects(*) + .returning( + Future.successful( + Right( + GetLogsResponse( + LogFilterLogs( + Seq( + FilterManager.TxLog( + logIndex = 0, + transactionIndex = 0, + transactionHash = ByteString(Hex.decode("123ffa")), + blockHash = ByteString(Hex.decode("123eeaa22a")), + blockNumber = 99, + address = Address("0x123456"), + data = ByteString(Hex.decode("ff33")), + topics = Seq(ByteString(Hex.decode("33")), ByteString(Hex.decode("55"))) + ) + ) + ) + ) + ) + ) + ) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getLogs", + List( + JObject( + "fromBlock" -> "0x0", + "toBlock" -> "latest", + "address" -> "0x2B5A350698C91E684EB08c10F7e462f761C0e681", + "topics" -> JArray(List(JNull, "0x00000000000000000000000000000000000000000000000000000000000001c8")) + ) + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult( + JArray( + List( + JObject( + "logIndex" -> JString("0x0"), + "transactionIndex" -> JString("0x0"), + "transactionHash" -> JString("0x123ffa"), + "blockHash" -> JString("0x123eeaa22a"), + "blockNumber" -> JString("0x63"), + "address" -> JString("0x0000000000000000000000000000000000123456"), + "data" -> JString("0xff33"), + "topics" -> JArray(List(JString("0x33"), JString("0x55"))) + ) + ) + ) + ) + } +} diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthTransactionSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthTransactionSpec.scala new file mode 100644 index 0000000000..7a00df37c4 --- /dev/null +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerEthTransactionSpec.scala @@ -0,0 +1,406 @@ +package io.iohk.ethereum.jsonrpc + +import akka.util.ByteString +import io.iohk.ethereum.domain._ +import io.iohk.ethereum.jsonrpc.EthService._ +import io.iohk.ethereum.jsonrpc.FilterManager.TxLog +import io.iohk.ethereum.jsonrpc.JsonSerializers.{OptionNoneToJNullSerializer, QuantitiesSerializer, UnformattedDataJsonSerializer} +import io.iohk.ethereum.jsonrpc.PersonalService._ +import io.iohk.ethereum.{Fixtures, LongPatience} +import org.bouncycastle.util.encoders.Hex +import org.json4s.JsonAST._ +import org.json4s.JsonDSL._ +import org.json4s.{DefaultFormats, Extraction, Formats} +import org.scalatest.concurrent.{Eventually, ScalaFutures} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +import scala.concurrent.Future + +// scalastyle:off magic.number +class JsonRpcControllerEthTransactionSpec + extends AnyFlatSpec + with Matchers + with JRCMatchers + with ScalaCheckPropertyChecks + with ScalaFutures + with LongPatience + with Eventually { + + implicit val formats: Formats = DefaultFormats.preservingEmptyValues + OptionNoneToJNullSerializer + + QuantitiesSerializer + UnformattedDataJsonSerializer + + it should "handle eth_getTransactionByBlockHashAndIndex request" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndexToRequest = blockToRequest.body.transactionList.size / 2 + + blockchain.storeBlock(blockToRequest).commit() + blockchain.saveBestKnownBlocks(blockToRequest.header.number) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionByBlockHashAndIndex", + List( + JString(s"0x${blockToRequest.header.hashAsHexString}"), + JString(s"0x${Hex.toHexString(BigInt(txIndexToRequest).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedStx = blockToRequest.body.transactionList.apply(txIndexToRequest) + val expectedTxResponse = Extraction.decompose( + TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndexToRequest)) + ) + + response should haveResult(expectedTxResponse) + } + + it should "handle eth_getRawTransactionByBlockHashAndIndex request" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndexToRequest = blockToRequest.body.transactionList.size / 2 + + blockchain.storeBlock(blockToRequest).commit() + blockchain.saveBestKnownBlocks(blockToRequest.header.number) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getRawTransactionByBlockHashAndIndex", + List( + JString(s"0x${blockToRequest.header.hashAsHexString}"), + JString(s"0x${Hex.toHexString(BigInt(txIndexToRequest).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndexToRequest) + + response should haveResult(expectedTxResponse) + } + + it should "handle eth_getRawTransactionByHash request" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + val txResponse: SignedTransaction = Fixtures.Blocks.Block3125369.body.transactionList.head + (mockEthService.getRawTransactionByHash _) + .expects(*) + .returning(Future.successful(Right(RawTransactionResponse(Some(txResponse))))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getRawTransactionByHash", + List( + JString("0xe9b2d3e8a2bc996a1c7742de825fdae2466ae783ce53484304efffe304ff232d") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult(encodeSignedTrx(txResponse)) + } + + it should "eth_sendTransaction" in new JsonRpcControllerFixture { + val params = JObject( + "from" -> Address(42).toString, + "to" -> Address(123).toString, + "value" -> 1000 + ) :: Nil + + val txHash = ByteString(1, 2, 3, 4) + + (personalService + .sendTransaction(_: SendTransactionRequest)) + .expects(*) + .returning(Future.successful(Right(SendTransactionResponse(txHash)))) + + val rpcRequest = newJsonRpcRequest("eth_sendTransaction", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveResult(JString(s"0x${Hex.toHexString(txHash.toArray)}")) + } + + it should "eth_getTransactionByBlockNumberAndIndex by tag" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + blockchain.saveBestKnownBlocks(blockToRequest.header.number) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionByBlockNumberAndIndex", + List( + JString(s"latest"), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedStx = blockToRequest.body.transactionList(txIndex) + val expectedTxResponse = Extraction.decompose( + TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) + ) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getTransactionByBlockNumberAndIndex by hex number" in new JsonRpcControllerFixture { + val blockToRequest = + Block(Fixtures.Blocks.Block3125369.header.copy(number = BigInt(0xc005)), Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionByBlockNumberAndIndex", + List( + JString(s"0xC005"), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedStx = blockToRequest.body.transactionList(txIndex) + val expectedTxResponse = Extraction.decompose( + TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) + ) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getTransactionByBlockNumberAndIndex by number" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionByBlockNumberAndIndex", + List( + JInt(Fixtures.Blocks.Block3125369.header.number), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedStx = blockToRequest.body.transactionList(txIndex) + val expectedTxResponse = Extraction.decompose( + TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) + ) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getRawTransactionByBlockNumberAndIndex by tag" in new JsonRpcControllerFixture { + // given + val blockToRequest: Block = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + blockchain.saveBestKnownBlocks(blockToRequest.header.number) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getRawTransactionByBlockNumberAndIndex", + List( + JString(s"latest"), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + + // when + val response = jsonRpcController.handleRequest(request).futureValue + + // then + val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getRawTransactionByBlockNumberAndIndex by hex number" in new JsonRpcControllerFixture { + // given + val blockToRequest = + Block(Fixtures.Blocks.Block3125369.header.copy(number = BigInt(0xc005)), Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getRawTransactionByBlockNumberAndIndex", + List( + JString(s"0xC005"), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + + // when + val response = jsonRpcController.handleRequest(request).futureValue + + // then + val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getRawTransactionByBlockNumberAndIndex by number" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + val txIndex = 1 + + blockchain.storeBlock(blockToRequest).commit() + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getRawTransactionByBlockNumberAndIndex", + List( + JInt(Fixtures.Blocks.Block3125369.header.number), + JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") + ) + ) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) + + response should haveResult(expectedTxResponse) + } + + it should "eth_getTransactionByHash" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + val txResponse = TransactionResponse(Fixtures.Blocks.Block3125369.body.transactionList.head) + (mockEthService.getTransactionByHash _) + .expects(*) + .returning(Future.successful(Right(GetTransactionByHashResponse(Some(txResponse))))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionByHash", + List( + JString("0xe9b2d3e8a2bc996a1c7742de825fdae2466ae783ce53484304efffe304ff232d") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult(Extraction.decompose(txResponse)) + } + + it should "eth_getTransactionCount" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getTransactionCount _) + .expects(*) + .returning(Future.successful(Right(GetTransactionCountResponse(123)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionCount", + List( + JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), + JString(s"latest") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x7b") + } + + it should "eth_getBlockTransactionCountByNumber " in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + (mockEthService.getBlockTransactionCountByNumber _) + .expects(*) + .returning(Future.successful(Right(GetBlockTransactionCountByNumberResponse(17)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getBlockTransactionCountByNumber", + List( + JString(s"0x123") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x11") + } + + it should "handle eth_getBlockTransactionCountByHash request" in new JsonRpcControllerFixture { + val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) + + blockchain.storeBlock(blockToRequest).commit() + + val rpcRequest = newJsonRpcRequest( + "eth_getBlockTransactionCountByHash", + List(JString(s"0x${blockToRequest.header.hashAsHexString}")) + ) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + val expectedTxCount = Extraction.decompose(BigInt(blockToRequest.body.transactionList.size)) + response should haveResult(expectedTxCount) + } + + it should "eth_getTransactionReceipt" in new JsonRpcControllerFixture { + val mockEthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) + + val arbitraryValue = 42 + + val mockRequest = GetTransactionReceiptRequest( + ByteString(Hex.decode("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")) + ) + + val mockResponse = Right( + GetTransactionReceiptResponse( + Some( + TransactionReceiptResponse( + transactionHash = ByteString(Hex.decode("23" * 32)), + transactionIndex = 1, + blockNumber = Fixtures.Blocks.Block3125369.header.number, + blockHash = Fixtures.Blocks.Block3125369.header.hash, + cumulativeGasUsed = arbitraryValue * 10, + gasUsed = arbitraryValue, + contractAddress = Some(Address(arbitraryValue)), + logs = Seq( + TxLog( + logIndex = 0, + transactionIndex = 1, + transactionHash = ByteString(Hex.decode("23" * 32)), + blockHash = Fixtures.Blocks.Block3125369.header.hash, + blockNumber = Fixtures.Blocks.Block3125369.header.number, + address = Address(arbitraryValue), + data = ByteString(Hex.decode("43" * 32)), + topics = Seq(ByteString(Hex.decode("44" * 32)), ByteString(Hex.decode("45" * 32))) + ) + ) + ) + ) + ) + ) + + (mockEthService.getTransactionReceipt _).expects(*).returning(Future.successful(mockResponse)) + + val request: JsonRpcRequest = newJsonRpcRequest( + "eth_getTransactionReceipt", + List(JString(s"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveResult( + JObject( + JField("transactionHash", JString("0x" + "23" * 32)), + JField("transactionIndex", JString("0x1")), + JField("blockNumber", JString("0x2fb079")), + JField("blockHash", JString("0x" + Hex.toHexString(Fixtures.Blocks.Block3125369.header.hash.toArray[Byte]))), + JField("cumulativeGasUsed", JString("0x1a4")), + JField("gasUsed", JString("0x2a")), + JField("contractAddress", JString("0x000000000000000000000000000000000000002a")), + JField( + "logs", + JArray( + List( + JObject( + JField("logIndex", JString("0x0")), + JField("transactionIndex", JString("0x1")), + JField("transactionHash", JString("0x" + "23" * 32)), + JField( + "blockHash", + JString("0x" + Hex.toHexString(Fixtures.Blocks.Block3125369.header.hash.toArray[Byte])) + ), + JField("blockNumber", JString("0x2fb079")), + JField("address", JString("0x000000000000000000000000000000000000002a")), + JField("data", JString("0x" + "43" * 32)), + JField("topics", JArray(List(JString("0x" + "44" * 32), JString("0x" + "45" * 32)))) + ) + ) + ) + ) + ) + ) + } +} diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala new file mode 100644 index 0000000000..6bf7e34498 --- /dev/null +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerFixture.scala @@ -0,0 +1,126 @@ +package io.iohk.ethereum.jsonrpc + +import akka.actor.ActorSystem +import akka.testkit.TestProbe +import akka.util.ByteString +import io.iohk.ethereum.{Fixtures, Timeouts} +import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup +import io.iohk.ethereum.consensus.{ConsensusConfigs, TestConsensus} +import io.iohk.ethereum.consensus.ethash.blocks.EthashBlockGenerator +import io.iohk.ethereum.consensus.ethash.validators.ValidatorsExecutor +import io.iohk.ethereum.crypto.ECDSASignature +import io.iohk.ethereum.db.storage.AppStateStorage +import io.iohk.ethereum.domain.{Block, BlockBody, SignedTransaction} +import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonRpcConfig +import io.iohk.ethereum.keystore.KeyStore +import io.iohk.ethereum.ledger.{BloomFilter, Ledger, StxLedger} +import io.iohk.ethereum.utils.{Config, FilterConfig} +import org.bouncycastle.util.encoders.Hex +import org.json4s.JsonAST.{JArray, JInt, JString, JValue} +import org.scalamock.scalatest.MockFactory + +import scala.concurrent.duration._ + +trait JsonRpcControllerFixture extends MockFactory with EphemBlockchainTestSetup with JsonMethodsImplicits { + def config: JsonRpcConfig = JsonRpcConfig(Config.config) + + def rawTrnHex(xs: Seq[SignedTransaction], idx: Int): Option[JString] = + xs.lift(idx) + .map(encodeSignedTrx) + + def encodeSignedTrx(x: SignedTransaction) = + encodeAsHex(RawTransactionCodec.asRawTransaction(x)) + + val version = Config.clientVersion + val blockGenerator = mock[EthashBlockGenerator] + + override implicit lazy val system = ActorSystem("JsonRpcControllerSpec_System") + + val syncingController = TestProbe() + override lazy val ledger = mock[Ledger] + override lazy val stxLedger = mock[StxLedger] + override lazy val validators = mock[ValidatorsExecutor] + override lazy val consensus: TestConsensus = buildTestConsensus() + .withValidators(validators) + .withBlockGenerator(blockGenerator) + + val keyStore = mock[KeyStore] + + val pendingTransactionsManager = TestProbe() + val ommersPool = TestProbe() + val filterManager = TestProbe() + + val ethashConfig = ConsensusConfigs.ethashConfig + override lazy val consensusConfig = ConsensusConfigs.consensusConfig + val fullConsensusConfig = ConsensusConfigs.fullConsensusConfig + val getTransactionFromPoolTimeout: FiniteDuration = 5.seconds + + val filterConfig = new FilterConfig { + override val filterTimeout: FiniteDuration = Timeouts.normalTimeout + override val filterManagerQueryTimeout: FiniteDuration = Timeouts.normalTimeout + } + + val currentProtocolVersion = 63 + + val appStateStorage = mock[AppStateStorage] + val web3Service = new Web3Service + val netService = mock[NetService] + val personalService = mock[PersonalService] + val debugService = mock[DebugService] + val qaService = mock[QAService] + val checkpointingService = mock[CheckpointingService] + + val ethService = new EthService( + blockchain, + appStateStorage, + ledger, + stxLedger, + keyStore, + pendingTransactionsManager.ref, + syncingController.ref, + ommersPool.ref, + filterManager.ref, + filterConfig, + blockchainConfig, + currentProtocolVersion, + config, + getTransactionFromPoolTimeout + ) + + protected def newJsonRpcController(ethService: EthService) = + new JsonRpcController( + web3Service, + netService, + ethService, + personalService, + None, + debugService, + qaService, + checkpointingService, + config + ) + + val jsonRpcController = newJsonRpcController(ethService) + + val blockHeader = Fixtures.Blocks.ValidBlock.header.copy( + logsBloom = BloomFilter.EmptyBloomFilter, + difficulty = 10, + number = 2, + gasLimit = 0, + gasUsed = 0, + unixTimestamp = 0 + ) + + val parentBlock = Block(blockHeader.copy(number = 1), BlockBody.empty) + + val r: ByteString = ByteString(Hex.decode("a3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a1")) + val s: ByteString = ByteString(Hex.decode("2d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee")) + val v: Byte = ByteString(Hex.decode("1b")).last + val sig = ECDSASignature(r, s, v) + + def newJsonRpcRequest(method: String, params: List[JValue]) = + JsonRpcRequest("2.0", method, Some(JArray(params)), Some(JInt(1))) + + def newJsonRpcRequest(method: String) = + JsonRpcRequest("2.0", method, None, Some(JInt(1))) +} diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerPersonalSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerPersonalSpec.scala new file mode 100644 index 0000000000..309b334c86 --- /dev/null +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerPersonalSpec.scala @@ -0,0 +1,229 @@ +package io.iohk.ethereum.jsonrpc + +import java.time.Duration + +import akka.util.ByteString +import io.iohk.ethereum.LongPatience +import io.iohk.ethereum.domain._ +import io.iohk.ethereum.jsonrpc.JsonSerializers.{OptionNoneToJNullSerializer, QuantitiesSerializer, UnformattedDataJsonSerializer} +import io.iohk.ethereum.jsonrpc.PersonalService._ +import org.bouncycastle.util.encoders.Hex +import org.json4s.JsonAST._ +import org.json4s.JsonDSL._ +import org.json4s.{DefaultFormats, Formats} +import org.scalatest.concurrent.{Eventually, ScalaFutures} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks + +import scala.concurrent.Future + +class JsonRpcControllerPersonalSpec + extends AnyFlatSpec + with Matchers + with JRCMatchers + with ScalaCheckPropertyChecks + with ScalaFutures + with LongPatience + with Eventually { + + implicit val formats: Formats = DefaultFormats.preservingEmptyValues + OptionNoneToJNullSerializer + + QuantitiesSerializer + UnformattedDataJsonSerializer + + it should "personal_importRawKey" in new JsonRpcControllerFixture { + val key = "7a44789ed3cd85861c0bbf9693c7e1de1862dd4396c390147ecf1275099c6e6f" + val keyBytes = ByteString(Hex.decode(key)) + val addr = Address("0x00000000000000000000000000000000000000ff") + val pass = "aaa" + + (personalService.importRawKey _) + .expects(ImportRawKeyRequest(keyBytes, pass)) + .returning(Future.successful(Right(ImportRawKeyResponse(addr)))) + + val params = JString(key) :: JString(pass) :: Nil + val rpcRequest = newJsonRpcRequest("personal_importRawKey", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult(addr.toString) + } + + it should "personal_newAccount" in new JsonRpcControllerFixture { + val addr = Address("0x00000000000000000000000000000000000000ff") + val pass = "aaa" + + (personalService.newAccount _) + .expects(NewAccountRequest(pass)) + .returning(Future.successful(Right(NewAccountResponse(addr)))) + + val params = JString(pass) :: Nil + val rpcRequest = newJsonRpcRequest("personal_newAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveStringResult(addr.toString) + } + + it should "personal_listAccounts" in new JsonRpcControllerFixture { + val addresses = List(34, 12391, 123).map(Address(_)) + val pass = "aaa" + + (personalService.listAccounts _) + .expects(ListAccountsRequest()) + .returning(Future.successful(Right(ListAccountsResponse(addresses)))) + + val rpcRequest = newJsonRpcRequest("personal_listAccounts") + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveResult(JArray(addresses.map(a => JString(a.toString)))) + } + + it should "personal_unlockAccount" in new JsonRpcControllerFixture { + val address = Address(42) + val pass = "aaa" + val params = JString(address.toString) :: JString(pass) :: Nil + + (personalService.unlockAccount _) + .expects(UnlockAccountRequest(address, pass, None)) + .returning(Future.successful(Right(UnlockAccountResponse(true)))) + + val rpcRequest = newJsonRpcRequest("personal_unlockAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveBooleanResult(true) + } + + it should "personal_unlockAccount for specified duration" in new JsonRpcControllerFixture { + val address = Address(42) + val pass = "aaa" + val dur = "1" + val params = JString(address.toString) :: JString(pass) :: JString(dur) :: Nil + + (personalService.unlockAccount _) + .expects(UnlockAccountRequest(address, pass, Some(Duration.ofSeconds(1)))) + .returning(Future.successful(Right(UnlockAccountResponse(true)))) + + val rpcRequest = newJsonRpcRequest("personal_unlockAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveBooleanResult(true) + } + + it should "personal_unlockAccount should handle possible duration errors" in new JsonRpcControllerFixture { + val address = Address(42) + val pass = "aaa" + val dur = "alksjdfh" + + val params = JString(address.toString) :: JString(pass) :: JString(dur) :: Nil + val rpcRequest = newJsonRpcRequest("personal_unlockAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveError(JsonRpcError(-32602, "Invalid method parameters", None)) + + val dur2 = Long.MaxValue + val params2 = JString(address.toString) :: JString(pass) :: JInt(dur2) :: Nil + val rpcRequest2 = newJsonRpcRequest("personal_unlockAccount", params2) + val response2 = jsonRpcController.handleRequest(rpcRequest2).futureValue + response2 should haveError( + JsonRpcError(-32602, "Duration should be an number of seconds, less than 2^31 - 1", None) + ) + } + + it should "personal_unlockAccount should handle null passed as a duration for compatibility with Parity and web3j" in new JsonRpcControllerFixture { + val address = Address(42) + val pass = "aaa" + val params = JString(address.toString) :: JString(pass) :: JNull :: Nil + + (personalService.unlockAccount _) + .expects(UnlockAccountRequest(address, pass, None)) + .returning(Future.successful(Right(UnlockAccountResponse(true)))) + + val rpcRequest = newJsonRpcRequest("personal_unlockAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveBooleanResult(true) + } + + it should "personal_lockAccount" in new JsonRpcControllerFixture { + val address = Address(42) + val params = JString(address.toString) :: Nil + + (personalService.lockAccount _) + .expects(LockAccountRequest(address)) + .returning(Future.successful(Right(LockAccountResponse(true)))) + + val rpcRequest = newJsonRpcRequest("personal_lockAccount", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveBooleanResult(true) + } + + it should "personal_sendTransaction" in new JsonRpcControllerFixture { + val params = JObject( + "from" -> Address(42).toString, + "to" -> Address(123).toString, + "value" -> 1000 + ) :: JString("passphrase") :: Nil + + val txHash = ByteString(1, 2, 3, 4) + + (personalService + .sendTransaction(_: SendTransactionWithPassphraseRequest)) + .expects(*) + .returning(Future.successful(Right(SendTransactionWithPassphraseResponse(txHash)))) + + val rpcRequest = newJsonRpcRequest("personal_sendTransaction", params) + val response = jsonRpcController.handleRequest(rpcRequest).futureValue + + response should haveResult(JString(s"0x${Hex.toHexString(txHash.toArray)}")) + } + + it should "personal_sign" in new JsonRpcControllerFixture { + + (personalService.sign _) + .expects( + SignRequest( + ByteString(Hex.decode("deadbeaf")), + Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))), + Some("thePassphrase") + ) + ) + .returns(Future.successful(Right(SignResponse(sig)))) + + val request: JsonRpcRequest = newJsonRpcRequest( + "personal_sign", + List( + JString(s"0xdeadbeaf"), + JString(s"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83"), + JString("thePassphrase") + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult( + "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" + ) + } + + it should "personal_ecRecover" in new JsonRpcControllerFixture { + + (personalService.ecRecover _) + .expects(EcRecoverRequest(ByteString(Hex.decode("deadbeaf")), sig)) + .returns( + Future.successful( + Right(EcRecoverResponse(Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))))) + ) + ) + + val request: JsonRpcRequest = newJsonRpcRequest( + "personal_ecRecover", + List( + JString(s"0xdeadbeaf"), + JString( + s"0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" + ) + ) + ) + + val response = jsonRpcController.handleRequest(request).futureValue + response should haveStringResult("0x9b2055d370f73ec7d8a03e965129118dc8f5bf83") + } +} diff --git a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala index 3d7b8fb34a..40d0d34609 100644 --- a/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala +++ b/src/test/scala/io/iohk/ethereum/jsonrpc/JsonRpcControllerSpec.scala @@ -1,55 +1,29 @@ package io.iohk.ethereum.jsonrpc -import java.time.Duration - -import akka.actor.ActorSystem -import akka.testkit.TestProbe -import akka.util.ByteString -import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup -import io.iohk.ethereum.consensus.blocks.PendingBlock -import io.iohk.ethereum.consensus.ethash.blocks.EthashBlockGenerator -import io.iohk.ethereum.consensus.ethash.validators.ValidatorsExecutor -import io.iohk.ethereum.consensus.validators.SignedTransactionValidator -import io.iohk.ethereum.consensus.{Consensus, ConsensusConfigs, TestConsensus} -import io.iohk.ethereum.crypto.{ECDSASignature, kec256} -import io.iohk.ethereum.db.storage.AppStateStorage -import io.iohk.ethereum.domain.{Address, Block, BlockBody, BlockHeader, SignedTransaction} import io.iohk.ethereum.jsonrpc.DebugService.{ListPeersInfoRequest, ListPeersInfoResponse} import io.iohk.ethereum.jsonrpc.EthService._ -import io.iohk.ethereum.jsonrpc.FilterManager.{LogFilterLogs, TxLog} import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonRpcConfig import io.iohk.ethereum.jsonrpc.JsonSerializers.{OptionNoneToJNullSerializer, QuantitiesSerializer, UnformattedDataJsonSerializer} import io.iohk.ethereum.jsonrpc.NetService.{ListeningResponse, PeerCountResponse, VersionResponse} -import io.iohk.ethereum.jsonrpc.PersonalService._ import io.iohk.ethereum.jsonrpc.server.http.JsonRpcHttpServer import io.iohk.ethereum.jsonrpc.server.ipc.JsonRpcIpcServer -import io.iohk.ethereum.keystore.KeyStore -import io.iohk.ethereum.ledger.{BloomFilter, Ledger, StxLedger} import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo import io.iohk.ethereum.network.p2p.messages.CommonMessages.Status import io.iohk.ethereum.network.p2p.messages.Versions -import io.iohk.ethereum.ommers.OmmersPool -import io.iohk.ethereum.ommers.OmmersPool.Ommers -import io.iohk.ethereum.transactions.PendingTransactionsManager -import io.iohk.ethereum.utils._ -import io.iohk.ethereum.{Fixtures, LongPatience, Timeouts} -import org.bouncycastle.util.encoders.Hex +import io.iohk.ethereum.{Fixtures, LongPatience} import org.json4s.JsonAST._ import org.json4s.JsonDSL._ import org.json4s.{DefaultFormats, Extraction, Formats} -import org.scalamock.scalatest.MockFactory import org.scalatest.concurrent.{Eventually, ScalaFutures} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scala.concurrent.Future import scala.concurrent.duration._ -import org.scalatest.flatspec.AnyFlatSpec -import org.scalatest.matchers.should.Matchers -// scalastyle:off file.size.limit -// scalastyle:off magic.number class JsonRpcControllerSpec - extends AnyFlatSpec + extends AnyFlatSpec with Matchers with JRCMatchers with ScalaCheckPropertyChecks @@ -60,102 +34,61 @@ class JsonRpcControllerSpec implicit val formats: Formats = DefaultFormats.preservingEmptyValues + OptionNoneToJNullSerializer + QuantitiesSerializer + UnformattedDataJsonSerializer - "JsonRpcController" should "handle valid sha3 request" in new TestSetup { - val rpcRequest = JsonRpcRequest("2.0", "web3_sha3", Some(JArray(JString("0x1234") :: Nil)), Some(1)) + "JsonRpcController" should "handle valid sha3 request" in new JsonRpcControllerFixture { + val rpcRequest = newJsonRpcRequest("web3_sha3", JString("0x1234") :: Nil) val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveStringResult("0x56570de287d73cd1cb6092bb8fdee6173974955fdef345ae579ee9f475ea7432") } - it should "fail when invalid request is received" in new TestSetup { - val rpcRequest = JsonRpcRequest("2.0", "web3_sha3", Some(JArray(JString("asdasd") :: Nil)), Some(1)) + it should "fail when invalid request is received" in new JsonRpcControllerFixture { + val rpcRequest = newJsonRpcRequest("web3_sha3", JString("asdasd") :: Nil) val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveError(JsonRpcErrors.InvalidParams("Invalid method parameters")) } - it should "handle clientVersion request" in new TestSetup { - val rpcRequest = JsonRpcRequest("2.0", "web3_clientVersion", None, Some(1)) + it should "handle clientVersion request" in new JsonRpcControllerFixture { + val rpcRequest = newJsonRpcRequest("web3_clientVersion") val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveStringResult(version) } - it should "Handle net_peerCount request" in new TestSetup { + it should "Handle net_peerCount request" in new JsonRpcControllerFixture { (netService.peerCount _).expects(*).returning(Future.successful(Right(PeerCountResponse(123)))) - val rpcRequest = JsonRpcRequest("2.0", "net_peerCount", None, Some(1)) + val rpcRequest = newJsonRpcRequest("net_peerCount") val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveStringResult("0x7b") } - it should "Handle net_listening request" in new TestSetup { + it should "Handle net_listening request" in new JsonRpcControllerFixture { (netService.listening _).expects(*).returning(Future.successful(Right(ListeningResponse(false)))) - val rpcRequest = JsonRpcRequest("2.0", "net_listening", None, Some(1)) + val rpcRequest = newJsonRpcRequest("net_listening") val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveBooleanResult(false) } - it should "Handle net_version request" in new TestSetup { + it should "Handle net_version request" in new JsonRpcControllerFixture { val netVersion = "99" (netService.version _).expects(*).returning(Future.successful(Right(VersionResponse(netVersion)))) - val rpcRequest = JsonRpcRequest("2.0", "net_version", None, Some(1)) + val rpcRequest = newJsonRpcRequest("net_version") val response = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveStringResult(netVersion) } - it should "eth_protocolVersion" in new TestSetup { - val rpcRequest = JsonRpcRequest("2.0", "eth_protocolVersion", None, Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult("0x3f") - } - - it should "handle eth_chainId" in new TestSetup { - val request = JsonRpcRequest("2.0", "eth_chainId", None, Some(1)) - val response = jsonRpcController.handleRequest(request).futureValue - - response should haveStringResult("0x3d") - } - - it should "handle eth_blockNumber request" in new TestSetup { - val bestBlockNumber = 10 - blockchain.saveBestKnownBlocks(bestBlockNumber) - - val rpcRequest = JsonRpcRequest("2.0", "eth_blockNumber", None, Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult(s"0xa") - } - - it should "eth_syncing" in new TestSetup { - (appStateStorage.getSyncStartingBlock _).expects().returning(100) - (appStateStorage.getBestBlockNumber _).expects().returning(200) - (appStateStorage.getEstimatedHighestBlock _).expects().returning(300) - - blockchain.saveBestKnownBlocks(200) - val rpcRequest = JsonRpcRequest("2.0", "eth_syncing", None, Some(1)) - - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveObjectResult( - "startingBlock" -> "0x64", - "currentBlock" -> "0xc8", - "highestBlock" -> "0x12c" - ) - } - - it should "only allow to call methods of enabled apis" in new TestSetup { + it should "only allow to call methods of enabled apis" in new JsonRpcControllerFixture { override def config: JsonRpcConfig = new JsonRpcConfig { override val apis = Seq("web3") override val accountTransactionsMaxBlocks = 50000 @@ -164,383 +97,18 @@ class JsonRpcControllerSpec override def ipcServerConfig: JsonRpcIpcServer.JsonRpcIpcServerConfig = ??? } - val ethRpcRequest = JsonRpcRequest("2.0", "eth_protocolVersion", None, Some(1)) + val ethRpcRequest = newJsonRpcRequest("eth_protocolVersion") val ethResponse = jsonRpcController.handleRequest(ethRpcRequest).futureValue ethResponse should haveError(JsonRpcErrors.MethodNotFound) - val web3RpcRequest = JsonRpcRequest("2.0", "web3_clientVersion", None, Some(1)) + val web3RpcRequest = newJsonRpcRequest("web3_clientVersion") val web3Response = jsonRpcController.handleRequest(web3RpcRequest).futureValue web3Response should haveStringResult(version) } - it should "handle eth_getBlockTransactionCountByHash request" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - - blockchain.storeBlock(blockToRequest).commit() - - val rpcRequest = JsonRpcRequest( - "2.0", - "eth_getBlockTransactionCountByHash", - Some(JArray(List(JString(s"0x${blockToRequest.header.hashAsHexString}")))), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - val expectedTxCount = Extraction.decompose(BigInt(blockToRequest.body.transactionList.size)) - response should haveResult(expectedTxCount) - } - - it should "handle eth_getBlockByHash request" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val blockTd = blockToRequest.header.difficulty - - blockchain - .storeBlock(blockToRequest) - .and(blockchain.storeTotalDifficulty(blockToRequest.header.hash, blockTd)) - .commit() - - val request = JsonRpcRequest( - "2.0", - "eth_getBlockByHash", - Some(JArray(List(JString(s"0x${blockToRequest.header.hashAsHexString}"), JBool(false)))), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - - val expectedBlockResponse = - Extraction.decompose(BlockResponse(blockToRequest, fullTxs = false, totalDifficulty = Some(blockTd))) - - response should haveResult(expectedBlockResponse) - } - - it should "handle eth_getBlockByNumber request" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val blockTd = blockToRequest.header.difficulty - - blockchain - .storeBlock(blockToRequest) - .and(blockchain.storeTotalDifficulty(blockToRequest.header.hash, blockTd)) - .commit() - - val request = JsonRpcRequest( - "2.0", - "eth_getBlockByNumber", - Some(JArray(List(JString(s"0x${Hex.toHexString(blockToRequest.header.number.toByteArray)}"), JBool(false)))), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - - val expectedBlockResponse = - Extraction.decompose(BlockResponse(blockToRequest, fullTxs = false, totalDifficulty = Some(blockTd))) - - response should haveResult(expectedBlockResponse) - } - - it should "handle eth_getUncleByBlockHashAndIndex request" in new TestSetup { - val uncle = Fixtures.Blocks.DaoForkBlock.header - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, BlockBody(Nil, Seq(uncle))) - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getUncleByBlockHashAndIndex", - Some( - JArray( - List( - JString(s"0x${blockToRequest.header.hashAsHexString}"), - JString(s"0x${Hex.toHexString(BigInt(0).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - - val expectedUncleBlockResponse = Extraction - .decompose(BlockResponse(uncle, None, pendingBlock = false)) - .removeField { - case ("transactions", _) => true - case _ => false - } - - response should haveResult(expectedUncleBlockResponse) - } - - it should "handle eth_getUncleByBlockNumberAndIndex request" in new TestSetup { - val uncle = Fixtures.Blocks.DaoForkBlock.header - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, BlockBody(Nil, Seq(uncle))) - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getUncleByBlockNumberAndIndex", - Some( - JArray( - List( - JString(s"0x${Hex.toHexString(blockToRequest.header.number.toByteArray)}"), - JString(s"0x${Hex.toHexString(BigInt(0).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - - val expectedUncleBlockResponse = Extraction - .decompose(BlockResponse(uncle, None, pendingBlock = false)) - .removeField { - case ("transactions", _) => true - case _ => false - } - - response should haveResult(expectedUncleBlockResponse) - } - - it should "handle eth_getTransactionByBlockHashAndIndex request" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndexToRequest = blockToRequest.body.transactionList.size / 2 - - blockchain.storeBlock(blockToRequest).commit() - blockchain.saveBestKnownBlocks(blockToRequest.header.number) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionByBlockHashAndIndex", - Some( - JArray( - List( - JString(s"0x${blockToRequest.header.hashAsHexString}"), - JString(s"0x${Hex.toHexString(BigInt(txIndexToRequest).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedStx = blockToRequest.body.transactionList.apply(txIndexToRequest) - val expectedTxResponse = Extraction.decompose( - TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndexToRequest)) - ) - - response should haveResult(expectedTxResponse) - } - - it should "handle eth_getRawTransactionByBlockHashAndIndex request" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndexToRequest = blockToRequest.body.transactionList.size / 2 - - blockchain.storeBlock(blockToRequest).commit() - blockchain.saveBestKnownBlocks(blockToRequest.header.number) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getRawTransactionByBlockHashAndIndex", - Some( - JArray( - List( - JString(s"0x${blockToRequest.header.hashAsHexString}"), - JString(s"0x${Hex.toHexString(BigInt(txIndexToRequest).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndexToRequest) - - response should haveResult(expectedTxResponse) - } - - it should "handle eth_getRawTransactionByHash request" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - val txResponse: SignedTransaction = Fixtures.Blocks.Block3125369.body.transactionList.head - (mockEthService.getRawTransactionByHash _) - .expects(*) - .returning(Future.successful(Right(RawTransactionResponse(Some(txResponse))))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getRawTransactionByHash", - Some( - JArray( - List( - JString("0xe9b2d3e8a2bc996a1c7742de825fdae2466ae783ce53484304efffe304ff232d") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult(encodeSignedTrx(txResponse)) - } - - it should "personal_importRawKey" in new TestSetup { - val key = "7a44789ed3cd85861c0bbf9693c7e1de1862dd4396c390147ecf1275099c6e6f" - val keyBytes = ByteString(Hex.decode(key)) - val addr = Address("0x00000000000000000000000000000000000000ff") - val pass = "aaa" - - (personalService.importRawKey _) - .expects(ImportRawKeyRequest(keyBytes, pass)) - .returning(Future.successful(Right(ImportRawKeyResponse(addr)))) - - val params = JArray(JString(key) :: JString(pass) :: Nil) - val rpcRequest = JsonRpcRequest("2.0", "personal_importRawKey", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult(addr.toString) - } - - it should "personal_newAccount" in new TestSetup { - val addr = Address("0x00000000000000000000000000000000000000ff") - val pass = "aaa" - - (personalService.newAccount _) - .expects(NewAccountRequest(pass)) - .returning(Future.successful(Right(NewAccountResponse(addr)))) - - val params = JArray(JString(pass) :: Nil) - val rpcRequest = JsonRpcRequest("2.0", "personal_newAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult(addr.toString) - } - - it should "personal_listAccounts" in new TestSetup { - val addresses = List(34, 12391, 123).map(Address(_)) - val pass = "aaa" - - (personalService.listAccounts _) - .expects(ListAccountsRequest()) - .returning(Future.successful(Right(ListAccountsResponse(addresses)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_listAccounts", None, Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveResult(JArray(addresses.map(a => JString(a.toString)))) - } - - it should "personal_unlockAccount" in new TestSetup { - val address = Address(42) - val pass = "aaa" - val params = JArray(JString(address.toString) :: JString(pass) :: Nil) - - (personalService.unlockAccount _) - .expects(UnlockAccountRequest(address, pass, None)) - .returning(Future.successful(Right(UnlockAccountResponse(true)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_unlockAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveBooleanResult(true) - } - - it should "personal_unlockAccount for specified duration" in new TestSetup { - val address = Address(42) - val pass = "aaa" - val dur = "1" - val params = JArray(JString(address.toString) :: JString(pass) :: JString(dur) :: Nil) - - (personalService.unlockAccount _) - .expects(UnlockAccountRequest(address, pass, Some(Duration.ofSeconds(1)))) - .returning(Future.successful(Right(UnlockAccountResponse(true)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_unlockAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveBooleanResult(true) - } - - it should "personal_unlockAccount should handle possible duration errors" in new TestSetup { - val address = Address(42) - val pass = "aaa" - val dur = "alksjdfh" - - val params = JArray(JString(address.toString) :: JString(pass) :: JString(dur) :: Nil) - val rpcRequest = JsonRpcRequest("2.0", "personal_unlockAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveError(JsonRpcError(-32602, "Invalid method parameters", None)) - - val dur2 = Long.MaxValue - val params2 = JArray(JString(address.toString) :: JString(pass) :: JInt(dur2) :: Nil) - val rpcRequest2 = JsonRpcRequest("2.0", "personal_unlockAccount", Some(params2), Some(1)) - val response2 = jsonRpcController.handleRequest(rpcRequest2).futureValue - response2 should haveError( - JsonRpcError(-32602, "Duration should be an number of seconds, less than 2^31 - 1", None) - ) - } - - it should "personal_unlockAccount should handle null passed as a duration for compatibility with Parity and web3j" in new TestSetup { - val address = Address(42) - val pass = "aaa" - val params = JArray(JString(address.toString) :: JString(pass) :: JNull :: Nil) - - (personalService.unlockAccount _) - .expects(UnlockAccountRequest(address, pass, None)) - .returning(Future.successful(Right(UnlockAccountResponse(true)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_unlockAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveBooleanResult(true) - } - - it should "personal_lockAccount" in new TestSetup { - val address = Address(42) - val params = JArray(JString(address.toString) :: Nil) - - (personalService.lockAccount _) - .expects(LockAccountRequest(address)) - .returning(Future.successful(Right(LockAccountResponse(true)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_lockAccount", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveBooleanResult(true) - } - - it should "personal_sendTransaction" in new TestSetup { - val params = JArray( - JObject( - "from" -> Address(42).toString, - "to" -> Address(123).toString, - "value" -> 1000 - ) :: JString("passphrase") :: Nil - ) - - val txHash = ByteString(1, 2, 3, 4) - - (personalService - .sendTransaction(_: SendTransactionWithPassphraseRequest)) - .expects(*) - .returning(Future.successful(Right(SendTransactionWithPassphraseResponse(txHash)))) - - val rpcRequest = JsonRpcRequest("2.0", "personal_sendTransaction", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveResult(JString(s"0x${Hex.toHexString(txHash.toArray)}")) - } - - it should "debug_listPeersInfo" in new TestSetup { + it should "debug_listPeersInfo" in new JsonRpcControllerFixture { val peerStatus = Status( protocolVersion = Versions.PV63, networkId = 1, @@ -561,1450 +129,70 @@ class JsonRpcControllerSpec .expects(ListPeersInfoRequest()) .returning(Future.successful(Right(ListPeersInfoResponse(peers)))) - val rpcRequest = JsonRpcRequest("2.0", "debug_listPeersInfo", None, Some(1)) + val rpcRequest = newJsonRpcRequest("debug_listPeersInfo") val response: JsonRpcResponse = jsonRpcController.handleRequest(rpcRequest).futureValue response should haveResult(JArray(peers.map(info => JString(info.toString)))) } - it should "eth_sendTransaction" in new TestSetup { - val params = JArray( - JObject( - "from" -> Address(42).toString, - "to" -> Address(123).toString, - "value" -> 1000 - ) :: Nil - ) - - val txHash = ByteString(1, 2, 3, 4) - - (personalService - .sendTransaction(_: SendTransactionRequest)) - .expects(*) - .returning(Future.successful(Right(SendTransactionResponse(txHash)))) - - val rpcRequest = JsonRpcRequest("2.0", "eth_sendTransaction", Some(params), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveResult(JString(s"0x${Hex.toHexString(txHash.toArray)}")) - } - - it should "eth_getWork" in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() - - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() - - val seed = s"""0x${"00" * 32}""" - val target = "0x1999999999999999999999999999999999999999999999999999999999999999" - val headerPowHash = s"0x${Hex.toHexString(kec256(BlockHeader.getEncodedWithoutNonce(blockHeader)))}" - - blockchain.save(parentBlock, Nil, parentBlock.header.difficulty, true) - (blockGenerator.generateBlock _) - .expects(parentBlock, *, *, *) - .returns(Right(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getWork", - None, - Some(JInt(1)) - ) - - val result: Future[JsonRpcResponse] = jsonRpcController.handleRequest(request) - - pendingTransactionsManager.expectMsg(PendingTransactionsManager.GetPendingTransactions) - pendingTransactionsManager.reply(PendingTransactionsManager.PendingTransactionsResponse(Nil)) - - ommersPool.expectMsg(OmmersPool.GetOmmers(parentBlock.hash)) - ommersPool.reply(Ommers(Nil)) - - val response = result.futureValue - response should haveResult( - JArray( - List( - JString(headerPowHash), - JString(seed), - JString(target) - ) - ) - ) - } - - it should "eth_getWork when fail to get ommers and transactions" in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() - - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() - - val seed = s"""0x${"00" * 32}""" - val target = "0x1999999999999999999999999999999999999999999999999999999999999999" - val headerPowHash = s"0x${Hex.toHexString(kec256(BlockHeader.getEncodedWithoutNonce(blockHeader)))}" + it should "rpc_modules" in new JsonRpcControllerFixture { + val request: JsonRpcRequest = newJsonRpcRequest("rpc_modules") - blockchain.save(parentBlock, Nil, parentBlock.header.difficulty, true) - (blockGenerator.generateBlock _) - .expects(parentBlock, *, *, *) - .returns(Right(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getWork", - None, - Some(JInt(1)) - ) - - val result: Future[JsonRpcResponse] = jsonRpcController.handleRequest(request) - - pendingTransactionsManager.expectMsg(PendingTransactionsManager.GetPendingTransactions) - ommersPool.expectMsg(OmmersPool.GetOmmers(parentBlock.hash)) - //on time out it should respond with empty list + val response = jsonRpcController.handleRequest(request).futureValue - val response = result.futureValue(timeout(Timeouts.longTimeout)) response should haveResult( - JArray( - List( - JString(headerPowHash), - JString(seed), - JString(target) - ) + JObject( + "net" -> "1.0", + "rpc" -> "1.0", + "personal" -> "1.0", + "eth" -> "1.0", + "web3" -> "1.0", + "daedalus" -> "1.0", + "debug" -> "1.0", + "qa" -> "1.0", + "checkpointing" -> "1.0" ) ) } - it should "eth_submitWork" in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() - - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() - - val nonce = s"0x0000000000000001" - val mixHash = s"""0x${"01" * 32}""" - val headerPowHash = "02" * 32 - - (blockGenerator.getPrepared _) - .expects(ByteString(Hex.decode(headerPowHash))) - .returns(Some(PendingBlock(Block(blockHeader, BlockBody(Nil, Nil)), Nil))) - (appStateStorage.getBestBlockNumber _).expects().returns(1) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_submitWork", - Some( - JArray( - List( - JString(nonce), - JString(s"0x$headerPowHash"), - JString(mixHash) - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveBooleanResult(true) - } - - it should "eth_submitHashrate" in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() + it should "daedalus_getAccountTransactions" in new JsonRpcControllerFixture { + val mockEthService: EthService = mock[EthService] + override val jsonRpcController = newJsonRpcController(mockEthService) - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() + val block = Fixtures.Blocks.Block3125369 + val sentTx = block.body.transactionList.head + val receivedTx = block.body.transactionList.last - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_submitHashrate", - Some( - JArray( - List( - JString(s"0x${"0" * 61}500"), - JString(s"0x59daa26581d0acd1fce254fb7e85952f4c09d0915afd33d3886cd914bc7d283c") + (mockEthService.getAccountTransactions _) + .expects(*) + .returning( + Future.successful( + Right( + GetAccountTransactionsResponse( + Seq( + TransactionResponse(sentTx, Some(block.header), isOutgoing = Some(true)), + TransactionResponse(receivedTx, Some(block.header), isOutgoing = Some(false)) + ) + ) ) ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveBooleanResult(true) - } - - it should "eth_hashrate" in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() - - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_hashrate", - None, - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x0") - } - - it should "eth_gasPrice" in new TestSetup { - blockchain - .storeBlock(Block(Fixtures.Blocks.Block3125369.header.copy(number = 42), Fixtures.Blocks.Block3125369.body)) - .commit() - blockchain.saveBestKnownBlocks(42) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_gasPrice", - None, - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x4a817c800") - } - - it should "eth_call" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config ) - (mockEthService.call _).expects(*).returning(Future.successful(Right(CallResponse(ByteString("asd"))))) - - val json = JArray( + val request: JsonRpcRequest = newJsonRpcRequest( + "daedalus_getAccountTransactions", List( - JObject( - "from" -> "0xabbb6bebfa05aa13e908eaa492bd7a8343760477", - "to" -> "0xda714fe079751fa7a1ad80b76571ea6ec52a446c", - "gas" -> "0x12", - "gasPrice" -> "0x123", - "value" -> "0x99", - "data" -> "0xFF44" - ), - JString("latest") + JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), + JInt(100), + JInt(200) ) ) - val rpcRequest = JsonRpcRequest("2.0", "eth_call", Some(json), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult("0x617364") - } - it should "eth_estimateGas" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.estimateGas _) - .expects(*) - .anyNumberOfTimes() - .returning(Future.successful(Right(EstimateGasResponse(2310)))) - - val callObj = JObject( - "from" -> "0xabbb6bebfa05aa13e908eaa492bd7a8343760477", - "to" -> "0xda714fe079751fa7a1ad80b76571ea6ec52a446c", - "gas" -> "0x12", - "gasPrice" -> "0x123", - "value" -> "0x99", - "data" -> "0xFF44" - ) - val callObjWithoutData = callObj.replace(List("data"), "") - - val table = Table( - "Requests", - JArray(List(callObj, JString("latest"))), - JArray(List(callObj)), - JArray(List(callObjWithoutData)) - ) - - forAll(table) { json => - val rpcRequest = JsonRpcRequest("2.0", "eth_estimateGas", Some(json), Some(1)) - val response = jsonRpcController.handleRequest(rpcRequest).futureValue - - response should haveStringResult("0x906") - } - - } - - it should "eth_getCode" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getCode _) - .expects(*) - .returning(Future.successful(Right(GetCodeResponse(ByteString(Hex.decode("FFAA22")))))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getCode", - Some( - JArray( - List( - JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), - JString(s"latest") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0xffaa22") - } - - it should "eth_getUncleCountByBlockNumber" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getUncleCountByBlockNumber _) - .expects(*) - .returning(Future.successful(Right(GetUncleCountByBlockNumberResponse(2)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getUncleCountByBlockNumber", - Some( - JArray( - List( - JString(s"0x12") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x2") - } - - it should "eth_getUncleCountByBlockHash " in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getUncleCountByBlockHash _) - .expects(*) - .returning(Future.successful(Right(GetUncleCountByBlockHashResponse(3)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getUncleCountByBlockHash", - Some( - JArray( - List( - JString(s"0x7dc64cb9d8a95763e288d71088fe3116e10dbff317c09f7a9bd5dd6974d27d20") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x3") - } - - it should "eth_getBlockTransactionCountByNumber " in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getBlockTransactionCountByNumber _) - .expects(*) - .returning(Future.successful(Right(GetBlockTransactionCountByNumberResponse(17)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getBlockTransactionCountByNumber", - Some( - JArray( - List( - JString(s"0x123") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x11") - } - - it should "eth_coinbase " in new TestSetup { - // Just record the fact that this is going to be called, we do not care about the returned value - (validators.signedTransactionValidator _: (() => SignedTransactionValidator)) - .expects() - .returns(null) - .anyNumberOfTimes() - - (ledger.consensus _: (() => Consensus)).expects().returns(consensus).anyNumberOfTimes() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_coinbase", - None, - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x000000000000000000000000000000000000002a") - } - - it should "eth_getTransactionByBlockNumberAndIndex by tag" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - blockchain.saveBestKnownBlocks(blockToRequest.header.number) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JString(s"latest"), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedStx = blockToRequest.body.transactionList(txIndex) - val expectedTxResponse = Extraction.decompose( - TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) - ) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getTransactionByBlockNumberAndIndex by hex number" in new TestSetup { - val blockToRequest = - Block(Fixtures.Blocks.Block3125369.header.copy(number = BigInt(0xc005)), Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JString(s"0xC005"), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedStx = blockToRequest.body.transactionList(txIndex) - val expectedTxResponse = Extraction.decompose( - TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) - ) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getTransactionByBlockNumberAndIndex by number" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JInt(Fixtures.Blocks.Block3125369.header.number), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedStx = blockToRequest.body.transactionList(txIndex) - val expectedTxResponse = Extraction.decompose( - TransactionResponse(expectedStx, Some(blockToRequest.header), Some(txIndex)) - ) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getRawTransactionByBlockNumberAndIndex by tag" in new TestSetup { - // given - val blockToRequest: Block = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - blockchain.saveBestKnownBlocks(blockToRequest.header.number) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getRawTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JString(s"latest"), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - - // when - val response = jsonRpcController.handleRequest(request).futureValue - - // then - val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getRawTransactionByBlockNumberAndIndex by hex number" in new TestSetup { - // given - val blockToRequest = - Block(Fixtures.Blocks.Block3125369.header.copy(number = BigInt(0xc005)), Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getRawTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JString(s"0xC005"), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - - // when - val response = jsonRpcController.handleRequest(request).futureValue - - // then - val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getRawTransactionByBlockNumberAndIndex by number" in new TestSetup { - val blockToRequest = Block(Fixtures.Blocks.Block3125369.header, Fixtures.Blocks.Block3125369.body) - val txIndex = 1 - - blockchain.storeBlock(blockToRequest).commit() - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getRawTransactionByBlockNumberAndIndex", - Some( - JArray( - List( - JInt(Fixtures.Blocks.Block3125369.header.number), - JString(s"0x${Hex.toHexString(BigInt(txIndex).toByteArray)}") - ) - ) - ), - Some(JInt(1)) - ) - val response = jsonRpcController.handleRequest(request).futureValue - val expectedTxResponse = rawTrnHex(blockToRequest.body.transactionList, txIndex) - - response should haveResult(expectedTxResponse) - } - - it should "eth_getBalance" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getBalance _) - .expects(*) - .returning(Future.successful(Right(GetBalanceResponse(17)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getBalance", - Some( - JArray( - List( - JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), - JString(s"latest") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x11") - } - - it should "eth_getStorageAt" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getStorageAt _) - .expects(*) - .returning(Future.successful(Right(GetStorageAtResponse(ByteString("response"))))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getStorageAt", - Some( - JArray( - List( - JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), - JString(s"0x01"), - JString(s"latest") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult(JString("0x" + Hex.toHexString(ByteString("response").toArray[Byte]))) - } - - it should "eth_getTransactionCount" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getTransactionCount _) - .expects(*) - .returning(Future.successful(Right(GetTransactionCountResponse(123)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionCount", - Some( - JArray( - List( - JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), - JString(s"latest") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x7b") - } - - it should "eth_getTransactionByHash" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - val txResponse = TransactionResponse(Fixtures.Blocks.Block3125369.body.transactionList.head) - (mockEthService.getTransactionByHash _) - .expects(*) - .returning(Future.successful(Right(GetTransactionByHashResponse(Some(txResponse))))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionByHash", - Some( - JArray( - List( - JString("0xe9b2d3e8a2bc996a1c7742de825fdae2466ae783ce53484304efffe304ff232d") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult(Extraction.decompose(txResponse)) - } - - it should "eth_sign" in new TestSetup { - - (personalService.sign _) - .expects( - SignRequest( - ByteString(Hex.decode("deadbeaf")), - Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))), - None - ) - ) - .returns(Future.successful(Right(SignResponse(sig)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_sign", - Some( - JArray( - List( - JString(s"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83"), - JString(s"0xdeadbeaf") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult( - "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" - ) - } - - it should "personal_sign" in new TestSetup { - - (personalService.sign _) - .expects( - SignRequest( - ByteString(Hex.decode("deadbeaf")), - Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))), - Some("thePassphrase") - ) - ) - .returns(Future.successful(Right(SignResponse(sig)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "personal_sign", - Some( - JArray( - List( - JString(s"0xdeadbeaf"), - JString(s"0x9b2055d370f73ec7d8a03e965129118dc8f5bf83"), - JString("thePassphrase") - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult( - "0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" - ) - } - - it should "personal_ecRecover" in new TestSetup { - - (personalService.ecRecover _) - .expects(EcRecoverRequest(ByteString(Hex.decode("deadbeaf")), sig)) - .returns( - Future.successful( - Right(EcRecoverResponse(Address(ByteString(Hex.decode("9b2055d370f73ec7d8a03e965129118dc8f5bf83"))))) - ) - ) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "personal_ecRecover", - Some( - JArray( - List( - JString(s"0xdeadbeaf"), - JString( - s"0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b" - ) - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x9b2055d370f73ec7d8a03e965129118dc8f5bf83") - } - - it should "eth_newFilter" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.newFilter _) - .expects(*) - .returning(Future.successful(Right(NewFilterResponse(123)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_newFilter", - Some( - JArray( - List( - JObject( - "fromBlock" -> "0x0", - "toBlock" -> "latest", - "address" -> "0x2B5A350698C91E684EB08c10F7e462f761C0e681", - "topics" -> JArray(List(JNull, "0x00000000000000000000000000000000000000000000000000000000000001c8")) - ) - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x7b") - } - - it should "eth_newBlockFilter" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.newBlockFilter _) - .expects(*) - .returning(Future.successful(Right(NewFilterResponse(999)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_newBlockFilter", - Some(JArray(List())), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x3e7") - } - - it should "eth_newPendingTransactionFilter" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.newPendingTransactionFilter _) - .expects(*) - .returning(Future.successful(Right(NewFilterResponse(2)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_newPendingTransactionFilter", - Some(JArray(List())), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveStringResult("0x2") - } - - it should "eth_uninstallFilter" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.uninstallFilter _) - .expects(*) - .returning(Future.successful(Right(UninstallFilterResponse(true)))) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_uninstallFilter", - Some(JArray(List(JString("0x1")))), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveBooleanResult(true) - } - - it should "eth_getFilterChanges" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getFilterChanges _) - .expects(*) - .returning( - Future.successful( - Right( - GetFilterChangesResponse( - FilterManager.LogFilterChanges( - Seq( - FilterManager.TxLog( - logIndex = 0, - transactionIndex = 0, - transactionHash = ByteString(Hex.decode("123ffa")), - blockHash = ByteString(Hex.decode("123eeaa22a")), - blockNumber = 99, - address = Address("0x123456"), - data = ByteString(Hex.decode("ff33")), - topics = Seq(ByteString(Hex.decode("33")), ByteString(Hex.decode("55"))) - ) - ) - ) - ) - ) - ) - ) - - val request: JsonRpcRequest = - JsonRpcRequest("2.0", "eth_getFilterChanges", Some(JArray(List(JString("0x1")))), Some(JInt(1))) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult( - JArray( - List( - JObject( - "logIndex" -> JString("0x0"), - "transactionIndex" -> JString("0x0"), - "transactionHash" -> JString("0x123ffa"), - "blockHash" -> JString("0x123eeaa22a"), - "blockNumber" -> JString("0x63"), - "address" -> JString("0x0000000000000000000000000000000000123456"), - "data" -> JString("0xff33"), - "topics" -> JArray(List(JString("0x33"), JString("0x55"))) - ) - ) - ) - ) - } - - it should "eth_getFilterLogs" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getFilterLogs _) - .expects(*) - .returning( - Future.successful( - Right( - GetFilterLogsResponse( - FilterManager.BlockFilterLogs( - Seq( - ByteString(Hex.decode("1234")), - ByteString(Hex.decode("4567")), - ByteString(Hex.decode("7890")) - ) - ) - ) - ) - ) - ) - - val request: JsonRpcRequest = - JsonRpcRequest("2.0", "eth_getFilterLogs", Some(JArray(List(JString("0x1")))), Some(JInt(1))) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult(JArray(List(JString("0x1234"), JString("0x4567"), JString("0x7890")))) - } - - it should "eth_getLogs" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - (mockEthService.getLogs _) - .expects(*) - .returning( - Future.successful( - Right( - GetLogsResponse( - LogFilterLogs( - Seq( - FilterManager.TxLog( - logIndex = 0, - transactionIndex = 0, - transactionHash = ByteString(Hex.decode("123ffa")), - blockHash = ByteString(Hex.decode("123eeaa22a")), - blockNumber = 99, - address = Address("0x123456"), - data = ByteString(Hex.decode("ff33")), - topics = Seq(ByteString(Hex.decode("33")), ByteString(Hex.decode("55"))) - ) - ) - ) - ) - ) - ) - ) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getLogs", - Some( - JArray( - List( - JObject( - "fromBlock" -> "0x0", - "toBlock" -> "latest", - "address" -> "0x2B5A350698C91E684EB08c10F7e462f761C0e681", - "topics" -> JArray(List(JNull, "0x00000000000000000000000000000000000000000000000000000000000001c8")) - ) - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult( - JArray( - List( - JObject( - "logIndex" -> JString("0x0"), - "transactionIndex" -> JString("0x0"), - "transactionHash" -> JString("0x123ffa"), - "blockHash" -> JString("0x123eeaa22a"), - "blockNumber" -> JString("0x63"), - "address" -> JString("0x0000000000000000000000000000000000123456"), - "data" -> JString("0xff33"), - "topics" -> JArray(List(JString("0x33"), JString("0x55"))) - ) - ) - ) - ) - } - - it should "rpc_modules" in new TestSetup { - val request: JsonRpcRequest = JsonRpcRequest("2.0", "rpc_modules", None, Some(JInt(1))) - - val response = jsonRpcController.handleRequest(request).futureValue - - response should haveResult( - JObject( - "net" -> "1.0", - "rpc" -> "1.0", - "personal" -> "1.0", - "eth" -> "1.0", - "web3" -> "1.0", - "daedalus" -> "1.0", - "debug" -> "1.0", - "qa" -> "1.0", - "checkpointing" -> "1.0" - ) - ) - } - - it should "eth_getTransactionReceipt" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - val arbitraryValue = 42 - - val mockRequest = GetTransactionReceiptRequest( - ByteString(Hex.decode("b903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")) - ) - - val mockResponse = Right( - GetTransactionReceiptResponse( - Some( - TransactionReceiptResponse( - transactionHash = ByteString(Hex.decode("23" * 32)), - transactionIndex = 1, - blockNumber = Fixtures.Blocks.Block3125369.header.number, - blockHash = Fixtures.Blocks.Block3125369.header.hash, - cumulativeGasUsed = arbitraryValue * 10, - gasUsed = arbitraryValue, - contractAddress = Some(Address(arbitraryValue)), - logs = Seq( - TxLog( - logIndex = 0, - transactionIndex = 1, - transactionHash = ByteString(Hex.decode("23" * 32)), - blockHash = Fixtures.Blocks.Block3125369.header.hash, - blockNumber = Fixtures.Blocks.Block3125369.header.number, - address = Address(arbitraryValue), - data = ByteString(Hex.decode("43" * 32)), - topics = Seq(ByteString(Hex.decode("44" * 32)), ByteString(Hex.decode("45" * 32))) - ) - ) - ) - ) - ) - ) - - (mockEthService.getTransactionReceipt _).expects(*).returning(Future.successful(mockResponse)) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "eth_getTransactionReceipt", - Some(JArray(List(JString(s"0xb903239f8543d04b5dc1ba6579132b143087c68db1b2168786408fcbce568238")))), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - response should haveResult( - JObject( - JField("transactionHash", JString("0x" + "23" * 32)), - JField("transactionIndex", JString("0x1")), - JField("blockNumber", JString("0x2fb079")), - JField("blockHash", JString("0x" + Hex.toHexString(Fixtures.Blocks.Block3125369.header.hash.toArray[Byte]))), - JField("cumulativeGasUsed", JString("0x1a4")), - JField("gasUsed", JString("0x2a")), - JField("contractAddress", JString("0x000000000000000000000000000000000000002a")), - JField( - "logs", - JArray( - List( - JObject( - JField("logIndex", JString("0x0")), - JField("transactionIndex", JString("0x1")), - JField("transactionHash", JString("0x" + "23" * 32)), - JField( - "blockHash", - JString("0x" + Hex.toHexString(Fixtures.Blocks.Block3125369.header.hash.toArray[Byte])) - ), - JField("blockNumber", JString("0x2fb079")), - JField("address", JString("0x000000000000000000000000000000000000002a")), - JField("data", JString("0x" + "43" * 32)), - JField("topics", JArray(List(JString("0x" + "44" * 32), JString("0x" + "45" * 32)))) - ) - ) - ) - ) - ) - ) - } - - it should "daedalus_getAccountTransactions" in new TestSetup { - val mockEthService = mock[EthService] - override val jsonRpcController = - new JsonRpcController( - web3Service, - netService, - mockEthService, - personalService, - None, - debugService, - qaService, - checkpointingService, - config - ) - - val block = Fixtures.Blocks.Block3125369 - val sentTx = block.body.transactionList.head - val receivedTx = block.body.transactionList.last - - (mockEthService.getAccountTransactions _) - .expects(*) - .returning( - Future.successful( - Right( - GetAccountTransactionsResponse( - Seq( - TransactionResponse(sentTx, Some(block.header), isOutgoing = Some(true)), - TransactionResponse(receivedTx, Some(block.header), isOutgoing = Some(false)) - ) - ) - ) - ) - ) - - val request: JsonRpcRequest = JsonRpcRequest( - "2.0", - "daedalus_getAccountTransactions", - Some( - JArray( - List( - JString(s"0x7B9Bc474667Db2fFE5b08d000F1Acc285B2Ae47D"), - JInt(100), - JInt(200) - ) - ) - ), - Some(JInt(1)) - ) - - val response = jsonRpcController.handleRequest(request).futureValue - val expectedTxs = Seq( - Extraction.decompose(TransactionResponse(sentTx, Some(block.header), isOutgoing = Some(true))), - Extraction.decompose(TransactionResponse(receivedTx, Some(block.header), isOutgoing = Some(false))) + val response = jsonRpcController.handleRequest(request).futureValue + val expectedTxs = Seq( + Extraction.decompose(TransactionResponse(sentTx, Some(block.header), isOutgoing = Some(true))), + Extraction.decompose(TransactionResponse(receivedTx, Some(block.header), isOutgoing = Some(false))) ) response should haveObjectResult("transactions" -> JArray(expectedTxs.toList)) } - - trait TestSetup extends MockFactory with EphemBlockchainTestSetup with JsonMethodsImplicits { - def config: JsonRpcConfig = JsonRpcConfig(Config.config) - - def rawTrnHex(xs: Seq[SignedTransaction], idx: Int): Option[JString] = - xs.lift(idx) - .map(encodeSignedTrx) - - def encodeSignedTrx(x: SignedTransaction) = - encodeAsHex(RawTransactionCodec.asRawTransaction(x)) - - val version = Config.clientVersion - val blockGenerator = mock[EthashBlockGenerator] - - override implicit lazy val system = ActorSystem("JsonRpcControllerSpec_System") - - val syncingController = TestProbe() - override lazy val ledger = mock[Ledger] - override lazy val stxLedger = mock[StxLedger] - override lazy val validators = mock[ValidatorsExecutor] - override lazy val consensus: TestConsensus = buildTestConsensus() - .withValidators(validators) - .withBlockGenerator(blockGenerator) - - val keyStore = mock[KeyStore] - - val pendingTransactionsManager = TestProbe() - val ommersPool = TestProbe() - val filterManager = TestProbe() - - val ethashConfig = ConsensusConfigs.ethashConfig - override lazy val consensusConfig = ConsensusConfigs.consensusConfig - val fullConsensusConfig = ConsensusConfigs.fullConsensusConfig - val getTransactionFromPoolTimeout: FiniteDuration = 5.seconds - - val filterConfig = new FilterConfig { - override val filterTimeout: FiniteDuration = Timeouts.normalTimeout - override val filterManagerQueryTimeout: FiniteDuration = Timeouts.normalTimeout - } - - val currentProtocolVersion = 63 - - val appStateStorage = mock[AppStateStorage] - val web3Service = new Web3Service - val netService = mock[NetService] - val personalService = mock[PersonalService] - val debugService = mock[DebugService] - val qaService = mock[QAService] - val checkpointingService = mock[CheckpointingService] - - val ethService = new EthService( - blockchain, - appStateStorage, - ledger, - stxLedger, - keyStore, - pendingTransactionsManager.ref, - syncingController.ref, - ommersPool.ref, - filterManager.ref, - filterConfig, - blockchainConfig, - currentProtocolVersion, - config, - getTransactionFromPoolTimeout - ) - - val jsonRpcController = - new JsonRpcController(web3Service, netService, ethService, personalService, None, debugService, qaService, checkpointingService, config) - - val blockHeader = Fixtures.Blocks.ValidBlock.header.copy( - logsBloom = BloomFilter.EmptyBloomFilter, - difficulty = 10, - number = 2, - gasLimit = 0, - gasUsed = 0, - unixTimestamp = 0 - ) - - val parentBlock = Block(blockHeader.copy(number = 1), BlockBody.empty) - - val r: ByteString = ByteString(Hex.decode("a3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a1")) - val s: ByteString = ByteString(Hex.decode("2d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee")) - val v: Byte = ByteString(Hex.decode("1b")).last - val sig = ECDSASignature(r, s, v) - } }