notes.CompileArtisan.dev

Blockchain Technology

Table of Contents

1. Distributed Systems

  • This is a computing paradigm where two or more nodes work with each other in a coordinated fashion, to achieve a common outcome.
  • The main challenges are:
    • Coordination between nodes
    • Fault tolerance

1.1. Brewer’s Cap Theorem

  • Consistency: Every node gets the same latest data.
  • Availability: Every request gets a response.
  • Partition Tolerance: The system keeps functioning even if the network gets split into isolated but functional groups of nodes.

Brewers’ CAP theorem states that

You can only have CP or AP systems. During a partition, you must sacrifice either consistency or availability.

  • Blockchains commonly choose AP, but they get eventual consistency.
    • In case of a partition, the two groups may not sync with each other at the moment
    • However, when the partition heals, the nodes communicate with each other (gossip protocol).

2. Introduction to Blockchain

2.1. What it is

A blockchain is a cryptographically secure, append-only, peer-to-peer distributed database structured as a chain of blocks maintained through decentralized consensus.

  • A blockchain is a secure decentralized digital ledger that records transactions as a sequence of linked blocks.
  • It was first proposed by Stuart Haber and W. Scott Stornetta in 1991.
  • In 2008, a paper written by an author ’Satoshi Nakamoto’ published the bitcoin whitepaper (blockchain for a digital currency).
  • Byzantine node: a node exhibiting arbitrary behavior

2.2. Gossip Protocol

  • This is where nodes constantly keep communicating with each other in a peer-to-peer network.

2.3. Transaction

  • A transaction is a single record of some action, mostly some transfer of value.
  • A block is a container that bundles multiple transactions into a single unit. Each block contains:
    • Block header
    • Transaction Data
    • Hash

2.4. Blockchain Vs Tradition Ledger

Traditional (centralized) ledger Blockchain distributed ledger
One central authority (e.g., a bank) holds the official copy Every node holds an identical copy
If the central server goes down, the system stops (single point of failure) Even if some nodes are cut off, the rest of the system keep functioning (Partition tolerance, Availability)
Consistency is guaranteed because there’s only one copy to begin with Consistency must be actively re-earned through consensus after a partition, it’s temporarily sacrificed
Trust is placed in the institution Trust is placed in the consensus mechanism, not any single party

In short: a traditional ledger sidesteps the CAP theorem entirely by having only one copy, while a blockchain’s distributed ledger must consciously trade off Consistency for Availability + Partition tolerance, then recover consistency over time via consensus.

2.4.1. Advantages of a Blockchain

  • More security
  • More transparency
  • No single-point-of-failure

2.4.2. Disadvantages of a Blockchain

  • Slower
  • High computational cost
  • Transactions are irreversible
  • High storage requirements

2.5. Types of Blockchain based on governance

Public Private Consortium Hybrid
Open to Anyone Single Organization Group of organizations who don’t need to trust each other Mix of Public and Private
Fully Decentralized Centrally Controlled Many organizations each have central control Depends
Very Transparent but Slow Not Transparant but Fast Medium at Both Depends

2.6. Smart Contracts

  • When you carry out transactions in a blockchain, instead of trusting a company, you trust some deterministic and immutable code stored on the blockchain. This code is called a smart contract.
  • A smart contract is a self-executing program that automates the actions required in a blockchain transaction.
  • They can be thought of as a vending machine.

2.7. Node

  • A node is a computer on the peer-to-peer / blockchain network.
  • There are 3 types of nodes on a blockchain.

    Full Node Simple Payment Verification (SPV/Light) Node Mining Node
    Stores the complete blockchain data They only verify and store specific transactions when needed They participate in validating transactions and adding new blocks to the blockchain
      They store only block headers  

2.8. Rough Flow of Blochchain

  • A transaction \(T_{x}\) occurs.
  • \(T_{x}\) is verified using a consensus mechanism (a polling mechanism).
  • Multiple transactions are grouped into a block (a collection of valid transactions). They’re arranged as a merkel tree.
  • The block is added to the chain (each block is linked to the previous block using cryptographic hashes).
  • The blockchain is updated.

2.9. Mempool

A mempool (short for “memory pool”) is a temporary holding area on a blockchain node where unconfirmed or pending transactions wait before being selected by a miner or validator.

2.10. Uses of Blockchain

  • Cryptocurrencies
  • Supply Chain Tracking
  • Healthcare
  • Voting Systems

3. Fundamentals

3.1. Hash

  • A hash is a unique digital fingerprint of data. It’s a mathematical blender.
  • A hash function will always return fixed length output, regardless of the input size.
  • It is deterministic (the same input always produces the same hash)
  • Even a tiny change in input produces a completely different hash. This is called the avalanche effect.
  • Hashes are puzzle-friendly because the avalanche effect makes sure there are no shortcuts.
  • Two inputs can never give the same hash.
  • Hashes provide:
    • Authentication: Verifies identity of sender
    • Integrity: Ensures data wasn’t tampered
    • Non-Repudiation: Makes sure that the sender can’t deny sending it

3.1.1. SHA-256

  • SHA-256 works on 512-bit blocks.
  • The last 64 bits are reserved for storing the length of the original message.
  • The message itself is stored in the rest of the 448 bits.
  1. Algorithm
    1. Read the input
    2. Convert it into binary
    3. Append a bit with value 1, to the end of this sequence.
    4. Pad this sequence with enough 0s to span across 448 bits.
    5. Pad the length of the original message with enough 0s to span across 64 bits.
    6. Define the working variables:

      H0 = 6a09e667
      H1 = bb67ae85
      H2 = 3c6ef372
      H3 = a54ff53a
      H4 = 510e527f
      H5 = 9b05688c
      H6 = 1f83d9ab
      H7 = 5be0cd19
      

      Initialize variables a, b, c, d, e, f, g, h with H0, H1, H2, H3, H4, H5, H6, H7 respectively.

    7. Define the round constants:
      • These are 64 words each of size 32 bits.
      • These are derived from the decimal part (eg. 2 from the number 1.2) of the cube roots of the first 64 prime numbers.

           K0 = 0x428a2f98
           K1 = 0x71374491
           K2 = 0xb5c0fbcf
           K3 = 0xe9b5dba50x3956c25b
           K4 = 0x59f111f1
           K5 = 0x923f82a4
           K6 = 0xab1c5ed50xd807aa98
           K7 = 0x12835b01
           ...
           K63 = 0xc67178f2
        
    8. Split the block of 512 bits into 16 unsigned words w0, w1, w2, .. w15 (each would be 32 bits).
    9. Expand these 16 words into 64 words (create w16, w17, … w63) using rotations, shifts and XOR. Each word is created as: \[W_i = \sigma_1(W_{i-2}) + W_{i-7} + \sigma_0(W_{i-15}) + W_{i-16} \] for \(16 \le i \le 63\)
      • The current word \(w_{i}\) depends on 4 different previous words (\(w_{i-2}\), \(w_{i-7}\), \(w_{i-15}\) and \(w_{i-16}\)).
      • The helper functions defined are:
        • \(\operatorname{ROTR}^{n}(x)\): Rotate right (right circular shift) all the bits of word \(x\), \(n\) times.
        • \(\operatorname{SHR}^{n}(x)\): Logical right shift (bits of the right are discarded on right shifting) all the bits of word \(x\), \(n\) times.
        • \(\sigma_{0}(x) = \operatorname{ROTR}^{7}(x) \oplus \operatorname{ROTR}^{18}(x) \oplus \operatorname{SHR}^{3}(x)\)
        • \(\sigma_{1}(x) = \operatorname{ROTR}^{17}(x) \oplus \operatorname{ROTR}^{19}(x) \oplus \operatorname{SHR}^{10}(x)\)
    10. For 64 times:
      1. Calculate: \[T_1 = h + \Sigma_1(e) + \operatorname{Ch}(e,f,g) + K[i] + W[i] \] \[T_2 = \Sigma_0(a) + \operatorname{Maj}(a,b,c) \] The helper functions/variables are defined as:
        • \(h\): This variable is one of the 8 variables defined earlier
        • \(\Sigma_{0}(x) = \operatorname{ROTR}^{2} \oplus \operatorname{ROTR}^{13} \oplus \operatorname{ROTR}^{22}\)
        • \(\Sigma_{1}(x) = \operatorname{ROTR}^{6} \oplus \operatorname{ROTR}^{11} \oplus \operatorname{ROTR}^{25}\)
        • \(\operatorname{Ch}(e, f, g) = (e \wedge f) \oplus (\not e \vee g)\)
          • This is called the Choice function and \(e\), \(f\) and \(g\) are 3 of the 8 working variables.
          • For every bit position, if the bit of \(c\) is 0, then the corresponding bit is taken from \(f\) and if the bit of \(c\) is 1, then the corresponding bit is taken from \(g\).
        • \(\operatorname{Maj}(a, b, c) = (a \wedge b) \oplus (a \wedge c) \oplus (b \wedge c)\)
          • This is the Majority function and for each bit position, the output is the bit value (0 or 1) that appears in at least two of the three inputs.
      2. Update the worker variables as:
        • \(\verb|H7| = \verb|H6|\)
        • \(\verb|H6| = \verb|H5|\)
        • \(\verb|H5| = \verb|H4|\)
        • \(\verb|H4| = (\verb|H3| + T_{1}) \mod(2^{32})\)
        • \(\verb|H3| = \verb|H2|\)
        • \(\verb|H2| = \verb|H1|\)
        • \(\verb|H1| = \verb|H0|\)
        • \(\verb|H0| = (T_{1} + T_{2}) \mod(2^{32})\)
    11. Concatenate all the worker variables and convert the result into hexadecimal.
  2. Rough Implementation in Python
    def main():
        message = "quickbrown"
        output = convert_string_to_binary(message)
        output+='1'
        output = pad_till_n(output, 448)
        output+= f"{len(message):0{64}b}"
        
        # working variables:
        H = [
            f"{int(x,16):032b}" for x in
            [
                 "6a09e667",
                 "bb67ae85",
                 "3c6ef372",
                 "a54ff53a",
                 "510e527f",
                 "9b05688c",
                 "1f83d9ab",
                 "5be0cd19",
             ]
        ]
        # round constants
        K = [
             0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5,
             0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5,
             0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
             0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174,
             0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC,
             0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
             0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7,
             0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967,
             0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
             0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85,
             0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3,
             0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
             0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5,
             0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3,
             0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
             0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
        ]
        # split the block into 16 words (w0, w1, ... w15) of 32 bits each
        w = []
        for i in range(0, 512, 32):
            w.append(output[i:i+32])
         
        # creating words w16, w17, ... w63 (helper functions defined later)
        for i in range(16, 64):
            value = (sigma_1(w[i-2]) + int(w[i-7], 2) + sigma_0(w[i-15]) + int(w[i-16], 2)) % (2**32)
            w.append(f"{value:032b}")
         
        # the 64 rounds (helper functions defined later)
        for i in range(64):
            T1 = (int(H[7], 2) + SIGMA_1(H[4]) + Ch(H[4], H[5], H[6]) + K[i] + int(w[i], 2)) % (2**32)
            T2 = (SIGMA_0(H[0]) + Maj(H[0], H[1], H[2])) % (2**32)
            H[7] = H[6]
            H[6] = H[5]
            H[5] = H[4]
            H[4] = f"{(int(H[3],2) + T1) % (2**32):032b}"
            H[3] = H[2]
            H[2] = H[1]
            H[1] = H[0]
            H[0] = f"{(T1 + T2) % (2**32):032b}"
         
        print(hex(int("".join(H), 2)))
     
     
     
     
    def convert_string_to_binary(txt: str):
        binary = ""
        for c in txt:
            binary += f"{ord(c)-96:08b}"
        return binary
     
    def pad_till_n(txt: str, n):
        return txt.rjust(n, "0")
     
     
    # helper functions for creating words w16, w17, ... w63
    def ROTR(w, n): 
        n = n%len(w)
        return w[-n:] + w[:-n]
     
    def SHR(w, n):
        return w[:-n].rjust(len(w), "0")
     
    def sigma_0(x):
        return int(ROTR(x, 7), 2) ^ int(ROTR(x, 18), 2) ^ int(SHR(x, 3), 2)
     
    def sigma_1(x):
        return int(ROTR(x, 17), 2) ^ int(ROTR(x, 19), 2) ^ int(SHR(x, 10), 2)
            
     
     
     
    # helper functions for the 64 rounds
    def SIGMA_0(x):
        return int(ROTR(x, 2), 2) ^ int(ROTR(x, 13), 2) ^ int(ROTR(x, 22), 2)
        
    def SIGMA_1(x):
        return int(ROTR(x, 6), 2) ^ int(ROTR(x, 11), 2) ^ int(ROTR(x, 25), 2)
     
    def Ch(e, f, g):
        return (int(e, 2) & int(f, 2)) ^ (~int(e, 2) & int(g, 2))
        
    def Maj(a, b, c):
        return (int(a, 2) & int(b, 2)) ^ (int(a, 2) & int(c, 2)) ^ (int(b, 2) & int(c, 2))
     
    main()
    
    
    0xc072e1042417d4a9c774c9ffeaea3d25440a6479978c2adc1cacdc9a894c35bc
    
  3. Using hashlib

    The steps in which this works is given below:

    1. Make bytes Object
      • A bytes object in Python is an immutable sequence of integers ranging from 0 to 255.
      • You can make it using by passing a list of integers as the parameter to the bytes function:

        list_bytes = bytes([72, 101, 108, 108, 111])
        print(list_bytes)
        
          b'Hello'
        
      • You can also use a b suffix to a string:

        list_bytes = b'hello'
        print(list_bytes)
        
          b'hello'
        
      • You can also use the .encode() method and it gives the same thing:

        list_bytes = 'hello'.encode()
        print(list_bytes)
        
          b'hello'
        
      • If you index a bytes object, you get the ASCII value:

        print(b'ABC'[0])
        
          65
        
    2. Use .sha256() method
      • From the hashlib module, the .sha256() method takes a bytes object as a parameter, and returns a HASH object:

        import hashlib
        print(type(hashlib.sha256(b'ABC')))
        
          <class '_hashlib.HASH'>
        
    3. Use .hexdigest() method
      • On the HASH object, you can call the .hexdigest() method to return the hashed value as hexadecimal characters:

        import hashlib
        hash_value = hashlib.sha256("text".encode()).hexdigest()
        print(hash_value)
        
          982d9e3eb996f559e633f4d194def3761d909f5a3b647d1a851fead67c32c9d1
        

