Author Archives: Leo Cheung

Akka Typed: Scheduler, PubSub

This is the final post of the 3-part blog series that centers around migrating an actor-based blockchain application from Akka classic to Akka Typed. In the previous post, we looked at the difference between the two APIs in starting actors and in tell-ing and ask-ing. This time, we’re going to cover the following topics:

  • Scheduler
  • Distributed PubSub

Scheduler

Akka classic’s Scheduler feature provides task-scheduling methods for recurring schedule (e.g. scheduleAtFixedRate(), which replaces the deprecated schedule()) or one-time schedule (scheduleOnce()), each with a few signature variants. For example, scheduleOnce() has more than a couple of different method signatures for different use cases:

// Schedule once for a `f: => Unit` by-name parameter
final def scheduleOnce(delay: FiniteDuration)(f: => Unit)(implicit executor: ExecutionContext): Cancellable

// Schedule once for a `Runnable`
def scheduleOnce(delay: Duration, runnable: Runnable)(implicit executor: ExecutionContext): Cancellable

// Schedule once for sending a message to another actor
final def scheduleOnce(delay: FiniteDuration, receiver: ActorRef, message: Any)(implicit executor: ExecutionContext, sender: ActorRef = Actor.noSender): Cancellable

The blockchain application uses schedulers in a number of places. For instance, to ensure that the potentially time-consuming proof generating process won’t take more than a set duration, the generatePoW() method within actor Miner uses scheduleOnce() to try complete a Promise with a timeout exception at a scheduled time:

private def generatePoW(block: Block)(implicit ec: ExecutionContext, timeout: FiniteDuration): Future[Long] = {
  val promise = Promise[Long]()
  context.system.scheduler.scheduleOnce(timeout){ promise tryFailure new TimeoutException(s"$block: $timeout") }
  Future{
    Try{
      val incrementedNonce =
        ProofOfWork.generateProof(bytesToBase64(block.hash), defaultDifficulty, defaultNonce)
      promise success incrementedNonce
    }.
    recover{
      case e: Exception => promise failure e
    }
  }
  promise.future
}

Similar to its counterpart in the classic API, Akka Typed Scheduler also provides task-scheduling methods for recurring and one-time schedules. Here’s the method signature of the typed version of scheduleOnce():

// Schedule once for a `Runnable`
abstract def scheduleOnce(delay: FiniteDuration, runnable: Runnable)(
    implicit executor: ExecutionContext): Cancellable

The variant that takes a f: => Unit by-name parameter is no longer available in Akka Typed. However, since Runnable only has a single abstract method (SAM), run(), the following expressions are essentially the same:

// Invoke scheduleOnce() with a Runnable object
context.system.scheduler.scheduleOnce(timeout, new Runnable { override def run() = ??? })

// Invoke scheduleOnce() with a `() => Unit` by-name parameter
context.system.scheduler.scheduleOnce(timeout, () => ???)

For example, the typed version of the proof-of-work generating method generatePoW() could be coded as follows:

private def generatePoW(block: Block)(implicit ec: ExecutionContext, timeout: FiniteDuration): Future[Long] = {
  val promise = Promise[Long]()
  context.system.scheduler.scheduleOnce(
    timeout, () => promise tryFailure new TimeoutException(s"$block: $timeout")
  )
  Future{
    Try{
      val incrementedNonce =
        ProofOfWork.generateProof(bytesToBase64(block.hash), defaultDifficulty, defaultNonce)
      promise success incrementedNonce
    }.
    recover{
      case e: Exception => promise failure e
    }
  }
  promise.future
}

Also seemingly missing is the other variant for sending a message to another actor. In fact, it has been moved into ActorContext as a separate scheduleOnce() method which can be invoked from within a typed actor’s context for scheduling a one-time delivery of message to another actor:

// Typed ActorContext's schedule once for sending a message to another actor
abstract def scheduleOnce[U](delay: FiniteDuration, target: ActorRef[U], msg: U): Cancellable

The above schedulers are all thread-safe. There is also this TimerScheduler that is not thread-safe, hence should be used within the actor that owns it. An advantage of using TimerScheduler, typically via Behaviors.withTimers(), is that it’s bound to the lifecycle of the containing actor and will be cancelled automatically when the actor is stopped.

Distributed PubSub

