Skip to content

Commit 8476c04

Browse files
authored
[ETCM-215] faucet rpc client (#798)
* add new RpcClient Base * add security in rpc client * change ssl config * change certificate config * change ssl config * change package from json rpc client * add new test * improvement in ssl context * add tls default folder * add new tls config from rpc client * fix and add README TLS config * change rpcclient * change name ssl function
1 parent a8d5b00 commit 8476c04

File tree

69 files changed

+867
-272
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+867
-272
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,42 @@ projectRoot $ docker build -f ./docker/monitoring-client.Dockerfile -t mantis-mo
130130
projectRoot $ docker run --network=host mantis-monitoring-client
131131
```
132132

133+
### TLS setup
134+
135+
Both the JSON RPC (on the node and faucet) can be additionally protected using TLS.
136+
On the development environment it's already properly configured with a development certificate.
137+
138+
#### Generating a new certificate
139+
140+
If a new certificate is required, create a new keystore with a certificate by running `./tls/gen-cert.sh`
141+
142+
#### Configuring the node
143+
144+
1. Configure the certificate and password file to be used at `mantis.network.rpc.http.certificate` key on the `application.conf` file:
145+
146+
keystore-path: path to the keystore storing the certificates (if generated through our script they are by default located in "./tls/mantisCA.p12")
147+
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
148+
password-file: path to the file with the password used for accessing the certificate keystore (if generated through our script they are by default located in "./tls/password")
149+
2. Enable TLS in specific config:
150+
- For JSON RPC: `mantis.network.rpc.http.mode=https`
151+
152+
#### Configuring the faucet
153+
154+
1. Configure the certificate and password file to be used at `mantis.network.rpc.http.certificate` key on the `faucet.conf` file:
155+
156+
keystore-path: path to the keystore storing the certificates (if generated through our script they are by default located in "./tls/mantisCA.p12")
157+
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
158+
password-file: path to the file with the password used for accessing the certificate keystore (if generated through our script they are by default located in "./tls/password")
159+
2. Enable TLS in specific config:
160+
- For JSON RPC: `mantis.network.rpc.http.mode=https`
161+
3. Configure the certificate used from RpcClient to connect with the node. Necessary if the node uses http secure.
162+
This certificate and password file to be used at `faucet.rpc-client.certificate` key on the `faucet.conf` file:
163+
164+
keystore-path: path to the keystore storing the certificates
165+
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
166+
password-file: path to the file with the password used for accessing the certificate keystore
167+
168+
133169
### Feedback
134170

135171
Feedback gratefully received through the Ethereum Classic Forum (http://forum.ethereumclassic.org/)

src/evmTest/scala/io/iohk/ethereum/vm/PrecompiledContractsSpecEvm.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import akka.util.ByteString
44
import io.iohk.ethereum.crypto
55
import io.iohk.ethereum.crypto._
66
import io.iohk.ethereum.domain.SignedTransaction.{FirstByteOfAddress, LastByteOfAddress}
7-
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
7+
import io.iohk.ethereum.security.SecureRandomBuilder
88
import io.iohk.ethereum.vm.utils.EvmTestEnv
99
import org.bouncycastle.crypto.params.ECPublicKeyParameters
1010
import org.scalatest.funsuite.AnyFunSuite

src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.iohk.ethereum.db.dataSource.{RocksDbConfig, RocksDbDataSource}
1515
import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode}
1616
import io.iohk.ethereum.db.storage.{AppStateStorage, Namespaces}
1717
import io.iohk.ethereum.domain.{Block, Blockchain, BlockchainImpl, ChainWeight}
18+
import io.iohk.ethereum.security.SecureRandomBuilder
1819
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
1920
import io.iohk.ethereum.mpt.MerklePatriciaTrie
2021
import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo
@@ -33,7 +34,7 @@ import io.iohk.ethereum.network.{
3334
PeerManagerActor,
3435
ServerActor
3536
}
36-
import io.iohk.ethereum.nodebuilder.{PruningConfigBuilder, SecureRandomBuilder}
37+
import io.iohk.ethereum.nodebuilder.PruningConfigBuilder
3738
import io.iohk.ethereum.sync.util.SyncCommonItSpec._
3839
import io.iohk.ethereum.sync.util.SyncCommonItSpecUtils._
3940
import io.iohk.ethereum.utils.ServerStatus.Listening

src/it/scala/io/iohk/ethereum/txExecTest/util/DumpChainApp.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import io.iohk.ethereum.network.handshaker.{EtcHandshaker, EtcHandshakerConfigur
2323
import io.iohk.ethereum.network.p2p.EthereumMessageDecoder
2424
import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
2525
import io.iohk.ethereum.network.{ForkResolver, PeerEventBusActor, PeerManagerActor}
26-
import io.iohk.ethereum.nodebuilder.{AuthHandshakerBuilder, NodeKeyBuilder, SecureRandomBuilder}
26+
import io.iohk.ethereum.nodebuilder.{AuthHandshakerBuilder, NodeKeyBuilder}
27+
import io.iohk.ethereum.security.SecureRandomBuilder
2728
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus}
2829
import monix.reactive.Observable
2930
import org.bouncycastle.util.encoders.Hex

src/main/resources/application.conf

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,17 +183,20 @@ mantis {
183183
# Listening port of JSON-RPC HTTP(S) endpoint
184184
port = 8546
185185

186+
certificate = null
187+
#certificate {
186188
# Path to the keystore storing the certificates (used only for https)
187189
# null value indicates HTTPS is not being used
188-
certificate-keystore-path = null
190+
# keystore-path = "tls/mantisCA.p12"
189191

190192
# Type of certificate keystore being used
191193
# null value indicates HTTPS is not being used
192-
certificate-keystore-type = null
194+
# keystore-type = "pkcs12"
193195

194196
# File with the password used for accessing the certificate keystore (used only for https)
195197
# null value indicates HTTPS is not being used
196-
certificate-password-file = null
198+
# password-file = "tls/password"
199+
#}
197200

198201
# Domains allowed to query RPC endpoint. Use "*" to enable requests from
199202
# any domain.

src/main/scala/io/iohk/ethereum/cli/CliCommands.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import io.iohk.ethereum.crypto._
77
import io.iohk.ethereum.domain.Address
88
import io.iohk.ethereum.utils.ByteStringUtils
99
import java.security.SecureRandom
10-
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
10+
import io.iohk.ethereum.security.SecureRandomBuilder
1111
import org.bouncycastle.util.encoders.Hex
1212

1313
object CliCommands extends SecureRandomBuilder {

src/main/scala/io/iohk/ethereum/consensus/TestConsensusBuilder.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.iohk.ethereum.consensus
22

33
import io.iohk.ethereum.nodebuilder._
4+
import io.iohk.ethereum.security.SecureRandomBuilder
45
import io.iohk.ethereum.utils.Logger
56

67
/**

src/main/scala/io/iohk/ethereum/crypto/EcKeyGen.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.iohk.ethereum.crypto
22

3-
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
3+
import io.iohk.ethereum.security.SecureRandomBuilder
44

55
/**
66
* A simple tool to generate ECDSA key pairs. Takes an optional positional argument [n] - number of key pairs

src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ object FaucetConfig {
3636
txGasPrice = faucetConfig.getLong("tx-gas-price"),
3737
txGasLimit = faucetConfig.getLong("tx-gas-limit"),
3838
txValue = faucetConfig.getLong("tx-value"),
39-
rpcAddress = faucetConfig.getString("rpc-address"),
39+
rpcAddress = faucetConfig.getString("rpc-client.rpc-address"),
4040
keyStoreDir = faucetConfig.getString("keystore-dir"),
4141
minRequestInterval = faucetConfig.getDuration("min-request-interval").toMillis.millis,
4242
handlerTimeout = faucetConfig.getDuration("handler-timeout").toMillis.millis,

src/main/scala/io/iohk/ethereum/faucet/FaucetSupervisor.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ object FaucetSupervisor {
1212
val name = "FaucetSupervisor"
1313
}
1414

15-
class FaucetSupervisor(walletRpcClient: WalletService, config: FaucetConfig, shutdown: () => Unit)(implicit
15+
class FaucetSupervisor(walletService: WalletService, config: FaucetConfig, shutdown: () => Unit)(implicit
1616
system: ActorSystem
1717
) extends Logger {
1818

19-
val childProps = FaucetHandler.props(walletRpcClient, config)
19+
val childProps = FaucetHandler.props(walletService, config)
2020

2121
val minBackoff: FiniteDuration = config.supervisor.minBackoff
2222
val maxBackoff: FiniteDuration = config.supervisor.maxBackoff

src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
package io.iohk.ethereum.faucet.jsonrpc
22

3-
import java.security.SecureRandom
4-
53
import akka.actor.ActorSystem
64
import io.iohk.ethereum.faucet.{FaucetConfigBuilder, FaucetSupervisor}
75
import io.iohk.ethereum.jsonrpc.server.controllers.ApisBase
86
import io.iohk.ethereum.jsonrpc.server.controllers.JsonRpcBaseController.JsonRpcConfig
97
import io.iohk.ethereum.jsonrpc.server.http.JsonRpcHttpServer
108
import io.iohk.ethereum.keystore.KeyStoreImpl
11-
import io.iohk.ethereum.mallet.service.RpcClient
12-
import io.iohk.ethereum.utils.{ConfigUtils, KeyStoreConfig, Logger}
9+
import io.iohk.ethereum.security.{SSLContextBuilder, SecureRandomBuilder}
10+
import io.iohk.ethereum.utils.{KeyStoreConfig, Logger}
1311

1412
import scala.concurrent.Await
15-
import scala.util.Try
1613

1714
trait ActorSystemBuilder {
1815
def systemName: String
@@ -26,13 +23,24 @@ trait FaucetControllerBuilder {
2623
}
2724

2825
trait FaucetRpcServiceBuilder {
29-
self: FaucetConfigBuilder with FaucetControllerBuilder with ActorSystemBuilder with ShutdownHookBuilder =>
26+
self: FaucetConfigBuilder
27+
with FaucetControllerBuilder
28+
with ActorSystemBuilder
29+
with SecureRandomBuilder
30+
with ShutdownHookBuilder
31+
with SSLContextBuilder =>
32+
33+
val keyStore =
34+
new KeyStoreImpl(
35+
KeyStoreConfig.customKeyStoreConfig(faucetConfig.keyStoreDir),
36+
secureRandom
37+
)
3038

31-
val keyStore = new KeyStoreImpl(KeyStoreConfig.customKeyStoreConfig(faucetConfig.keyStoreDir), new SecureRandom())
32-
val rpcClient = new RpcClient(faucetConfig.rpcAddress)
33-
val walletService = new WalletService(rpcClient, keyStore, faucetConfig)
39+
val walletRpcClient: WalletRpcClient =
40+
new WalletRpcClient(faucetConfig.rpcAddress, () => sslContext("faucet.rpc-client"))
41+
val walletService = new WalletService(walletRpcClient, keyStore, faucetConfig)
3442
val faucetSupervisor: FaucetSupervisor = new FaucetSupervisor(walletService, faucetConfig, shutdown)(system)
35-
val faucetRpcService = new FaucetRpcService(faucetConfig)(system)
43+
val faucetRpcService = new FaucetRpcService(faucetConfig)
3644
}
3745

3846
trait FaucetJsonRpcHealthCheckBuilder {
@@ -63,27 +71,20 @@ trait FaucetJsonRpcControllerBuilder {
6371
val faucetJsonRpcController = new FaucetJsonRpcController(faucetRpcService, jsonRpcConfig)
6472
}
6573

66-
trait SecureRandomBuilder {
67-
self: FaucetConfigBuilder =>
68-
lazy val secureRandom: SecureRandom =
69-
ConfigUtils
70-
.getOptionalValue(rawMantisConfig, _.getString, "secure-random-algo")
71-
.flatMap(name => Try { SecureRandom.getInstance(name) }.toOption)
72-
.getOrElse(new SecureRandom())
73-
}
74-
7574
trait FaucetJsonRpcHttpServerBuilder {
7675
self: ActorSystemBuilder
7776
with JsonRpcConfigBuilder
7877
with SecureRandomBuilder
7978
with FaucetJsonRpcHealthCheckBuilder
80-
with FaucetJsonRpcControllerBuilder =>
79+
with FaucetJsonRpcControllerBuilder
80+
with SSLContextBuilder =>
8181

8282
val faucetJsonRpcHttpServer = JsonRpcHttpServer(
8383
faucetJsonRpcController,
8484
faucetJsonRpcHealthCheck,
8585
jsonRpcConfig.httpServerConfig,
86-
secureRandom
86+
secureRandom,
87+
() => sslContext("mantis.network.rpc.http")
8788
)
8889
}
8990

@@ -107,6 +108,7 @@ class FaucetServer
107108
with ApisBuilder
108109
with JsonRpcConfigBuilder
109110
with SecureRandomBuilder
111+
with SSLContextBuilder
110112
with FaucetControllerBuilder
111113
with FaucetRpcServiceBuilder
112114
with FaucetJsonRpcHealthCheckBuilder
@@ -127,5 +129,4 @@ class FaucetServer
127129
case Right(jsonRpcServer) => jsonRpcServer.run()
128130
case Left(error) => throw new RuntimeException(s"$error")
129131
}
130-
131132
}

src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetHandlerBuilder.scala renamed to src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetHandlerSelector.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import akka.util.Timeout
66
import io.iohk.ethereum.faucet.{FaucetConfigBuilder, FaucetHandler, FaucetSupervisor}
77
import monix.eval.Task
88

9-
trait FaucetHandlerBuilder {
9+
trait FaucetHandlerSelector {
1010
self: FaucetConfigBuilder with RetrySupport =>
1111

1212
val handlerPath = s"user/${FaucetSupervisor.name}/${FaucetHandler.name}"
@@ -15,7 +15,7 @@ trait FaucetHandlerBuilder {
1515

1616
lazy val handlerTimeout: Timeout = Timeout(faucetConfig.handlerTimeout)
1717

18-
def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
18+
def selectFaucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
1919
Task.deferFuture(
2020
retry(() => system.actorSelection(handlerPath).resolveOne(handlerTimeout.duration), attempts, delay)(
2121
system.dispatcher,

src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetRpcService.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ import io.iohk.ethereum.utils.Logger
1313
class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
1414
extends FaucetConfigBuilder
1515
with RetrySupport
16-
with FaucetHandlerBuilder
16+
with FaucetHandlerSelector
1717
with Logger {
1818

1919
implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout)
2020

2121
def sendFunds(sendFundsRequest: SendFundsRequest): ServiceResponse[SendFundsResponse] =
22-
faucetHandler()
22+
selectFaucetHandler()
2323
.flatMap(handler =>
2424
handler
2525
.askFor[Any](FaucetHandlerMsg.SendFunds(sendFundsRequest.address))
@@ -28,7 +28,7 @@ class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
2828
.onErrorRecover(handleErrors)
2929

3030
def status(statusRequest: StatusRequest): ServiceResponse[StatusResponse] =
31-
faucetHandler()
31+
selectFaucetHandler()
3232
.flatMap(handler => handler.askFor[Any](FaucetHandlerMsg.Status))
3333
.map(handleStatusResponse orElse handleErrors)
3434
.onErrorRecover(handleErrors)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package io.iohk.ethereum.faucet.jsonrpc
2+
3+
import akka.actor.ActorSystem
4+
import akka.http.scaladsl.model.Uri
5+
import io.circe.syntax._
6+
import akka.util.ByteString
7+
import io.iohk.ethereum.domain.Address
8+
import io.iohk.ethereum.jsonrpc.client.RpcClient
9+
import io.iohk.ethereum.jsonrpc.client.RpcClient.RpcError
10+
import io.iohk.ethereum.security.SSLError
11+
import io.iohk.ethereum.utils.Logger
12+
import javax.net.ssl.SSLContext
13+
import monix.eval.Task
14+
15+
import scala.concurrent.ExecutionContext
16+
17+
class WalletRpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLContext])(implicit
18+
system: ActorSystem,
19+
ec: ExecutionContext
20+
) extends RpcClient(node, getSSLContext)
21+
with Logger {
22+
import io.iohk.ethereum.jsonrpc.client.CommonJsonCodecs._
23+
24+
def getNonce(address: Address): Task[Either[RpcError, BigInt]] =
25+
doRequest[BigInt]("eth_getTransactionCount", List(address.asJson, "latest".asJson))
26+
27+
def sendTransaction(rawTx: ByteString): Task[Either[RpcError, ByteString]] =
28+
doRequest[ByteString]("eth_sendRawTransaction", List(rawTx.asJson))
29+
}

src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletService.scala

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
package io.iohk.ethereum.faucet.jsonrpc
22

33
import akka.util.ByteString
4+
import cats.data.EitherT
45
import io.iohk.ethereum.domain.{Address, Transaction}
56
import io.iohk.ethereum.faucet.FaucetConfig
7+
import io.iohk.ethereum.jsonrpc.client.RpcClient.RpcError
68
import io.iohk.ethereum.keystore.KeyStore.KeyStoreError
79
import io.iohk.ethereum.keystore.{KeyStore, Wallet}
8-
import io.iohk.ethereum.mallet.common.Err
9-
import io.iohk.ethereum.mallet.service.RpcClient
1010
import io.iohk.ethereum.network.p2p.messages.CommonMessages.SignedTransactions.SignedTransactionEnc
1111
import io.iohk.ethereum.rlp
1212
import io.iohk.ethereum.utils.{ByteStringUtils, Logger}
1313
import monix.eval.Task
1414

15-
class WalletService(rpcClient: RpcClient, keyStore: KeyStore, config: FaucetConfig) extends Logger {
15+
class WalletService(walletRpcClient: WalletRpcClient, keyStore: KeyStore, config: FaucetConfig) extends Logger {
1616

17-
def sendFunds(wallet: Wallet, addressTo: Address): Task[Either[Err, ByteString]] = {
18-
Task {
19-
(for {
20-
nonce <- rpcClient.getNonce(wallet.address)
21-
txId <- rpcClient.sendTransaction(prepareTx(wallet, addressTo, nonce))
22-
} yield txId) match {
23-
case Right(txId) =>
24-
val txIdHex = s"0x${ByteStringUtils.hash2string(txId)}"
25-
log.info(s"Sending ${config.txValue} ETH to $addressTo in tx: $txIdHex.")
26-
Right(txId)
27-
case Left(error) =>
28-
log.error(s"An error occurred while using faucet", error)
29-
Left(error)
30-
}
17+
def sendFunds(wallet: Wallet, addressTo: Address): Task[Either[RpcError, ByteString]] = {
18+
(for {
19+
nonce <- EitherT(walletRpcClient.getNonce(wallet.address))
20+
txId <- EitherT(walletRpcClient.sendTransaction(prepareTx(wallet, addressTo, nonce)))
21+
} yield txId).value map {
22+
case Right(txId) =>
23+
val txIdHex = s"0x${ByteStringUtils.hash2string(txId)}"
24+
log.info(s"Sending ${config.txValue} ETC to $addressTo in tx: $txIdHex.")
25+
Right(txId)
26+
case Left(error) =>
27+
log.error(s"An error occurred while using faucet", error)
28+
Left(error)
3129
}
3230
}
3331

src/main/scala/io/iohk/ethereum/mallet/service/CommonJsonCodecs.scala renamed to src/main/scala/io/iohk/ethereum/jsonrpc/client/CommonJsonCodecs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.iohk.ethereum.mallet.service
1+
package io.iohk.ethereum.jsonrpc.client
22

33
import akka.util.ByteString
44
import io.circe._

0 commit comments

Comments
 (0)