Last active
November 9, 2022 06:02
-
-
Save BharatKalluri/5376fbf39cce04ebd1dcd655a699770e to your computer and use it in GitHub Desktop.
An simple implementation of blockchain using python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"from hashlib import sha256\n", | |
"import json" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Hashes, the digital fingerprint" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824\n", | |
"936a185caaa266bb9cbe981e9e05cb78cd732b0b3280eb944412bb6f8f8f07af\n" | |
] | |
} | |
], | |
"source": [ | |
"print(sha256(b\"hello\").hexdigest())\n", | |
"print(sha256(b\"helloworld\").hexdigest())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Let's define an arbitary condition for the hashes, let's say the hash value has to start with 000. This will define what a singned block signifies" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def is_signed_block(hash_value: str):\n", | |
" return hash_value.startswith(\"000\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"now to mine the block, we should find a nonce with the data whose hash will start with 000. As that's when we can declare a block signed." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def mine(block_data: dict):\n", | |
" for possible_nonce in range(0, 10000000000000):\n", | |
" data_to_hash = {**block_data, \"nonce\": possible_nonce}\n", | |
" hash_value = sha256(json.dumps(data_to_hash).encode()).hexdigest()\n", | |
" if is_signed_block(hash_value):\n", | |
" return possible_nonce\n", | |
" raise Exception(\"could not mine block!\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class Block:\n", | |
" data: dict\n", | |
" prev_block_hash: str\n", | |
" nonce: int\n", | |
" curr_block_hash: str\n", | |
"\n", | |
" def __init__(self, data: dict, prev_block_hash: str) -> None:\n", | |
" self.data = data\n", | |
" self.prev_block_hash = prev_block_hash\n", | |
" self.nonce = mine({\n", | |
" \"prev_block_hash\": prev_block_hash,\n", | |
" \"data\": data,\n", | |
" })\n", | |
" self.curr_block_hash = sha256(json.dumps({\n", | |
" \"prev_block_hash\": prev_block_hash,\n", | |
" \"data\": data,\n", | |
" \"nonce\": self.nonce\n", | |
" }).encode()).hexdigest()\n", | |
"\n", | |
" def display(self):\n", | |
" return json.dumps(self.__dict__, indent=4)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"INCEPTION_HASH = \"0000000000000000000000000000000000000000000000000000000000000000\"\n", | |
"INCEPTION_BLOCK: Block = Block(\n", | |
" data={}, \n", | |
" prev_block_hash=INCEPTION_HASH,\n", | |
")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class Blockchain:\n", | |
" block_map: dict[str, Block] = {}\n", | |
" latest_block_hash = None\n", | |
"\n", | |
" def __init__(self) -> None:\n", | |
" self.block_map[INCEPTION_HASH] = INCEPTION_BLOCK\n", | |
" self.latest_block_hash = INCEPTION_HASH\n", | |
"\n", | |
" def add_block(self, block_data: dict):\n", | |
" block = Block(data=block_data, prev_block_hash=self.latest_block_hash)\n", | |
" self.block_map[block.curr_block_hash] = block\n", | |
" self.latest_block_hash = block.curr_block_hash\n", | |
"\n", | |
" def show(self):\n", | |
" pointer = self.latest_block_hash\n", | |
" while True:\n", | |
" pointed_block = self.block_map[pointer]\n", | |
" print(pointed_block.display())\n", | |
" pointer=pointed_block.prev_block_hash\n", | |
" if pointer == INCEPTION_BLOCK.prev_block_hash:\n", | |
" break\n" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"{\n", | |
" \"data\": {\n", | |
" \"userId\": \"immutabledata\"\n", | |
" },\n", | |
" \"prev_block_hash\": \"0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531\",\n", | |
" \"nonce\": 10628,\n", | |
" \"curr_block_hash\": \"0003ed123bbbbd1ae2f0141f9389da14cec9230f81be414c1cca2c8f3f2e4511\"\n", | |
"}\n", | |
"{\n", | |
" \"data\": {\n", | |
" \"userId\": \"bharatkalluri\"\n", | |
" },\n", | |
" \"prev_block_hash\": \"0000000000000000000000000000000000000000000000000000000000000000\",\n", | |
" \"nonce\": 1353,\n", | |
" \"curr_block_hash\": \"0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531\"\n", | |
"}\n" | |
] | |
} | |
], | |
"source": [ | |
"blockchain = Blockchain()\n", | |
"blockchain.add_block({\"userId\": \"bharatkalluri\"})\n", | |
"blockchain.add_block({\"userId\": \"immutabledata\"})\n", | |
"blockchain.show()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"def verify_blockchain(blocks: list[dict]):\n", | |
" # assumes the blocks are LIFO\n", | |
" blocks = blocks[::-1]\n", | |
" for block in blocks:\n", | |
" curr_block_hash = block['curr_block_hash']\n", | |
" block_data = {\n", | |
" 'prev_block_hash': block['prev_block_hash'],\n", | |
" \"data\": block['data'],\n", | |
" 'nonce': block['nonce']\n", | |
" }\n", | |
" is_block_untampered = sha256(json.dumps(block_data).encode()).hexdigest() == curr_block_hash\n", | |
" if is_block_untampered:\n", | |
" print(\"verified block: \", block)\n", | |
" else:\n", | |
" raise Exception(\"data has been tampered with!\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"verified block: {'data': {'userId': 'bharatkalluri'}, 'prev_block_hash': '0000000000000000000000000000000000000000000000000000000000000000', 'nonce': 1353, 'curr_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531'}\n", | |
"verified block: {'data': {'userId': 'immutabledata'}, 'prev_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531', 'nonce': 10628, 'curr_block_hash': '0003ed123bbbbd1ae2f0141f9389da14cec9230f81be414c1cca2c8f3f2e4511'}\n" | |
] | |
} | |
], | |
"source": [ | |
"verify_blockchain(\n", | |
" [\n", | |
" {\n", | |
" 'data': {'userId': 'immutabledata'},\n", | |
" 'prev_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531',\n", | |
" 'nonce': 10628,\n", | |
" 'curr_block_hash': '0003ed123bbbbd1ae2f0141f9389da14cec9230f81be414c1cca2c8f3f2e4511',\n", | |
" },\n", | |
" {\n", | |
" 'data': {'userId': 'bharatkalluri'},\n", | |
" 'prev_block_hash': '0000000000000000000000000000000000000000000000000000000000000000',\n", | |
" 'nonce': 1353,\n", | |
" 'curr_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531',\n", | |
" },\n", | |
" ]\n", | |
")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"But if any data is tampered with, then we'll see an error since hash's will be mismatching" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"ename": "Exception", | |
"evalue": "data has been tampered with!", | |
"output_type": "error", | |
"traceback": [ | |
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", | |
"\u001b[0;31mException\u001b[0m Traceback (most recent call last)", | |
"Cell \u001b[0;32mIn [12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m verify_blockchain(\n\u001b[1;32m 2\u001b[0m [\n\u001b[1;32m 3\u001b[0m {\n\u001b[1;32m 4\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mdata\u001b[39;49m\u001b[39m'\u001b[39;49m: {\u001b[39m'\u001b[39;49m\u001b[39muserId\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39mimmutabledata\u001b[39;49m\u001b[39m'\u001b[39;49m},\n\u001b[1;32m 5\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mprev_block_hash\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39m0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531\u001b[39;49m\u001b[39m'\u001b[39;49m,\n\u001b[1;32m 6\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mnonce\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m10628\u001b[39;49m,\n\u001b[1;32m 7\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mcurr_block_hash\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39m0003ed123bbbbd1ae2f0141f9389da14cec9230f81be414c1cca2c8f3f2e4511\u001b[39;49m\u001b[39m'\u001b[39;49m,\n\u001b[1;32m 8\u001b[0m },\n\u001b[1;32m 9\u001b[0m {\n\u001b[1;32m 10\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mdata\u001b[39;49m\u001b[39m'\u001b[39;49m: {\u001b[39m'\u001b[39;49m\u001b[39muserId\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39mharatkalluri\u001b[39;49m\u001b[39m'\u001b[39;49m},\n\u001b[1;32m 11\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mprev_block_hash\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39m0000000000000000000000000000000000000000000000000000000000000000\u001b[39;49m\u001b[39m'\u001b[39;49m,\n\u001b[1;32m 12\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mnonce\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m1353\u001b[39;49m,\n\u001b[1;32m 13\u001b[0m \u001b[39m'\u001b[39;49m\u001b[39mcurr_block_hash\u001b[39;49m\u001b[39m'\u001b[39;49m: \u001b[39m'\u001b[39;49m\u001b[39m0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531\u001b[39;49m\u001b[39m'\u001b[39;49m,\n\u001b[1;32m 14\u001b[0m },\n\u001b[1;32m 15\u001b[0m ]\n\u001b[1;32m 16\u001b[0m )\n", | |
"Cell \u001b[0;32mIn [10], line 15\u001b[0m, in \u001b[0;36mverify_blockchain\u001b[0;34m(blocks)\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[39mprint\u001b[39m(\u001b[39m\"\u001b[39m\u001b[39mverified block: \u001b[39m\u001b[39m\"\u001b[39m, block)\n\u001b[1;32m 14\u001b[0m \u001b[39melse\u001b[39;00m:\n\u001b[0;32m---> 15\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mException\u001b[39;00m(\u001b[39m\"\u001b[39m\u001b[39mdata has been tampered with!\u001b[39m\u001b[39m\"\u001b[39m)\n", | |
"\u001b[0;31mException\u001b[0m: data has been tampered with!" | |
] | |
} | |
], | |
"source": [ | |
"verify_blockchain(\n", | |
" [\n", | |
" {\n", | |
" 'data': {'userId': 'immutabledata'},\n", | |
" 'prev_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531',\n", | |
" 'nonce': 10628,\n", | |
" 'curr_block_hash': '0003ed123bbbbd1ae2f0141f9389da14cec9230f81be414c1cca2c8f3f2e4511',\n", | |
" },\n", | |
" {\n", | |
" 'data': {'userId': 'haratkalluri'},\n", | |
" 'prev_block_hash': '0000000000000000000000000000000000000000000000000000000000000000',\n", | |
" 'nonce': 1353,\n", | |
" 'curr_block_hash': '0002e24d15509dbf2d533175050b5b7f03a760bec0ae38d84d646853a1fb4531',\n", | |
" },\n", | |
" ]\n", | |
")" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3 (ipykernel)", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.10.6" | |
}, | |
"vscode": { | |
"interpreter": { | |
"hash": "b9c18bec4ecf53db4d10428e8dec8853c714b9da82ad63fbfb33ac9f26e70b94" | |
} | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 4 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment