I consider myself a late adopter. Everyone talks about blockchain these days. Everyone tries to apply the technology everywhere, even when it does't make sense. So let's learn by doing and try to implement the simple blockchain from scratch. And let's do this in Ada!

Wikipedia defines blockchain as:

A blockchain, originally block chain, is a continuously growing list of records, called blocks, which are linked and secured using cryptography. Each block typically contains a cryptographic hash of the previous block, a timestamp, and transaction data. By design, a blockchain is resistant to modification of the data.

Twitter has a different opinion:

Twitter and Blockchain

So it looks like we have to model:

  • blocks: timestamped records that are able to store some kind of payload
  • a chain of blocks, a.k.a., the blockchain
  • a way to prove the validity of the whole blockchain
  • a proof of work to implement the commit protocol.

Block can be implemented as a new Ada type, Block.Object:

with Ada.Calendar; use Ada.Calendar;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;

package Simple_Blockchain.Block is
   type Object is private;
   
   -- public methods omitted for brevity

private

   type Object is
      record
         -- cryptographic hash of the current block
         Hash : String (1 .. 64);
         -- cryptographic hash of the previous block
         Previous_Hash : String (1 .. 64);
         Timestamp : Time;
         -- transaction data payload
         Data : Unbounded_String;
      end record;
end Simple_Blockchain.Block;

The complete, working code can be found at tomekw/simple_blockchain Github repository.

We will create new blocks with a Make function acting as a constructor:

function Make (Previous_Hash : String, Data : String) return Object is
   Now : Time := Clock;
begin
   return (
      -- current hash is a product of new hash attributes
      -- and the whole history of hashes of previous blocks
      Hash => Calculate_Hash (Previous_Hash, Now, Data),
      Previous_Hash => Previous_Hash,
      Timestamp => Now,
      Data => To_Unbounded_String (Data)
   );
end Make;

How do we calculate a hash then?

function Calculate_Hash (Previous_Hash : String; Timestamp : Time; Data : String) return String is
begin
   -- with GNAT.SHA256; use GNAT;
   return SHA256.Digest (Previous_Hash & Image (Timestamp) & Data);
end Calculate_Hash;

We have the blocks but they don't mean anything without being linked in a chain. A simple blockchain can be stored in a growable vector. Luckily, Ada provides an excellent containers library:

with Ada.Containers.Vectors; use Ada.Containers;

with Simple_Blockchain.Block; use Simple_Blockchain;

package Simple_Blockchain.Blockchain is
   use type Block.Object;

   package Block_Vectors is new Vectors (Index_Type => Positive, Element_Type => Block.Object);

   type Object is private;

   -- public methods omitted for brevity

private

   type Object is
      record
         Blocks : Block_Vectors.Vector;
      end record;
end Simple_Blockchain.Blockchain;

Now, we are able to Append new blocks to the chain:

The_Blockchain : Blockchain.Object := Blockchain.Make;

-- Let's use a string of zeroes as a first hash
First_Hash : String (1 .. 64) := (others => '0');

First_Block : Block.Object := Block.Make (Previous_Hash => First_Hash, Data => "First block");
Second_Block : Block.Object := Block.Make (Previous_Hash => Block.Get_Hash (First_Block), Data => "Second block");

Blockchain.Append (The_Blockchain, First_Block);
Blockchain.Append (The_Blockchain, Second_Block);

How do we know the blockchain is valid?

  1. The stored block hash is the same as the calculated one
  2. The stored previous hash is the same as the hash of the preceding block.

These rules apply to all block in the chain.

function Is_Valid (This : Blockchain.Object) return Boolean is
   Current_Block : Block.Object;
   Next_Block : Block.Object;
begin
   -- iterate over the blockchain in pairs
   for I in Get_Blocks (This).First_Index .. (Get_Blocks (This).Last_Index - 1) loop
      Current_Block := Block_Vectors.Element (Get_Blocks (This), I);
      Next_Block := Block_Vectors.Element (Get_Blocks (This), I + 1);

      -- confirm the stored block hash is the same as the calculated one
      if Block.Get_Hash (Next_Block) /= Block.Calculate_Hash (Block.Get_Previous_Hash (Next_Block), Block.Get_Timestamp (Next_Block), Block.Get_Data (Next_Block)) then
         return False;
      end if;

      -- confirm the stored previous hash is the same as the hash of the preceding block
      if Block.Get_Hash (Current_Block) /= Block.Get_Previous_Hash (Next_Block) then
         return False;
      end if;
   end loop;

   return True;
end Is_Valid;

Our newly created blockchain should be valid:

Blockchain.Is_Valid (The_Blockchain); -- => TRUE

The last part to implement is the proof of work. In the blockchain terminology it's called mining. Instead of simply appending new blocks to the chain we will mine them. Proof of work will be satisfied as a number of leading zeroes in the block hash defined as a blockchain Difficulty:

procedure Mine_Block (This : in out Blokchain.Object; Data : String) is
   New_Block : Block.Object;
   Previous_Hash : String (1 .. 64);
begin
   if Is_Empty (This) then
      Previous_Hash := (others => '0');
   else
      Previous_Hash := Block.Get_Hash (Last_Block (This));
   end if;

   New_Block := Block.Make (Previous_Hash => Previous_Hash, Data => Data);

   -- the important part: recalculate the hash until it starts
   -- with the expected number of zeroes
   while Block.Get_Hash (New_Block) (1 .. Get_Difficulty (This)) /= Expected_Hash_Prefix (This) loop
      Block.Recalculate_Hash (New_Block);
   end loop;

   Block_Vectors.Append (This.Blocks, New_Block);
end Mine_Block;

Now, instead of appending blocks, it's possible to simply mine new ones:

Blockchain.Mine_Block (The_Blockchain, "Third block");

Is_Valid function has to be adjusted to take into account the expected hash prefix validation:

function Is_Valid (This : Blockchain.Object) return Boolean is
   Current_Block : Block.Object;
   Next_Block : Block.Object;
begin
   -- code omitted
   
      if Block.Get_Hash (Next_Block) (1.. Get_Difficulty (This)) /= Expected_Hash_Prefix (This) then
         return False;
      end if;

   -- code omitted

   return True;
end Is_Valid;

There is more to the blockchain technolgy than this: sending transactions, wallets, and signatures. However, this post should demistify the concept of blockchains to the reader.

The complete, working code, and a short demo, can be found at tomekw/simple_blockchain Github repository.

with Ada.Text_IO; use Ada.Text_IO;

with Simple_Blockchain.Block;
with Simple_Blockchain.Blockchain;

use Simple_Blockchain;

function Simple_Blockchain_Demo return Integer is
   The_Blockchain : Blockchain.Object := Blockchain.Make (Difficulty => 6);
begin
   Put_Line ("Simple blockchain demo");
   New_Line;

   Put_Line ("Mining first block...");
   Blockchain.Mine_Block (The_Blockchain, Data => "First block");
   Put_Line ("Block mined.");
   New_Line;

   Put_Line ("Mining second block...");
   Blockchain.Mine_Block (The_Blockchain, Data => "Second block");
   Put_Line ("Block mined.");
   New_Line;

   Put_Line ("Mining third block...");
   Blockchain.Mine_Block (The_Blockchain, Data => "Third block");
   Put_Line ("Block mined.");
   New_Line;

   Put_Line ("Is blockchain valid? " & Blockchain.Is_Valid (The_Blockchain)'Image);
   New_Line;

   Put_Line ("Printing blockchain...");
   Put_Line (Blockchain.Image (The_Blockchain));

   return 0;
end Simple_Blockchain_Demo;

Demo output:

Simple blockchain demo

Mining first block...
Block mined.

Mining second block...
Block mined.

Mining third block...
Block mined.

Is blockchain valid? TRUE

Printing blockchain...
Blockchain - difficulty:  6, blocks:  3
Hash: 000000f78298d2028737f802a82edb955e0e4cfdc06b514325da2a037f41b754, Previous hash: 0000000000000000000000000000000000000000000000000000000000000000, Timestamp: 2018-06-14 13:26:11, Nonce:  10942584, Data: First block
Hash: 000000ddfddf305f15fefdada0939139ea33dc94bca1e57eda5f16d902fec55f, Previous hash: 000000f78298d2028737f802a82edb955e0e4cfdc06b514325da2a037f41b754, Timestamp: 2018-06-14 13:26:17, Nonce:  6008350, Data: Second block
Hash: 000000e4530387b122e91de4fedc84b77406a819713c9601b09b29695cb6e336, Previous hash: 000000ddfddf305f15fefdada0939139ea33dc94bca1e57eda5f16d902fec55f, Timestamp: 2018-06-14 13:26:32, Nonce:  17905780, Data: Third block

Enjoy!