CP-20200215

Cryptography Primitives Intermediate February 15, 2020

Back to All Tasks

Problem Statement

You are tasked with implementing a simple blockchain in Python that uses SHA-256 as its hash function. Your blockchain will consist of blocks, each containing an index, timestamp, data, previous hash, and its own hash. The goal is to understand how hash functions ensure the integrity and immutability of the blockchain. Implement a function to create new blocks and add them to the chain. Ensure that the hash of each block correctly references the previous block's hash and that the hash itself meets certain difficulty criteria (proof of work). This will involve finding a nonce such that when the block data is hashed, the resulting hash starts with several leading zeros.

Concepts

  • Hash Functions
  • Collision Resistance

Constraints

  • Use Python's hashlib library for SHA-256 hashing.
  • Implement a proof-of-work algorithm where the hash must start with two leading zeros.
  • Your blockchain should be able to handle at least 10 blocks without errors.

Security Notes

  • Ensure that all data in each block is correctly hashed and linked to prevent tampering.
  • Consider how changing even one piece of data in a block would affect the integrity of subsequent blocks.

Solutions

Cpp Solution

#include <iostream>
#include <vector>
#include <string>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <openssl/sha.h>

// Block class to represent each block in the blockchain
class Block {
public:
    int index;
    std::time_t timestamp;
    std::string data;
    std::string previousHash;
    std::string hash;
    int nonce;

    // Constructor for creating a new block
    Block(int idx, const std::string& dta, const std::string& prevHash) {
        index = idx;
        timestamp = std::time(nullptr);
        data = dta;
        previousHash = prevHash;
        nonce = 0;
        hash = calculateHash();
    }

    // Function to calculate the SHA-256 hash of the block's data
    std::string calculateHash() const {
        std::stringstream ss;
        ss << index << timestamp << data << previousHash << nonce;
        std::string input = ss.str();
        unsigned char output[SHA256_DIGEST_LENGTH];
        SHA256(reinterpret_cast<const unsigned char*>(input.c_str()), input.size(), output);
        std::ostringstream hashString;
        for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
            hashString << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(output[i]);
        }
        return hashString.str();
    }

    // Proof of work method to find a nonce that makes the block's hash start with two leading zeros
    void proofOfWork(int difficulty) {
        std::string target(difficulty, '0');
        while (hash.substr(0, difficulty) != target) {
            ++nonce;
            hash = calculateHash();
        }
    }
};

// Blockchain class to manage the chain of blocks
class Blockchain {
private:
    std::vector<Block> chain;
    int difficulty;

public:
    // Constructor initializes the blockchain with a genesis block
    Blockchain(int diff) : difficulty(diff) {
        Block genesisBlock(0, "Genesis Block", "0");
        genesisBlock.proofOfWork(difficulty);
        chain.push_back(genesisBlock);
    }

    // Function to get the last block in the blockchain
    Block getLastBlock() const {
        return chain.back();
    }

    // Function to add a new block to the blockchain
    void addBlock(const std::string& data) {
        int index = static_cast<int>(chain.size());
        Block newBlock(index, data, getLastBlock().hash);
        newBlock.proofOfWork(difficulty);
        chain.push_back(newBlock);
    }
};

int main() {
    Blockchain myBlockchain(2); // Create a blockchain with difficulty of 2 leading zeros

    // Add blocks to the blockchain
    for (int i = 1; i <= 10; ++i) {
        std::stringstream ss;
        ss << "Block " << i;
        myBlockchain.addBlock(ss.str());
    }

    // Output the contents of the blockchain
    for (const auto& block : myBlockchain.chain) {
        std::cout << "Index: " << block.index << "
Timestamp: " << block.timestamp << "
Data: " << block.data << "
Previous Hash: " << block.previousHash << "
Current Hash: " << block.hash << "
Nonce: " << block.nonce << "\n----------\n";
    }

    return 0;
}

This C++ program implements a simple blockchain using SHA-256 hashing and proof-of-work to ensure the integrity and immutability of the chain. The program defines two main classes: Block and Blockchain.
The Block class represents each block in the blockchain, storing its index, timestamp, data, previous hash, current hash, and nonce. It includes methods for calculating the hash of the block's contents and performing proof-of-work to find a valid nonce that makes the hash start with a specified number of leading zeros.
The Blockchain class manages the chain of blocks. It initializes with a genesis block, which is created during construction. The addBlock method allows adding new blocks to the blockchain by creating a new Block object, calculating its hash using proof-of-work, and appending it to the chain.
In the main function, we create a Blockchain instance with a difficulty level of 2 (requiring hashes to start with two leading zeros) and then add ten blocks to the chain. Each block contains simple data indicating its index in the blockchain. After adding the blocks, the program outputs the details of each block in the chain.
This implementation demonstrates how hash functions ensure the integrity and immutability of the blockchain: changing even one piece of data in a block would require recalculating the hashes of all subsequent blocks to maintain their validity.