forked from FilippoBurresi/FWBD_Tendering
-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.py
434 lines (362 loc) · 14.7 KB
/
utils.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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
from tkinter import *
from tkinter import messagebox
import pandas as pd
from web3 import Web3
import string
import random
import json
def initialize_contract(ganache_URL,address,abi):
"""
inizialitization of the contract and connection ethereum-ganache-python
with web3
Parameters
--------------
ganache_URL : the string of the ganache URL
address: the string of the contract address
abi: the contract ABI in string
Returns
--------------
the objects web3 and contract for the interaction
"""
web3=Web3(Web3.HTTPProvider(ganache_URL))
web3.eth.defaultAccount=web3.eth.accounts[0]
abi=json.loads(abi)
address=web3.toChecksumAddress(address)
contract=web3.eth.contract(address=address,abi=abi)
return web3,contract
def create_tender(web3,contract,input_dict_pa):
"""
the PA creates a new tender on the blockchain
Parameters
--------------
web3 object
contract object
input_dict_pa, the input dictionary with the values needed for the tender
creation (tender name, description, number of seconds to send hash,
number of seconds to send data, the weights - price, time, environment)
Returns
--------------
Nothing, it creates on the blockchain a new tender, error message may appear
"""
try:
tender_name=input_dict_pa["tender name"].get()
description=input_dict_pa["description"].get()
n_days_1=int(input_dict_pa["n seconds to send hash"].get())
n_days_2=int(input_dict_pa["n seconds to send file"].get())
weight_1_price=int(input_dict_pa["weight price"].get())
weight_2_time=int(input_dict_pa["weight time"].get())
weight_3_envir=int(input_dict_pa["weight environment"].get())
create_tender_solidity(web3,contract,tender_name,description,n_days_2,n_days_1,weight_1_price,weight_2_time,weight_3_envir)
messagebox.showinfo("Create Tender", "the function has been called successfully")
except Exception as e:
messagebox.showerror("Create Tender", str(e) + " You might not have the permission to call this function")
def create_tender_solidity(web3,contract,tender_name,description,n_days_1,n_days_2,weight_1_price,weight_2_time,weight_3_envir):
contract.functions.CreateTender(tender_name,description,n_days_1,n_days_2,weight_1_price,weight_2_time,weight_3_envir).transact()
def allowed_companies_ids(web3,contract,input_dict):
"""
the PA gives permissions for creating BIDS
Parameters
--------------
web3 object
contract object
input_dict, dictionary from tkinter with key allowed companies, a string
with comma separated ids
Returns
--------------
Nothing, it creates on the blockchain the permissions, error message may appear
"""
try:
list_allowed=input_dict["allowed companies"].get().split(",")
for i in list_allowed:
contract.functions.addFirm(web3.eth.accounts[int(i)]).transact()
messagebox.showinfo("Allowed Companies", "The companies have been added to the allowed list")
except Exception as e:
messagebox.showerror("Allowed Companies", str(e)+ " You might not have the permission to call this function")
def assign_winner(web3,contract,input_dict):
try:
tender_id=int(input_dict["tender id"].get())
contract.functions.compute_scores(tender_id).transact()
contract.functions.assign_winner(tender_id).transact()
winning_address,score=contract.functions.displayWinner(tender_id).call()
messagebox.showinfo("Assign Winner", "The winner of the tender has been appointed")
return web3.eth.accounts.index(winning_address)
except Exception as e:
messagebox.showerror("Allowed Companies", str(e)+ " You might not have the permission to call this function or the Tender is not closed yet")
def get_tenders_status(web3,contract):
"""
creates a dataframe with the tenders, active and closed
Parameters
--------------
web3 object
contract object
Returns
--------------
the dataframe with the infos
"""
num_tenders=contract.functions.getTendersLength().call()
l=[]
for i in range(num_tenders):
key,status=contract.functions.isPending(i).call()
list=contract.functions.see_TenderDetails(i).call()
list.append(status)
l.append(list)
df=pd.DataFrame(l,columns=["tender id","name","description","weights","number participants","winning contractor","pending?"])
df["price weight"]=df["weights"].apply(lambda x: x[0])
df["time weight"]=df["weights"].apply(lambda x: x[1])
df["environment weight"]=df["weights"].apply(lambda x: x[2])
df.drop('weights', inplace=True, axis=1)
return df
def see_active_tenders(web3,contract, input_dict):
"""
creates a dataframe with the active tenders
Parameters
--------------
web3 object
contract object
input_dict, dictionary with the previous dataframe (tkinter object)
Returns
--------------
shows in the interface the dataframe with the infos
"""
try:
input_dict['tv1'].delete(*input_dict['tv1'].get_children())
df=get_tenders_status(web3,contract)
df = df[df["pending?"]==True]
df.drop('pending?', inplace=True, axis=1)
df.drop('winning contractor', inplace=True, axis=1)
df.drop("number participants", inplace=True, axis=1)
#from here the code "print" the dataframe
input_dict['tv1']["column"] = list(df.columns)
for column in input_dict['tv1']["column"]:
input_dict['tv1'].column(column,minwidth=0, width=78, stretch=NO)
input_dict['tv1']["show"] = "headings"
for column in input_dict['tv1']["columns"]:
input_dict['tv1'].heading(column, text=column) # let the column heading = column name
df_rows = df.to_numpy().tolist() # turns the dataframe into a list of lists
for row in df_rows:
input_dict['tv1'].insert("", "end", values=row)
except Exception as e:
messagebox.showerror("Allowed Companies", str(e))
def see_closed_tenders(web3,contract, input_dict):
"""
creates a dataframe with the closed tenders
Parameters
--------------
web3 object
contract object
input_dict, dictionary with the previous dataframe (tkinter object)
Returns
--------------
shows in the interface the dataframe with the infos
"""
try:
input_dict['tv1'].delete(*input_dict['tv1'].get_children())
df=get_tenders_status(web3,contract)
df = df[df["pending?"]==False]
df.drop('pending?', inplace=True, axis=1)
dict_address = {address: web3.eth.accounts.index(address) for address in web3.eth.accounts}
df["winning contractor"] = df["winning contractor"].apply(lambda x: dict_address.get(x,""))
#from here the code "print" the dataframe
input_dict['tv1']["column"] = list(df.columns)
for column in input_dict['tv1']["column"]:
input_dict['tv1'].column(column,minwidth=0, width=67, stretch=NO)
input_dict['tv1']["show"] = "headings"
for column in input_dict['tv1']["columns"]:
input_dict['tv1'].heading(column, text=column) # let the column heading = column name
df_rows = df.to_numpy().tolist() # turns the dataframe into a list of lists
for row in df_rows:
input_dict['tv1'].insert("", "end", values=row)
except Exception as e:
messagebox.showerror("Allowed Companies", str(e))
def get_bids_details(web3,contract,input_dict):
"""
creates a dataframe with the bids of a closed tender
Parameters
--------------
web3 object
contract object
input_dict, dictionary with the previous dataframe (tkinter object)
and the tender id
Returns
--------------
shows the dataframe with the infos
"""
try:
input_dict['tv1'].delete(*input_dict['tv1'].get_children())
tender_id = int(input_dict['tender id'].get())
if contract.functions.see_TenderDetails(tender_id).call()[5]!="0x0000000000000000000000000000000000000000":
num_bids=contract.functions.getResultsLenght(tender_id).call()
bids_list=[]
for i in range(0,num_bids):
address,score,winner=contract.functions.getResultsValue(tender_id,i).call()
bids_list.append(contract.functions.getBidDetails(tender_id,address).call())
print(bids_list)
df = pd.DataFrame(bids_list,columns=["address","description","separator used","score","winner?"])
dict_address={address:web3.eth.accounts.index(address) for address in web3.eth.accounts}
df["account"]=df["address"].apply(lambda x: dict_address[x])
df["price"]=df["description"].apply(lambda x: x[0])
df["time"]=df["description"].apply(lambda x: x[1])
df["environment"]=df["description"].apply(lambda x: x[2])
df=df[["account","separator used","price","time","environment","score","winner?"]]
df.sort_values(by="score",ascending=True,inplace=True)
input_dict['tv1']["column"] = list(df.columns)
for column in input_dict['tv1']["column"]:
input_dict['tv1'].column(column,minwidth=0, width=78, stretch=NO)
input_dict['tv1']["show"] = "headings"
for column in input_dict['tv1']["columns"]:
input_dict['tv1'].heading(column, text=column) # let the column heading = column name
df_rows = df.to_numpy().tolist() # turns the dataframe into a list of lists
for row in df_rows:
input_dict['tv1'].insert("", "end", values=row)
else:
messagebox.showerror("Error", "The PA has not appointed the winner yet")
except Exception as e:
messagebox.showerror("Error", str(e))
#### COMPANIES INTERFACE
def send_bid(web3,contract, input_dict):
"""
sends the encrypted bid
Parameters
--------------
web3 object
contract object
input_dict, dictionary with the values of the offer (tkinter object):
tender id, price, time, environment (1-4)
Returns
--------------
sends to ethereum the hash of the offer and creates a txt with the file
to be submitted afterwards
"""
try:
tender_id=int(input_dict["tender id"].get())
price=input_dict["price"].get()
time=input_dict["time"].get()
envir=input_dict["environment"].get()
if int(envir) not in [1,2,3,4]:
messagebox.showerror("Error", "the variable environment has to be 1,2,3,4")
else:
list_values_to_hash=[price,time,envir]
unencrypted_message,separator=to_string_and_sep(list_values_to_hash)
hash=encrypt(unencrypted_message)
send_bid_solidity(web3,contract,tender_id,hash)
save_txt(web3,str(web3.eth.defaultAccount),str(separator),unencrypted_message,str(tender_id))
messagebox.showinfo("Send bid", "The bid has been sent successfully")
except:
messagebox.showerror("Allowed Companies", "you might not have permissions to create a bid or the tender is closed. Remember you can send only one bid per Tender")
def send_unencrypted(web3,contract, input_dict):
"""
sends the unencrypted bid
Parameters
--------------
web3 object
contract object
input_dict, dictionary (tkinter object) with the input file to send:
Returns
--------------
sends to ethereum the un-encrypted offer
"""
filename=input_dict["link"]["text"]
try:
tender_id,unencrypted_message,separator=load_txt(filename)
send_unencrypted_solidity(web3,contract,tender_id,unencrypted_message,separator)
messagebox.showinfo("Bid Completed", "The unencripted bid has been sent")
except:
messagebox.showerror("Allowed Companies", "you might not have permissions to conclude a bid, you are not in the window time to conclude the bid or the data you sent doesn't match with the previous ones")
def to_string_and_sep(l):
"""
convert a list of strings into a string separated with a random separator
Parameters
--------------
list: target list
ex. ["hi","I","am"]
Returns
--------------
the string with the separator and the separator
ex. "hi##I##am##", "##"
"""
s="".join(l)
while True:
separator=get_random_separator()
if separator not in s:
unencrypted_message=separator.join(l)+separator
print(unencrypted_message,separator)
return unencrypted_message,separator
def get_random_separator(length=10):
"""
returns a random separator
--------------
length: how many characters the separator should have
Returns
--------------
the separator
"""
allowed_characters = string.ascii_letters + string.digits + string.punctuation
separator = ''.join(random.choice(allowed_characters) for i in range(length))
return separator
def encrypt(unencrypted_message):
"""
encrypts a message using solidity SHA3
--------------
unencrypted_message: Message to encrypt
Returns
--------------
the hash
"""
hash=bytes(Web3.soliditySha3(['string[]'], [unencrypted_message]))
return hash
def send_bid_solidity(web3,contract,tender_id,hash):
"""
sends the bid hash to ethereum
--------------
web3 object
contract object
tender_id
hash to send
Returns
--------------
nothing, executes the solidity function
"""
contract.functions.placeBid(int(tender_id),hash).transact()
def save_txt(web3,user_id,separator,unencrypted_message,tender_id):
"""
creates the txt to send afterwards
--------------
web3 object
contract object
tender_id
unencrypted_message the message with the separator
separator the separator used
Returns
--------------
nothing, creates the file
"""
dict_address={address:web3.eth.accounts.index(address) for address in web3.eth.accounts}
file=open("offer_{}.txt".format(dict_address[user_id]),"w")
file.writelines([separator+"\n",unencrypted_message+"\n",tender_id])
file.close()
def load_txt(filename):
"""
load the txt to send
--------------
Returns
--------------
nothing, loads the file
"""
file=open(filename,"r")
separator,unencrypted_message,tender_id=[i.replace("\n","") for i in file.readlines()]
file.close()
return tender_id,unencrypted_message,separator
def send_unencrypted_solidity(web3,contract,tender_id, unencrypted_message,separator):
"""
sends the unencrypted bid to ethereum
--------------
web3 object
contract object
tender_id
unencrypted_message
separator used
Returns
--------------
nothing, executes the solidity function
"""
contract.functions.concludeBid(int(tender_id),unencrypted_message,separator).transact()