Simple blockchain in Ada
Jun 20, 2018I 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:
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?
- The stored block hash is the same as the calculated one
- 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!