Skip to content
New issue

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

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

Already on GitHub? # to your account

[ETCM-252] faucet - handle account #783

Merged
merged 17 commits into from
Nov 18, 2020
Merged

Conversation

biandratti
Copy link
Contributor

@biandratti biandratti commented Nov 6, 2020

Description

The Faucet assumes that the ops user should configure an account on the faucet.keystore-dir before start. When starting the “faucet”, it automatically loads the account, and prepares the context to be used.

Proposed Solution

When starting the Faucet, it initializes an actor that handles the account. I added a supervisor that monitors the actor that handles the account.

We have 3 cases:

  1. If it can't load the wallet, the app shutdown automatically.
  2. While trying to load the wallet with the configuration. At this moment the wallet is disable. So the status of the "faucet" is “FaucetUnavailable”.
  3. When the wallet is loaded, the status changes, now it is WalletAvailable and we can use the "faucet" with normality.

Important Changes Introduced

When the wallet is loaded successfully, the status of "faucet" changed, now it is WalletAvailable.

@biandratti biandratti changed the title WIP [ETCM-252] faucet - handle account [ETCM-252] faucet - handle account Nov 12, 2020
var wallet: Wallet = _

override def preStart(): Unit = {
log info "||=== Faucet Handler Hook ===||"
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we log that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

log removed, it is not necessary.

walletService.getWallet.runSyncUnsafe() match {
case Left(error) =>
log.error(s"Couldn't initialize wallet - error: $error")
//context become unavailable
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should kill the faucet or try to recover

Copy link
Contributor

Choose a reason for hiding this comment

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

This one is still not addressed. We could end up with the "running" but nonworking faucet. The better idea is to kill the whole app. It will be easier to notice (ex. in metrics)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like this idea. So, I separated 3 cases.
1_ if it can't load the wallet, the app shutdown.
2_ While trying to load the wallet with the configuration. At this moment the wallet is disable “FaucetUnavailable”.
3_ When the wallet is loaded, the status is WalletAvailable and we can use the faucet with normality.

If this approach results ok, I will add this description in the documentation.
What do you think?

I have pending another task of improvements, unit test in the supervisor actor.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it is a good direction

@biandratti biandratti requested a review from ntallar November 16, 2020 16:25
Copy link
Contributor

@mmrozek mmrozek left a comment

Choose a reason for hiding this comment

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

LGTM

import FaucetHandler.FaucetHandlerMsg._
import FaucetHandler.FaucetHandlerResponse._

var wallet: Wallet = _
Copy link
Contributor

@mirkoAlic mirkoAlic Nov 18, 2020

Choose a reason for hiding this comment

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

why not context become available(wallet)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

implicit lazy val actorTimeout: Timeout = Timeout(config.responseTimeout)
implicit val ec: ExecutionContext = system.dispatcher

private def faucetHandler(): Task[ActorRef] =
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe this could be something we receive from params of via implementing a trait. I think it will help when we added tests for the Service.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

case Left(err) =>
throw new RuntimeException(s"Cannot unlock wallet for use in faucet (${config.walletAddress}), because of $err")
}
class FaucetRpcService(config: FaucetConfig)(implicit system: ActorSystem) extends RetrySupport with Logger {
Copy link
Contributor

Choose a reason for hiding this comment

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

// TODO: Add unit tests for this class. (it could be done in a different task)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Add task: ETCM-395

ByteString(rlp.encode(stx.tx.toRLPEncodable))
def status(statusRequest: StatusRequest): ServiceResponse[StatusResponse] =
faucetHandler()
.flatMap(handler => handler.askFor[Any](FaucetHandlerMsg.Status))
Copy link
Contributor

Choose a reason for hiding this comment

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

what happens if we can't reach the actor? i mean in terms of response...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Pending task: ETCM-396

_ ->
log.info("actor system finished")
)(system.dispatcher),
1.seconds
Copy link
Contributor

Choose a reason for hiding this comment

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

why not use the pre-existing mantis config value?

# timeout for shutting down the ActorSystem
  shutdown-timeout = "15.seconds"

Copy link
Contributor

Choose a reason for hiding this comment

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

i mean as in src/main/scala/io/iohk/ethereum/nodebuilder/NodeBuilder.scala:

trait ShutdownHookBuilder {
  self: Logger =>
  ...
  lazy val shutdownTimeoutDuration: Duration = Config.shutdownTimeout
...
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


val mockRpcClient = mock[RpcClient]
val mockKeyStore = mock[KeyStore]
val config: FaucetConfig =
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
val config: FaucetConfig =
val config: FaucetConfig =
FaucetConfig(
walletAddress = wallet.address,
walletPassword = "",
txGasPrice = 10,
txGasLimit = 20,
txValue = 1,
rpcAddress = "",
keyStoreDir = "",
minRequestInterval = 10.seconds,
handlerTimeout = 10.seconds,
responseTimeout = 10.seconds,
supervisor = mock[SupervisorConfig]
)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

val mockRpcClient = mock[RpcClient]
val mockKeyStore = mock[KeyStore]
val config: FaucetConfig =
FaucetConfig(
Copy link
Contributor

Choose a reason for hiding this comment

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

^ far more readable if someone is using just github to check the code, or is in a server environment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

with NormalPatience {

"Faucet Handler" - {
"without wallet unlocked" - {
Copy link
Contributor

Choose a reason for hiding this comment

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

what about the scenario about Faucet initialization succeeded?

"Faucet Handler" - {
"without wallet unlocked" - {

"should try to unlock the Wallet if it is not initialized but decryption failed" in new TestSetup {
Copy link
Contributor

Choose a reason for hiding this comment

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

For me this test should like:
"Faucet Handler" + "without wallet unlocked" + "should not respond in case wallet unlock fails"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

"Faucet Handler" - {
"without wallet unlocked" - {

"should try to unlock the Wallet if it is not initialized but decryption failed" in new TestSetup {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"should try to unlock the Wallet if it is not initialized but decryption failed" in new TestSetup {
"should not respond in case wallet unlock fails" in new TestSetup {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@biandratti biandratti added the BREAKS API Affects services that interacts with APIs label Nov 18, 2020
@biandratti biandratti merged commit aaf3c85 into develop Nov 18, 2020
@mirkoAlic mirkoAlic deleted the ETCM-252-faucet-handle-account branch November 19, 2020 11:25
val name = "FaucetSupervisor"
}

class FaucetSupervisor(walletRpcClient: WalletService, config: FaucetConfig, shutdown: () => Unit)(implicit
Copy link
Contributor

@mirkoAlic mirkoAlic Nov 19, 2020

Choose a reason for hiding this comment

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

I think is better to directly pass the Handler, i mean, why the supervisor needs to know about the WalletService?


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

def faucetHandler()(implicit system: ActorSystem): Task[ActorRef] = {
Copy link
Contributor

@mirkoAlic mirkoAlic Nov 19, 2020

Choose a reason for hiding this comment

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

(minor) selectFaucetHandler IMO in that way is more obvious that is a function, and what is doing

import io.iohk.ethereum.faucet.{FaucetConfigBuilder, FaucetHandler, FaucetSupervisor}
import monix.eval.Task

trait FaucetHandlerBuilder {
Copy link
Contributor

@mirkoAlic mirkoAlic Nov 19, 2020

Choose a reason for hiding this comment

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

FaucetHandlerSelector ?

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
BREAKS API Affects services that interacts with APIs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants