# Phase 5: Secure Payment
# Time complexity tests
# Author: Norbert Lovinger; xlovin00@vutbr.cz
# Date: 16th May 2023

from noknow.core import ZK, ZKSignature, ZKParameters, ZKData, ZKProof
from queue import Queue
from threading import Thread
import time

payment_amout = "100"
service_verify_time = 0


def user(iq: Queue, oq: Queue):
    user_zk = ZK.new(curve_name="secp256k1", hash_alg="sha3_256")
    signature = user_zk.create_signature(payment_amout)
    oq.put(signature.dump())

    token = iq.get()

    proof = user_zk.sign(payment_amout, token).dump()

    oq.put(proof)

    print("Success!" if iq.get() else "Failure!")


def service_operator(iq: Queue, oq: Queue):
    operator_password = "Password"
    operator_zk = ZK.new(curve_name="secp384r1", hash_alg="sha3_512")
    operator_signature: ZKSignature = operator_zk.create_signature(operator_password)

    sig = iq.get()
    user_signature = ZKSignature.load(sig)
    user_zk = ZK(user_signature.params)

    token = operator_zk.sign(operator_password, user_zk.token())
    oq.put(token.dump(separator=":"))

    proof = ZKData.load(iq.get())
    token = ZKData.load(proof.data, ":")

    total_time2 = 0
    if not operator_zk.verify(token, operator_signature):
        oq.put(False)
    else:        
        for i in range(100):
            start_time = time.time()
            oq.put(user_zk.verify(proof, user_signature, data=token)) # type: ignore
            end_time = time.time()
            total_time2 += end_time - start_time
    service_verify_time = total_time2 / 100
    print(f"Average time for service verifying the proof is {service_verify_time:.6f} milliseconds.")
        

def main():
    q1, q2 = Queue(), Queue()
    threads = [
        Thread(target=user, args=(q1, q2)),
        Thread(target=service_operator, args=(q2, q1)),
    ]
    for func in [Thread.start, Thread.join]:
        for thread in threads:
            func(thread)

if __name__ == "__main__":
    main()
    
# Performance tests
def average_time(func,number):
    total_time = 0
    for i in range(number):
        start_time = time.time()
        func()
        end_time = time.time()
        total_time += end_time - start_time
    return total_time / number

def user_sign():
    user_zk = ZK.new(curve_name="secp256k1", hash_alg="sha3_256")
    signature = user_zk.create_signature(payment_amout)

user_sign_time = average_time(user_sign,100)

print(f"Average time for signing 100 user's payment amounts is {user_sign_time:.6f} milliseconds.")

user_zk = ZK.new(curve_name="secp256k1", hash_alg="sha3_256")
signature = user_zk.create_signature(payment_amout)

def service_sign():
    operator_password = "Password"
    operator_zk = ZK.new(curve_name="secp384r1", hash_alg="sha3_512")
    user_zk = ZK(signature.params)
    token = operator_zk.sign(operator_password, user_zk.token())

service_proof_time = average_time(service_sign,100)
print(f"Average time for service token proof signing is {service_proof_time:.6f} milliseconds.")