A cryptocurrency typically maintains a decentralized ledger as distributed copies of a growing blockchain kept by individual nodes on the system. The blockchain application being used for this actor migration exercise achieves that by means of running a Distributed Publish Subscribe service on an Akka cluster. Named topics (in this case “new-transactions” and “new-block”) can be created and subscribers to a given topic will be sent objects submitted to the topic by the publishers.

In the Akka classic API, the mediator actor, DistributedPubSubMediator, which is supposed to be started on each of the allocated cluster nodes is responsible for managing a registry of actor references and replicating the entries to peer actors among the nodes.

Below is how the mediator actor started from within the Blockchainer actor of a cluster node registers subscribers (in this case the Blockchainer actor itself) and takes published topical objects to be consumed by peer cluster nodes:

// Akka classic pubsub mediator
val mediator: ActorRef = DistributedPubSub(context.system).mediator
mediator ! Subscribe("new-transactions", self)
mediator ! Subscribe("new-block", self)
// ...
mediator ! Publish("new-transactions", AddTransactions(trans, append))
mediator ! Publish("new-block", UpdateBlockchain(block))

The Akka Typed pubsub abandons the “universal” mediator actor in favor of “topic-specific” actors. The typed version of Distributed PubSub functionality initiated from within the Blockchainer actor of a cluster node is as follows:

// Akka Typed pubsub.Topic
val topicTrans = context.spawn(Topic[Req]("new-transactions"), "pubsubNewTransactions")
val topicBlock = context.spawn(Topic[Req]("new-block"), "pubsubNewBlock")
topicTrans ! Topic.Subscribe(context.self)
topicBlock ! Topic.Subscribe(context.self)
//...
topicTrans ! Topic.Publish(AddTransactions(trans, append))
topicBlock ! Topic.Publish(UpdateBlockchain(block))

Final thoughts

This concludes the mini blog series that covers the basics and selected features of Akka Typed actors. The resulting blockchain application in Akka Typed will be published on GitHub along with an overview in a separate blog.

The term “behavior” is commonly used in the standard Actor Model when describing how actors operate and mutate their internal states in accordance with the business logic. As can be seen across sample snippets in this blog series, it’s ubiquitous in the Akka Typed API, with all the Behavior-typed methods from the Behaviors factory object spanning the entire lifecycle of actors. In comparison with putting all actor business logic inside the receive partial function “blackbox” in Akka classic, it does make the using of the various methods more intuitive, especially for new comers.

With some self-discipline in sticking to programming best-practices, the loosely-typed Akka classic API has an advantage of embracing complex non-blocking message processing functionality with minimal boilerplate code. The message “loop” within an actor in the form of a partial function along with the hotswapping feature via context.become provides a simple yet robust construct for processing messages. I’m certainly going to miss its simplicity and elegance. That being said, moving towards the typed Akka API is inevitable if one plans to use Akka actors not just for a one-time project. It’s the right direction for sticking to idiomatic functional programming.

Akka Typed: Spawn, Tell, Ask

This is the 2nd post of the 3-part blog series about migrating an Akka classic actor-based application to one with Akka typed actors. In the 1st post, we looked at how to convert a classic actor into a typed actor.

Obviously, the goal of this mini blog series isn’t to cover all of the classic-to-typed-actors migration how-to’s, given the vast feature set Akka provides. The application being migrated is a blockchain application that mimics mining of a decentralized cryptocurrency. We’re going to cover just the key actor features used by the application, namely:

  • Starting an actor
  • Tell
  • Ask
  • Scheduler
  • Distributed PubSub

In this blog post, we’ll go over the first three bullet items.

Starting an Akka actor

In the Akka classic API, context method actorOf() starts an actor with its “properties” provided by the configuration factory Props. For example, actor Miner is started from within its parent actor as follows:

// Starting classic actor `Miner`
val miner: ActorRef = context.actorOf(Miner.props(accountKey, timeoutPoW), "miner")

It’s common to provide the actor class properties for Props by means of an actor’s companion object method especially when the actor class takes parameters.

// Classic actor `Miner` class
object Miner {
  def props(accountKey: String, timeoutPoW: Long): Props = Props(classOf[Miner], accountKey, timeoutPoW)
  // ...
}

class Miner(accountKey: String, timeoutPoW: Long) extends Actor with ActorLogging {
  // ...
}

Starting an actor in Akka Typed is performed using actor context method spawn(). The typed version of actor Miner would be started from within its parent actor like below:

// Starting typed actor `Miner`
val miner: ActorRef[Miner.Mining] = context.spawn(Miner(accountKey, timeoutPoW), "miner")

An actor’s underlying class properties can now be defined using Behavior methods, hence the Props configuration factory is no longer needed. Below is how the typed Miner can be defined using method Behaviors.setup() from within the actor’s companion object:

// Typed actor `Miner` class
object Miner {
  // ...
  def apply(accountKey: String, timeoutPoW: Long): Behavior[Mining] =
    Behaviors.setup(context =>
      new Miner(context, accountKey, timeoutPoW).messageLoop()
    )
}

class Miner private(context: ActorContext[Miner.Mining], accountKey: String, timeoutPoW: Long) {
  // ...
}

Starting top-level actors

What about in the main program before any actors have been created? In Akka classic API, one can simply invoke actorOf() from the ActorSystem to start main actors. Below is how the main program of a blockchain mining application spawn the top-level actors for mining simulations.

object Main {
  def main(args: Array[String]): Unit = {
    // Parse `args` and load configurations for the cluster and main actors
    // ...

    implicit val system = ActorSystem("blockchain", conf)
    val blockchainer = system.actorOf(
        Blockchainer.props(minerAccountKey, timeoutMining, timeoutValidation), "blockchainer"
      )
    val simulator = system.actorOf(
        Simulator.props(blockchainer, transFeedInterval, miningAvgInterval), "simulator"
      )

    // ...
  }
}

Perhaps for consistency reasons, Akka Typed makes actors always started from within an actor context using method spawn(), thus an explicit ActorContext is needed for top-level main actors. The following snippet shows how the typed version of the blockchain application delegates to the Starter actor for starting the main actors after the main program has loaded actor properties from program arguments and configuration file. The top-level user-defined actor Starter is regarded as the “user guardian”.

object Main {
  object Starter {
    def apply(accountKey: String, timeoutMining: Long, timeoutValidation: Long,
              transFeedInterval: Long, miningAvgInterval: Long, test: Boolean): Behavior[NotUsed] =
      Behaviors.setup { context =>
        Cluster(context.system)

        val blockchainer = context.spawn(Blockchainer(accountKey, timeoutMining, timeoutValidation), "blockchainer")
        val simulator = context.spawn(Simulator(blockchainer, transFeedInterval, miningAvgInterval), "simulator")

        if (test)
          simulator ! Simulator.QuickTest
        else
          simulator ! Simulator.MiningLoop

        Behaviors.receiveSignal { case (_, Terminated(_)) => Behaviors.stopped }
      }
  }

  def main(args: Array[String]): Unit = {
    // Parse `args` and load configurations for the cluster and main actors
    // ...

    implicit val system = ActorSystem(
      Starter(minerAccountKey, timeoutMining, timeoutValidation, transFeedInterval, miningAvgInterval, test),
      "blockchain",
      conf
    )
  }
}

The fire-and-forget “tell”

The most common communication means among actors is via method tell in a fire-and-forget fashion, which in essence implies that messages are sent with an at-most-once guarantee.

Akka classic tell and the symbolic ! variant have the following method signatures:

// Akka classic methods `tell` and `!`
final def tell(msg: Any, sender: ActorRef): Unit
abstract def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit

In Akka Typed, methods tell and ! have signatures as follows:

// Akka Typed methods `tell` and `!`
abstract def tell(msg: T): Unit
def !(msg: T): Unit

Though messages being sent are now strictly typed in the new API, expressions consisting of tell in Akka classic and Akka Typed, both returning a Unit, essentially look and feel the same.

The request-response query “ask”

The other commonly used communication means among actors is the request-response query via method ask.

Below are method signatures of Akka classic ask and ?:

// Akka classic methods `ask` and `?`
def ask(actorSelection: ActorSelection, message: Any)(implicit timeout: Timeout): Future[Any]
def ?(message: Any)(implicit timeout: Timeout, sender: ActorRef = Actor.noSender): Future[Any]

Here’s how the Blockchainer actor uses the classic ask to request for a new mined block from actor Miner and handle the Future query result:

