├── .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 |
empty page!
32 | {% endblock %} 33 |