فهرست منبع

intergrating json-ld into blockchain code

justtheboss97 4 سال پیش
والد
کامیت
25f3342f11
6فایلهای تغییر یافته به همراه360 افزوده شده و 18 حذف شده
  1. 27 8
      create_keys.py
  2. 1 0
      data/block.json
  3. 6 4
      gui.py
  4. 315 0
      gui/data.py
  5. 10 6
      lib/transaction.py
  6. 1 0
      main.py

+ 27 - 8
create_keys.py

@@ -15,7 +15,7 @@ from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives.asymmetric import rsa
 from cryptography.hazmat.primitives import serialization
 
-def make_keys(company):
+def make_keys(contract_name):
     #(pubkey,privkey)=rsa.newkeys(2048)
     private_key = rsa.generate_private_key(
         public_exponent=65537,
@@ -29,7 +29,11 @@ def make_keys(company):
         format=serialization.PublicFormat.SubjectPublicKeyInfo
     )
 
-    with open('companies\\' + company + '_publickey.key','wb') as f:
+    path = 'contract_keys\\' + contract_name + '\\'
+
+    os.mkdir(path)
+    
+    with open(path + 'publickey.key','wb') as f:
         f.write(pem)
 
     pem = private_key.private_bytes(
@@ -38,17 +42,32 @@ def make_keys(company):
         encryption_algorithm=serialization.NoEncryption()
     )
 
-    with open('companies\\' + company + '_privatekey.key','wb') as f:
+    with open(path + 'privatekey.key','wb') as f:
         f.write(pem)    
     
-def get_keys(company):
-    path = 'companies\\' + company + '_publickey.key'
-    if not os.path.exists(path):
-        make_keys(company)
+def get_keys(contract_name):
+    path = 'contract_keys\\' + contract_name  + '\\publickey.key'
 
     with open(path, 'rb') as f:
         public_key = serialization.load_pem_public_key(
             f.read(),
             backend=default_backend()
             )
-        return public_key
+        return public_key
+
+def get_private_key(contract_name):
+    path = 'contract_keys\\' + contract_name  + '\\privatekey.key'
+
+    with open(path, "rb") as key_file:
+        private_key = serialization.load_pem_private_key(
+            key_file.read(),
+            password=None,
+            backend=default_backend()
+        )
+    return private_key
+
+
+def get_plain_key(contract_name):
+    path = 'contract_keys\\' + contract_name  + '\\publickey.key'
+    with open(path, 'rt') as f:
+        return f.read().split('-----')[2].strip().replace('\n', '')

+ 1 - 0
data/block.json

@@ -0,0 +1 @@
+[{"timestamp": 1611064065, "previous": "This is the founding block for our DSP project", "transactions": []}, {"timestamp": 1611064075, "previous": "5c52e2061332950ab1d95031206ff75084a9926fd3e2a59d4a89ccfc72e0ac7e", "transactions": []}, {"timestamp": 1611064085, "previous": "c218b13bc771b00d15349dfb5bce01359b84118c23c09b876b69e71f7328252e", "transactions": []}, {"timestamp": 1611064095, "previous": "63ed433a0f3b26b0e010b29c0a4e04e01e55e62b7042639e97165a91027d8ceb", "transactions": []}]

+ 6 - 4
gui.py

@@ -1,7 +1,8 @@
 from tkinter import *
 from linked_data import Chain, format_dict
 import pprint
-from gui.gui_helpers import add_contract
+# from gui.gui_helpers import add_contract
+from gui.data import new_contract, find_transaction, add_term
 
 from lib.transaction import Transaction
 from lib.contract import Contract
@@ -24,9 +25,10 @@ root.title('dsp blockchain')
 # accept_status.set('False') # default value
 # OptionMenu(root, accept_status, 'False', 'True').grid(row=4, column=4)
 
-   
-    
-Button(root, text="New contract", command=add_contract).grid(row=1, column=0)
+
+Button(root, text="New contract", command=new_contract).grid(row=1, column=0, padx=10, pady=5)
+Button(root, text="Find contract", command=find_transaction).grid(row=1, column=1, padx=10, pady=5)
+Button(root, text="Add term", command=add_term).grid(row=1, column=2, padx=10, pady=5)
 
 # Button(root, text="View contract", command=myClick).grid(row=0, column=0)
 

+ 315 - 0
gui/data.py

@@ -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

+ 10 - 6
lib/transaction.py

@@ -34,11 +34,15 @@ class Transaction:
             return True
         return False
 
-    def set_contract(self, contract: Contract):
+    def set_contract(self, contract: dict):
         if not self.locked:
             self.id = str(uuid.uuid1())
-            self.data = contract.serialize()
-            self.log(f'Contract set: {contract.id} | {contract.title}')
+            # self.data = contract.serialize()
+            self.data = contract
+            self.timestamp = time.time()
+            self.key = contract['id']
+            # self.log(f'Contract set: {contract.id} | {contract.title}')'
+            self.log(f'Contract set: {contract["id"]}')
             return True
         return False
 
@@ -64,12 +68,12 @@ class Transaction:
         return False
 
 
-    def hash(self, recipient=None):
+    def hash(self, contract=None):
         string_object = json.dumps(self.data, sort_keys=True)
         block_string = string_object.encode()
 
         # open the public key file
-        pubkey = get_keys(recipient) #Sender public key - input field neccessary
+        pubkey = get_keys(contract) #Sender public key - input field neccessary
 
         encrypted_data = pubkey.encrypt(
             block_string,
@@ -83,7 +87,7 @@ class Transaction:
         self.hash_value = encrypted_data
         self.log(f'Hashed: {self.hash_value}')
 
-        return encrypted_data
+        self.data = encrypted_data
 
     def log(self, text):
         print(f'[ TRANS ] {text}')

+ 1 - 0
main.py

@@ -112,6 +112,7 @@ if __name__ == '__main__':
 
                 found = chain.find_transactions(address)
 
+                return found
                 return json.dumps(found, sort_keys=True)
 
             def exposed_push_contract(self, contract):