(miner ? Miner.Mine(blockPrev, trans))(tmoMining).mapTo[Block] onComplete{
  case Success(block) =>
    mediator ! Publish("new-block", UpdateBlockchain(block))
    miner ! Miner.DoneMining
  case Failure(e) =>
    log.error(s"[Req.Mining] ${this}: ERROR: $e")
    e match {
      case _: BusyException => self ! AddTransactions(trans, append = false)
      case _ => miner ! Miner.DoneMining
    }
}

Note that the tmoMining timeout value is being explicit passed in as a method argument and mapTo[Block] is necessary for mapping the returned Future[Any] to the proper type.

In addition, there are also a few general-purpose AskSupport methods allowing a query between two actors both specified as parameters. Here’s the method signature of one of the ask variants:

// Akka classic sender-explicit `ask`
def ask(actorRef: ActorRef, message: Any, sender: ActorRef)(implicit timeout: Timeout): Future[Any]

As for Akka Typed, a context method ask is provided with the following signature:

// Akka Typed context method `ask`
abstract def ask[Req, Res](target: RecipientRef[Req], createRequest: (ActorRef[Res]) => Req)(
    mapResponse: (Try[Res]) => T)(implicit responseTimeout: Timeout, classTag: ClassTag[Res]): Unit

Below is how the typed version of actor Blockchainer would use context ask to query typed actor Miner:

implicit val tmoMining: Timeout = Timeout(timeoutMining.millis)
context.ask(miner, ref => Miner.Mine(blockPrev, trans, ref)) {
  case Success(r) =>
    r match {
      case MiningResult(block) =>
        topicBlock ! Topic.Publish(UpdateBlockchain(block))
        miner ! Miner.DoneMining
        MiningResult(block)
      case _ =>
        OtherException(s"Unknown mining result $r")
    }
  case Failure(e) =>
    context.log.error(s"[Req.Mining] ${this}: ERROR: $e")
    e match {
      case _: BusyException =>
        context.self ! AddTransactions(trans, append = false)
        BusyException(e.getMessage)
      case _ =>
        miner ! Miner.DoneMining
        OtherException(e.getMessage)
    }
}

Note that context ask doesn’t return the query result as a Future to be handled by a callback such as onComplete. Rather, it expects one to handle the query response by providing a Try[Res] => T function.

Akka Typed also provides AskPattern methods that return Futures with below method signatures:

// Akka Typed Future-returning methods `ask` and `?`
def ask[Res](replyTo: (ActorRef[Res]) => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res]
def ?[Res](replyTo: (ActorRef[Res]) => Req)(implicit timeout: Timeout, scheduler: Scheduler): Future[Res]

That’s all for this post. In the next blog, we’ll wrap up this mini blog series with the remaining topics (i.e. scheduler and distributed pubsub).

From Akka Untyped To Typed Actors

This is part 1 of a 3-part blog series about how to migrate an Akka classic actor-based application to one with Akka typed actors. In this post, we’ll focus on how a typical classic actor can be replaced with a typed actor.

Akka’s move from its loosely-typed classic actor API to Akka Typed has been met with mixed feelings in the Akka community. On one hand, people are happy with the Actor toolkit being “morphed” into a suitably typed API. On the other hand, the general expectation is that it isn’t going to be a straight forward find-and-replace change.

Actors interact by means of non-blocking message passing. Each actor maintains a “mailbox” and messages addressed to it get processed in the order of receipt. Akka classic actors are loosely typed mainly because their message processing logic relies on the implementation of the abstract method receive, which has the following signature:

// Akka Actor.Receive
type Receive = PartialFunction[Any, Unit]
abstract def receive: Receive

It takes an input of type Any (i.e. allowing messages of any type) and isn’t obligated to return anything. In addition, as a partial function it allows by-design non-exhaustive matching against message types.

Actor “behaviors”

In Akka Typed, processing logic of the messages received by an actor is defined in methods that return Behavior[T] of a given message type T. A factory object Behaviors provides a number of predefined Behavior[T]s (e.g. Behaviors.same, Behaviors.stopped) and general methods (e.g. Behaviors.receiveMessage()) for user-defined message processing logic.

Akka’s official style guide proposes two different flavors of the typed API: functional vs object-oriented. I would highly recommend reading through examples and the pros and cons of the two styles presented there. In brief, the functional approach mutates an actor’s state by successively passing in the state as a parameter to the processing method, whereas the alternative embraces the object-oriented principles and relaxes the use of mutable class variables for state maintenance.

An Akka classic actor for blockchain mining

