A Rudimentary Blockchain Written in Crystal

So, what is a blockchain? It’s a list (chain) of blocks linked and secured by digital fingerprints (also known as crypto hashes).

The easiest way to think of it is as a linked list data structure. That being said, a linked list only required to have a reference to the previous element; a block must have an identifier depending on the previous block’s identifier, meaning that you cannot replace a block without recomputing every single block that comes after.

For now, think of blockchain as a series of blocks with some data linked with a chain, the chain being the hash of the previous block.

Blockchains have become more practical today due to the reduced sized of and increased amount of space in hard drives. A few decades ago, not so much. Each full node on the Bitcoin network will have a copy of the entire blockchain (150GB+)! That is a copy of every block ever mined over the last 11 years.

On to CrystalChain

I’m going to write this as a brief run-through of the code and how a blockchain’s core structure is set up.

There are different hashing algorithms, and for this project, I chose SHA256, which is the one used in Bitcoin.

Each block is stored with a timestamp and an index. In theory, you only need one or the other. In CrystalChain, we’re going to store both. To help ensure integrity throughout the blockchain, each block will have a self-identifying hash.

Like Bitcoin, each block’s hash will be a cryptographic hash of the block’s (index, timestamp, data, and the hash of the previous block’s hash previous_hash). The data can be anything you want for now.

block.cr

require "openssl"
require "./proof_of_work"
require "./transaction"
require "json"

module CrystalChain
  class Block
    include ProofOfWork
    include JSON::Serializable
    property current_hash : String, index : Int32, 
             nonce : Int32, previous_hash : String

    def initialize(index = 0, 
                   data = "data", 
                   transactions = [] of Transaction, 
                   previous_hash = "hash")
      @data = data
      @index = index
      @timestamp = Time.utc
      @previous_hash = previous_hash
      @nonce = proof_of_work
      @current_hash = calc_hash_with_nonce(@nonce)
      @transactions = transactions
    end

    def self.first(data = "Genesis Block")
      Block.new(data: data, previous_hash: "0")
    end

    def self.next(previous_block, transactions = [] of Transaction)
      Block.new(
        transactions: transactions,
        index: previous_block.index + 1,
        previous_hash: previous_block.current_hash
      )
    end

    private def hash_block
      hash = OpenSSL::Digest.new("SHA256")
      hash.update("#{@index}#{@timestamp}#{@data}#{@previous_hash}")
      hash.final.hexstring
    end

    def recalculate_hash
      @nonce = proof_of_work
      @current_hash = calc_hash_with_nonce(@nonce)
    end

  end
end

This file will create all of our blocks. Each block contains the following data:

1) index: indicates the index of the block ex: 0,1 2) timestamp: timestamp in epoch, number of seconds since 1 Jan 1970 3) data: the actual data that needs to be stored on the blockchain. 4) previoushash: the hash of the previous block, this is the chain/link between the blocks 5) nonce: this is the number that is to be mined/found. 6) currenthash: The hash value of the current block, this is generated by combining all the above attributes and passing it to a hashing algorithm

Also, in this block of code, the self.first method starts the blockchain and generates the genesis block of said chain like so:

#CrystalChain::Block:0x10b13ac80 
@current_hash="acb701a9b70ccf5a0617d654e6b8a7155a8c888910d34df692db92355964d54e", 
@data="Genesis Blockerinooo", @index=0, @timestamp=2020-07-13 17:54:02 +03:00, @previous_hash="0"

After the first method runs, all subsequent blocks are created by the next method. By storing the previous hash of each block, a block can’t be modified without changing the hash of every consecutive block. This is demonstrated in the example below.

If the data in a block is changed, all hashes of the future blocks must be changed as well. This is because the hash of the block depends on the value of the previous hash of the past block.

This chain of hashes acts as cryptographic proof and helps ensure that once a block is added to the blockchain it cannot be replaced or removed.

That concludes the first post of this blockchain series. In the next post I will be going over the proof of work and how the blocks are mined.

If you are interested in checking out the code and/or playing around with it, you can find it on my GitHub at the link below:

https://github.com/nolyoi/crystal-chain