Building a Proof of Authority Blockchain in python

Image showing a group of people standing in a circle around a network of floating objects.

Introduction

The concept of blockchain was proposed by Satoshi Nakamoto in 2008 [1] as a solution to the double spending problem in digital currencies. The first implementation of blockchain was Bitcoin [1]. Since then, blockchain has been used in many other applications, such as smart contracts, supply chain management, and healthcare.

Underlying technlology

Since the introduction of Bitcoin, there have been more than 1600 cryptocurrencies in circulation. Amongst others these include Ethereum [2], Ripple [3], Litecoin, IOTA [4], and Cardano. [5, P. 70] The underlying technology of these cryptocurrencies is more or less the same. A basic blockchain composes of the following components:

  • Transactions: Can be any data, such as digital currencies, contracts, or medical records. Contain a sender, a receiver, and a value.
  • Blocks: Contain a list of transactions, a timestamp, and a hash of the previous block.
  • Blockchain: A chain of blocks. Each block contains a hash of the previous block, which links the blocks together.
  • Consensus protocol: A protocol that allows the nodes in the network to agree on the state of the blockchain. The most common consensus protocol is Proof-of-Work (PoW) [1]. In our case study, we implemented the blockchain using Proof-of-Authority (PoA). [5, P. 96], [6]
  • Network: A network of nodes. New transacitons are broadcasted to all nodes in the network. Each node maintains a copy of the blockchain.

Immutability of Blockchain

The immutability characteristic of blockchain technology is one of its core principles, ensuring that once data has been written to the blockchain, it cannot be altered or deleted. This feature is crucial for the integrity and trustworthiness of the data stored within the blockchain.

Illustration of Blockchain Immutability. The figure shows a chain of blocks where each block is linked to its predecessor through a cryptographic hash.

Illustration of Blockchain Immutability. The figure shows a chain of blocks where each block is linked to its predecessor through a cryptographic hash.

The immutability is achieved through the use of cryptographic hashes. Each block contains the hash of its previous block, creating a chain of blocks. This hash is a digital fingerprint of the block’s contents, including its transactions and the hash of the previous block. If an attacker attempts to alter a transaction, the hash of the block would change, and so would the hashes of all subsequent blocks, due to the cryptographic linkage. This would be easily detectable by other nodes in the network, who would reject the tampered version of the blockchain.

Moreover, blockchain networks typically employ consensus protocols, such as PoW or PoA, which require significant compu- tational effort to add a new block. This means that to alter a block, an attacker would need to redo the work for the altered block and all blocks after it, which becomes exponentially difficult as the chain grows, further cementing the blockchain’s immutability. In our case study, we implemented the blockchain using PoA whith a simplyfied approach that does not require significant computational effort to add a new block.

Peer-to-Peer Network Structure

A peer-to-peer (P2P) network is a decentralized communication model in which each party has the same capabilities and either party can initiate a communication session. Unlike traditional client-server network architectures, P2P networks do not rely on a central server to facilitate communication and resource sharing. Instead, each node, commonly referred to as a peer, acts as both a client and a server.

Schematic Representation of a Peer-to-Peer Network. The figure illustrates a network where nodes, represented as circles, are interconnected. Each node is directly connected to all others without a central server, highlighting the distributed nature of P2P networks. This topology enables direct data exchange, resilient communication, and resource sharing among peers.

Schematic Representation of a Peer-to-Peer Network. The figure illustrates a network where nodes, represented as circles, are interconnected. Each node is directly connected to all others without a central server, highlighting the distributed nature of P2P networks. This topology enables direct data exchange, resilient communication, and resource sharing among peers.

In the context of blockchain, a P2P network underpins the distributed ledger technology, allowing for a resilient and scalable method of validating and sharing transactions. Each peer in a blockchain P2P network maintains a copy of the ledger and participates in consensus protocols to validate new blocks. This ensures that no single point of failure exists and that the system remains resistant to censorship and tampering.

The P2P network architecture is inherently scalable, as the addition of new peers increases the total computational power and storage capacity of the network. It is also more robust against attacks or failures, as the distributed nature of the network means that information can still be exchanged even if parts of the network are compromised or go offline.

Objective of the Case Study

