Skip to content

Commit aae11d2

Browse files
committed
init
0 parents  commit aae11d2

26 files changed

+724
-0
lines changed

Diff for: __pycache__/_util.cpython-37.pyc

447 Bytes
Binary file not shown.

Diff for: __pycache__/bcclient.cpython-37.pyc

1.7 KB
Binary file not shown.

Diff for: __pycache__/bcpeer.cpython-37.pyc

2.1 KB
Binary file not shown.

Diff for: __pycache__/client.cpython-37.pyc

1.7 KB
Binary file not shown.

Diff for: _util.py

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#-*- coding: utf-8 -*-
2+
3+
def get_list_from_file(filename):
4+
f = open(filename, "r")
5+
obtained_list = []
6+
while True:
7+
line = f.readline()
8+
line = line.replace('\n', '')
9+
if not line: break
10+
obtained_list.append(line)
11+
f.close()
12+
return obtained_list

Diff for: bc_layer/__pycache__/_params.cpython-37.pyc

498 Bytes
Binary file not shown.

Diff for: bc_layer/__pycache__/_util.cpython-37.pyc

1.33 KB
Binary file not shown.

Diff for: bc_layer/__pycache__/block.cpython-37.pyc

1.4 KB
Binary file not shown.

Diff for: bc_layer/__pycache__/blockchain.cpython-37.pyc

7.26 KB
Binary file not shown.

Diff for: bc_layer/__pycache__/ecc.cpython-37.pyc

2.46 KB
Binary file not shown.

Diff for: bc_layer/_params.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
""" ECC parameters """
2+
ecc_curves = [
3+
"secp256r1",
4+
"secp384r1",
5+
"secp521r1",
6+
"Brainpool-p256r1",
7+
"Brainpool-p384r1",
8+
"Brainpool-p512r1"
9+
] # a kind of ecc curves
10+
ecc_curve_default = ecc_curves[0] # first curve is default curve
11+
12+
""" blockchain parameters """
13+
max_block_size = 65536 # 64 KB = 65,536 bytes
14+
max_tx_in_block = 1 # For one block, maybe 120~130 tx can be inserted
15+
genesis_block_seed = "@" # Seed to generate the prev_hash in the genesis block (promised)
16+
fault_factor = 2/3

Diff for: bc_layer/_util.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#-*- coding: utf-8 -*-
2+
import pickle
3+
import hashlib
4+
import time
5+
6+
def gen_timestamp():
7+
""" generate a timestamp() """
8+
return time.time()
9+
10+
def dict_to_bin(dictionary):
11+
""" dictionary to binary """
12+
return pickle.dumps(dictionary)
13+
14+
def bin_to_dict(binary):
15+
""" binary to dictionary """
16+
return pickle.loads(binary)
17+
18+
def bin_to_str(binary):
19+
""" binary to string """
20+
return binary.decode()
21+
22+
def str_to_bin(string):
23+
""" string to binary """
24+
return string.encode()
25+
26+
def size_of_dict(dictionary):
27+
""" get the size of dictionary in bytes? """
28+
return len(dict_to_bin(dictionary))
29+
30+
def hash256(data):
31+
""" calculate a hashed data by hashlib """
32+
t = type(data)
33+
if t is dict:
34+
return hashlib.sha256(dict_to_bin(data)).hexdigest()
35+
elif t is str:
36+
return hashlib.sha256(str_to_bin(data)).hexdigest()
37+
38+

Diff for: bc_layer/block.py

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from . import _util
2+
3+
class Block():
4+
def __init__(self, prev_hash, payload=[], gen_time=_util.gen_timestamp()):
5+
self.prev_hash = prev_hash
6+
self.payload = payload # list of dictionary? double dictionary?
7+
self.merkle_root = self.__make_merkle_root(self.payload)
8+
self.gen_time = gen_time
9+
10+
def to_dict(self):
11+
new_block = {
12+
'prev_hash': self.prev_hash,
13+
'merkle_root': self.merkle_root,
14+
'gen_time': self.gen_time,
15+
'payload': self.payload
16+
}
17+
return new_block
18+
19+
def __make_merkle_root(self, payload):
20+
if payload == []:
21+
return _util.hash256("")
22+
23+
# hashing all transactions in block
24+
to_merkle_tree = []
25+
for tx in payload:
26+
to_merkle_tree.append(_util.hash256(tx))
27+
28+
while True:
29+
new_to_merkle_tree = []
30+
while len(to_merkle_tree) != 0 :
31+
if len(to_merkle_tree) == 1:
32+
block1 = to_merkle_tree.pop()
33+
block2 = block1
34+
else :
35+
block1 = to_merkle_tree.pop()
36+
block2 = to_merkle_tree.pop()
37+
hashed_block = _util.hash256(block1 + block2)
38+
new_to_merkle_tree.append(hashed_block)
39+
if len(new_to_merkle_tree) == 1 :
40+
merkle_root = new_to_merkle_tree.pop()
41+
return merkle_root
42+
to_merkle_tree = new_to_merkle_tree
43+
44+
def check_merkle_root(self):
45+
return self.__make_merkle_root() == self.merkle_root

Diff for: bc_layer/blockchain.py

+263
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
#-*- coding: utf-8 -*-
2+
from . import _util
3+
from . import _params
4+
from . import ecc
5+
from . import block
6+
7+
class Blockchain():
8+
def __init__(self, id, ecc_curve=_params.ecc_curve_default, miner_list=[]):
9+
self.id = id
10+
self.debug = 1
11+
self.miner_list = miner_list
12+
self.ecc = ecc.Ecc(ecc_curve)
13+
14+
# storage settings
15+
self.txpool = [] # list of request msg
16+
self.ledger = [
17+
block.Block(prev_hash=_util.hash256(_params.genesis_block_seed), gen_time=0).to_dict()
18+
]
19+
20+
# consensus settings
21+
self.view = 0
22+
self.status = None
23+
self.leader = None
24+
25+
# Processing
26+
self.request_msg = None
27+
self.requester_id = None
28+
self.preprepare_msg = None
29+
self.prepare_msgs = []
30+
self.commit_msgs = []
31+
self.reply_msgs = [] # for the client
32+
33+
# handlers
34+
self.handler = {
35+
"REQU": self.handler_request,
36+
"PPRE": self.handler_preprepare,
37+
"PREP": self.handler_prepare,
38+
"COMM": self.handler_commit,
39+
"REPL": self.handler_reply
40+
}
41+
42+
def print_debug(self, msg):
43+
if self.debug:
44+
print("[%s] (v:%d,s:%s) %s" % (self.id, self.view, self.status, msg))
45+
46+
def isleader_todo(self):
47+
# view % num of miners = leader
48+
if self.leader == None:
49+
leader_index = self.view % len(self.miner_list)
50+
self.leader = self.miner_list[leader_index]
51+
return (self.id == self.leader) and (self.status == None) and (len(self.txpool) > 0)
52+
53+
def round_finish(self):
54+
accepted_block = self.preprepare_msg["m"]
55+
self.ledger.append(accepted_block)
56+
accepted_txs = accepted_block["payload"]
57+
for tx in accepted_txs:
58+
if tx in self.txpool:
59+
self.txpool.remove(tx)
60+
else:
61+
self.print_debug("Error: no same transactions")
62+
self.view += 1
63+
64+
self.request_msg = None
65+
self.requester_id = None
66+
self.preprepare_msg = None
67+
self.prepare_msgs = []
68+
self.commit_msgs = []
69+
self.reply_msgs = []
70+
71+
self.status = None
72+
self.leader = None
73+
74+
self.print_debug("Round is finished, Status: " + str(self.status) + ", Now View: " + str(self.view))
75+
76+
def get_last_block_prev_hash(self):
77+
# prev_hash
78+
last_block = self.ledger[len(self.ledger)-1]
79+
del last_block["payload"]
80+
prev_hash = _util.hash256(last_block) # without payload = header
81+
return prev_hash
82+
83+
def get_txpool_one(self):
84+
return [self.txpool[0]]
85+
86+
def get_txpool_max_block_size(self, prev_hash):
87+
txs = []
88+
new_block = block.Block(prev_hash, txs)
89+
current_size = _util.size_of_dict(new_block.to_dict())
90+
residual_size = _params.max_block_size - current_size
91+
92+
max_count_tx = 0
93+
for tx in self.txpool:
94+
tx_size = _util.size_of_dict(tx)
95+
if residual_size - tx_size >= 0:
96+
residual_size = residual_size - tx_size
97+
max_count_tx = max_count_tx + 1
98+
else:
99+
break
100+
101+
txs = []
102+
for _ in range(max_count_tx):
103+
txs.append(self.txpool.pop())
104+
return txs
105+
106+
def gen_request_msg(self):
107+
""" for a client to broadcast the request msg(transaction) """
108+
msg = {}
109+
msg["client"] = self.id
110+
msg["m"] = "EXAMPLE TRANSACTION"
111+
msg["t"] = _util.gen_timestamp()
112+
113+
self.status = "REQUEST"
114+
return msg
115+
116+
def gen_preprepare_msg(self):
117+
""" Only leader generates a (PPRE) msg in a View """
118+
""" The status of the leader will go through (PREP) """
119+
msg = {}
120+
msg["status"] = "PREPREPARE"
121+
msg["v"] = self.view
122+
msg["n"] = 1
123+
124+
prev_hash = self.get_last_block_prev_hash()
125+
txs = self.get_txpool_one() # self.get_txpool_max_block_size(prev_hash)
126+
new_block = block.Block(prev_hash, txs) # error
127+
msg["m"] = new_block.to_dict()
128+
129+
self.status = "PREPARE"
130+
self.preprepare_msg = msg # for leader, preprepare_msg is prepared
131+
return msg
132+
133+
def gen_prepare_msg(self):
134+
msg = {}
135+
msg["status"] = "PREPARE"
136+
msg["v"] = self.view
137+
msg["d(m)"] = _util.hash256(self.preprepare_msg)
138+
msg["n"] = self.preprepare_msg["n"]
139+
msg["i"] = self.id
140+
141+
self.status = "PREPARE"
142+
self.prepare_msgs.append(msg) # my msg
143+
return msg
144+
145+
def gen_commit_msg(self):
146+
msg = {}
147+
msg["status"] = "COMMIT"
148+
msg["v"] = self.view
149+
msg["d(m)"] = _util.hash256(self.preprepare_msg)
150+
msg["n"] = self.preprepare_msg["n"]
151+
msg["i"] = self.id
152+
153+
self.status = "COMMIT"
154+
self.commit_msgs.append(msg) # my msg
155+
return msg
156+
157+
def gen_reply_msg(self):
158+
msg = {}
159+
msg["status"] = "REPLY"
160+
msg["v"] = self.view
161+
msg["n"] = self.preprepare_msg["n"]
162+
msg["i"] = self.id
163+
164+
self.status = "REPLY"
165+
requester = self.requester_id
166+
return (requester, msg)
167+
168+
def handler_request(self, src_id, msgdata):
169+
self.requester_id = msgdata["client"]
170+
self.request_msg = msgdata
171+
self.txpool.append(msgdata)
172+
self.print_debug("Received (REQU) from " + str(self.requester_id))
173+
return True
174+
175+
def handler_preprepare(self, src_id, msgdata):
176+
self.print_debug("Received (PPRE) from " + str(self.leader))
177+
def verify_txs(txs):
178+
return True
179+
180+
def verify_preprepare(self, msgdata):
181+
# msg checking
182+
if msgdata["status"] != "PREPREPARE":
183+
return False
184+
elif msgdata["v"] != self.view:
185+
return False
186+
187+
# block checking
188+
last_block = self.ledger[len(self.ledger)-1]
189+
del last_block["payload"]
190+
prev_hash = _util.hash256(last_block) # without payload = header
191+
192+
if msgdata["m"]["prev_hash"] != prev_hash:
193+
return False
194+
elif msgdata["m"]["gen_time"] >= _util.gen_timestamp():
195+
return False
196+
elif not verify_txs(msgdata["m"]["payload"]):
197+
return False
198+
199+
return True
200+
201+
if verify_preprepare(self, msgdata):
202+
self.preprepare_msg = msgdata
203+
self.requester_id = msgdata["m"]["payload"][0]["client"] # 1 transaction is in it
204+
return "PREP"
205+
else:
206+
return False
207+
208+
def handler_prepare(self, src_id, msgdata):
209+
self.print_debug("Received (PREP) from " + str(msgdata["i"]))
210+
def verify_prepare(self, msgdata):
211+
if msgdata["status"] != "PREPARE":
212+
return False
213+
elif msgdata["v"] != self.view:
214+
return False
215+
elif msgdata["d(m)"] != _util.hash256(self.preprepare_msg):
216+
return False
217+
return True
218+
219+
if verify_prepare(self, msgdata) and (self.status == "PREPARE"):
220+
self.prepare_msgs.append(msgdata)
221+
if len(self.prepare_msgs) > int((_params.fault_factor * (len(self.miner_list)-1))) and (self.status == "PREPARE"):
222+
self.status = "PREPARED"
223+
return "COMM"
224+
else:
225+
return False
226+
else:
227+
return False
228+
229+
def handler_commit(self, src_id, msgdata):
230+
self.print_debug("Received (COMM) from " + str(msgdata["i"]))
231+
def verify_commit(self, msgdata):
232+
if msgdata["status"] != "COMMIT":
233+
return False
234+
elif msgdata["v"] != self.view:
235+
return False
236+
elif msgdata["d(m)"] != _util.hash256(self.preprepare_msg):
237+
return False
238+
return True
239+
240+
if verify_commit(self, msgdata) and (self.status == "COMMIT"):
241+
self.commit_msgs.append(msgdata)
242+
if len(self.commit_msgs) > int((_params.fault_factor * (len(self.miner_list)-1))) and (self.status == "COMMIT"):
243+
self.status = "COMMITTED"
244+
return "REPL"
245+
else:
246+
return False
247+
else:
248+
return False
249+
250+
def handler_reply(self, src_id, msgdata):
251+
self.print_debug("(Client) Received (REPL) from " + str(msgdata["i"]))
252+
if self.status == "REQUEST" and self.view == msgdata["v"]:
253+
self.reply_msgs.append(msgdata)
254+
if len(self.reply_msgs) > int((_params.fault_factor * (len(self.miner_list)-1))):
255+
self.view += 1
256+
self.reply_msgs = []
257+
self.status = None
258+
self.print_debug("Round is finished, Status: " + str(self.status) + ", Now View: " + str(self.view))
259+
return "REQU"
260+
else:
261+
return False
262+
else:
263+
return False

0 commit comments

Comments
 (0)