-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple_chain.py
220 lines (176 loc) · 5.72 KB
/
simple_chain.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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# -*- coding: utf-8 -*-
import sys
import datetime
import hashlib
import time
from json import dumps
class Message:
def __init__(self, data):
self.hash = None
self.prev_hash = None
self.timestamp = time.time()
self.size = len(data.encode('utf-8')) # length in bytes
self.data = data
self.payload_hash = self._hash_payload()
def _hash_payload(self):
return hashlib.sha256(bytearray(str(self.timestamp) + str(self.data), "utf-8")).hexdigest()
def _hash_message(self):
return hashlib.sha256(bytearray(str(self.prev_hash) + self.payload_hash, "utf-8")).hexdigest()
def link(self, message):
""" Link the message to the previous one via hashes."""
self.prev_hash = message.hash
def seal(self):
""" Get the message hash. """
self.hash = self._hash_message()
def validate(self):
""" Check whether the message is valid or not. """
if self.payload_hash != self._hash_payload():
raise InvalidMessage("Invalid payload hash in message: " + str(self))
if self.hash != self._hash_message():
raise InvalidMessage("Invalid message hash in message: " + str(self))
def __repr__(self):
return 'Message<hash: {}, prev_hash: {}, data: {}>'.format(
self.hash, self.prev_hash, self.data[:20]
)
class Block:
def __init__(self, *args):
self.messages = []
self.timestamp = None
self.prev_hash = None
self.hash = None
if args:
for arg in args:
self.add_message(arg)
def _hash_block(self):
return hashlib.sha256(bytearray(str(self.prev_hash) + str(self.timestamp) + self.messages[-1].hash, "utf-8")).hexdigest()
def add_message(self, message):
if len(self.messages) > 0:
message.link(self.messages[-1])
message.seal()
message.validate()
self.messages.append(message)
def link(self, block):
""" The block hash only incorporates the head message hash
which then transitively includes all prior hashes.
"""
self.prev_hash = block.hash
def seal(self):
self.timestamp = time.time()
self.hash = self._hash_block()
def validate(self):
""" Validates each message hash, then chain integrity, then the block hash.
Calls each message's validate() method.
If a message fails validation, this method captures the exception and
throws InvalidBlock since an invalid message invalidates the whole block.
"""
for i, msg in enumerate(self.messages):
try:
msg.validate()
if i > 0 and msg.prev_hash != self.messages[i-1].hash:
raise InvalidBlock("Invalid block: Message #{} has invalid message link in block: {}".format(i, str(self)))
except InvalidMessage as ex:
raise InvalidBlock("Invalid block: Message #{} failed validation: {}. In block: {}".format(
i, str(ex), str(self))
)
def __repr__(self):
dicts = {}
keys = ["hash","previoushash","messages","time"]
values = [str(self.hash), self.prev_hash, (self.messages), str(self.timestamp)]
j=0;
for i in keys:
dicts[i]=values[j]
j=j+1
print(dicts)
# jsonstring=dumps(dicts)
# print(jsonstring)
file = open("static/blockchain.json", "a")
file.write(str(dicts).replace("'",'"'))
file.close()
return 'Block<hash: {}, prev_hash: {}, messages: {}, time: {}>'.format(
self.hash, self.prev_hash, len(self.messages), self.timestamp
)
class SimpleChain:
def __init__(self):
self.chain = []
def add_block(self, block):
""" Add a block if valid."""
if len(self.chain) > 0:
block.prev_hash = self.chain[-1].hash
block.seal()
block.validate()
self.chain.append(block)
def validate(self):
""" Validates each block, in order.
An invalid block invalidates the chain.
"""
for i, block in enumerate(self.chain):
try:
block.validate()
except InvalidBlock as exc:
raise InvalidBlockchain("Invalid blockchain at block number {} caused by: {}".format(i, str(exc)))
return True
def __repr__(self):
return 'SimpleChain<blocks: {}>'.format(len(self.chain))
class InvalidMessage(Exception):
def __init__(self,*args,**kwargs):
Exception.__init__(self,*args,**kwargs)
class InvalidBlock(Exception):
def __init__(self,*args,**kwargs):
Exception.__init__(self,*args,**kwargs)
class InvalidBlockchain(Exception):
def __init__(self,*args,**kwargs):
Exception.__init__(self,*args,**kwargs)
def manager():
chain = SimpleChain()
block = Block()
msg = """
Basic implementation of a Blockchain. Changes are inmutable. Be aware.
Action set:
- add message to the existing block (1)
- add existing block to the chain (2)
- show a block (index will be asked) (3)
- show the whole chain (4)
- validate the chain integrity (5)
- exit the program (6)
The validate action will kill the program if the integrity if the chain
is compromised.
"""
print(msg)
while True:
print()
decide = input("Your action: ")
if decide == "1":
block.add_message(Message(input("Enter your data:")))
elif decide == "2":
if len(block.messages) > 0:
chain.add_block(block)
block = Block()
else: print("Block is empty, try adding some messages")
elif decide == "3":
index = int(input("Provide the index: "))
if len(chain.chain)>0:
try: print(chain.chain[index])
except: print("An issue occurred")
elif decide == "4":
for b in chain.chain:
print(b)
print("----------------")
elif decide == "5":
if chain.validate(): print("Integrity validated.")
else:
break
############################################################################3
def doit(mess):
chain = SimpleChain()
block = Block()
block.add_message(Message(mess))
if len(block.messages) > 0:
chain.add_block(block)
block = Block()
else: print("Block is empty, try adding some messages")
# index =0
# if len(chain.chain)>0:
# try: print(chain.chain[index])
# except: print("An issue occurred")
if __name__ == "__main__":
manager()