I have developed a fully-functional blockchain-based cryptocurrency node and a Command Line interface (CLI) for users to interact with the node.
All scripts are written in Python and use Flask framework for server code. The node here, is consistent with (and has been tested with) a network of 15 nodes (list of nodes here), each using different languages (Javascript, Go, Java, Python) and different backend frameworks.
This README file has the following two sections- Project Development Details and Technical Details You may jump to the Technical details sections by clicking here
This Project was developed for the Programming Club, IIT Kanpur from April 2020 to July 2020.
- I learnt about an entire new domain of cryptocurrency and blockchain- got hands-on learning on how cryptocurrencies and blockchains work. Also, looked at other real-life applications of blockchain
- I learnt about and implemented Multi-Threading for effeciently mining blocks. Implementing Multi-threading in Python was truly one of the most challenging parts of the project. Since the
Threading
library in Python is not very well documented, I had to figure out how a number of methods and objects work by trying and iterating. - Modelling a large project using appropriate Classes and Methods- Thinking about how to best model the project so as to minimise writing repititive code and improve readability of code was a big challenge
- I learnt a lot about Cryptography and implemented Digital Signatures- This included generating keys, signing a given message and verifying signatures. I got hands-on experience with the
cryptography
library in Python - I learnt how to obtain universal hostnames through tunneling softwares like ngrok
- I got firsthand experience with using Flask framework to develop backend server
- Better OOP modelling - In hindsight, I think the project could be modelled in a more effecient manner. Adding a number of methods in Block and transaction classes, might have eliminated repitive code and also, would have subsumed some independent functions.
- Dynamic target- In my node, the target for mining is fixed. However, in real cryptocurrencies the target is dynamically determined.
- Chain Reorganisation - The node would face issues in case two blocks are mined with the same timestamp or with the same index. Adding a function for reorganisation of chain in case multiple valid blocks have the same index would be a nice feature.
- Developing Web-App based UI - Currently, the frontend of the project is a Command Line Interface. However, it is not very user-friendly. A web-app would be much more handy for users.
- Write code to preferentially include Transactions in Block for mining- The Miner of my node, simply picks up as many transactions as it can (within the maximum block size) starting from index 0. However, in reality, the miners usually pick up transactions in the decreasing order of the transaction fee, so as to maximise earning from mining. Implementing such a function which preferentially includes transactions in a block would make the Miner closer to real-life.
- Assignments phase- I implemented various parts of the full projects independently. The assignments repository can be found here- abhimanyusethia12/IITKBucks_assignment
- Project Building phase- I put together the various parts implemented at assignments into a main application.
- Deployment phase- One node of the project had been deployed on https://iitkbucks.pclub.in from 13th July 2020 till 26th July 2020. Through trying to communicate with that node, I found and eliminated the bugs in my applications.
user_interface_IITKBucks.py
is a CLI (Command Line Interface) for the user to interact with my node.More Details on CLI
The entire server code for the node is written in app.py
. It consists of-
- Global Blockchain Variables
- Endpoints of Server
- Definition of Various Functions
- Code for Initialisation of Server
All classes are defined in classes.py
and are inherited by app.py
. More Details on Classes
(You may read each of the above sections in detail sequentially or jump to any specific section you want to read by clicking on the relevant point/link above)
The user is allowed to perform the following actions by running the user_interface_IITKBucks.py
script-
- Check Balance - allows user to check balance for any public key or alias
- Create Account - generates a pair of public and private keys
- Transfer Coins - allows user to create a transaction with any number of outputs
- Add Alias - alows user to add an alias for his public key
The following classes, along with the respective methods are defined in classes.py
:
block_body
block_header
block
input_class
output_class
transaction_class
The blockchain data is stored in the following data structures as declared in app.py
:
blockchain
: list = list of block objectspending_trans
: list = list of (pending) transaction objectspeers
: list of strings = each string is a URL of a peerpotential_peers
: list = each string is a URL of a potential peerpeer_limit
: int = max number of peers allowed for a nodemy_url
: string = my URLunused_output_dict
: dict = dictionary consisting of {(transID,index_of_output):output_object}block_reward
: int = no of coins set as block rewardmax_blockbody_size
: int = maximum size of the block body, in number of bitstarget_value
: string = target value for mining as a hexadecimal stringmining_thread
:threading.Thread
object = worker thread for miningunused_output_pubkey
: dict = {public key:[list of tuples (transID, index)] where each tuple represents an unused output of the public key]}alias_dict
: dict = {'alias':public key}
Sets up a server on Flask with following endpoints-
/getBlock/<n>
- accepts aGET request
and sends back binary data of nth block/getPendingTransactions
- accepts aGET request
and sends back pending transactions dictionary in JSON format1/newPeer
- accepts aPOST request
with data as json file of the form{url:url_string}
and adds the url to list of peers, unless peer_limit has been achieved or URL is already present in list of peers/getPeers
- accepts aGET request
and returns the list of peers in JSON format 2/newBlock
- accepts aPOST request
with block data (in binary format) when a new block is mined; adds the block to the blockchain after verification and processing/newTransaction
- accepts aPOST request
with transaction data in JSON format 3; adds transaction to the list of pending transactions, after verification of transaction/addAlias
- accepts aPOST request
with alias and public key in JSON format4; adds {alias:public key} mapping toalias_dict
/getPublicKey
- accepts aPOST request
with alias in JSON format ({'alias':alias}); sends back public key corresponding to the alias, in JSON format ({'publicKey':public key})/getUnusedOutputs
- accepts aPOST request
with either alias or public key or both ({'alias':alias, 'publicKey: public key}); sends back list of unused outputs, corresponding to the public key, in JSON format.
The following functions have been defined in app.py
-
mining()
- starts mining blocksfind_peers()
process_block(block_object)
- -removes transaction from pending transactions -removes inputs from unused outputs -adds outputs to unused outputs -adds block to local blockchain -sends block to peers on/newBlock
endpointinitialize()
- -callsfinds_peers()
-asks for blockchain from peers and pending transactionsverify_block(block_object)
- -every transaction in the block must be valid (including the coinbase transaction) -hash of parent block must be correct -header of block must be rightly calculated -correctness of nonce i.e. it gives a hash less than the targetverify_txn(transaction_object)
-all inputs exist in list of unused outputs -verifies signatures5 -verifies that number of output coins <= number of input coins
- Response format from
/getPendingTransaction
endpoint
Content-Type header for the HTTP response will be application/json
The format of the data will be:
{
[
{
"inputs": [
{
"transactionID": "<hex representation of the transaction ID of this input>",
"index": <index of this input in the list of outputs of the transaction>,
"signature": "<hex representation of the signature for this input>"
},
{
"transactionID": "<hex representation of the transaction ID of this input>",
"index": <index of this input in the list of outputs of the transaction>,
"signature": "<hex representation of the signature for this input>"
}
],
"outputs": [
{
"amount": <number of coins>,
"recipient": "<public key of recipient>"
},
{
"amount": <number of coins>,
"recipient": "<public key of recipient>"
},
{
"amount": <number of coins>,
"recipient": "<public key of recipient>"
}
]
},
{
"inputs": [
{
"transactionID": "<hex representation of the transaction ID of this input>",
"index": <index of this input in the list of outputs of the transaction>,
"signature": "<hex representation of the signature for this input>"
},
...
],
"outputs": [
{
"amount": <number of coins>,
"recipient": "<public key of recipient>"
},
...
]
},
...
]
}
- Response format from
/newPeer
endpoint is :
{
"peers": ["https://dryblockchain.com", "http://a38dc2.ngrok.io", "https://blockchain.pclub.in:8787", "http://202.23.145.3:5000"]
}
- Format in which data is expected to be received at
/newTransaction
endpoint:
{
"inputs": [
{
"transactionID": "<hex representation of the transaction ID of this input>",
"index": <index of this input in the list of outputs of the transaction>,
"signature": "<hex representation of the signature for this input>"
},
...
],
"outputs": [
{
"amount": <number of coins>,
"recipient": "<public key of recipient>"
},
...
]
}
- Format in which data is expected to be received at
addAlias
endpoint:
{
'alias':alias,
'publicKey':pub_key
}
- The signature verification function assumes that the user signs the following message for each input using
SHA256 hash
andPSS padding
with salt length32
.
[32 bytes for transaction ID of the input][4 bytes for the index of the input][32 bytes for the sha256 hash of the output data]
Here the output data
refers to the following:
[number of outputs][number of coins in output 1][length of the public key for output 1][the public key for output 1][number of coins in output 2][length of the public key for output 2][the public key for output 2]...