k = 11;
field = "pallas";

constant "EarlyExec" {
    EcFixedPointShort VALUE_COMMIT_VALUE,
    EcFixedPoint VALUE_COMMIT_RANDOM,
    EcFixedPointBase NULLIFIER_K,
}

witness "EarlyExec" {
    # Proposal parameters
    Base proposal_auth_calls_commit,
    Base proposal_creation_blockwindow,
    Base proposal_duration_blockwindows,
    Base proposal_user_data,
    Base proposal_blind,

    # DAO parameters
    Base dao_proposer_limit,
    Base dao_quorum,
    Base dao_early_exec_quorum,
    Base dao_approval_ratio_quot,
    Base dao_approval_ratio_base,
    Base dao_gov_token_id,
    Base dao_notes_public_x,
    Base dao_notes_public_y,
    Base dao_proposer_public_x,
    Base dao_proposer_public_y,
    Base dao_proposals_public_x,
    Base dao_proposals_public_y,
    Base dao_votes_public_x,
    Base dao_votes_public_y,
    Base dao_exec_secret,
    Base dao_early_exec_secret,
    Base dao_bulla_blind,

    # Votes
    Base yes_vote_value,
    Base all_vote_value,
    Scalar yes_vote_blind,
    Scalar all_vote_blind,

    # Check whether the proposal has expired or not
    Base current_blockwindow,

    # Signature secret
    Base signature_secret,
}

circuit "EarlyExec" {
    # Derive DAO executor public key
    dao_exec_public = ec_mul_base(dao_exec_secret, NULLIFIER_K);
    dao_exec_public_x = ec_get_x(dao_exec_public);
    dao_exec_public_y = ec_get_y(dao_exec_public);

    # Derive DAO early executor public key for 2FA
    dao_early_exec_public = ec_mul_base(dao_early_exec_secret, NULLIFIER_K);
    dao_early_exec_public_x = ec_get_x(dao_early_exec_public);
    dao_early_exec_public_y = ec_get_y(dao_early_exec_public);

    dao_bulla = poseidon_hash(
        dao_proposer_limit,
        dao_quorum,
        dao_early_exec_quorum,
        dao_approval_ratio_quot,
        dao_approval_ratio_base,
        dao_gov_token_id,
        dao_notes_public_x,
        dao_notes_public_y,
        dao_proposer_public_x,
        dao_proposer_public_y,
        dao_proposals_public_x,
        dao_proposals_public_y,
        dao_votes_public_x,
        dao_votes_public_y,
        dao_exec_public_x,
        dao_exec_public_y,
        dao_early_exec_public_x,
        dao_early_exec_public_y,
        dao_bulla_blind,
    );

    # Proposal bulla being valid means DAO bulla is also valid because
    # dao-propose-main.zk already checks that when we first create the
    # proposal - so it is redundant to check DAO bulla exists here.
    proposal_bulla = poseidon_hash(
        proposal_auth_calls_commit,
        proposal_creation_blockwindow,
        proposal_duration_blockwindows,
        proposal_user_data,
        dao_bulla,
        proposal_blind,
    );
    constrain_instance(proposal_bulla);
    constrain_instance(proposal_auth_calls_commit);

    # Enforce that the proposal has not expired
    end_time = base_add(proposal_creation_blockwindow, proposal_duration_blockwindows);
    less_than_strict(current_blockwindow, end_time);
    constrain_instance(current_blockwindow);

    # Create Pedersen commitments for win_votes and total_votes, and
    # constrain the commitments' coordinates.
    yes_vote_value_c = ec_mul_short(yes_vote_value, VALUE_COMMIT_VALUE);
    yes_vote_blind_c = ec_mul(yes_vote_blind, VALUE_COMMIT_RANDOM);
    yes_vote_commit = ec_add(yes_vote_value_c, yes_vote_blind_c);
    constrain_instance(ec_get_x(yes_vote_commit));
    constrain_instance(ec_get_y(yes_vote_commit));

    all_vote_value_c = ec_mul_short(all_vote_value, VALUE_COMMIT_VALUE);
    all_vote_blind_c = ec_mul(all_vote_blind, VALUE_COMMIT_RANDOM);
    all_vote_commit = ec_add(all_vote_value_c, all_vote_blind_c);
    constrain_instance(ec_get_x(all_vote_commit));
    constrain_instance(ec_get_y(all_vote_commit));

    # Check that dao_early_exec_quorum is less than or equal to all_vote_value
    one = witness_base(1);
    all_vote_value_1 = base_add(all_vote_value, one);
    less_than_strict(dao_early_exec_quorum, all_vote_value_1);

    # approval_ratio_quot / approval_ratio_base <= yes_vote / all_vote
    #
    # The above is also equivalent to this:
    #
    # all_vote * approval_ratio_quot <= yes_vote * approval_ratio_base
    lhs = base_mul(all_vote_value, dao_approval_ratio_quot);
    rhs = base_mul(yes_vote_value, dao_approval_ratio_base);
    rhs_1 = base_add(rhs, one);
    less_than_strict(lhs, rhs_1);

    # Derive a public key for the signature and constrain its coordinates
    signature_public = ec_mul_base(signature_secret, NULLIFIER_K);
    constrain_instance(ec_get_x(signature_public));
    constrain_instance(ec_get_y(signature_public));
}

