/***********************************************************************
 * Distributed under the MIT software license, see the accompanying    *
 * file COPYING or https://www.opensource.org/licenses/mit-license.php.*
 ***********************************************************************/

#ifndef SECP256K1_MODULE_CHILLDKG_VSS_H
#define SECP256K1_MODULE_CHILLDKG_VSS_H

#include "../../../include/secp256k1.h"

#include "../../group.h"
#include "../../hash.h"
#include "../../scalar.h"

/* This file contains the internal verifiable secret sharing (VSS) primitives
 * of the ChillDKG module, mirroring chilldkg_ref/vss.py of the bip-frost-dkg
 * reference implementation.
 *
 * A polynomial f of degree at most t-1 is represented by an array of t
 * coefficients, f(x) = coeffs[0] + coeffs[1]*x + ... + coeffs[t-1]*x^(t-1).
 * A VSS commitment is an array of t group elements com[j] = coeffs[j]*G.
 * Commitment entries may be the point at infinity (see VSSCommitment in the
 * reference). */

/* Derive the t polynomial coefficients from a 32-byte seed (VSS.generate):
 *   coeffs[j] = Scalar.from_bytes_checked(TH("BIP DKG/vss coeffs", seed32 || u32be(j)))
 * Returns 1 on success and 0 if some coefficient overflows the group order
 * (negligible probability; the caller declassifies the return value). */
static int secp256k1_chilldkg_vss_gen_coeffs(const secp256k1_hash_ctx *hash_ctx, secp256k1_scalar *coeffs, size_t t, const unsigned char *seed32);

/* Evaluate the polynomial at x via Horner's method (Polynomial.eval). */
static void secp256k1_chilldkg_vss_poly_eval(secp256k1_scalar *out, const secp256k1_scalar *coeffs, size_t t, const secp256k1_scalar *x);

/* Return the secret share for the participant with the given id, i.e.,
 * f(id+1) (VSS.secshare_for). The addition of 1 is performed in scalar
 * arithmetic so that id == UINT32_MAX cannot overflow. */
static void secp256k1_chilldkg_vss_secshare_for(secp256k1_scalar *out, const secp256k1_scalar *coeffs, size_t t, uint32_t id);

/* Compute the commitments com[j] = coeffs[j]*G (VSS.commit). A zero
 * coefficient (negligible probability) yields the point at infinity. */
static void secp256k1_chilldkg_vss_commit(const secp256k1_context *ctx, secp256k1_ge *coms, const secp256k1_scalar *coeffs, size_t t);

/* Compute the public share of the participant with the given id
 * (VSSCommitment.pubshare):
 *   out = sum_j (id+1)^j * coms[j]
 * Commitment entries that are the point at infinity contribute nothing. All
 * inputs are public, so variable-time algorithms are used. The result may be
 * the point at infinity; the caller must handle that case. */
static void secp256k1_chilldkg_vss_pubshare(secp256k1_gej *out, const secp256k1_ge *coms, size_t t, uint32_t id);

/* Component-wise addition of two commitments (VSSCommitment.__add__):
 * out[j] = a[j] + b[j]. All inputs are public. Entries in the result may be
 * the point at infinity. */
static void secp256k1_chilldkg_vss_commitment_add(secp256k1_ge *out, const secp256k1_ge *a, const secp256k1_ge *b, size_t t);

/* Verify secshare*G == pubshare (VSSCommitment.verify_secshare). */
static int secp256k1_chilldkg_vss_verify_secshare(const secp256k1_context *ctx, const secp256k1_scalar *secshare, const secp256k1_ge *pubshare);

/* Tweak a VSS commitment such that the resulting threshold public key has an
 * unspendable BIP 341 Taproot script path (VSSCommitment.invalid_taproot_commit):
 *   tweak = Scalar.from_bytes_checked(TH("TapTweak", xonly(coms[0])))
 *   out_coms[0] = coms[0] + tweak*G, out_coms[j] = coms[j] for j > 0
 * The tweak must additionally be added to all secret shares of the
 * commitment. Outputs the tweak and pubtweak = tweak*G (which may be the
 * point at infinity if tweak is zero, with negligible probability). Returns 1
 * on success and 0 if coms[0] is the point at infinity or the tweak hash
 * overflows the group order (negligible probability). */
static int secp256k1_chilldkg_vss_invalid_taproot_commit(const secp256k1_context *ctx, secp256k1_ge *out_coms, secp256k1_scalar *tweak, secp256k1_ge *pubtweak, const secp256k1_ge *coms, size_t t);

#endif
