Skip to content

Instantly share code, notes, and snippets.

@melpomene
Last active November 13, 2021 07:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save melpomene/735d7f9a4437ed47a05fd2cdfb00a988 to your computer and use it in GitHub Desktop.
Save melpomene/735d7f9a4437ed47a05fd2cdfb00a988 to your computer and use it in GitHub Desktop.
Toy blockchain implementation demonstrating Proof of work
# This Python file uses the following encoding: utf-8
from copy import deepcopy
from hashlib import sha256
class Blockchain:
def __init__(self, difficulty = 1):
genesis_block = GenesisBlock()
self.chain_tail = genesis_block
self.geneis_block = genesis_block
self.difficulty = difficulty
def is_genesis(self, block):
return self.geneis_block == block
def add_block(self, block):
assert self.chain_tail == block.last_block
assert block.sign == Block.sign(block, self.chain_tail)
self.chain_tail = block
def add_data_as_block(self, data):
return self.add_block(Block(self.chain_tail, data))
def validate(self):
block = self.chain_tail
while True:
assert block.sign == Block.sign(block, block.last_block)
assert self.valid_proof_of_work(block.sign)
block = block.last_block
if self.is_genesis(block):
return True
def valid_proof_of_work(self, sign):
return sign[:self.difficulty] == "a" * self.difficulty
def show(self):
block = self.chain_tail
chain = []
while True:
chain.insert(0, block)
if self.is_genesis(block):
break
block = block.last_block
print "{: >65}\t{: >30}\t{: >30}\t{: >30}".format(*("SIGN", "DATA", "COST", "LAST BLOCK SIGN"))
for block in chain:
print(block)
class Block:
@classmethod
def sign_raw(cls, data, last_sign, proof_of_work):
return sha256(last_sign + data + proof_of_work ).hexdigest()
@classmethod
def sign(cls, block, last_block):
return Block.sign_raw(block.data, last_block.sign, block.proof_of_work)
def __init__(self, last_block, data, proof_of_work):
self.data = data
self.last_block = last_block
self.proof_of_work = proof_of_work
self.sign = Block.sign_raw(data, last_block.sign, proof_of_work)
def __eq__(self, other):
return self.sign == other.sign
def __str__(self):
return "{: >20}\t{: >30}\t{: >30}\t{: >30}".format(*(self.sign, self.data, self.proof_of_work, self.last_block.sign))
class GenesisBlock(Block):
def __init__(self):
self.sign = "I AM GENESIS"
self.data = "I AM GENESIS"
def __str__(self):
return "I\tAM\tGENESIS"
class Node:
def mine_block(self, blockchain, last_block, data):
cost = 0
while True:
cost += 1
candidate = Block.sign_raw(data, last_block.sign, str(cost))
if blockchain.valid_proof_of_work(candidate):
candidate_block = Block(last_block, data, str(cost))
blockchain.add_block(candidate_block)
return candidate_block
class EvilNode(Node):
def fake_blockchain(self, blockchain, steps_back, evil_data):
corrupted_chain = deepcopy(blockchain)
tamper_block = self.get_block(blockchain.chain_tail, steps_back)
remaining_data = self.get_data(blockchain.chain_tail, steps_back-1)
corrupted_chain.chain_tail = tamper_block
evil_block = self.mine_block(corrupted_chain, tamper_block, evil_data)
fake_chain = []
fake_block = evil_block
total_cost = evil_block.proof_of_work
for data in remaining_data:
fake_block = self.mine_block(corrupted_chain, fake_block, data)
total_cost += fake_block.proof_of_work
corrupted_chain = deepcopy(blockchain)
corrupted_chain.chain_tail = fake_block
return corrupted_chain, total_cost
def get_block(self, block, steps_back):
if steps_back == 0:
return block
else:
return self.get_block(block.last_block, steps_back -1)
def get_data(self, block, steps_back):
if steps_back == 0:
return [block.data]
else:
return self.get_data(block.last_block, steps_back -1) + [block.data]
def main():
print "Creating blockchain\n\n"
blockchain = Blockchain(3)
node = Node()
text = [
"Det blir godt å sove",
"Sove hele natten",
"Helt til neste dag",
"Det blir godt å sove",
"Sove hele natten",
"Helt til neste dag"
]
for data in text:
node.mine_block(blockchain, blockchain.chain_tail, data)
print "Valid Blockchain" if blockchain.validate() else "Invalid blockchain"
print "\n\n\n"
blockchain.show()
print "\n\n\n"
print "Evil Node enters the game"
print "\n\n\n"
evil_node = EvilNode()
corrupted_chain, total_cost = evil_node.fake_blockchain(blockchain, 3, "C̷̪͈͈͉̘͓̲̱͚̗̀̄͂͑͆̅̒̋̉̉͊̕͝o̷̲͔̊̈́̊̓͆͑̈́͊͑̑̈́̈̽̓̕r̸̞̊̇ȑ̶̨͉́̀̃̓̌̀̉̈́̑̈́͗͘ṵ̶̧̲̿̃̓̃p̶̡͕̠̺̭̼͙̜̄͋t̴̛̙̺͎̟̘̳̃̃͐̋͑̾͑͐͋͝e̶̻̳͗́͋̔̍̈͋́̚͠͠d̸̛͕͕͚͇̰̞̈́̎̂")
print "Valid Blockchain" if corrupted_chain.validate() else "Invalid blockchain"
corrupted_chain.show()
print "\n\n\n"
print "It cost evil node " + str(total_cost) + " hashes be evil"
if __name__ == '__main__':
main()
@melpomene
Copy link
Author

