|
@@ -0,0 +1,315 @@
|
|
|
|
|
+from tkinter import *
|
|
|
|
|
+from lib.chain import Chain, Block
|
|
|
|
|
+from lib.transaction import Transaction
|
|
|
|
|
+from lib.block import CURRENT
|
|
|
|
|
+import rpyc
|
|
|
|
|
+import time
|
|
|
|
|
+import json
|
|
|
|
|
+from create_keys import make_keys, get_private_key, get_plain_key
|
|
|
|
|
+import os
|
|
|
|
|
+from cryptography.hazmat.primitives import hashes
|
|
|
|
|
+from cryptography.hazmat.primitives.asymmetric import padding
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def new_contract():
|
|
|
|
|
+ def add():
|
|
|
|
|
+ name = str(title.get())
|
|
|
|
|
+ if not os.path.isdir('contract_keys\\' + name):
|
|
|
|
|
+ make_keys(name)
|
|
|
|
|
+ pubkey = get_plain_key(name)
|
|
|
|
|
+ # set discriptors of contract
|
|
|
|
|
+ contract = {'id':pubkey,
|
|
|
|
|
+ 'type':'init',
|
|
|
|
|
+ 'date of initiation':time.time(),
|
|
|
|
|
+ 'data': {'client':str(sender.get()),
|
|
|
|
|
+ 'contractor':str(recipient.get()),
|
|
|
|
|
+ 'discription':str(description.get()),
|
|
|
|
|
+ 'name':name},
|
|
|
|
|
+ 'terms' : {'deadline': int(deadline.get()),
|
|
|
|
|
+ 'accepted': False,
|
|
|
|
|
+ 'progress': '0%',
|
|
|
|
|
+ 'price':int(price.get()),
|
|
|
|
|
+ 'Sign time': 'not yet signed',
|
|
|
|
|
+ 'comments':{}
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ conn = rpyc.connect(host='localhost', port=42069, keepalive=True)
|
|
|
|
|
+
|
|
|
|
|
+ t = Transaction()
|
|
|
|
|
+ t.set_contract(contract)
|
|
|
|
|
+ t.hash(contract=name)
|
|
|
|
|
+ conn.root.push_transaction(t.serialize(out_json=True))
|
|
|
|
|
+
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+ newWindow.destroy()
|
|
|
|
|
+ else:
|
|
|
|
|
+ print('Contract name already exists, choose another')
|
|
|
|
|
+ newWindow.destroy()
|
|
|
|
|
+ new_contract()
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ newWindow = Tk()
|
|
|
|
|
+ newWindow.title('Add contract')
|
|
|
|
|
+
|
|
|
|
|
+ # create all text boxes
|
|
|
|
|
+ Label(newWindow, text='Title: ').grid(row=0, column=0)
|
|
|
|
|
+ title = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ title.grid(row=0, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ title.insert(0, 'test')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Description: ').grid(row=1, column=0)
|
|
|
|
|
+ description = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ description.grid(row=1, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ description.insert(0, 'test description')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Deadline: ').grid(row=2, column=0)
|
|
|
|
|
+ deadline = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ deadline.grid(row=2, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ deadline.insert(0, '1')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Price: ').grid(row=3, column=0)
|
|
|
|
|
+ price = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ price.grid(row=3, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ price.insert(0, '69420')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Sender: ').grid(row=4, column=0)
|
|
|
|
|
+ sender = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ sender.grid(row=4, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ sender.insert(0, 'justin')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Recipient: ').grid(row=5, column=0)
|
|
|
|
|
+ recipient = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ recipient.grid(row=5, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ recipient.insert(0, 'adam')
|
|
|
|
|
+
|
|
|
|
|
+ # create the add contract button
|
|
|
|
|
+ Button(newWindow, text = "Create contract", command=add).grid(row=6, column=1)
|
|
|
|
|
+
|
|
|
|
|
+def find_transaction():
|
|
|
|
|
+ def find():
|
|
|
|
|
+ conn = rpyc.connect(host='localhost', port=42069, keepalive=True)
|
|
|
|
|
+ iden = get_plain_key(str(key.get()))
|
|
|
|
|
+ transactions = [x.data for x in conn.root.find_transactions(iden)]
|
|
|
|
|
+ transactions = decrypt_transactions(transactions, str(key.get()))
|
|
|
|
|
+ print(current_contract_state(transactions, iden=iden))
|
|
|
|
|
+
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+ newWindow.destroy()
|
|
|
|
|
+
|
|
|
|
|
+ newWindow = Tk()
|
|
|
|
|
+ newWindow.title('Add contract')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='Contract key: ').grid(row=0, column=0)
|
|
|
|
|
+ key = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ key.grid(row=0, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ key.insert(0, '1')
|
|
|
|
|
+
|
|
|
|
|
+ Button(newWindow, text='find contracts', command=find).grid(row=1, column=1)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def add_term():
|
|
|
|
|
+ def add():
|
|
|
|
|
+ conn = rpyc.connect(host='localhost', port=42069, keepalive=True)
|
|
|
|
|
+
|
|
|
|
|
+ iden = get_plain_key(str(contract.get()))
|
|
|
|
|
+ attr = str(change.get())
|
|
|
|
|
+ value = str(values.get())
|
|
|
|
|
+ comment = str(comments.get())
|
|
|
|
|
+ transactions = [x.data for x in conn.root.find_transactions(iden)]
|
|
|
|
|
+ transactions = decrypt_transactions(transactions, str(contract.get()))
|
|
|
|
|
+ state = current_contract_state(transactions, iden)
|
|
|
|
|
+
|
|
|
|
|
+ if attr in state['data'] or attr in state['terms']:
|
|
|
|
|
+ t = 'modify'
|
|
|
|
|
+ else: t = 'update'
|
|
|
|
|
+
|
|
|
|
|
+ # add new or updated info to block
|
|
|
|
|
+ update = {'id':iden,
|
|
|
|
|
+ 'update id':len(get_updates(transactions, iden=iden)),
|
|
|
|
|
+ 'updated':attr,
|
|
|
|
|
+ 'type':t,
|
|
|
|
|
+ 'last update':time.time(),
|
|
|
|
|
+ 'accepted': True,
|
|
|
|
|
+ 'change':{attr:value}}
|
|
|
|
|
+
|
|
|
|
|
+ # check if comment is passed and add it if necassary
|
|
|
|
|
+ if comment:
|
|
|
|
|
+ update['comment'] = comment
|
|
|
|
|
+
|
|
|
|
|
+ t = Transaction()
|
|
|
|
|
+ t.set_contract(update)
|
|
|
|
|
+ t.hash(contract=iden)
|
|
|
|
|
+ conn.root.push_transaction(t.serialize(out_json=True))
|
|
|
|
|
+
|
|
|
|
|
+ #### TEST CODE PLEASE IGONRE ####
|
|
|
|
|
+ update = {
|
|
|
|
|
+ 'id':iden,
|
|
|
|
|
+ 'update id':0,
|
|
|
|
|
+ 'accept':True,
|
|
|
|
|
+ 'type':'accept',
|
|
|
|
|
+ 'last update':time.time()
|
|
|
|
|
+ }
|
|
|
|
|
+ if comment:
|
|
|
|
|
+ update['comment'] = comment
|
|
|
|
|
+
|
|
|
|
|
+ t = Transaction()
|
|
|
|
|
+ t.set_contract(update)
|
|
|
|
|
+ t.hash(contract=iden)
|
|
|
|
|
+ conn.root.push_transaction(t.serialize(out_json=True))
|
|
|
|
|
+
|
|
|
|
|
+ #### END TEST CODE PLEASE DONT IGONRE ####
|
|
|
|
|
+
|
|
|
|
|
+ conn.close()
|
|
|
|
|
+ newWindow.destroy()
|
|
|
|
|
+
|
|
|
|
|
+ newWindow = Tk()
|
|
|
|
|
+ newWindow.title('Add contract')
|
|
|
|
|
+
|
|
|
|
|
+ # create all text boxes
|
|
|
|
|
+ Label(newWindow, text='Contract: ').grid(row=0, column=0)
|
|
|
|
|
+ contract = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ contract.grid(row=0, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ contract.insert(0, '1')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='What to change or add: ').grid(row=1, column=0)
|
|
|
|
|
+ change = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ change.grid(row=1, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ change.insert(0, 'progress')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='New value: ').grid(row=2, column=0)
|
|
|
|
|
+ values = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ values.grid(row=2, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ values.insert(0, '50%')
|
|
|
|
|
+
|
|
|
|
|
+ Label(newWindow, text='comment (optional): ').grid(row=2, column=0)
|
|
|
|
|
+ comments = Entry(newWindow, width=35, borderwidth=5)
|
|
|
|
|
+ comments.grid(row=3, column=1, columnspan=3, padx=10, pady=10)
|
|
|
|
|
+ comments.insert(0, 'dit is een comment')
|
|
|
|
|
+
|
|
|
|
|
+ Button(newWindow, text='Add term', command=add).grid(row=4, column=1)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def current_contract_state(transactions, iden):
|
|
|
|
|
+ # set up contract state
|
|
|
|
|
+ state = {'id':iden,
|
|
|
|
|
+ 'data':{},
|
|
|
|
|
+ 'terms':{}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ # save not yet approved updates
|
|
|
|
|
+ updates = {}
|
|
|
|
|
+
|
|
|
|
|
+ # loop over all transaction in block
|
|
|
|
|
+ for item in transactions:
|
|
|
|
|
+
|
|
|
|
|
+ # save data about our project
|
|
|
|
|
+ if item['id'] == iden:
|
|
|
|
|
+
|
|
|
|
|
+ # set state to inital contract state
|
|
|
|
|
+ if item['type'] == 'init':
|
|
|
|
|
+ state = item
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ # if update is not accept update, save it to updates dict
|
|
|
|
|
+ if item['type'] != 'accept':
|
|
|
|
|
+
|
|
|
|
|
+ # comments are always accepted
|
|
|
|
|
+ if item['updated'] != 'comments':
|
|
|
|
|
+ updates[item['update id']] = item
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ # if accepted is true, retrieve update form dict and add to contract state
|
|
|
|
|
+ elif item['accept'] == True:
|
|
|
|
|
+ item = updates[item['update id']]
|
|
|
|
|
+
|
|
|
|
|
+ # set attr to whatever has been updated
|
|
|
|
|
+ attr = item['updated']
|
|
|
|
|
+
|
|
|
|
|
+ # check if the update was removal
|
|
|
|
|
+ if item['type'] == 'remove':
|
|
|
|
|
+
|
|
|
|
|
+ # delete item in the right part of the dict
|
|
|
|
|
+ try:
|
|
|
|
|
+ if attr in state['data']:
|
|
|
|
|
+ del state['data'][attr]
|
|
|
|
|
+ else:
|
|
|
|
|
+ del state['terms'][attr]
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+ if item['type'] == 'update':
|
|
|
|
|
+ state['terms'][attr] = item['change'][attr]
|
|
|
|
|
+
|
|
|
|
|
+ # check if updated attr is data or terms
|
|
|
|
|
+ elif attr in state['data']:
|
|
|
|
|
+ state['data'][attr] = item['change'][attr]
|
|
|
|
|
+
|
|
|
|
|
+ elif attr in state['terms']:
|
|
|
|
|
+
|
|
|
|
|
+ # special case for comments
|
|
|
|
|
+ if attr == 'comments' and item['type'] not in ['remove', 'init']:
|
|
|
|
|
+
|
|
|
|
|
+ k = list(item['change'][attr].keys())[0]
|
|
|
|
|
+ state['terms'][attr][k] = item['change'][attr][k]
|
|
|
|
|
+
|
|
|
|
|
+ # case for normal updates
|
|
|
|
|
+ else:
|
|
|
|
|
+ state['terms'][attr] = item['change'][attr]
|
|
|
|
|
+
|
|
|
|
|
+ # special cases for last update and date of initiation
|
|
|
|
|
+ elif 'last update' in item:
|
|
|
|
|
+ state['last update'] = item['last update']
|
|
|
|
|
+ return state
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def get_updates(transactions, iden=None, attr=None, accepted=None):
|
|
|
|
|
+ updates = []
|
|
|
|
|
+
|
|
|
|
|
+ # get all blocks and loop over transactions
|
|
|
|
|
+ for item in transactions:
|
|
|
|
|
+
|
|
|
|
|
+ # if attr is '' get updates for all attributes
|
|
|
|
|
+ if attr == None:
|
|
|
|
|
+
|
|
|
|
|
+ # if iden is None, get updates for all contracts
|
|
|
|
|
+ # updates for all contracts and attrs
|
|
|
|
|
+ if iden == None:
|
|
|
|
|
+ if item['type'] != 'init':
|
|
|
|
|
+ updates.append(item)
|
|
|
|
|
+
|
|
|
|
|
+ # updates for specific contracts from all attr
|
|
|
|
|
+ else:
|
|
|
|
|
+ if item['type'] != 'init' and item['id'] == iden:
|
|
|
|
|
+ updates.append(item)
|
|
|
|
|
+ else:
|
|
|
|
|
+
|
|
|
|
|
+ # updates for specific attrs from all contract
|
|
|
|
|
+ if iden == None:
|
|
|
|
|
+ if item['type'] != 'init' and item['updated'] == attr:
|
|
|
|
|
+ updates.append(item)
|
|
|
|
|
+ # updates for specific contract and specific attr
|
|
|
|
|
+ else:
|
|
|
|
|
+ if item['type'] != 'init' and item['updated'] == attr and item['id'] == iden:
|
|
|
|
|
+ updates.append(item)
|
|
|
|
|
+ return updates
|
|
|
|
|
+
|
|
|
|
|
+def decrypt_transactions(transactions, name):
|
|
|
|
|
+ private_key = get_plain_key(name)
|
|
|
|
|
+ decrypted = []
|
|
|
|
|
+ for encrypted in transactions:
|
|
|
|
|
+ decrypting_message = private_key.decrypt(
|
|
|
|
|
+ encrypted,
|
|
|
|
|
+ padding.OAEP(
|
|
|
|
|
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
|
|
|
+ algorithm=hashes.SHA256(),
|
|
|
|
|
+ label=None
|
|
|
|
|
+ )
|
|
|
|
|
+ )
|
|
|
|
|
+ decrypted.append(decrypting_message)
|
|
|
|
|
+
|
|
|
|
|
+ return decrypted
|