Skip to content

Commit c6a172f

Browse files
author
Łukasz Gąsior
committed
Add iele_ transaction endpoints
1 parent a5d0cc9 commit c6a172f

File tree

9 files changed

+191
-11
lines changed

9 files changed

+191
-11
lines changed

src/main/resources/application.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ mantis {
190190
}
191191

192192
# Enabled JSON-RPC APIs over the JSON-RPC endpoint
193-
# Available choices are: eth, web3, net, personal, test, daedalus
193+
# Available choices are: eth, web3, net, personal, test, daedalus, iele
194194
apis = "eth,web3,net,personal,daedalus"
195195

196196
# Maximum number of blocks for daedalus_getAccountTransactions

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -565,12 +565,6 @@ object EthJsonMethodsImplicits extends JsonMethodsImplicits {
565565
data = data.getOrElse(ByteString("")))
566566
}
567567

568-
private def optionalQuantity(input: JValue): Either[JsonRpcError, Option[BigInt]] =
569-
input match {
570-
case JNothing => Right(None)
571-
case o => extractQuantity(o).map(Some(_))
572-
}
573-
574568
implicit val daedalus_getAccountTransactions =
575569
new JsonDecoder[GetAccountTransactionsRequest] with JsonEncoder[GetAccountTransactionsResponse] {
576570
def decodeJson(params: Option[JArray]): Either[JsonRpcError, GetAccountTransactionsRequest] =

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import io.iohk.ethereum.ledger.{InMemoryWorldStateProxy, Ledger}
2222
import io.iohk.ethereum.mining.BlockGenerator
2323
import io.iohk.ethereum.ommers.OmmersPool
2424
import io.iohk.ethereum.rlp
25+
import io.iohk.ethereum.rlp.RLPImplicits._
2526
import io.iohk.ethereum.rlp.RLPImplicitConversions._
2627
import io.iohk.ethereum.rlp.RLPList
2728
import io.iohk.ethereum.rlp.UInt256RLPImplicits._
@@ -114,7 +115,19 @@ object EthService {
114115
gasPrice: BigInt,
115116
value: BigInt,
116117
data: ByteString)
118+
119+
case class IeleCallTx(
120+
from: Option[ByteString],
121+
to: Option[ByteString],
122+
gas: Option[BigInt],
123+
gasPrice: BigInt,
124+
value: BigInt,
125+
functionName: Option[String] = None,
126+
arguments: Option[Seq[ByteString]] = None,
127+
contractCode: Option[ByteString])
128+
117129
case class CallRequest(tx: CallTx, block: BlockParam)
130+
case class IeleCallRequest(tx: IeleCallTx, block: BlockParam)
118131
case class CallResponse(returnData: ByteString)
119132
case class EstimateGasResponse(gas: BigInt)
120133

@@ -554,6 +567,22 @@ class EthService(
554567
}
555568
}
556569