The primary objective of this case study is to deepen the understanding and practical application of cryptographic principles within the framework of blockchain technology. To do this, we implemented a simple blockchain using PoA as the consensus protocol. This included the design and construction of the data structures necessary for a blockchain system, such as transactions and blocks, as well as the implementation of cryptographic operations, such as encryption and digital signatures. We also simulated the behavior of a blockchain network through the creation of a ’Node’ class, which represented the network’s participants in a simplified model. Finally, we applied the PoA consensus mechanism to validate and agree upon the state of the blockchain among nodes.

Design and Implementation

Implementing a blockchain and running it in a simulation of multiple nodes is a complex undertaking. To simplify the implementation, we first planned out the software in a UML diagram. A simplyfied version of the UML diagram is shown in figure 11. The full UML diagram can be found in appendix A. The diagram shows the nodes, highlighted in green, as the central part of the software. Nodes can communicate with eachother via TCP sockets. Each node holds a list of contacts containing names and addresses of all other nodes in the network. A Message class is implemented to allow communication using a set list of message ids for different purposes. All communication related classes are highlighted in red. Each node holds a copy of the blockchain which in turn consists of a list of linked blocks. The blocks themselfs hold a list of transactions. The blockchain related classes are highlighted in blue. Only the Authority Node can create blocks. The Simulation class is used to create and manage a chosen number of nodes and authority nodes. The BlockchainApp class can be seens as an interface to the blockchain application. It allows the user to interact with the simulation and choose different nodes to perform actions like creating Transactions.

UML Diagram of the blockchain application. Red indicates classes used to communicate between nodes. Blue indicates classes concerned with the blockchain itself. Green indicates classes representing the nodes in the network. and White indicates the classes used to simulate and interact with the network.

UML Diagram of the blockchain application. Red indicates classes used to communicate between nodes. Blue indicates classes concerned with the blockchain itself. Green indicates classes representing the nodes in the network. and White indicates the classes used to simulate and interact with the network.

The following sections will describe in detail, how the P2P network was implemented, how the blockchain was constructed, and how the user interface was set up.

Node Communication

Joining the network: The Node class is set up in such a way that when an object is created, it automatically starts a TCP server using a Communicator object. A Contact object is then created to represent the node. It includes the nodes name and its address which is obtained from the Communicator object. In addition to that, a Rivest-Shamir-Adleman (RSA) keypair is generated, using the classes created for assignment 2 [7], of which the public key is added to the Contact object.

To join a network, the newly created node needs the address of the ’Genesis Node’ which is the first node in the network. If there is no address present to connect to the genesis node, the node will be the first node in the network and will be assigned the role of the ’Genesis Node’. If there is an address present, the node will send a Message object to the genesis node with the message id JOIN_NETWORK and its own Contact object as the message body. The genesis node will then add the new node to its list of contacts and send a Message object back to the new node with the message id ADD_PEERS and a list of all contacts in the network as the message body. Additionally the genesis node will send a Message object to all other nodes in the network with the message id ADD_PEERS and the new nodes Contact object as the message body. Finally the genesis node will send a Message object to the new node with the message id GENESIS_BLOCK and the genesis nodes blockchain as the message body. The new node will then add all contacts in the message body to its own list of contacts and create a new blockchain with the genesis block as the first block. The new node is now part of the network and can communicate with all other nodes in the network. This whole process is illustrated in figure 4.

Illustration of the process of joining a network.

Illustration of the process of joining a network.

All other communication between nodes is also handeled using Message objects. The following message ids are used:


JOIN_NETWORK = "join_network"               # Request to join the network
ADD_PEERS = "add_peers"                     # Send a list of all known peers
GENESIS_BLOCK = "genesis_block"             # Send the genesis block
PUBLISH_TRANSACTION = "publish_transaction" # Publish a transaction
VERIFY_TRANSACTION = "verify_transaction"   # Verify a transaction
PUBLISH_BLOCK = "publish_block"             # Publish a block to all authority nodes 
VERIFY_BLOCK = "verify_block"               # Verify a block
DISTRIBUTE_BLOCK = "distribute_block"       # Distribute a block to all nodes

Transaction creation and verification: The Transaction class represents the blueprint for creating transaction objects within the blockchain. Each transaction is uniquely identified by an id, which is a random 10-digit number generated upon the transaction’s creation. The class constructor requires the sender’s name, the receiver’s name, and the value of the transaction as arguments, ensuring that these essential details are encapsulated within every transaction instance.

Transactions are timestamped, providing a chronological order that is crucial for maintaining the blockchain’s integrity. The timestamp uses the ISO 8601 format, which is a widely accepted standard for representing dates and times.

A key feature of the Transaction class is its ability to be securely signed and verified. The sign_transaction method allows a transaction to be signed with the sender’s private key, creating a digital signature that is stored within the transaction. This signature is later used by the verify_signature method to ensure that the transaction has not been tampered with and that it indeed originates from the claimed sender. This verification process uses the sender’s public key to decrypt the signature and compare it to the transaction’s hash, which is calculated using the SHA-256 algorithm.

The class also provides a to_dict method for serialising the transaction into a dictionary format, which is useful for network transmission and storage. Conversely, the from_dict class method allows for the deserialisation of a transaction, creating a new Transaction object from a dictionary representation.

The Node class has a method create_transaction that initiates a new transaction. When a node intends to transfer value, it invokes this method with the recipient’s contact information and the amount to be transferred. The method constructs a Transaction object, which is then signed using the node’s private key to ensure authenticity. This signature is crucial as it allows other nodes in the network to verify the transaction’s origin. Once signed, the transaction is added to the node’s transaction pool, a temporary holding area for transactions that have not yet been confirmed and added to the blockchain.

After creating a transaction, the node must inform other nodes of its existence. This is done through the Communicator object within the Node class, which broadcasts a message containing the transaction data to all peer nodes. The broadcasting is facilitated by the broadcast_message method of the Communicator class, which sends the message to each peer node in the network. This dissemination ensures that all nodes become aware of the new transaction and can begin the verification process.

Verification is a critical step to ensure that transactions are valid and can be added to the blockchain. The Node class includes a verify_transaction method that checks the validity of a transaction. It ensures that the transaction exists in the transaction pool and has not been altered since it was signed. The node retrieves the sender’s public key and uses it to verify the transaction’s signature. If the verification is successful, the node adds a verification record to its ledger and broadcasts a verification message to the network, indicating that it has verified the transaction’s legitimacy. Transactions are deemed ’verified’ only if all nodes in the network have verified them.

Block creation and verification: The AuthorityNode class, an extension of the Node class, is imbued with the authority to create blocks. It achieves this through the create_block method, which takes a list of transaction identifiers as input. This method begins by ensuring that all transactions are verified and legitimate. It then proceeds to create a new block by including these transactions, referencing the hash of the previous block in the chain, and signing the block with the authority node’s private key to seal its authenticity. The newly created block is then added to the authority node’s block pool, a temporary storage for blocks that are awaiting verification by other nodes in the network.

Once a block is created, the AuthorityNode must publish it to the network for verification. This is done by broadcasting a message containing the block’s data to other peer authority nodes. The AuthorityNode uses the Communicator object to send out a PUBLISH_BLOCK message, which includes the serialised form of the block. This action initiates the consensus process, as other authority nodes will receive the block and begin the verification process.

Verification of blocks by an AuthorityNode is a two-step process. First, the node checks if the block exists within its block pool, ensuring that it is a recognised block awaiting verification. Following this, the node verifies the block’s signature using the public key of the block’s creator. If the signature is valid and the block is consistent with the blockchain’s rules, the node will broadcast a VERIFY_BLOCK message to the network, signalising its confirmation of the block’s validity. This collective verification by all authority nodes ensures a high level of security and consensus before a block is added to the blockchain.

When a block has been verified by all authority nodes, it is considered to have achieved consensus. This is a pivotal moment in the blockchain process, as it marks the point at which the block is accepted as a valid part of the chain. The AuthorityNode class handles this by checking if the verification record for the block includes all peer authorities. If consensus is reached, the node that created the block broadcasts a DISTRIBUTE_BLOCK message to all nodes in the network, signalising that the block is ready to be added to the blockchain. This message is crucial as it triggers the nodes to update their local copies of the blockchain with the newly accepted block, ensuring that the blockchain remains consistent across the entire network. The block is then officially appended to the blockchain, and any transactions contained within it are considered confirmed. Additionally and equally as important, every block, containing one or more transactions that have been part of the recently added block, is removed from the transaction pool of each node. This ensures that no transaction can be included in more than one block, preventing inconcisentcies and double-spending.

Class interfaces

Each node and authority node had to offer a set of methods to make it possible to interact with the blockchain. Besides the previously mentioned methods for transaction and block creation as well as verification, the nodes also had to offer the methods to return all verified and pending transactions and blocks. The following sections will describe the interfaces of the Node and AuthorityNode classes in detail.

Node Interface: Node objects offer the following methods:


def get_verified_transactions(self):
  ...
def get_pending_transactions(self):
  ...
def get_node_pending_transactions(self):
  ...
def get_node_created_transactions(self):
  ...
def get_transaction_by_id(self, transaction_id): 
  ...
def get_verified_blocks(self): 
  ...
def stop(self): 
  ...

These methods take only the self.transaction_pool, the self.verification_records, the self.blockchain and the self.name of the node as a basis to generate the needed information. One important design choice should be highlighted: While the get_verified_transactions only returns the transactions that are verified but not in the blockchain, the get_node_created_transactions function also returns all functions that the node in question has created, including the ones that are already in the blockchain. This was done because the verified transactions are important to the logic of the User Interface (UI), while the list of transactions, crated by one specific node is only important for a visual feedback.

AuthorityNode Interface: In addition to the methods offered by the Node class, the AuthorityNode class offers the following methods:


def get_pending_blocks(self): 
  ...
def get_node_pending_blocks(self): 
  ...
def get_block_by_id(self, block_id): 
  ...
def get_block_by_id(self, block_hash): 
  ...

These methods are used by the simulation and the UI to get information about the blocks that are yet to be verified by all authority nodes.

User Interface

Terminal Application: The user interface and its menu structure is highly inspired by the example provided in the end of the Assignment. First, a siple terminal application was developed, which is shown in figure 5. When starting the application, the user is greeted by Menu 1 with the options to Select a node, Print all verified transactions, Print all pending transactions, Print all verified blocks, Print all pending blocks and Exit Program. Since the simulation object stores a list of all nodes, it can be used to populate the list of nodes and do operations on them. To display the information about the current transactions and blocks, the first node in the list is used to access the blockchain and transaction pool as well as the block- and transaction verification records. When the user chooses to select a node, Menu 1.1 will be displayed. This menu allows the user to select a node from the list of nodes. With a node selected, Menu 1.1.1 is shown. This menu gives the option to Create a transaction, Create a block if the transactions are valid (this option only appears when the node is authoritative), Verify a block in the blockchain, Print transactions created by this node, Print pending transactions that this node has not yet verified and Exit to menu 1.1

The terminal application for Menu 1

The terminal application for Menu 1

The menu was implemented in a Menu class which allows the navigation between different options using the arrow keys instead of the user having to type their selection. Besides that the interaction only via terminal might seem intuitive to a developer but would pose a challenge to a user. Therefore, a simple Graphical User Interface (GUI) was implemented using the pyqt6 library.

Graphical User Interface: The GUI was implemented in the BlockchainApp.py file. Before a window is presented to the user, a Simulation object is created with two Authority Nodes and two regular ones. Then the first node in the list will create two transactions, one of which is verified by all nodes while the other stays unverified. This will set up the application for demonstration purposes since the user can then see the difference between verified and unverified transactions and has the option to create a block right away. The GUI for Menu 1 is shown in figure 6.

The graphical user interface for Menu 1

The graphical user interface for Menu 1

The gui is set up in a way so that a user can not perform actions that are not allowed. For example, if the user selects a node that is not authoritative, the option to create a block will not be displayed. The same goes for when a user wants to create a transaction only nodes that are not the node itself will be displayed. To make the interaction more visible, figure 7 shows a flowchart of some selected actions that can be performed by the user. The figure does not show some windows that dont offer original views. For example the window to Show all pending Blocks is very similar to the window that displays All pending Transactions. The same goes for verifying a transaction compared to the verification of a block. The figure also does not show some confirmation windows.

Flowchart of the GUI. Not all options are shown due to space constraints

Flowchart of the GUI. Not all options are shown due to space constraints

Test Results

Expected behaviour

When the application is started, the simulation will have two authority nodes and two regular nodes. The first node to be created is regarded the Genesis Node so it creates the genesis block for the blockchain. All subsequent nodes use the Genesis Node to sign themselvs up to the network where they also receive a copy of the genesis block. Finally, the simulation creates two transactions using the first node as sender and the second node as receiver. When the GUI is available for the user we can see these transactions as expected under the menu point Print all verified transactions as shown in figure 8, and the menu point Print all pending transactions as displayed in figure 9.

The GUI displaying the verified transactions.

The GUI displaying the verified transactions.

The GUI displaying the pending transactions.

The GUI displaying the pending transactions.

Since the blockchain is initialised with a genesis block, it is shown under the menu point Print all verified blocks in the blockchain as shown in figure 10. The genesis block is the first block in the blockchain and is the only block that does not have a previous block. The genesis block is also the only block that does not have a transaction. This is because the genesis block is created before any transactions are created.

When a block is added to the blockchain and it includes a transaction that is also present in another block, that second block which was not yet verified by all authority nodes, will not be available for verification any longer.

GUI displaying the verified blocks in the blockchain.

GUI displaying the verified blocks in the blockchain.

Shortcomings

When signing a transaction with a invalid private key, the transactions cannot be verified by the other nodes. While the nodes dont accept the transaction, the GUI still displays that the transaction was successfully verified. When taking a look at the pending transactions after that, it will still show that the transaction was not verified by any node. After a node has entered the network there is no way to remove it later. There is also no check of balance so a node can create a transaction with whatever it wants as the data.

Discussion

The GUI offers a more intuitive alternative to the terminal application. It makes it easy to demonstrate the functionality of the blockchain in an understandable way. Displaying which node has and which has not verified a transaction or a block, allows the user to see the consensus process in action. As shown in the test results, there still is a visual error where a transaction, which can not be verified will show the message ”successfully verified transaction: ...” even though the transaction was not actually verified. A further improvement of the simulation would be to implement a way to run selected nodes in auto pilot. This would make it easier to add new transactions and blocks in networks with many nodes without having to manually verify each transaction and block for each node.

The simulation could also be improved by adding a way to remove nodes from the network. This would make it possible to simulate a node going offline. There should also be a way to check the balance of a node. This would make it possible to check if a node has enough funds to create a transaction in the first place.

References

[1] S. Nakamoto, “Bitcoin : A peer-to-peer electronic cash system,” vol. 61, 2009, pp. 200–202.

[2] D. D. Wood, “Ethereum: A secure decentralised generalised transaction ledger,” 2014.

[3] D. Schwartz, N. Youngs, and A. Britto, “The ripple protocol consensus algorithm,” 2014.

[4] S. Popov, “The tangle,” 2015.

[5] B. Hill, S. Chopra, P. Valencourt, and N. Prusty, Blockchain Developer’s Guide: Develop smart applications with Blockchain technologies-Ethereum, JavaScript, Hyperledger Fabric, and Corda. Packt Publishing Ltd, 2018.

[6] N. R. Kasi, R. S, and M. Karuppiah, “Chapter 1 - blockchain architecture, taxonomy, challenges, and applications,” in Blockchain Technology for Emerging Applications, ser. Hybrid Computational Intelligence for Pattern Analysis, S. H. Islam, A. K. Pal, D. Samanta, and S. Bhattacharyya, Eds. Academic Press, 2022, pp. 1–31. [Online]. Available: https://www.sciencedirect.com/science/article/pii/B9780323901932000016

[7] M. Schedel, “Rsa encryption: Implementation, application, and security analysis in secure communication,” Univeristy of Stavanger, Tech. Rep., 2023.

You can have a look at the repository here.

UML Diagram of the blockchain application. Red indicates classes used to communicate between nodes. Blue indicates classes concerned with the blockchain itself. Green indicates classes representing the nodes in the network. and White indicates the classes used to simulate and interact with the network.

UML Diagram of the blockchain application. Red indicates classes used to communicate between nodes. Blue indicates classes concerned with the blockchain itself. Green indicates classes representing the nodes in the network. and White indicates the classes used to simulate and interact with the network.