Monthly Archives: May 2021

Actor-based Blockchain In Akka Typed

As blockchain computing continues to steadily gain momentum across various industries, relevant platforms such as Ethereum, Hyperledger, etc, have emerged and prospered. Even though the term blockchain has evolved beyond a mere keyword for cryptocurrency, its core underlying operational structure still adheres to how a cryptocurrency fundamentally maintains a decentralized ledger — that is, as distributed copies of a growing blockchain kept by individual nodes on the system to agree on an eventual version of the blockchain via a consensual algorithm.

Blockchain application using Akka classic actors

In 2020, I developed an Actor-based blockchain application (source code at GitHub) in Scala using Akka Actor’s classic API. While it’s primarily for proof of concept, the application does utilize relevant cryptographic functions (e.g. public key cryptography standards), hash data structure (e.g. Merkle trees), along with a simplified proof-of-work consensus algorithm to simulate mining of a decentralized cryptocurrency on a scalable cluster.

Since the blockchain application consists of just a handful of Actors mostly handling common use cases, migrating it to using the typed Actor API serves a great trial exercise for something new. While it was never expected to be a trivial find-and-replace task, it was also not a particularly difficult one. A recent mini-blog series highlights the migration how-to’s of some key actor/cluster features used by the blockchain application.

The Akka Typed blockchain application

For the impatient, source code for the Akka Typed blockchain application is at this GitHub link.

Written in Scala, the build tool for the application is the good old sbt, with the library dependencies specified in “{project-root}/built.sbt”. Besides akka-actor-typed and akka-cluster-typed for the typed actor/cluster features, Bouncy Castle and Apache Commons Codec are included for processing public key files in PKCS#8 PEM format.

For proof of concept purpose, the blockchain application can be run on a single computer on multiple Shell command terminals, using the default configurations specified in “{project-root}/src/main/resources/application.conf”. The configuration file consists of information related to Akka cluster/remoting transport protocol, seed nodes, etc. The cluster setup can be reconfigured to run on an actual network of computer nodes in the cloud. Also included in the configuration file are a number of configurative parameters for mining of the blockchain, such as mining reward, time limit, proof-of-work difficulty level, etc.

The underlying data structures of the blockchain

Since all the changes to the blockchain application are only for migrating actors to Akka Typed, the underlying data structures for the blockchain, its inner structural dependencies as well as associated cryptographic functions remain unchanged.

As an attempt to make this blog post an independent one, some content of the application overview is going to overlap the previous overview for the Akka classic application. Nonetheless, I’m including some extra diagrams for a little more clarity.

Below is a diagram showing the blockchain’s underlying data structures:

  • Account, TransactionItem, Transactions
  • MerkleTree
  • Block, RootBlock, LinkedBlock
  • ProofOfWork

The centerpiece of the blockchain data structures is the abstract class Block which is extended by RootBlock (i.e. the “genesis block”) and LinkedBlock. Each of the individual blocks is identified by the hash field and backward-linked to its predecessor via hashPrev.

Field difficulty carries the difficulty level pre-set in the application configuration. The nonce field is initialized also from configuration and will be updated with the proof value returned from the proof-of-work consensual algorithm (which is difficulty-dependent).

Class Transactions represents a sequence of transaction items, along with the sender/receiver (of type Account) and timestamp. Both the transaction sequence and its hashed value merkleRoot are kept in a Block object. The Account class is identified by field key which is the Base64 public key of the PKCS keypair possessed by the account owner. As for object ProofOfWork, it’s a “static” class for keeping the consensual algorithmic methods for proof-of-work.

For a deeper dive of the various objects’ inner workings, please read the following blogs:

  1. Transaction Hash Tree in a Blockchain
  2. Blockchain Mining and Proof-of-Work

Source code for the data structures can be found under akkablockchain/model in the GitHub repo.

The typed actors that “run” the blockchain

As for the actors, aside from being revised from loosely to strictly typed actors, their respective functionalities within a given cluster node as well as among the peer actors on other cluster nodes remain unchanged.

The following diagram highlights the hierarchical flow logic of the various actors on a given cluster node:

Starter – On each cluster node, the main program of blockchain application is initialized with starting up the top-level Starter actor (a.k.a. the user guardian), which in turn spawns two actors: Blockchainer and Simulator.

Blockchainer – The Blockchainer actor on any given cluster node maintains a copy of the blockchain and transaction queue for a miner identified by their cryptographic public key. It collects submitted transactions in the queue and updates the blockchain according to the proof-of-work consensual rules by means of the cluster-wide distributed pub/sub. The actor delegates mining work to its child actor Miner and validation of mined blocks to child actor BlockInspector.

Miner – The mining tasks of carrying out computationally demanding proof-of-work is handled by the Miner actor. Using an asynchronous routine bound by a configurable time-out, actor Miner returns the proofs back to the parent actor via the Akka request-response ask queries.

BlockInspector – This other child actor of Blockchainer is responsible for validating content of a newly mined block before it can be appended to the existing blockchain. The validation verifies that generated proof within the block as well as the intertwined hash values up the chain of the historical blocks. The result is then returned to the parent actor via Akka ask.

Simulator – Actor Simulator simulates mining requests and transaction submissions sent to the Blockchainer actor on the same node. It generates periodic mining requests by successively calling Akka scheduler function scheduleOnce with random variations of configurable time intervals. Transaction submissions are delegated to actor TransactionFeeder.

TransactionFeeder – This child actor of Simulator periodically submits transactions to actor Blockchainer via an Akka scheduler. Transactions are created with random user accounts and transaction amounts. Accounts are represented by their cryptographic public keys. For demonstration purpose, a number of PKCS#8 PEM keypair files were created and kept under “{project-root}/src/main/resources/keys/” to save initial setup time.

Since the overall functional flow of this application remains the same as the old one, this previously published diagram is also worth noting:

Akka Blockchain - functional flow

Source code for the actors can be found under akkablockchain/actor in the GitHub repo.

Areas that could be feature-enhanced

This blockchain application is primarily for proof of concept, thus the underlying data structure and security features have been vastly simplified. For it to get a little closer to a real-world cryptocurrency, addition/enhancement of features in a few areas should be considered. The following bullet items are a re-cap of the “Feature enhancement” section from the old blog:

Data encryption: The transactions stored in the blockchain unencrypted. Individual transaction items could be encrypted, each of which to be stored with the associated cryptographic signature, requiring miners to verify the signature while allowing only those who have the corresponding private key for the transaction items to see the content.

Self regulation: A self-regulatory mechanism that adjusts the difficulty level of the Proof-of-Work in accordance with network load would help stabilize the digital currency. For example, in the event of a significant plunge, Bitcoin would impose self-regulatory reduction in the PoW difficulty requirement to temporarily make mining more rewarding that helped dampen the fall.

Currency supply: In a cryptocurrency like Bitcoin, issuance of the mining reward by the network is essentially how the digital coins are “minted”. To keep inflation rate under control as the currency supply increases, the rate of coin minting must be proportionately regulated over time. Bitcoin has a periodic “halfing” mechanism that reduces the mining reward by half for every 210,000 blocks added to the blockchain and will cease producing new coins once the total supply reaches 21 million coins.

Blockchain versioning: Versioning of the blockchain would make it possible for future feature enhancement, algorithmic changes or security fix by means of a fork, akin to Bitcoin’s soft/hard forks, without having to discard the old system.

User Interface: The existing application focuses mainly on how to operate a blockchain network, thus supplementing it with, say, a Web-based user interface (e.g. using Akka HTTP/Play framework) for miners to participate mining would certainly make it a more user-friendly system.

Sample console log: running on 3 cluster nodes

Running the application and examining its output from the console log would reveal how multiple miners, each on a separate cluster node, “collaboratively” compete for growing a blockchain with a consensual algorithm. In particular, pay attention to:

  • attempts by individual miners to succeed or fail in building new blocks due to timeout by the Proof-of-Work routine
  • miners’ requests for adding new blocks to their blockchain rejected by their occupied mining actor
  • how the individual copies of the blockchain possessed by the miners “evolve” by accepting the first successfully expanded blockchain done by themselves or their peers
  • the number of trials in Proof-of-Work for a given block (i.e. displayed as BLK()) as its rightmost argument (e.g. BLK(p98k, T(fab1, 3099/2), 2021-05-11 18:05:13, 3, 2771128); for more details about how the mining of blockchain works, please see this blog post)
  • how Akka cluster nodes switch their “leader” upon detecting failure (due to termination by users, network crashes, etc)

Below is sample output of running the blockchain application with default configuration on 3 cluster nodes.