A blockchain implementation in C++ - Software Systems Spring 2024 Final Project - Olin College of Engineering
A blockchain is a decentralized and distributed digital ledger that records transactions across multiple computers in a way that is immutable and transparent. Celestrix Blockchain provides a simple implementation of this technology in C++, demonstrating functionalities such as block creation, proof-of-work (mining), transaction management, block signing, and verification, with support for printing and verifying the blockchain's integrity.
The instructions to obtain the dependent libraries are below.
This library is used for verifying block signature in the blockchain.
Linux - run the below commands in a terminal
sudo apt-get update
sudo apt-get install libssl-dev
macOS - run the below commands in a terminal (using homebrew) If you do not have homebrew, the instructions to obtain that are here: https://brew.sh
brew install openssl
- Clone the repository to your local machine.
- Navigate to the project directory. (your pwd should be
CelestrixBlockchain/
)
To compile and run Celestrix Blockchain using an IDE, you can
simply build the project using the parent CMakeLists.txt,
and then run the main
executable.
Alternatively, if you want to build from CMake using CLI:
mkdir build
cd build
cmake ..
make
To compile and run Celestrix Blockchain using CLI, you can compile the source code using a C++ compiler with C++20 support:
g++ -std=c++20 -o blockchain main.cpp block.cpp blockchain.cpp -lssl -lcrypto
Then, run the compiled executable: ./blockchain
![CelestrixBlockchain](https://private-user-images.githubusercontent.com/19605629/327424745-9c76b588-cea6-4081-9c5a-f2fac77ea00c.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjU5NDAzMjAsIm5iZiI6MTcyNTk0MDAyMCwicGF0aCI6Ii8xOTYwNTYyOS8zMjc0MjQ3NDUtOWM3NmI1ODgtY2VhNi00MDgxLTljNWEtZjJmYWM3N2VhMDBjLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA5MTAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwOTEwVDAzNDcwMFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTU4ODE0NjRjYTIyOThmMjRmNmYyMTY2NzBkMWZlZTcyZTZmN2ZlYzUxMDE5ZWQyMTk3YjJmNzBhYmVjOTNkOGQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.F0cWg2-PHmACHNJo5EVktGk5yxfq5GMG074-Vik96wg)
Initializing a new Blockchain is very simple because you only have to pass the desired difficulty for mining as a parameter.
int difficulty = 4;
Blockchain blockchain(difficulty);
The universal standard is that the first block to be initialized and added to a blockchain is called the genesis block.
To add a block, you need to create transactions which can be done by, for example:
// Create data for transaction
std::vector<std::byte> data = {std::byte{0x01}, std::byte{0x02}, std::byte{0x03}};
// Create transaction
Transaction transaction(1, data);
Once you have created the desired amount of transactions, you can initialize a new block with the following parameters. Once after it is initialized, we need to mine the block and then sign it using a private key before adding it to the blockchain. (more on that below)
Block block(int index, int version, std::vector<std::byte> previousHash,
std::time_t timestamp, std::vector<Transaction> transactions, int nonce, int difficultyTarget);
block.addTransaction(transaction);
block.mineBlock(difficulty);
block.signBlock(privateKey);
blockchain.addBlock(block);
Signing a block is not necessary to add the block to a blockchain (in this project), but it is considered as the best-practice to maintain the integrity of a blockchain and keep the transaction data secure.
To do this, we need to first initialize a public key and private key pair that will be used for signing & verifying a block.
std::pair<EVP_PKEY *, EVP_PKEY *> keyPair = Block::generateEVPKeyPair();
EVP_PKEY *privateKey = keyPair.first;
EVP_PKEY *publicKey = keyPair.second;
We can sign a block with the generated private key once after we have initialized a block.
block.signBlock(privateKey);
Similarly, we can verify the block signature by using the generated public key and printing the verification result in the standard output.
if (block.verifyBlockSignature(publicKey)) {
std::cout << "Signature of block is valid." << std::endl;
} else {
std::cout << "Signature of block is invalid." << std::endl;
}
For ease of use, one can print the entire blockchain by simply using:
blockchain.printBlockchain();
One of the requirements once after we have manually created a blockchain is to actually verify if the blocks are connected properly by checking the linkage of the block hashes. In this project, one can verify the blockchain and print the verification result in the standard output using:
if (blockchain.isChainValid()) {
std::cout << "Blockchain is valid." << std::endl;
} else {
std::cout << "Blockchain is invalid." << std::endl;
}