570+
def ieleCall(req: IeleCallRequest): ServiceResponse[CallResponse] = {
571+
import req.tx
572+
573+
val args = tx.arguments.getOrElse(Nil)
574+
val dataEither = (tx.functionName, tx.contractCode) match {
575+
case (Some(functionName), None) => Right(rlp.encode(RLPList(functionName, args)))
576+
case (None, Some(contractCode)) => Right(rlp.encode(RLPList(contractCode, args)))
577+
case _ => Left(JsonRpcErrors.InvalidParams("Iele transaction should contain either functionName or contractCode"))
578+
}
579+
580+
dataEither match {
581+
case Right(data) => call(CallRequest(CallTx(tx.from, tx.to, tx.gas, tx.gasPrice, tx.value, ByteString(data)), req.block))
582+
case Left(error) => Future.successful(Left(error))
583+
}
584+
}
585+
557586
def estimateGas(req: CallRequest): ServiceResponse[EstimateGasResponse] = {
558587
Future {
559588
doCall(req)(ledgerHolder().binarySearchGasEstimation).map(gasUsed => EstimateGasResponse(gasUsed))
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package io.iohk.ethereum.jsonrpc
2+
3+
import akka.util.ByteString
4+
import io.iohk.ethereum.jsonrpc.EthService._
5+
import io.iohk.ethereum.jsonrpc.JsonRpcController.JsonDecoder
6+
import io.iohk.ethereum.jsonrpc.JsonRpcErrors.InvalidParams
7+
import io.iohk.ethereum.jsonrpc.PersonalService.{InvalidAddress, SendIeleTransactionRequest}
8+
import org.json4s.JsonAST.{JArray, JObject, JString, JValue}
9+
10+
object IeleJsonMethodsImplicits extends JsonMethodsImplicits {
11+
12+
def extractIeleCall(obj: JObject): Either[JsonRpcError, IeleCallTx] = {
13+
def toEitherOpt[A, B](opt: Option[Either[A, B]]): Either[A, Option[B]] =
14+
opt.map(_.right.map(Some.apply)).getOrElse(Right(None))
15+
16+
for {
17+
from <- toEitherOpt((obj \ "from").extractOpt[String].map(extractBytes))
18+
to <- toEitherOpt((obj \ "to").extractOpt[String].map(extractBytes))
19+
gas <- optionalQuantity(obj \ "gas")
20+
gasPrice <- optionalQuantity(obj \ "gasPrice")
21+
value <- optionalQuantity(obj \ "value")
22+
functionName = (obj \ "functionName").extractOpt[String]
23+
contractCode <- toEitherOpt((obj \ "contractCode").extractOpt[String].map(extractBytes))
24+
arguments = (obj \ "arguments").extractOpt[Seq[String]].map(_.map(in => ByteString(decode(in))))
25+
} yield IeleCallTx(
26+
from = from,
27+
to = to,
28+
gas = gas,
29+
gasPrice = gasPrice.getOrElse(0),
30+
value = value.getOrElse(0),
31+
functionName = functionName,
32+
contractCode = contractCode,
33+
arguments = arguments)
34+
}
35+
36+
implicit val iele_call = new JsonDecoder[IeleCallRequest] {
37+
def decodeJson(params: Option[JArray]): Either[JsonRpcError, IeleCallRequest] =
38+
params match {
39+
case Some(JArray((txObj: JObject) :: (blockValue: JValue) :: Nil)) =>
40+
for {
41+
blockParam <- extractBlockParam(blockValue)
42+
tx <- extractIeleCall(txObj)
43+
} yield IeleCallRequest(tx, blockParam)
44+
case _ => Left(InvalidParams())
45+
}
46+
}
47+
48+
49+
protected def extractIeleTx(input: Map[String, JValue]): Either[JsonRpcError, IeleTransactionRequest] = {
50+
def optionalQuantity(name: String): Either[JsonRpcError, Option[BigInt]] = input.get(name) match {
51+
case Some(v) => extractQuantity(v).map(Some(_))
52+
case None => Right(None)
53+
}
54+
55+
for {
56+
from <- input.get("from") match {
57+
case Some(JString(s)) => extractAddress(s)
58+
case Some(_) => Left(InvalidAddress)
59+
case _ => Left(InvalidParams("TX 'from' is required"))
60+
}
61+
62+
to <- input.get("to") match {
63+
case Some(JString(s)) =>
64+
extractAddress(s).map {
65+
case addr if addr.toUInt256.isZero => None
66+
case addr => Some(addr)
67+
}
68+
69+
case Some(_) => Left(InvalidAddress)
70+
case None => Right(None)
71+
}
72+
73+
value <- optionalQuantity("value")
74+
75+
gas <- optionalQuantity("gas")
76+
77+
gasPrice <- optionalQuantity("gasPrice")
78+
79+
nonce <- optionalQuantity("nonce")
80+
81+
functionName = input.get("functionName").flatMap(_.extractOpt[String])
82+
83+
arguments = input.get("arguments").flatMap(_.extractOpt[Seq[String]].map(_.map(in => ByteString(decode(in)))))
84+
85+
contractCode <- input.get("contractCode") match {
86+
case Some(JString(s)) => extractBytes(s).map(Some(_))
87+
case Some(_) => Left(InvalidParams())
88+
case None => Right(None)
89+
}
90+
91+
} yield IeleTransactionRequest(from, to, value, gas, gasPrice, nonce, functionName, arguments, contractCode)
92+
}
93+
94+
implicit val iele_sendTransaction = new JsonDecoder[SendIeleTransactionRequest] {
95+
def decodeJson(params: Option[JArray]): Either[JsonRpcError, SendIeleTransactionRequest] =
96+
params match {
97+
case Some(JArray(JObject(tx) :: _)) =>
98+
extractIeleTx(tx.toMap).map(SendIeleTransactionRequest)
99+
case _ =>
100+
Left(InvalidParams())
101+
}
102+
}
103+
104+
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ trait JsonMethodsImplicits {
3333
protected def encodeAsHex(input: BigInt): JString =
3434
JString(s"0x${input.toString(16)}")
3535

36-
private def decode(s: String): Array[Byte] = {
36+
protected def decode(s: String): Array[Byte] = {
3737
val stripped = s.replaceFirst("^0x", "")
3838
val normalized = if (stripped.length % 2 == 1) "0" + stripped else stripped
3939
Hex.decode(normalized)
@@ -78,6 +78,12 @@ trait JsonMethodsImplicits {
7878
Left(InvalidParams("could not extract quantity"))
7979
}
8080

81+
protected def optionalQuantity(input: JValue): Either[JsonRpcError, Option[BigInt]] =
82+
input match {
83+
case JNothing => Right(None)
84+
case o => extractQuantity(o).map(Some(_))
85+
}
86+
8187
protected def extractTx(input: Map[String, JValue]): Either[JsonRpcError, TransactionRequest] = {
8288
def optionalQuantity(name: String): Either[JsonRpcError, Option[BigInt]] = input.get(name) match {
8389
case Some(v) => extractQuantity(v).map(Some(_))

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ object JsonRpcController {
4141
new JsonRpcConfig {
4242
override val apis: Seq[String] = {
4343
val providedApis = rpcConfig.getString("apis").split(",").map(_.trim.toLowerCase)
44-
val invalidApis = providedApis.diff(List("web3", "eth", "net", "personal", "daedalus", "test"))
44+
val invalidApis = providedApis.diff(List("web3", "eth", "net", "personal", "daedalus", "test", "iele"))
4545
require(invalidApis.isEmpty, s"Invalid RPC APIs specified: ${invalidApis.mkString(",")}")
4646
providedApis
4747
}
@@ -65,6 +65,7 @@ object JsonRpcController {
6565
val Debug = "debug"
6666
val Rpc = "rpc"
6767
val Test = "test"
68+
val Iele = "iele"
6869
}
6970

7071
}
@@ -80,6 +81,7 @@ class JsonRpcController(
8081
import JsonRpcController._
8182
import EthJsonMethodsImplicits._
8283
import TestJsonMethodsImplicits._
84+
import IeleJsonMethodsImplicits._
8385
import JsonMethodsImplicits._
8486
import JsonRpcErrors._
8587

@@ -93,7 +95,8 @@ class JsonRpcController(
9395
Apis.Rpc -> handleRpcRequest,
9496
Apis.Admin -> PartialFunction.empty,
9597
Apis.Debug -> PartialFunction.empty,
96-
Apis.Test -> handleTestRequest
98+
Apis.Test -> handleTestRequest,
99+
Apis.Iele -> handleIeleRequest
97100
)
98101

99102
private def enabledApis = config.apis :+ Apis.Rpc // RPC enabled by default
@@ -215,6 +218,15 @@ class JsonRpcController(
215218
handle[SetEtherbaseRequest, SetEtherbaseResponse](testService.setEtherbase, req)
216219
}
217220

221+
private def handleIeleRequest: PartialFunction[JsonRpcRequest, Future[JsonRpcResponse]] = {
222+
case req @ JsonRpcRequest(_, "iele_sendTransaction", _, _) =>
223+
println("Handle iele_sendTx")
224+
handle[SendIeleTransactionRequest, SendTransactionResponse](personalService.sendIeleTransaction, req)
225+
case req @ JsonRpcRequest(_, "iele_call", _, _) =>
226+
println("Handling iele_call")
227+
handle[IeleCallRequest, CallResponse](ethService.ieleCall, req)
228+
}
229+
218230
private def handlePersonalRequest: PartialFunction[JsonRpcRequest, Future[JsonRpcResponse]] = {
219231
case req @ JsonRpcRequest(_, "personal_importRawKey", _, _) =>
220232
handle[ImportRawKeyRequest, ImportRawKeyResponse](personalService.importRawKey, req)

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,13 @@ import io.iohk.ethereum.domain.{Account, Address, Blockchain}
1212
import io.iohk.ethereum.jsonrpc.PersonalService._
1313
import io.iohk.ethereum.keystore.{KeyStore, Wallet}
1414
import io.iohk.ethereum.jsonrpc.JsonRpcErrors._
15+
import io.iohk.ethereum.rlp.RLPList
1516
import io.iohk.ethereum.transactions.PendingTransactionsManager
1617
import io.iohk.ethereum.transactions.PendingTransactionsManager.{AddOrOverrideTransaction, PendingTransactionsResponse}
1718
import io.iohk.ethereum.utils.{BlockchainConfig, TxPoolConfig}
19+
import io.iohk.ethereum.rlp
20+
import io.iohk.ethereum.rlp.RLPImplicits._
21+
import io.iohk.ethereum.rlp.RLPImplicitConversions._
1822

1923
import scala.concurrent.ExecutionContext.Implicits.global
2024
import scala.concurrent.Future
@@ -43,6 +47,8 @@ object PersonalService {
4347
case class SendTransactionRequest(tx: TransactionRequest)
4448
case class SendTransactionResponse(txHash: ByteString)
4549

50+
case class SendIeleTransactionRequest(tx: IeleTransactionRequest)
51+
4652
case class SignRequest(message: ByteString, address: Address, passphrase: Option[String])
4753
case class SignResponse(signature: ECDSASignature)
4854

@@ -156,6 +162,24 @@ class PersonalService(
156162
}
157163
}
158164

165+
def sendIeleTransaction(request: SendIeleTransactionRequest): ServiceResponse[SendTransactionResponse] = {
166+
import request.tx
167+
168+
val args = tx.arguments.getOrElse(Nil)
169+
val dataEither = (tx.functionName, tx.contractCode) match {
170+
case (Some(functionName), None) => Right(rlp.encode(RLPList(functionName, args)))
171+
case (None, Some(contractCode)) => Right(rlp.encode(RLPList(contractCode, args)))
172+
case _ => Left(JsonRpcErrors.InvalidParams("Iele transaction should contain either functionName or contractCode"))
173+
}
174+
175+
dataEither match {
176+
case Right(data) =>
177+
sendTransaction(SendTransactionRequest(TransactionRequest(tx.from, tx.to, tx.value, tx.gasLimit, tx.gasPrice, tx.nonce, Some(ByteString(data)))))
178+
case Left(error) =>
179+
Future.successful(Left(error))
180+
}
181+
}
182+
159183
def deleteWallet(request: DeleteWalletRequest): ServiceResponse[DeleteWalletResponse] = Future {
160184
unlockedWallets.remove(request.address)
161185

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,14 @@ case class TransactionRequest(
2525
payload = data.getOrElse(ByteString.empty)
2626
)
2727
}
28+
29+
case class IeleTransactionRequest(
30+
from: Address,
31+
to: Option[Address] = None,
32+
value: Option[BigInt] = None,
33+
gasLimit: Option[BigInt] = None,
34+
gasPrice: Option[BigInt] = None,
35+
nonce: Option[BigInt] = None,
36+
functionName: Option[String] = None,
37+
arguments: Option[Seq[ByteString]] = None,
38+
contractCode: Option[ByteString])

src/universal/conf/network.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ mantis {
9898
}
9999

100100
# Enabled JSON-RPC APIs over the JSON-RPC endpoint
101-
# Available choices are: eth, web3, net, personal, daedalus
101+
# Available choices are: eth, web3, net, personal, test, daedalus, iele
102102
# apis = "eth,web3,net,personal,daedalus"
103103
}
104104
}

0 commit comments

Comments
 (0)