-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathblockchain_with_inline_explaination.py
200 lines (175 loc) · 13 KB
/
blockchain_with_inline_explaination.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#Building a Blockchain On Python Using Web Application Framework, Flask And Testing It On Postman
'''
What is a Web Application Framework?
A web framework or web application framework is a software framework that is designed to support the
development of web applications including web services, web resources, and web APIs. Web frameworks provide a
standard way to build and deploy web applications.
Reason for using a Web Application Framework for a Blockchain is because we want to build a blockchain that
can be used by anyone online using some servers.
Web Application Framework I ll be using - Flask 0.12.2
What is a Postman?
Postman is a powerful HTTP client for testing web services. Postman makes it easy to test, develop and
document APIs by allowing users to quickly put together both simple and complex HTTP requests.
#To Be Installed -
Flask - pip install Flask==0.12.2
Postman - https://www.getpostman.com/
'''
#Getting Started
#Importing the libraries
import datetime
#Because each block ll have its own timestamp (the exact date and time the block was created or mined)
import hashlib #For hashing the blocks
import json
#For converting Python Object to JSON Strings, Will use the dump function.
#For more in details refer to https://www.w3schools.com/python/python_json.asp
from flask import Flask, jsonify
'''From flask lbrary, importing the flask class because to create an object of flask class which ll be the
web application itself. Jsonify is a function use to return the messages to the Postman when ll interact with
the blockchain'''
#Part I - Building a blockchain - Architecture
class Blockchain:
def __init__(self):
self.chain=[] #Initlization of blockchain With an empty list
self.create_block(proof=1, previous_hash='0')
'''.create_block is a future function that i ll make, which ll create and also append the block to the
blockchain after solving proof of work, Creating the Genesis Block with two arguments, previous_hash
is in string because we ll use SHA256 lib which only uses encoded string'''
def create_block(self,proof,previous_hash):
'''create_block function contains a dictionary with the following keys of the block to be mined,
This function ll also be used to verify the proof of work, Dictionaries associate values with keys so
you can look them up efficiently (by key) later on.'''
block={'index':len(self.chain)+1,
#Index is the new block mined or addded to the blockchain-[(self.chain) is the blockchain itself, (+1) is the new block added now]
'timestamp':str(datetime.datetime.now()),
#datetimestamp is in string format so that we dont encounter any format issues working with the json format.
'proof':proof, #Proof of work & Previous hash argument ll be defined later.
'previous_hash':previous_hash,
}
self.chain.append(block) #Appending/Adding the new block to the blockchain
return block #Returning the block to display the informations i.e keys on the POSTMAN.
def get_previous_block(self): #get_previous_block function ll return or get the last block of the blockchain at anytime.
return self.chain[-1] #Index [-1] refers to the last element of the list self.chain[]
def proof_of_work(self,previous_proof):
'''Proof of work function having cryptographic SHA256 puzzle/problem where the miners need to solve it
to get the block append to the blockchain successfully. It ll have two arguments, one is (self) because
we ll apply the proof of work method from the instance object that will be created from the class and
the other (previous_proof) is another element of the problem that the miners ll encounter to find the
new proof'''
new_proof=1
'''new_proof is a variable that is initialized to 1 because to solve the problem we are going to
increament this new proof variable by one at each iteration of a while loop until we get the right
proof. Basically we are here trying to solve the problem by hit and trial method'''
check_proof=False
'''Check_proof is a variable that ll check the new proof is the right proof or not.
Its the Condition for the while loop. check_proof=false means its not the right proof'''
while check_proof is False:
hash_operation=hashlib.sha256(str(new_proof**2-previous_proof**2).encode()).hexdigest()
'''hash_operation is a variable of a string of 64 characters which uses sha256 function from the
hashlib library for the cryptographic problem defined (Operation) and is encoded in bytestring
format and converted into hexadecimal number.
https://medium.com/@dwernychukjosh/sha256-encryption-with-python-bf216db497f9
Operation can be anything but the only condition is that it cant be symmetrical,
it can only be unsymmetrical because when the operation is symmetrical
i.e. new_proof+previous_proof=previous_proof+new_proof (a+b=b+a condition for symmetricity)
and in symmetricity when we increament the new proof at some point the new proof
ll be a old previous proof.'''
if hash_operation[:4]=='0000':
'''SLicing the hash_operation to check first four nos. are zeros,
More the leading zeros we put more harder to find the golden nonce.'''
check_proof=True
else:
new_proof +=1 #x += y statement is just a shorthand for x = x + y in python. [condition to re run the loop]
return new_proof
def hash(self,block):
#Its a hash function that ll hash each block. Hash function ll be needed by the POW function and previous function that we ll define next.
encoded_block=json.dumps(block,sort_keys=True).encode()
'''encoded_block is a variable which ll convert the block dictionary having the four keys to Json format
i.e. object to a string. We use json dumps instead of direct string method because in Part 2 we ll put
the block dictionary into a json file, therefore the blocks originally ll have a JSON format'''
return hashlib.sha256(encoded_block).hexdigest() #Returning the cryptographic hash of the encoded block.
def is_chain_valid(self,chain):
'''Its a function that ll check each block has correct proof of work i.e proof of the problem that
we defined is correct with the four leading zeros for each block and the previous_hash of the block
is equal to hash of the previous block, One of the arguments is chain because the checks ll be
performed on each blocks of the chain.'''
previous_block=chain[0]
'''previous_block is a variable that is initiazed to chain[0] i.e. the 1st block of the blockchain.
We ll start a while loop because we are going to itterate on all the blocks of the chain.
At the end of the while loop we ll update the value of the previous_block variable to the new block.'''
block_index=1
#block_index is a looping variable representing block number for each block. It has been intialized to the first block.
while block_index<len(chain):
'''block_index<len(chain) indicates the last block of the chain, This condition wont stop the
loop until block index has reached the final index of the chain. At the end of the while loop
ll increament block index by 1 until it reaches the final index given by the length of the chain.'''
#1ST CHECK - previous hash of the current block is the hash of the previous block
block=chain[block_index] #current block that to be checked
if block['previous_hash']!=self.hash(previous_block):
'''This condition is for invalid blocks which means Hash of the current created block
(stored as a key, 'previous_hash' in the dictionary) is not equal to hash of the previous block
(hash function that we defined to hash each block in the chain).
We need to take self because hash is a method of our blockchain class.'''
return False #If the above condition meets we have to return False because the block is invalid.
#2ND CHECK - Proof of each block is valid
previous_proof=previous_block['proof'] #Taking previous_proof of the previous block by the key proof from the dictionary.
proof=block['proof'] #Taking Proof of the current block [block=chain[block_index] is the current block]
hash_operation=hashlib.sha256(str(proof**2-previous_proof**2).encode()).hexdigest()
'''Computing the hash operation of the previous block and proof of the current block and
checking if this hash operation starts with four leading zeros, this ll indicate wether the
proof of the current block is valid or not.'''
if hash_operation[:4]!='0000': #If the hash operation doesnt have the four leading block it ll return False.
return False
previous_block = block
#Updating previous_block so it becomes current block because at the next iiteration of loop, current block ll become the next block of chain.
block_index +=1 #Updating the loop variable.
return True #Returning True if everything goes right.
#Part II - Mining the Blockchain
#Creating a Web App
app = Flask(__name__) #To Create Web App, Follow Flask Quickstart for in depth details.
#Creating a Blockchain
blockchain=Blockchain() #Creating blockchain object for the Blockchain class.
#Mining a new block - 1st GET Request
@app.route('/mine_block',methods=['GET'])
#the route() decorator to tell Flask what URL should trigger our function, Follow Flask Quickstart for details.
#For GET method argument follow 'HTTP Methods' from the quickstart.
def mine_block():
'''mine_block function mines a block by solving proof of work problem by finding the proof based on the
previous proof i.e. the last proof given on the last block. Getting this proof means success in mining
the block. Once the proof is achieved, we ll get the other keys required to create a block
i.e index, timestamp, proof, previous_hash.
In order to to get the proof we apply proof_of_work function and in order to apply
proof_of_work function we need previous proof, to get previous_proof we have get_previous_block function
with applying the proof key of dictionary.'''
previous_block=blockchain.get_previous_block()
previous_proof=previous_block['proof']
proof=blockchain.proof_of_work(previous_proof)
previous_hash=blockchain.hash(previous_block)
'''To add a block to the blockchain we have a function create_block which not only create just but also
append it to the blockchain, to call create_block function we need previous_hash argument,
to get previous_hash argument we uses hash function to the previous hash of the blockchain.'''
block=blockchain.create_block(proof,previous_hash) #Since create_block is a method so we need to call it from blockchain object.
'''Its a variable which ll contain the information of the block and as well as a message. We are going
to return the response in json format, so we are making the response a dictionary so we ll include the keys
of the block we just mined and a new key as congratulation message.'''
response={'message':'Congrats, You just mined a block',
'index':block['index'],
'timestamp':block['timestamp'],
'proof':block['proof'],
'previous_hash':block['previous_hash']}
return jsonify(response), 200
'''We are also returning HTTP Status code for success meaning everything was fine i.e. the block was mined
and successfully added to the chain and there was no error so we are good to display-
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200'''
#Getting the full Blockchain - 2nd GET Request
@app.route('/get_chain',methods=['GET'])
def get_chain(): #get_chain is a function that ll get the whole blockchain so the below responses can be performed or achieved.
response={'chain':blockchain.chain,
#From the blockchain object we ll get the chain attribute that is the chain variable. This ll get the whole blockchain to display.
'length':len(blockchain.chain)
}
'''As new blocks ll be mined and the chain ll be populated with new blocks so to calculate the
length of the chain we ll use the len to get the total number of blocks in the blockchain
i.e. length of the blockchain.'''
return jsonify(response), 200
#Running the app
app.run(host='0.0.0.0', port=5000)