# Verifiable encryption inside ZK
# Normally this algo will be hardened due to malleability attacks
# on the ciphertext, but the ZK proof ensures that the ciphertext
# cannot be modified.
#
# This is basically the el gamal scheme in ZK

constant "Encrypt" {}

witness "Encrypt" {
    # We are encrypting values to this public key
    EcNiPoint pubkey,

    # Emphemeral secret value
    Base ephem_secret,

    # Values we are encrypting
    Base value_1,
    Base value_2,
    Base value_3,
}

circuit "Encrypt" {
    ################################################
    # 1. Derive shared secret using DH
    ################################################

    ephem_pub = ec_mul_var_base(ephem_secret, pubkey);
	ephem_pub_x = ec_get_x(ephem_pub);
	ephem_pub_y = ec_get_y(ephem_pub);
    # Used by the receiver to also derive the same shared secret
	constrain_instance(ephem_pub_x);
	constrain_instance(ephem_pub_y);

    shared_secret = poseidon_hash(ephem_pub_x, ephem_pub_y);

    ################################################
    # 2. Derive blinding factors for witness values
    ################################################

    N1 = witness_base(1);
    N2 = witness_base(2);
    N3 = witness_base(3);

    blind_1 = poseidon_hash(shared_secret, N1);
    blind_2 = poseidon_hash(shared_secret, N2);
    blind_3 = poseidon_hash(shared_secret, N3);

    ################################################
    # 3. Encrypt the values by applying blinds
    ################################################

    # This could be add or mul
    enc_value_1 = base_mul(value_1, blind_1);
    enc_value_2 = base_mul(value_2, blind_2);
    enc_value_3 = base_mul(value_3, blind_3);

    constrain_instance(enc_value_1);
    constrain_instance(enc_value_2);
    constrain_instance(enc_value_3);
}