Let’s look at a blockchain mining Scala snippet used in an Actor-based cryptocurrency system as an example. The original code written in Akka classic actors is like this:

// Akka classic actor `Miner`
object Miner {
  def props(accountKey: String, timeoutPoW: Long): Props = Props(classOf[Miner], accountKey, timeoutPoW)

  sealed trait Mining
  case class Mine(blockPrev: Block, trans: Transactions) extends Mining
  case object DoneMining extends Mining
}

class Miner(accountKey: String, timeoutPoW: Long) extends Actor with ActorLogging {
  import Miner._

  implicit val ec: ExecutionContext = context.dispatcher
  implicit val timeout = timeoutPoW.millis

  override def receive: Receive = idle

  def idle: Receive = {
    case Mine(blockPrev, trans) =>
      context.become(busy)

      val recipient = sender()
      val newBlock = generateNewBlock(blockPrev, trans)

      generatePoW(newBlock).map{ newNonce =>
          recipient ! newBlock.copy(nonce = newNonce)
        }.
        recover{ case e: Exception =>
          recipient ! Status.Failure(e)
        }

    case _ =>
      // Do nothing
  }

  def busy: Receive = {
    case Mine(b, t) =>
      log.error(s"[Mining] Miner.Mine($b, $t) received but $this is busy!")
      sender() ! Status.Failure(new Blockchainer.BusyException(s"$this is busy!"))

    case DoneMining =>
      context.become(idle)
      log.info(s"[Mining] Miner.DoneMining received.")
  }

  private def generateNewBlock(blockPrev: Block, trans: Transactions): LinkedBlock = ???

  private def generatePoW(block: Block)(implicit ec: ExecutionContext, timeout: FiniteDuration): Future[Long] = ???
}

It should be noted that Miner is an actor that serves to return a mined block (of type Blockchainer.Req) upon receiving an ask query by another actor (Blockchainer). Within actor Miner, method generatePoW() produces asynchronously a value of Future[Long] which then gets embedded in a block to be sent back to the querying actor.

ADT for actor message type

Before composing the typed actor version of Miner, let’s first look at what message type we would allow it to receive, since the actor reference of a typed actor is strictly typed.

// Akka typed actor `Miner`
object Miner {
  sealed trait Mining
  case class Mine(blockPrev: Block, trans: Transactions, replyTo: ActorRef[Blockchainer.Req]) extends Mining
  case object DoneMining extends Mining
  // ...
}

Similar to how message types are commonly set up in an Akka classic actor, they’re also generally defined as an ADT (algebraic data type) in Akka Typed. Since the actor reference is now typed, the ADT plays an additional role – its base trait becomes the type parameter of the actor. By defining the actor reference of Miner as ActorRef[Miner.Mining], the actor will only be able to take messages of type Miner.Mining or its subtypes.

Because of the type constraint for a given Akka typed actor, the good old non-type binding sender() is no longer supported. To ensure an actor is able to reply to the sender with proper message type, it’s common to see messages sent across typed actors explicitly carrying the sender’s reference. Case class Mine in the Akka typed actor has a replyTo of type ActorRef[Blockchainer.Req] because message Mine is sent via an ask query by actor Blockchainer which expects suitable message type (in this case, Blockchainer.Req) to be returned by actor Miner.

Functional or object-oriented style?

For our Miner actor, the functional approach would be to define all behaviors as methods (e.g. Behaviors.sendMessage) within the object Miner alone. The standard object-oriented alternative would be to add the companion Miner class that extends AbstractBehavior. For this blockchain mining application, I’m going to pick the object-oriented approach, partly for the personal preference of the more structural companion class-object model.

Extending AbstractBehavior would require implementation of abstract method onMessage() which has the following signature:

// AbstractBehavior.onMessage()
abstract def onMessage(msg: T): Behavior[T]

The typed Miner actor would look like this:

// Akka typed actor `Miner`
object Miner {
  // ...
  def apply(accountKey: String, timeoutPoW: Long): Behavior[Mining] =
    Behaviors.setup(context =>
      new Miner(context, accountKey, timeoutPoW)
    )
}

class Miner private(context: ActorContext[Miner.Mining], accountKey: String, timeoutPoW: Long)
    extends AbstractBehavior[Miner.Mining](context) {
  import Miner._
  override onMessage(msg: Mining): Behavior[Mining] = msg match {
    case Mine(...) => ???
    case DoneMining => ???
  }
  // ...
}

Mimicking context.become in Akka Typed

The “hotswapping” feature within the message loop of an Akka classic actor, context.become, is a useful functionality for state switching upon receipt of designated messages. Surprisingly, I haven’t been able to find any concrete examples of how the good old context.become should be done in Akka Typed. Mimicking the feature using the functional approach seems pretty straight forward, but since I’m taking the object-oriented approach it’s not immediately clear to me how onMessage() fits into the “behavioral” switching scheme.

Method onMessage(msg:T) takes a received message and processes it in accordance with user-defined logic. Problem lies in the existence of the msg:T argument in the method. As soon as the actor is up, the initial message(s) received will be passed in, and in the case of a context.become switching to another Behavior method the received message would need to be delegated to the relayed method. However, Behavior methods are designed for taking a user-defined T => Behavior[T] function (or partial function), thus the initial msg:T must be handled from within onMessage(). This results in duplicated message processing logic among the Behavior methods.

Rather than taking the standard object-oriented approach, our Akka Typed Miner actor is defined with a companion class but without extending AbstractBehavior, thus leaving onMessage() out of the picture. A default message handler, messageLoop(), within class Miner is called upon instantiation by the companion object’s apply() to kick off a “behavior switching” loop. Behavior method idle() gets called, executes its business logic before conditionally relaying to another Behavior method busy() which, in turn, does its work and conditionally relays back to idle().

object Miner {
  // ...
  def apply(accountKey: String, timeoutPoW: Long): Behavior[Mining] =
    Behaviors.setup(context =>
      new Miner(context, accountKey, timeoutPoW).messageLoop()
      // ^^^ Instantiate class `Miner` and run `messageLoop()`
    )
}

class Miner private(context: ActorContext[Miner.Mining], accountKey: String, timeoutPoW: Long) {
  import Miner._
  private def messageLoop(): Behavior[Mining] = idle()  // <<< Switch to `idle() behavior`
  private def idle(): Behavior[Mining] = ???  // <<< Conditionally relay to `busy()`
  private def busy(): Behavior[Mining] = ???  // <<< Conditionally relay back to `idle()`
  // ...    
}

The blockchain mining actor in Akka Typed

Putting everything together, here’s what the blockchain mining actor in Akka Typed is like:

// Akka typed actor `Miner`
object Miner {
  sealed trait Mining
  case class Mine(blockPrev: Block, trans: Transactions, replyTo: ActorRef[Blockchainer.Req]) extends Mining
  case object DoneMining extends Mining

  def apply(accountKey: String, timeoutPoW: Long): Behavior[Mining] =
    Behaviors.setup(context =>
      new Miner(context, accountKey, timeoutPoW).messageLoop()
    )
}

class Miner private(context: ActorContext[Miner.Mining], accountKey: String, timeoutPoW: Long) {
  import Miner._

  implicit val ec: ExecutionContext = context.executionContext
  implicit val timeout = timeoutPoW.millis

  private def messageLoop(): Behavior[Mining] = idle()  // <-- Switch to `idle() behavior`

  private def idle(): Behavior[Mining] = Behaviors.receiveMessage{
    case Mine(blockPrev, trans, replyTo) =>
      val newBlock = generateNewBlock(blockPrev, trans)
      generatePoW(newBlock).map(newNonce =>
          replyTo ! Blockchainer.MiningResult(newBlock.copy(nonce = newNonce))
        ).
        recover{ case e: Exception => Blockchainer.OtherException(s"$e") }
      busy()  // <-- Switch to `busy() behavior`

    case DoneMining =>
      Behaviors.same
  }

  private def busy(): Behavior[Mining] = Behaviors.receiveMessage{
    case Mine(blockPrev, trans, replyTo) =>
      context.log.error(s"[Mining] Miner.Mine($blockPrev, $trans) received but $this is busy!")
      replyTo ! Blockchainer.BusyException(s"$this is busy!")
      Behaviors.same

    case DoneMining =>
      context.log.info(s"[Mining] Miner.DoneMining received.")
      idle()  // <-- Switch back to `idle() behavior`
  }

  private def generateNewBlock(blockPrev: Block, trans: Transactions): LinkedBlock = ???

  private def generatePoW(block: Block)(implicit ec: ExecutionContext, timeout: FiniteDuration): Future[Long] = ???
}