% python toyblockchain.py
Creating blockchain


Valid Blockchain




                                                             SIGN	                          DATA	                          COST	               LAST BLOCK SIGN
I	AM	GENESIS
aaa7d5ea7828f2338ad924c170ca1e2c2d8f422ce91224e7ed7221599b4613eb	       Det blir godt å sove	                          4036	                  I AM GENESIS
aaaf5b903ed733533985e573dd9aac59359579e2fe1aae39f876c34dbe93efdb	              Sove hele natten	                           359	aaa7d5ea7828f2338ad924c170ca1e2c2d8f422ce91224e7ed7221599b4613eb
aaaa680ed741fd0bc5c44be4dadd8ec4d90b44820175fb172f09aaa55b197997	          Helt til neste dag	                          7635	aaaf5b903ed733533985e573dd9aac59359579e2fe1aae39f876c34dbe93efdb
aaacb9c4a3820c6c30135af15a8e458c034496bbbcf8bcfadd5efdf1b6b6a0fc	       Det blir godt å sove	                          1148	aaaa680ed741fd0bc5c44be4dadd8ec4d90b44820175fb172f09aaa55b197997
aaa9827ad3081273bcc68ccce26f184d758e2276cbf0c1fd2309fd5242f9f81d	              Sove hele natten	                          1487	aaacb9c4a3820c6c30135af15a8e458c034496bbbcf8bcfadd5efdf1b6b6a0fc
aaae12da9cb6d85166ba9dbe71a997ec5dd21fdefad7694bdeda9cbe7efb1a8b	          Helt til neste dag	                          2455	aaa9827ad3081273bcc68ccce26f184d758e2276cbf0c1fd2309fd5242f9f81d




Evil Node enters the game




Valid Blockchain
                                                             SIGN	                          DATA	                          COST	               LAST BLOCK SIGN
I	AM	GENESIS
aaa7d5ea7828f2338ad924c170ca1e2c2d8f422ce91224e7ed7221599b4613eb	       Det blir godt å sove	                          4036	                  I AM GENESIS
aaaf5b903ed733533985e573dd9aac59359579e2fe1aae39f876c34dbe93efdb	              Sove hele natten	                           359	aaa7d5ea7828f2338ad924c170ca1e2c2d8f422ce91224e7ed7221599b4613eb
aaaa680ed741fd0bc5c44be4dadd8ec4d90b44820175fb172f09aaa55b197997	          Helt til neste dag	                          7635	aaaf5b903ed733533985e573dd9aac59359579e2fe1aae39f876c34dbe93efdb
aaa6668499dd13d76dd376a55df65925ececa8cca4264664273c263b29f66b78	C̷̪͈͈͉̘͓̲̱͚̗̀̄͂͑͆̅̒̋̉̉͊̕͝o̷̲͔̊̈́̊̓͆͑̈́͊͑̑̈́̈̽̓̕r̸̞̊̇ȑ̶̨͉́̀̃̓̌̀̉̈́̑̈́͗͘ṵ̶̧̲̿̃̓̃p̶̡͕̠̺̭̼͙̜̄͋t̴̛̙̺͎̟̘̳̃̃͐̋͑̾͑͐͋͝e̶̻̳͗́͋̔̍̈͋́̚͠͠d̸̛͕͕͚͇̰̞̈́̎̂	                          1995	aaaa680ed741fd0bc5c44be4dadd8ec4d90b44820175fb172f09aaa55b197997
aaa5f8d66ccb6589b28761ae123a6fecc58eafa8f9be0df4e5ee003209c45641	       Det blir godt å sove	                          5140	aaa6668499dd13d76dd376a55df65925ececa8cca4264664273c263b29f66b78
aaafd36d9c06fe6dd7a421c35e58e3db735d9b7f5a7874b50efa2f6821f91749	              Sove hele natten	                          7288	aaa5f8d66ccb6589b28761ae123a6fecc58eafa8f9be0df4e5ee003209c45641
aaa04a7a08ac5e2b7d450d2a02ba1dd8e046c9883f8af53e9c782bc5d30ecb87	          Helt til neste dag	                          2950	aaafd36d9c06fe6dd7a421c35e58e3db735d9b7f5a7874b50efa2f6821f91749




It cost evil node 1995514072882950to be evil

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment