├── .gitignore ├── README.md ├── blockchain.py ├── blocks └── 1.json ├── requirements.txt ├── server.py └── templates ├── base.html └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | 4 | __pycache__ 5 | 6 | env 7 | venv 8 | .venv 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blockchain 2 | Blockchain simulation with a web interface on Flask. 3 | 4 | ## How it works 5 | ### Main view 6 | 7 | 8 | ### Checking the integrity of 4 newly created blocks 9 | 10 | 11 | ### All blocks are mined 12 | 13 | 14 | ### The last block is manually corrupted in `./blocks` and new block is added 15 | 16 | 17 | ### What is blockchain? 18 | 19 | **Blockchain** – is a continuously growing list of records, called blocks, which are linked and secured using cryptography hash. Each block typically contains a hash pointer as a link to a previous block and a timestamp. By design, blockchains are inherently resistant to modification of the data. For getting hash used _SHA-256_ 20 | 21 | ### What is POW 22 | 23 | **POW** _(proof of work)_ is a piece of data which is difficult (costly, time-consuming) to produce but easy for others to verify and which satisfies certain requirements. Producing a proof of work can be a random process with low probability so that a lot of trial and error is required on average before a valid proof of work is generated. Bitcoin uses the Hashcash proof of work system. 24 | 25 | ## How to run 26 | 27 | ``` 28 | pip install -r requirements.txt 29 | 30 | python server.py 31 | ``` 32 | 33 | Now head over to http://127.0.0.1:5000/, and you should see main page of blockchain, where you can add new block, check blocks integrity and mined blocks using POW algorithm. 34 | 35 | 36 | -------------------------------------------------------------------------------- /blockchain.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import os 4 | from time import time 5 | 6 | BLOCKCHAIN_DIR = os.curdir + "/blocks/" 7 | 8 | 9 | def check_blocks_integrity(): 10 | result = [] 11 | cur_proof = -1 12 | for i in range(2, int(get_next_block())): 13 | prev_index = str(i - 1) 14 | cur_index = str(i) 15 | tmp = {"block": "", "result": "", "proof": ""} 16 | try: 17 | file_dict = json.load(open(f"{BLOCKCHAIN_DIR}{cur_index}.json")) 18 | cur_hash = file_dict["prev_hash"] 19 | cur_proof = file_dict["proof"] 20 | except Exception as e: 21 | print(e) 22 | 23 | try: 24 | prev_hash = hashlib.sha256( 25 | open(BLOCKCHAIN_DIR + prev_index + ".json", "rb").read() 26 | ).hexdigest() 27 | except Exception as e: 28 | print(e) 29 | 30 | tmp["block"] = prev_index 31 | tmp["proof"] = cur_proof 32 | if cur_hash == prev_hash: 33 | tmp["result"] = "ok" 34 | else: 35 | tmp["result"] = "error" 36 | result.append(tmp) 37 | return result 38 | 39 | 40 | def check_block(index): 41 | cur_index = str(index) 42 | prev_index = str(int(index) - 1) 43 | cur_proof = -1 44 | cur_hash = 0 45 | prev_hash = 0 46 | tmp = {"block": "", "result": "", "proof": ""} 47 | try: 48 | file_dict = json.load(open(BLOCKCHAIN_DIR + cur_index + ".json")) 49 | cur_hash = file_dict["prev_hash"] 50 | cur_proof = file_dict["proof"] 51 | except Exception as e: 52 | print(e) 53 | try: 54 | prev_hash = hashlib.sha256( 55 | open(BLOCKCHAIN_DIR + prev_index + ".json", "rb").read() 56 | ).hexdigest() 57 | except Exception as e: 58 | print(e) 59 | tmp["block"] = prev_index 60 | tmp["proof"] = cur_proof 61 | if cur_hash == prev_hash: 62 | tmp["result"] = "ok" 63 | else: 64 | tmp["result"] = "error" 65 | return tmp 66 | 67 | 68 | def get_hash(file_name): 69 | file_name = str(file_name) 70 | if not file_name.endswith(".json"): 71 | file_name += ".json" 72 | try: 73 | with open(BLOCKCHAIN_DIR + file_name, "rb") as file: 74 | return hashlib.sha256(file.read()).hexdigest() 75 | except Exception: 76 | print(f"File {file_name} is not found") 77 | raise 78 | 79 | 80 | def get_next_block(): 81 | files = os.listdir(BLOCKCHAIN_DIR) 82 | index_list = [int(file.split(".")[0]) for file in files] 83 | cur_index = sorted(index_list)[-1] 84 | next_index = cur_index + 1 85 | return str(next_index) 86 | 87 | 88 | def is_valid_proof(last_proof, proof, difficulty): 89 | guess = f"{last_proof}{proof}".encode() 90 | guess_hash = hashlib.sha256(guess).hexdigest() 91 | return guess_hash[:difficulty] == "0" * difficulty 92 | 93 | 94 | def get_POW(file_name, difficulty=1): 95 | # POW - proof of work 96 | file_name = str(file_name) 97 | if file_name.endswith(".json"): 98 | file_name = int(file_name.split(".")[0]) 99 | else: 100 | file_name = int(file_name) 101 | 102 | last_proof = json.load(open(BLOCKCHAIN_DIR + str(file_name - 1) + ".json"))["proof"] 103 | proof = 0 104 | while is_valid_proof(last_proof, proof, difficulty) is False: 105 | proof += 1 106 | cur_block = json.load(open(BLOCKCHAIN_DIR + str(file_name) + ".json")) 107 | cur_block["proof"] = proof 108 | cur_block["prev_hash"] = get_hash(str(file_name - 1)) 109 | with open(BLOCKCHAIN_DIR + str(file_name) + ".json", "w") as file: 110 | json.dump(cur_block, file, indent=4, ensure_ascii=False) 111 | 112 | 113 | def write_block(text, make_proof=False): 114 | cur_index = get_next_block() 115 | prev_index = str(int(cur_index) - 1) 116 | prev_block_hash = get_hash(prev_index) 117 | data = { 118 | "text": text, 119 | "prev_hash": prev_block_hash, 120 | "timestamp": time(), 121 | "proof": -1, 122 | "index": cur_index, 123 | } 124 | 125 | with open(f"{BLOCKCHAIN_DIR}{cur_index}.json", "w") as file: 126 | json.dump(data, file, indent=4, ensure_ascii=False) 127 | if make_proof: 128 | get_POW(str(cur_index)) 129 | 130 | 131 | if __name__ == "__main__": 132 | for data in range(10): 133 | write_block(str(data), True) 134 | for data in range(2, 10): 135 | print(check_block(str(data))) 136 | print(check_blocks_integrity()) 137 | -------------------------------------------------------------------------------- /blocks/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "text" : "Genesis block", 3 | "hash": "", 4 | "timestamp": 0, 5 | "proof": -1, 6 | "index": "1" 7 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | blinker==1.6.2 2 | click==8.1.3 3 | Flask==2.3.1 4 | itsdangerous==2.1.2 5 | Jinja2==3.1.2 6 | MarkupSafe==2.1.2 7 | Werkzeug==2.3.2 8 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import render_template, redirect, url_for 3 | from flask import request 4 | import blockchain 5 | 6 | app = Flask(__name__) 7 | 8 | 9 | @app.route("/", methods=["GET", "POST"]) 10 | def index(): 11 | if request.method == "POST": 12 | text = request.form["text"] 13 | if len(text) < 1: 14 | return redirect(url_for("index")) 15 | 16 | make_proof = request.form.get("make_proof", False) 17 | blockchain.write_block(text, make_proof) 18 | return redirect(url_for("index")) 19 | return render_template("index.html") 20 | 21 | 22 | @app.route("/check", methods=["POST"]) 23 | def integrity(): 24 | results = blockchain.check_blocks_integrity() 25 | if request.method == "POST": 26 | return render_template("index.html", results=results) 27 | return render_template("index.html") 28 | 29 | 30 | @app.route("/mining", methods=["POST"]) 31 | def mining(): 32 | if request.method == "POST": 33 | max_index = int(blockchain.get_next_block()) 34 | 35 | for i in range(2, max_index): 36 | blockchain.get_POW(i) 37 | return render_template("index.html", querry=max_index) 38 | return render_template("index.html") 39 | 40 | 41 | if __name__ == "__main__": 42 | app.run(debug=True) 43 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Blockchain 11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 |
23 |
24 |
25 |

Add new block to blockhain: Check it in ./blocks

26 |
27 |
28 |
29 |
30 | {% block content %} 31 |

empty page!

32 | {% endblock %} 33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |

Check blocks integrity: If data in ./blocks is manualy changed...

41 |
42 |
43 |
44 |
45 | {% block check %}{% endblock %} 46 |
47 |
48 |
49 | 50 | 51 |
52 |
53 |
54 |

Mine Make POW

55 |
56 |
57 |
58 |
59 | {% block mining %}{% endblock %} 60 |
61 |
62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} {% block content %} 2 |
3 |
4 | 5 | 7 |
8 | 9 |
10 | 14 |
15 |
16 | 17 |
18 |
19 |
{% endblock %} {% block check %} 20 |
21 | 22 |
23 |
24 | 35 |
{% endblock %} 36 |
{% block mining %} 37 |
38 | 39 |
40 | {% if querry %} 41 |
42 | 45 |

{% endif %} 46 | 47 | 48 |


{% endblock %} --------------------------------------------------------------------------------