3.2. Cryptographic Keys

  • These are mathematical codes verify a user’s identify and authorize transactions.
  • They keys are generated as a pair (public key and private key).
  • You can mathematically derive public key from private key but never private key from public key.

3.2.1. Based on Number of Keys

Asymmetric Cryptography Symmetric Cryptography
Receiver’s public key used for encryption, and receiver’s private key is used for decryption A common private key is used for encryption and decryption
It is also known as public key cryptography It is also known as private key cryptography
  1. RSA (Rivest Shamir Adelman) Algorithm
    • This is the model widely used Public Key Cryptography (PKC).
    • To encrypt a message there is no need to exchange a secret key separately.
    1. Key Generation
      • Select \(p\), \(q\), where \(p\) and \(q\) are prime.
      • Calculate \(n = p \times q\)
      • Select integer \(e\), such that \(\text{gcd}(\phi(n), e) = 1; 1 < e < \phi(n)\)
      • Calculate \(d = e^{-1}\mod(\phi(n))\)
      • Public Key \(\text{KU} = {e,n}\)
      • Private Key \(\text{KR} = {d,n}\)
    2. Encryption (through an example)
      • \(p=3\), \(q=11\)
      • \(n=p \times q = 3 \times 11 = 33\)
      • \(\phi(n) = \phi(33) = \phi(3 \times 11) = 2 \times 10 = 20\)
        • Verification of φ(33): \(Z_{33}^{*}\) = (all the numbers from 1 to 33, coprime with \(33\)) = [1,2,4,5,7,8,10,13,14,16,17,19,20,23,25,26,28,29,31,32]
      • Consider \(e=7\) (because it satisifies the GCD criteria):
      • \(d = e^{-1} \mod(20)\)

        • \(d = 7^{-1} \mod(20)\)
        \(q\) \(a\) \(m\) \(r\) \(u_{0}\) \(u_{1}\) \(u\)
        0 7 20 6 1 0 1

3.3. Blockchain Address

  • A blockchain address is derived from the public key using hash functions.
  • The flow is
    • Private Key is created
    • Public Key is derived from the public key.
    • Hash function transforms the public key to generate the blockchain address.

3.4. Message Digest

  • This is a fixed length alphanumeric string that acts as a digital fingerprint.
  • It’s basically the hash value of the message.
  • It ensures data integrity.

3.5. Digital Signature

3.5.1. What it is

  • This is created by taking the hashed message and encrypting it using the sender’s private key.
  • Anyone can verify that this message was sent by the actual sender by using the sender’s public key to decrypt it and get back the original message.
  • A message digest verifies the integrity of data only.
  • A digital signature verifies authenticity, integrity, AND ownership of a transaction (broader purpose)

3.5.2. How it works

3.6. Miner

  • A miner is an individual or a company that computationally validate transactions and add new blocks to the blockchain.
  • They solve complex mathematical problems to prove that they have performed computational work before they are allowed to add a new block to the blockchain.
  • This process secures the blockchain, prevents fraud and enables decentralized consensus.
  • Mining is used to secure transactions utilizing high performance computing systems.

3.7. Difficulty

3.8. Nonce

  • Nonce stands for number used only once.
  • It is a 32 bit number such that the hash of the block satisfies the network’s difficulty requirement.
  • A golden nonce is the specific number a cryptocurrency miner finds that produces a valid block hash below the network’s required target difficulty.
  • If a miner obtains the golden nonce, they obtain the
    • Block Reward
    • Gas
  • The basic algorithm is:

    nonce = 0
    
    while hash doesn't satisfy difficulty:
        nonce += 1
        calculate hash
    
    stop when valid
    

3.9. Consensus

  • It is the procss by which all the nodes in a blockchain agree on the validity of transactions and the current state of the blockchain.
  • Consensus mechanism is what determines which block/blockchain is accepted by the network.
  • It ensures that every participant in the blockchain has the same copy of the ledger.
CAP Theorem Consensus
Explains trade offs achieves agreement
   
Feature Proof of Work (PoW) Proof of Stake (PoS)
Basis of selection Computational puzzle-solving (brute-force nonce guessing) Amount of cryptocurrency staked as collateral
Resource cost Huge electricity + specialized hardware (ASICs) Minimal energy; no special hardware needed
Security mechanism Attacker must out-compute the entire honest network’s hash power Malicious validators get slashed — their staked funds are confiscated
Speed / throughput Slower (Bitcoin ≈ 10 min/block, ~7 tx/sec) Faster validation, higher throughput
Sybil-attack resistance Strong — fake identities are cheap, but computing power isn’t Strong — fake identities are cheap, but staking real capital isn’t
Example networks Bitcoin Ethereum (post-2022), Cardano

Slashing (the enforcement mechanism): If a validator behaves maliciously, like approving fraudulent transactions, breaking protocol rules, the network can confiscate part or all of their staked assets.

3.10. Generations of Blockchain

Blockchain 1.0 Blockchain 2.0 Blockchain 3.0 Blockchain X
Only for cryptocurrency Cryptocurrency + Smart Contracts Blockchain used not only for finance but for health care, governance, etc Future use case of blockchain as a search engine

4. Execution Flow of a Blockchain

\[\text{Transaction} \rightarrow \text{Mempool} \rightarrow \text{Mining / Block Creation} \rightarrow \text{Blockchain} \rightarrow \text{Validation} \]

4.1. How Blockchains Accumulate Blocks

  1. A node starts at transaction by signing it with its private key.
  2. The transaction is propagated by using much desirable gossip protocol to peers, which validates the transaction based on pre-set criteria.
  3. Once the transaction is validated, it is included in a block, which is then propagated on to the network. At this point, the transaction is considered confirmed.
  4. The newly created block now becomes part of the ledger and the next block links itself cryptographically back to this block.

4.2. Example

Implement a simple blockchain where miners find a valid nonce by repeatedly changing it until the block hash satisfies a predefined difficulty condition.

import hashlib
import time

class Block:
 
    def __init__(self, data, previous_hash):
        self.data = data
        self.timestamp = time.time() # import time
        self.previous_hash = previous_hash
        self.nonce = 0
        self.hash = ""



4.3. Example

Build a blockchain network consisting of 3 independent university nodes (A, B and C). Each university has a public-private key pair that acts as its blockchain identity. Design and implement a simplified and decentralized blockchain based certificate verification system in which the university’s issue digitally signed certificates and employers can independently verify them without relying on a central database. When a university issues a certificate:

  • The certificate details forms a transaction.
  • The transaction is digitally signed using the university’s private key.
  • Other nodes can verify signatures using the university’s public key.
  • Valid transactions are grouped into a block

The attributes of the block contains:

  • Blocknumber
  • Timestamp
  • Transactions
  • Previous block hash
  • Transaction hash
  • Current block hash

The participating nodes reach agreement using a consensus mechanism.

The validated block is replicated across peer to peer network.

An employer can verify a certificate using the student’s certificate ID and the issuing university’s public key.

4.4. Example

Implement a simple public blockchain for agriculture supply chain where:

  1. A farmer registers products on the blockchain.
  2. The supply chain team updates the produce status during transportation.
  3. The customer receives the produce.
  4. The produce reaches the customer
  5. The farmer recieves the benefit of the reward.
  6. All transactions must be stored in blocks linked using cryptographic hashes.

5. Merkle Tree

  • It’s a tree where every node is the hash of its child.
    • The leaf nodes (the lowest nodes in the tree) contain the hash of a transaction.
    • The non-leaf nodes contain the hash of its child.
  • Merkle trees are also called hash trees.

merkletree.png