blockchain

Build A Blockchain In Elixir – The Final Part

In this part, we are going to move further from our previous post and build our own blockchain and now we are going to validate our chain if it is valid and whatever we are doing is right & we are going into the right path.

So there are two very basic fundamental properties of blocks in a chain, i.e. next block should have the hash of the previous block and the current hash should be valid and not contains any tempered data. So let’s go ahead and write our tests for validation of our blockchain and then we will write our functions to make them pass.

defmodule ExChain.BlockchainTest do

....

   test "validate a chain", %{blockchain: blockchain} do
      # add block into blockchain
      blockchain = Blockchain.add_block(blockchain, "some-block-data")
      # assert if blockchain is valid
      assert Blockchain.valid_chain?(blockchain)
    end
    ...
 endCode language: Elixir (elixir)

Here we have added a simple test where we have created a new blockchain then added a new block with some-block-data string as data into the block.

We have to write some more tests so that we can test the negative cases also when we temper the data or hash into the blockchain.

defmodule ExChain.BlockchainTest do

...

test "when we temper data in existing chain", %{
      blockchain: blockchain
    } do
      blockchain =
        blockchain
        |> Blockchain.add_block("blockchain-data-block-1")
        |> Blockchain.add_block("blockchain-data-block-2")
        |> Blockchain.add_block("blockchain-data-block-3")

      # validate if blockchain is valid
      assert Blockchain.valid_chain?(blockchain)
      # temper the blockchain, assume at location 2
      index = 2
      tempered_block = put_in(Enum.at(blockchain.chain, index).data, "tempered_data")

      blockchain = %Blockchain{chain: List.replace_at(blockchain.chain, index, tempered_block)}

      # should invalidate the blockchain
      refute Blockchain.valid_chain?(blockchain)
    end

    test "when we temper hash in existing chain", %{
      blockchain: blockchain
    } do
      blockchain =
        blockchain
        |> Blockchain.add_block("blockchain-data-block-1")
        |> Blockchain.add_block("blockchain-data-block-2")
        |> Blockchain.add_block("blockchain-data-block-3")

      # validate if blockchain is valid
      assert Blockchain.valid_chain?(blockchain)
      # temper the blockchain, assume at location 2
      index = 2
      tempered_block = put_in(Enum.at(blockchain.chain, index).hash, "tempered_hash")

      blockchain = %Blockchain{chain: List.replace_at(blockchain.chain, index, tempered_block)}

      # should invalidate the blockchain
      refute Blockchain.valid_chain?(blockchain)
    end
    
    
    ....
endCode language: Elixir (elixir)

Here we have created a new blockchain and added 3 more blocks and then in one test we temper the data and update the blockchain, in the second test we have tempered the hash of the block and we give to test.

mix testCode language: Markdown (markdown)

Now obviously these three newly added tests should fail. So we should write some functions so that we can validate the tests. Update the file lib/blockchain.ex and update with the following changes.

defmodule ExChain.Blockchain do
  @moduledoc """
  This module contains the blockchain related functions
  """
  alias __MODULE__
  alias ExChain.Blockchain.Block
  
  ...
  
  @spec valid_chain?(Blockchain.t()) :: boolean()
  def valid_chain?(%__MODULE__{chain: chain}) do
    chain
    |> Enum.chunk_every(2, 1, :discard)
    |> Enum.all?(fn [prev_block, block] ->
      valid_last_hash?(prev_block, block) && valid_block_hash?(prev_block)
    end)
  end

  # Private functions

  defp valid_last_hash?(
         %Block{hash: hash} = _last_block,
         %Block{last_hash: last_hash} = _current_block
       ) do
    hash == last_hash
  end

  defp valid_block_hash?(current_block) do
    current_block.hash == Block.block_hash(current_block)
  end
  
  ...
  
  endCode language: Elixir (elixir)

Here we are iterating the chain and comparing the last block hash value with the last_hash value in the current block. The second validation we will do for the current block. We will take the current hash value and then we will generate a hash of that block on the fly and will compare in case the hash of the block is compromised.

Now if you run the tests then it should pass all the tests.

mix tests
Finished in 0.1 seconds
10 tests, 0 failuresCode language: Markdown (markdown)

All of our tests should pass, Well done! we have now created a basic blockchain with the Merkel tree philosophy where the hash of current value is stored in further blocks so that we can tell that particular blockchain is valid or not.

We will stop here and then in the next post we will talk about the longest chain winsproperty of the blockchain and will implement it in our blockchain.

I hope you have enjoyed this article. If you liked it, consider buying a coffee here … Thank You

Leave a Comment

Your email address will not be published. Required fields are marked *