Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,42 @@ projectRoot $ docker build -f ./docker/monitoring-client.Dockerfile -t mantis-mo
projectRoot $ docker run --network=host mantis-monitoring-client
```

### TLS setup

Both the JSON RPC (on the node and faucet) can be additionally protected using TLS.
On the development environment it's already properly configured with a development certificate.

#### Generating a new certificate

If a new certificate is required, create a new keystore with a certificate by running `./tls/gen-cert.sh`

#### Configuring the node

1. Configure the certificate and password file to be used at `mantis.network.rpc.http.certificate` key on the `application.conf` file:

keystore-path: path to the keystore storing the certificates (if generated through our script they are by default located in "./tls/mantisCA.p12")
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
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")
2. Enable TLS in specific config:
- For JSON RPC: `mantis.network.rpc.http.mode=https`

#### Configuring the faucet

1. Configure the certificate and password file to be used at `mantis.network.rpc.http.certificate` key on the `faucet.conf` file:

keystore-path: path to the keystore storing the certificates (if generated through our script they are by default located in "./tls/mantisCA.p12")
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
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")
2. Enable TLS in specific config:
- For JSON RPC: `mantis.network.rpc.http.mode=https`
3. Configure the certificate used from RpcClient to connect with the node. Necessary if the node uses http secure.
This certificate and password file to be used at `faucet.rpc-client.certificate` key on the `faucet.conf` file:

keystore-path: path to the keystore storing the certificates
keystore-type: type of certificate keystore being used (if generated through our script use "pkcs12")
password-file: path to the file with the password used for accessing the certificate keystore


### Feedback

Feedback gratefully received through the Ethereum Classic Forum (http://forum.ethereumclassic.org/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import akka.util.ByteString
import io.iohk.ethereum.crypto
import io.iohk.ethereum.crypto._
import io.iohk.ethereum.domain.SignedTransaction.{FirstByteOfAddress, LastByteOfAddress}
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.vm.utils.EvmTestEnv
import org.bouncycastle.crypto.params.ECPublicKeyParameters
import org.scalatest.funsuite.AnyFunSuite
Expand Down
3 changes: 2 additions & 1 deletion src/it/scala/io/iohk/ethereum/sync/util/CommonFakePeer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import io.iohk.ethereum.db.dataSource.{RocksDbConfig, RocksDbDataSource}
import io.iohk.ethereum.db.storage.pruning.{ArchivePruning, PruningMode}
import io.iohk.ethereum.db.storage.{AppStateStorage, Namespaces}
import io.iohk.ethereum.domain.{Block, Blockchain, BlockchainImpl, ChainWeight}
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.ledger.InMemoryWorldStateProxy
import io.iohk.ethereum.mpt.MerklePatriciaTrie
import io.iohk.ethereum.network.EtcPeerManagerActor.PeerInfo
Expand All @@ -33,7 +34,7 @@ import io.iohk.ethereum.network.{
PeerManagerActor,
ServerActor
}
import io.iohk.ethereum.nodebuilder.{PruningConfigBuilder, SecureRandomBuilder}
import io.iohk.ethereum.nodebuilder.PruningConfigBuilder
import io.iohk.ethereum.sync.util.SyncCommonItSpec._
import io.iohk.ethereum.sync.util.SyncCommonItSpecUtils._
import io.iohk.ethereum.utils.ServerStatus.Listening
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import io.iohk.ethereum.network.handshaker.{EtcHandshaker, EtcHandshakerConfigur
import io.iohk.ethereum.network.p2p.EthereumMessageDecoder
import io.iohk.ethereum.network.rlpx.RLPxConnectionHandler.RLPxConfiguration
import io.iohk.ethereum.network.{ForkResolver, PeerEventBusActor, PeerManagerActor}
import io.iohk.ethereum.nodebuilder.{AuthHandshakerBuilder, NodeKeyBuilder, SecureRandomBuilder}
import io.iohk.ethereum.nodebuilder.{AuthHandshakerBuilder, NodeKeyBuilder}
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.utils.{Config, NodeStatus, ServerStatus}
import monix.reactive.Observable
import org.bouncycastle.util.encoders.Hex
Expand Down
9 changes: 6 additions & 3 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,20 @@ mantis {
# Listening port of JSON-RPC HTTP(S) endpoint
port = 8546

certificate = null
#certificate {
# Path to the keystore storing the certificates (used only for https)
# null value indicates HTTPS is not being used
certificate-keystore-path = null
# keystore-path = "tls/mantisCA.p12"

# Type of certificate keystore being used
# null value indicates HTTPS is not being used
certificate-keystore-type = null
# keystore-type = "pkcs12"

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

# Domains allowed to query RPC endpoint. Use "*" to enable requests from
# any domain.
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/io/iohk/ethereum/cli/CliCommands.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import io.iohk.ethereum.crypto._
import io.iohk.ethereum.domain.Address
import io.iohk.ethereum.utils.ByteStringUtils
import java.security.SecureRandom
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
import io.iohk.ethereum.security.SecureRandomBuilder
import org.bouncycastle.util.encoders.Hex

object CliCommands extends SecureRandomBuilder {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.iohk.ethereum.consensus

import io.iohk.ethereum.nodebuilder._
import io.iohk.ethereum.security.SecureRandomBuilder
import io.iohk.ethereum.utils.Logger

/**
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/io/iohk/ethereum/crypto/EcKeyGen.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.iohk.ethereum.crypto

import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
import io.iohk.ethereum.security.SecureRandomBuilder

/**
* A simple tool to generate ECDSA key pairs. Takes an optional positional argument [n] - number of key pairs
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/io/iohk/ethereum/faucet/FaucetConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object FaucetConfig {
txGasPrice = faucetConfig.getLong("tx-gas-price"),
txGasLimit = faucetConfig.getLong("tx-gas-limit"),
txValue = faucetConfig.getLong("tx-value"),
rpcAddress = faucetConfig.getString("rpc-address"),
rpcAddress = faucetConfig.getString("rpc-client.rpc-address"),
keyStoreDir = faucetConfig.getString("keystore-dir"),
minRequestInterval = faucetConfig.getDuration("min-request-interval").toMillis.millis,
handlerTimeout = faucetConfig.getDuration("handler-timeout").toMillis.millis,
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/io/iohk/ethereum/faucet/FaucetSupervisor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ object FaucetSupervisor {
val name = "FaucetSupervisor"
}

class FaucetSupervisor(walletRpcClient: WalletService, config: FaucetConfig, shutdown: () => Unit)(implicit
class FaucetSupervisor(walletService: WalletService, config: FaucetConfig, shutdown: () => Unit)(implicit
system: ActorSystem
) extends Logger {

val childProps = FaucetHandler.props(walletRpcClient, config)
val childProps = FaucetHandler.props(walletService, config)

val minBackoff: FiniteDuration = config.supervisor.minBackoff
val maxBackoff: FiniteDuration = config.supervisor.maxBackoff
Expand Down
45 changes: 23 additions & 22 deletions src/main/scala/io/iohk/ethereum/faucet/jsonrpc/FaucetBuilder.scala
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package io.iohk.ethereum.faucet.jsonrpc

import java.security.SecureRandom

import akka.actor.ActorSystem
import io.iohk.ethereum.faucet.{FaucetConfigBuilder, FaucetSupervisor}
import io.iohk.ethereum.jsonrpc.server.controllers.ApisBase
import io.iohk.ethereum.jsonrpc.server.controllers.JsonRpcBaseController.JsonRpcConfig
import io.iohk.ethereum.jsonrpc.server.http.JsonRpcHttpServer
import io.iohk.ethereum.keystore.KeyStoreImpl
import io.iohk.ethereum.mallet.service.RpcClient
import io.iohk.ethereum.utils.{ConfigUtils, KeyStoreConfig, Logger}
import io.iohk.ethereum.security.{SSLContextBuilder, SecureRandomBuilder}
import io.iohk.ethereum.utils.{KeyStoreConfig, Logger}

import scala.concurrent.Await
import scala.util.Try

trait ActorSystemBuilder {
def systemName: String
Expand All @@ -26,13 +23,24 @@ trait FaucetControllerBuilder {
}

trait FaucetRpcServiceBuilder {
self: FaucetConfigBuilder with FaucetControllerBuilder with ActorSystemBuilder with ShutdownHookBuilder =>
self: FaucetConfigBuilder
with FaucetControllerBuilder
with ActorSystemBuilder
with SecureRandomBuilder
with ShutdownHookBuilder
with SSLContextBuilder =>

val keyStore =
new KeyStoreImpl(
KeyStoreConfig.customKeyStoreConfig(faucetConfig.keyStoreDir),
secureRandom
)

val keyStore = new KeyStoreImpl(KeyStoreConfig.customKeyStoreConfig(faucetConfig.keyStoreDir), new SecureRandom())
val rpcClient = new RpcClient(faucetConfig.rpcAddress)
val walletService = new WalletService(rpcClient, keyStore, faucetConfig)
val walletRpcClient: WalletRpcClient =
new WalletRpcClient(faucetConfig.rpcAddress, () => sslContext("faucet.rpc-client"))
val walletService = new WalletService(walletRpcClient, keyStore, faucetConfig)
val faucetSupervisor: FaucetSupervisor = new FaucetSupervisor(walletService, faucetConfig, shutdown)(system)
val faucetRpcService = new FaucetRpcService(faucetConfig)(system)
val faucetRpcService = new FaucetRpcService(faucetConfig)
}

trait FaucetJsonRpcHealthCheckBuilder {
Expand Down Expand Up @@ -63,27 +71,20 @@ trait FaucetJsonRpcControllerBuilder {
val faucetJsonRpcController = new FaucetJsonRpcController(faucetRpcService, jsonRpcConfig)
}

trait SecureRandomBuilder {
self: FaucetConfigBuilder =>
lazy val secureRandom: SecureRandom =
ConfigUtils
.getOptionalValue(rawMantisConfig, _.getString, "secure-random-algo")
.flatMap(name => Try { SecureRandom.getInstance(name) }.toOption)
.getOrElse(new SecureRandom())
}

trait FaucetJsonRpcHttpServerBuilder {
self: ActorSystemBuilder
with JsonRpcConfigBuilder
with SecureRandomBuilder
with FaucetJsonRpcHealthCheckBuilder
with FaucetJsonRpcControllerBuilder =>
with FaucetJsonRpcControllerBuilder
with SSLContextBuilder =>

val faucetJsonRpcHttpServer = JsonRpcHttpServer(
faucetJsonRpcController,
faucetJsonRpcHealthCheck,
jsonRpcConfig.httpServerConfig,
secureRandom
secureRandom,
() => sslContext("mantis.network.rpc.http")
)
}

Expand All @@ -107,6 +108,7 @@ class FaucetServer
with ApisBuilder
with JsonRpcConfigBuilder
with SecureRandomBuilder
with SSLContextBuilder
with FaucetControllerBuilder
with FaucetRpcServiceBuilder
with FaucetJsonRpcHealthCheckBuilder
Expand All @@ -127,5 +129,4 @@ class FaucetServer
case Right(jsonRpcServer) => jsonRpcServer.run()
case Left(error) => throw new RuntimeException(s"$error")
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import akka.util.Timeout
import io.iohk.ethereum.faucet.{FaucetConfigBuilder, FaucetHandler, FaucetSupervisor}
import monix.eval.Task

trait FaucetHandlerBuilder {
trait FaucetHandlerSelector {
self: FaucetConfigBuilder with RetrySupport =>

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

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

def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
def selectFaucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
Task.deferFuture(
retry(() => system.actorSelection(handlerPath).resolveOne(handlerTimeout.duration), attempts, delay)(
system.dispatcher,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import io.iohk.ethereum.utils.Logger
class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem)
extends FaucetConfigBuilder
with RetrySupport
with FaucetHandlerBuilder
with FaucetHandlerSelector
with Logger {

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

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

def status(statusRequest: StatusRequest): ServiceResponse[StatusResponse] =
faucetHandler()
selectFaucetHandler()
.flatMap(handler => handler.askFor[Any](FaucetHandlerMsg.Status))
.map(handleStatusResponse orElse handleErrors)
.onErrorRecover(handleErrors)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.iohk.ethereum.faucet.jsonrpc

import akka.actor.ActorSystem
import akka.http.scaladsl.model.Uri
import io.circe.syntax._
import akka.util.ByteString
import io.iohk.ethereum.domain.Address
import io.iohk.ethereum.jsonrpc.client.RpcClient
import io.iohk.ethereum.jsonrpc.client.RpcClient.RpcError
import io.iohk.ethereum.security.SSLError
import io.iohk.ethereum.utils.Logger
import javax.net.ssl.SSLContext
import monix.eval.Task

import scala.concurrent.ExecutionContext

class WalletRpcClient(node: Uri, getSSLContext: () => Either[SSLError, SSLContext])(implicit
system: ActorSystem,
ec: ExecutionContext
) extends RpcClient(node, getSSLContext)
with Logger {
import io.iohk.ethereum.jsonrpc.client.CommonJsonCodecs._

def getNonce(address: Address): Task[Either[RpcError, BigInt]] =
doRequest[BigInt]("eth_getTransactionCount", List(address.asJson, "latest".asJson))

def sendTransaction(rawTx: ByteString): Task[Either[RpcError, ByteString]] =
doRequest[ByteString]("eth_sendRawTransaction", List(rawTx.asJson))
}
32 changes: 15 additions & 17 deletions src/main/scala/io/iohk/ethereum/faucet/jsonrpc/WalletService.scala
Original file line number Diff line number Diff line change
@@ -1,33 +1,31 @@
package io.iohk.ethereum.faucet.jsonrpc

import akka.util.ByteString
import cats.data.EitherT
import io.iohk.ethereum.domain.{Address, Transaction}
import io.iohk.ethereum.faucet.FaucetConfig
import io.iohk.ethereum.jsonrpc.client.RpcClient.RpcError
import io.iohk.ethereum.keystore.KeyStore.KeyStoreError
import io.iohk.ethereum.keystore.{KeyStore, Wallet}
import io.iohk.ethereum.mallet.common.Err
import io.iohk.ethereum.mallet.service.RpcClient
import io.iohk.ethereum.network.p2p.messages.CommonMessages.SignedTransactions.SignedTransactionEnc
import io.iohk.ethereum.rlp
import io.iohk.ethereum.utils.{ByteStringUtils, Logger}
import monix.eval.Task

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

def sendFunds(wallet: Wallet, addressTo: Address): Task[Either[Err, ByteString]] = {
Task {
(for {
nonce <- rpcClient.getNonce(wallet.address)
txId <- rpcClient.sendTransaction(prepareTx(wallet, addressTo, nonce))
} yield txId) match {
case Right(txId) =>
val txIdHex = s"0x${ByteStringUtils.hash2string(txId)}"
log.info(s"Sending ${config.txValue} ETH to $addressTo in tx: $txIdHex.")
Right(txId)
case Left(error) =>
log.error(s"An error occurred while using faucet", error)
Left(error)
}
def sendFunds(wallet: Wallet, addressTo: Address): Task[Either[RpcError, ByteString]] = {
(for {
nonce <- EitherT(walletRpcClient.getNonce(wallet.address))
txId <- EitherT(walletRpcClient.sendTransaction(prepareTx(wallet, addressTo, nonce)))
} yield txId).value map {
case Right(txId) =>
val txIdHex = s"0x${ByteStringUtils.hash2string(txId)}"
log.info(s"Sending ${config.txValue} ETC to $addressTo in tx: $txIdHex.")
Right(txId)
case Left(error) =>
log.error(s"An error occurred while using faucet", error)
Left(error)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.iohk.ethereum.mallet.service
package io.iohk.ethereum.jsonrpc.client

import akka.util.ByteString
import io.circe._
Expand Down
Loading