Skip to content

Instantly share code, notes, and snippets.

@BharatKalluri
Last active November 9, 2022 06:02
Show Gist options
  • Save BharatKalluri/5376fbf39cce04ebd1dcd655a699770e to your computer and use it in GitHub Desktop.
Save BharatKalluri/5376fbf39cce04ebd1dcd655a699770e to your computer and use it in GitHub Desktop.
An simple implementation of blockchain using python
Display the source blob
Display the rendered blob
Raw
{
"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