#ifndef TOPRF_UPDATE_H
#define TOPRF_UPDATE_H
#include <stdint.h>
#include <sodium.h>
#include "dkg.h"
#include "toprf.h"

#define noise_xk_handshake1_SIZE 48UL
#define noise_xk_handshake2_SIZE 48UL
#define noise_xk_handshake3_SIZE 64UL
#define toprf_update_noise_key_SIZE (32UL)
#define toprf_update_sessionid_SIZE 32U
#define toprf_update_commitment_HASHBYTES 32U
#define toprf_update_encrypted_shares_SIZE (TOPRF_Share_BYTES * 2)
#define toprf_keyid_SIZE 32U

typedef DKG_Message TOPRF_Update_Message;

// todo refactor this, it's the same in tp-dkg and stp-dkg
/** @struct TOPRF_Update_Cheater
    This struct communicates one detected violation of the protocol.

    @var TOPRF_Update_Cheater::step This is the step in which the violation occured.
    @var TOPRF_Update_Cheater::error This is the error code specifying the violation.
    @var TOPRF_Update_Cheater::peer This specifies which peer caused the violation.
    @var TOPRF_Update_Cheater::other_peer This optionally specifies which
         peer reported the violation, set to 0xfe if unused.
 */
typedef struct {
  int step;
  int error;
  uint8_t peer;
  uint8_t other_peer;
  int invalid_index;
} TOPRF_Update_Cheater;

/** @enum TOPRF_Update_Err - TOPRF-Update Error codes
 */
typedef enum {
  TOPRF_Update_Err_OK = 0,
  TOPRF_Update_Err_ISize,
  TOPRF_Update_Err_OSize,
  TOPRF_Update_Err_OOB,
  TOPRF_Update_Err_Send,
  TOPRF_Update_Err_CheatersFound,
  TOPRF_Update_Err_CheatersFull,
  TOPRF_Update_Err_InvSessionID,
  TOPRF_Update_Err_VSSShare,
  TOPRF_Update_Err_VSSCommit,
  TOPRF_Update_Err_Noise,
  TOPRF_Update_Err_NoiseEncrypt,
  TOPRF_Update_Err_NoiseDecrypt,
  TOPRF_Update_Err_HMac,
  TOPRF_Update_Err_NoSubVSPSFail,
  TOPRF_Update_Err_NotEnoughDealers,
  TOPRF_Update_Err_TooManyCheaters,
  TOPRF_Update_Err_DKGFinish,
  TOPRF_Update_Err_FTMULTStep1,
  TOPRF_Update_Err_FTMULTZKCommitments,
  TOPRF_Update_Err_InvPoint,
  TOPRF_Update_Err_CommmitmentsMismatch,
  TOPRF_Update_Err_Proto,
  TOPRF_Update_Err_BadReconstruct,
  TOPRF_Update_Err_Reconstruct,
  TOPRF_Update_Err_BroadcastEnv = 32,
  TOPRF_Update_Err_Env = 64
} TOPRF_Update_Err;

/** @enum TOPRF_Update_STP_Steps - TOPRF-Update protocol steps for the STP
 */
typedef enum {
  TOPRF_Update_STP_Broadcast_NPKs,
  TOPRF_Update_STP_Route_Noise_Handshakes1,
  TOPRF_Update_STP_Route_Noise_Handshakes2,
  TOPRF_Update_STP_Broadcast_DKG_Hash_Commitments,
  TOPRF_Update_STP_Broadcast_DKG_Commitments,
  TOPRF_Update_STP_Route_Encrypted_Shares,
  TOPRF_Update_STP_Broadcast_Complaints,
  TOPRF_Update_STP_Broadcast_DKG_Defenses,
  TOPRF_Update_STP_Broadcast_DKG_Transcripts,
  TOPRF_Update_STP_Route_Mult_Step1,
  TOPRF_Update_STP_Broadcast_Mult_Commitments,
  TOPRF_Update_STP_Route_Encrypted_Mult_Shares,
  TOPRF_Update_STP_Broadcast_Mult_Complaints,
  TOPRF_Update_STP_Broadcast_Mult_Defenses,
  TOPRF_Update_STP_Broadcast_Reconst_Mult_Shares,
  TOPRF_Update_STP_Route_ZK_Challenge_Commitments,
  TOPRF_Update_STP_Route_ZK_commitments,
  TOPRF_Update_STP_Broadcast_ZK_nonces,
  TOPRF_Update_STP_Broadcast_ZK_Proofs,
  TOPRF_Update_STP_Broadcast_ZK_Disclosures,
  TOPRF_Update_STP_Broadcast_Mult_Ci,
  TOPRF_Update_STP_Broadcast_VSPS_Disclosures,
  TOPRF_Update_STP_Reconstruct_Delta,
  TOPRF_Update_STP_Done
} TOPRF_Update_STP_Steps;

/** @struct TOPRF_Update_STPState

    This struct contains the state of the STP during the execution of
    the TOPRF Update protocol.

    Most values of this struct are internal variables and should not
    be used. The following variables are useful and can be used by
    users of this API:

    @var TOPRF_Update_STPState:n This field contains the value N,
         specifying the total number of peers participating in this
         protocol.

    @var TOPRF_Update_STPState:t This field contains the value T,
         specifying the threshold necessary to use shared secret
         generated by this DKG.

    @var TOPRF_Update_STPState:cheaters This field contains a list of
         cheaters and protocol violators at the end of a failed
         protocol run.

    @var TOPRF_Update_STPState:delta contains the final public delta
         value, which can be applied to previous results of the OPRF
         with the old key, to update those results to the new key.
*/
// todo consider making this struct private
typedef struct {
  TOPRF_Update_STP_Steps step;
  TOPRF_Update_STP_Steps prev;
  uint8_t sessionid[dkg_sessionid_SIZE];
  uint8_t n;
  uint8_t t;
  uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES];
  uint8_t sig_sk[crypto_sign_SECRETKEYBYTES];
  uint64_t *last_ts;
  uint64_t ts_epsilon;
  const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES];

  uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES];
  uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES];
  uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES];
  uint16_t p_complaints_len;
  uint16_t *p_complaints;
  uint16_t y2_complaints_len;
  uint16_t *y2_complaints;

  uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES];
  uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES];
  uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES];

  size_t cheater_len;
  TOPRF_Update_Cheater (*cheaters)[];
  size_t cheater_max;
  uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES];
  uint8_t delta[crypto_scalarmult_ristretto255_BYTES];
  crypto_generichash_state transcript_state;
  uint8_t transcript[crypto_generichash_BYTES];
} TOPRF_Update_STPState;

/** Gets the size of a TOPRF_Update_STPState struct

    Useful if you need to allocate space for it, be mindful though
    - if you do - that the struct MUST be aligned at 32 B boundary!
    If in doubt check out how the python wrapper handles this.
 */
size_t toprf_update_stpstate_size(void);

/** Gets the value of N from a TOPRF_Update_STPState struct
 */
uint8_t toprf_update_stpstate_n(const TOPRF_Update_STPState *ctx);

/** Gets the value of T from a TOPRF_Update_STPState struct
 */
uint8_t toprf_update_stpstate_t(const TOPRF_Update_STPState *ctx);

/** Gets the number of cheaters discovered so far from a TOPRF_Update_STPState struct
 */
size_t toprf_update_stpstate_cheater_len(const TOPRF_Update_STPState *ctx);

/** Gets the sessionid from a TOPRF_Update_STPState struct
 */
const uint8_t* toprf_update_stpstate_sessionid(const TOPRF_Update_STPState *ctx);

/** Gets the final result, the public delta value from a TOPRF_Update_STPState struct

    use this only if the protocol finished succesfully.
 */
const uint8_t* toprf_update_stpstate_delta(const TOPRF_Update_STPState *ctx);

/** Gets the commitments for the shares of the new key from a TOPRF_Update_STPState struct

    use this only if the protocol finished succesfully.
 */
const uint8_t* toprf_update_stpstate_commitments(const TOPRF_Update_STPState *ctx);

/** Gets the current step of the STP in the protocol from a TOPRF_Update_STPState struct
 */
int toprf_update_stpstate_step(const TOPRF_Update_STPState *ctx);

#define toprfupdate_stp_start_msg_SIZE ( sizeof(TOPRF_Update_Message)                          \
                                       + crypto_generichash_BYTES/*dst*/                       \
                                       + toprf_keyid_SIZE                                      \
                                       + crypto_sign_PUBLICKEYBYTES                            )

/** Starts a new execution of a STP TOPRF update protocol.

    This function initializes the state of the STP and creates an
    initial message containing the parameters for the peers.

    @param [in] ctx : pointer to a TOPRF_Update_STPState struct, this
                struct will be initialized by this function.

    @param [in] ts_epsilon: how many seconds a message can be old,
                before it is considered unfresh and is rejected. The
                correct value here is difficult to set, small local
                executions with only 2-out-of-3 setups will work with
                as few as 2-3 seconds, big deployments with
                126-out-of-127 might need up to a few hours...

    @param [in] n: the number of peers participating in this execution.

    @param [in] t: the threshold necessary to use the results of this
                update. This value should be the same as it was for
                the previous key being shared.

    @param [in] proto_name: an array of bytes used as a domain
                seperation tag (DST). Set it to the name of your application.

    @param [in] proto_name_len: the size of the array proto_name, to
                allow non-zero terminated DSTs.

    @param [in] keyid: The id of the key to be updated, MUST be
                exactly toprf_keyid_SIZE bytes long.

    @param [in] sig_pks: a pointer to a list of long-term signing
                keys. This list MUST have n+1 entries, with the STP's
                own long-term sig pubkey at index 0, and the rest of
                the items must be in order.

    @param [in] ltssk: The long-term signing private key of the STP.

    @param [in] msg0_len: the size of memory allocated to the msg0 parameter.
           should be exactly tpdkg_msg0_SIZE;

    @param [out] msg0: a message to be sent to all peers to initalize them.

    @return 0 if no errors.
 **/
int toprf_update_start_stp(TOPRF_Update_STPState *ctx, const uint64_t ts_epsilon,
                           const uint8_t n, const uint8_t t,
                           const char *proto_name, const size_t proto_name_len,
                           const uint8_t keyid[toprf_keyid_SIZE],
                           const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES],
                           const uint8_t ltssk[crypto_sign_SECRETKEYBYTES],
                           const size_t msg0_len, TOPRF_Update_Message *msg0);

/**
   This function sets all the variable sized buffers in the TOPRF_Update_STPState structure.

   A number of buffers are needed in the STP state that depend on the N and T parameters.
   These can be allocated on the stack as follows:

   @code
    uint16_t stp_p_complaints[n*n];
    uint16_t stp_y2_complaints[n*n];
    uint64_t last_ts[n];

    uint8_t stp_p_commitments_hashes[n][toprf_update_commitment_HASHBYTES];
    uint8_t stp_p_share_macs[n*n][crypto_auth_hmacsha256_BYTES];
    uint8_t stp_p_commitments[n*n][crypto_core_ristretto255_BYTES];

    uint8_t stp_k0p_commitments[dealers*(n+1)][crypto_core_ristretto255_BYTES];
    uint8_t stp_zk_challenge_commitments[dealers][3][crypto_scalarmult_ristretto255_SCALARBYTES];
    uint8_t stp_zk_challenge_e_i[dealers][crypto_scalarmult_ristretto255_SCALARBYTES];

    uint8_t k0p_final_commitments[n][crypto_scalarmult_ristretto255_BYTES];
    TOPRF_Update_Cheater stp_cheaters[t*t - 1];
    toprf_update_stp_set_bufs(&stp,
                              stp_p_complaints,
                              stp_y2_complaints,
                              &stp_cheaters,
                              sizeof(stp_cheaters) / sizeof(TOPRF_Update_Cheater),
                              &stp_p_commitments_hashes,
                              &stp_p_share_macs,
                              &stp_p_commitments,
                              &k0_commitments,
                              &stp_k0p_commitments,
                              &stp_zk_challenge_commitments,
                              &stp_zk_challenge_e_i,
                              &k0p_final_commitments,
                              last_ts);
   @endcode

   @param [in] cheater_max: is the number of max cheat attempts to be
          recorded. Normally the maximum is n*n-1. It should be provided as
          (sizeof(cheaters) / sizeof(TP_DKG_Cheater))

 */
void toprf_update_stp_set_bufs(TOPRF_Update_STPState *ctx,
                               uint16_t p_complaints[],
                               uint16_t y2_complaint[],
                               TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max,
                               uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES],
                               uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES],
                               uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES],
                               uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES],
                               uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES],
                               uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES],
                               uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES],
                               uint8_t (*k0p_final_commitments)[][crypto_scalarmult_ristretto255_BYTES],
                               uint64_t *last_ts);

/** @enum TOPRF-Update protocol steps for the peers
 */
typedef enum {
  TOPRF_Update_Peer_Broadcast_NPK_SIDNonce,
  TOPRF_Update_Peer_Rcv_NPK_SIDNonce,
  TOPRF_Update_Peer_Noise_Handshake,
  TOPRF_Update_Peer_Finish_Noise_Handshake,
  TOPRF_Update_Peer_Rcv_CHashes_Send_Commitments,
  TOPRF_Update_Peer_Rcv_Commitments_Send_Shares,
  TOPRF_Update_Peer_Verify_Commitments,
  TOPRF_Update_Peer_Handle_DKG_Complaints,
  TOPRF_Update_Peer_Defend_DKG_Accusations,
  TOPRF_Update_Peer_Check_Shares,
  TOPRF_Update_Peer_Finish_DKG,
  TOPRF_Update_Peer_Confirm_Transcripts,
  TOPRF_Update_Peer_Rcv_Mult_CHashes_Send_Commitments,
  TOPRF_Update_Peer_Send_K0P_Shares,
  TOPRF_Update_Peer_Recv_K0P_Shares,
  TOPRF_Update_Peer_Handle_Mult_Share_Complaints,
  TOPRF_Update_Peer_Defend_Mult_Accusations,
  TOPRF_Update_Peer_Check_Mult_Shares,
  TOPRF_Update_Peer_Disclose_Mult_Shares,
  TOPRF_Update_Peer_Reconstruct_Mult_Shares,
  TOPRF_Update_Peer_Send_ZK_Challenge_Commitments,
  TOPRF_Update_Peer_Send_ZK_Commitments,
  TOPRF_Update_Peer_Send_ZK_nonces,
  TOPRF_Update_Peer_Send_ZK_proofs,
  TOPRF_Update_Peer_Verify_ZK_proofs,
  TOPRF_Update_Peer_Disclose_ZK_Cheaters,
  TOPRF_Update_Peer_Reconstruct_ZK_Shares,
  TOPRF_Update_Peer_Send_Mult_Ci,
  TOPRF_Update_Peer_Final_VSPS_Checks,
  TOPRF_Update_Peer_Disclose_VSPS_Cheaters,
  TOPRF_Update_Peer_Reconstruct_VSPS_Shares,
  TOPRF_Update_Peer_Send_k0p_Share,
  TOPRF_Update_Peer_Final_OK,
  TOPRF_Update_Peer_Done
} TOPRF_Update_Peer_Steps;

/** @enum TOPRF_Update_Message_Type
    message identifiers used in the TOPRF-Update protocol
 */
typedef enum {
  toprfupdate_stp_start_msg,
  toprfupdate_peer_init_msg,
  toprfupdate_stp_bc_init_msg,
  toprfupdate_peer_ake1_msg,
  toprfupdate_peer_ake2_msg,
  toprfupdate_peer_dkg1_msg,
  toprfupdate_stp_bc_dkg1_msg,
  toprfupdate_peer_dkg2_msg,
  toprfupdate_stp_bc_dkg2_msg,
  toprfupdate_peer_dkg3_msg,
  toprfupdate_peer_verify_shares_msg,
  toprfupdate_stp_bc_verify_shares_msg,
  toprfupdate_peer_share_key_msg,
  toprfupdate_stp_bc_key_msg,
  toprfupdate_peer_bc_transcript_msg,
  toprfupdate_stp_bc_transcript_msg,
  toprfupdate_peer_mult1_msg,
  toprfupdate_stp_bc_mult1_msg,
  toprfupdate_peer_mult_coms_msg,
  toprfupdate_stp_bc_mult_coms_msg,
  toprfupdate_peer_mult2_msg,
  toprfupdate_peer_verify_mult_shares_msg,
  toprfupdate_peer_share_mult_key_msg,
  toprfupdate_stp_bc_mult_key_msg,
  toprfupdate_peer_reconst_mult_shares_msg,
  toprfupdate_stp_bc_reconst_mult_shares_msg,
  toprfupdate_peer_zkp1_msg,
  toprfupdate_stp_bc_zkp1_msg,
  toprfupdate_peer_zkp2_msg,
  toprfupdate_stp_bc_zkp2_msg,
  toprfupdate_peer_zkp3_msg,
  toprfupdate_stp_bc_zkp3_msg,
  toprfupdate_peer_zkp4_msg,
  toprfupdate_stp_bc_zkp4_msg,
  toprfupdate_peer_zk_disclose_msg,
  toprfupdate_stp_bc_zk_disclose_msg,
  toprfupdate_peer_mult3_msg,
  toprfupdate_stp_bc_mult3_msg,
  toprfupdate_peer_vsps_disclose_msg,
  toprfupdate_stp_bc_vsps_disclose_msg,
  toprfupdate_peer_end2_msg,
  toprfupdate_stp_end3_msg,
} TOPRF_Update_Message_Type;


/** @struct TOPRF_Update_ZK_params

    This struct bundles the private parameters to a ZK proof.
*/
typedef struct {
  uint8_t d[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t s[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t x[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t s_1[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t s_2[crypto_scalarmult_ristretto255_SCALARBYTES];
} TOPRF_Update_ZK_params;

/** @struct TOPRF_Update_ZK_proof

    This struct bundles values of the ZK proof.
*/
typedef struct {
  uint8_t y[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t w[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t z[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t w_1[crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t w_2[crypto_scalarmult_ristretto255_SCALARBYTES];
} TOPRF_Update_ZK_proof;

/** @struct TOPRF_Update_PeerState

    This struct contains the state of a peer during the execution of
    the TOPRF Update protocol.

    Most values of this struct are internal variables and should not
    be used. The following variables are useful and can be used by
    users of this API:

    @var TOPRF_Update_PeerState:n This field contains the value N,
         specifying the total number of peers participating in this
         protocol.

    @var TOPRF_Update_PeerState:t This field contains the value T,
         specifying the threshold necessary to use shared secret
         generated by this DKG.

    @var TOPRF_Update_PeerState:index This field contains the index of
         the peer,

    @var TOPRF_Update_PeerState:cheaters This field contains a list of
         cheaters and protocol violators at the end of a failed
         protocol run.

*/
typedef struct {
  TOPRF_Update_Peer_Steps step;
  TOPRF_Update_Peer_Steps prev;
  uint8_t sessionid[dkg_sessionid_SIZE];
  uint8_t n;
  uint8_t t;
  uint8_t index;
  TOPRF_Share kc0_share[2];
  uint8_t (*kc0_commitments)[][crypto_core_ristretto255_BYTES];
  uint8_t sig_pk[crypto_sign_PUBLICKEYBYTES];
  uint8_t sig_sk[crypto_sign_SECRETKEYBYTES];
  uint8_t noise_pk[crypto_scalarmult_BYTES];
  uint8_t noise_sk[crypto_scalarmult_SCALARBYTES];
  uint64_t stp_last_ts;
  uint64_t *last_ts;
  uint64_t ts_epsilon;
  const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES];
  uint8_t (*peer_noise_pks)[][crypto_scalarmult_BYTES];
  Noise_XK_device_t *dev;
  Noise_XK_session_t *(*noise_outs)[];
  Noise_XK_session_t *(*noise_ins)[];

  uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE];

  TOPRF_Share (*p_shares)[][2];
  uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES];
  uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES];
  uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES];
  uint16_t p_complaints_len;
  uint16_t *p_complaints;
  uint8_t my_p_complaints_len;
  uint8_t *my_p_complaints;
  TOPRF_Share p_share[2];

  uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES];
  TOPRF_Share (*k0p_shares)[][2];
  uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES];
  uint8_t k0p_tau[crypto_core_ristretto255_SCALARBYTES];

  uint8_t zk_chal_nonce[2][crypto_core_ristretto255_SCALARBYTES];
  uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES];
  uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES];
  TOPRF_Update_ZK_params zk_params;

  TOPRF_Share k0p_share[2];
  uint8_t k0p_commitment[crypto_core_ristretto255_BYTES];

  size_t cheater_len;
  TOPRF_Update_Cheater (*cheaters)[];
  size_t cheater_max;
  crypto_generichash_state transcript_state;
  uint8_t transcript[crypto_generichash_BYTES];
} TOPRF_Update_PeerState;

/** Gets the size of a TOPRF_Update_PeerState struct

    Useful if you need to allocate space for it, be mindful though
    - if you do - that the struct MUST be aligned at 32 B boundary!
    If in doubt check out how the python wrapper handles this.
 */
size_t toprf_update_peerstate_size(void);

/** Gets the value of N from a TOPRF_Update_PeerState struct
 */
uint8_t toprf_update_peerstate_n(const TOPRF_Update_PeerState *ctx);

/** Gets the value of T from a TOPRF_Update_PeerState struct
 */
uint8_t toprf_update_peerstate_t(const TOPRF_Update_PeerState *ctx);

/** Gets the sessionid from a TOPRF_Update_PeerState struct
 */
const uint8_t* toprf_update_peerstate_sessionid(const TOPRF_Update_PeerState *ctx);

/** Gets the final result, the private share of the updated OPRF key
    from a TOPRF_Update_PeerState struct

    use this only if the protocol finished succesfully.
 */
const uint8_t* toprf_update_peerstate_share(const TOPRF_Update_PeerState *ctx);

/** Gets the final result, the public commitment to a share of the
    updated OPRF key from a TOPRF_Update_PeerState struct

    use this only if the protocol finished succesfully.
 */
const uint8_t* toprf_update_peerstate_commitment(const TOPRF_Update_PeerState *ctx);

/** Gets the final result, the all the public commitments to the
    shares of the updated OPRF key from a TOPRF_Update_PeerState
    struct

    use this only if the protocol finished succesfully.
 */
const uint8_t* toprf_update_peerstate_commitments(const TOPRF_Update_PeerState *ctx);

/** Gets the current step of the protocol from a TOPRF_Update_PeerState struct
 */
int toprf_update_peerstate_step(const TOPRF_Update_PeerState *ctx);

/** Starts a new execution of a TOPRF-Update protocol for a peer.

    This function initializes the state of the peer.

    @param [in] ctx : pointer to a TOPRF_Update_PeerState struct, this struct
                will be initialized by this function.

    @param [in] ts_epsilon: how many seconds a message can be old,
                before it is considered unfresh and is rejected. The
                correct value here is difficult to set, small local
                executions with only 2-out-of-3 setups will work with
                as few as 2-3 seconds, big deployments with
                126-out-of-127 might need up to a few hours...

    @param [in] lt_sk: the long-term private signing key of the peer.

    @param [in] noise_sk: the Noise XK private key of the peer.

    @param [in] msg0: the initiating msg sent from the TP after the TP
                run tpdkg_tp_start().

    @param [out] keyid: they id of the key to be updated, use this
                 info to look up the N-T, parameters, the key to be
                 updated, and the various long-term public keys of the
                 other shareholders.

    @param [out] stp_ltpk: the STPs long-term public signing
                 key. Should be used to verify if this key is actually
                 authorized to initiate an STP DKG with the peer.

    @return 0 if no errors.
 **/
TOPRF_Update_Err toprf_update_start_peer(TOPRF_Update_PeerState *ctx,
                                         const uint64_t ts_epsilon,
                                         const uint8_t lt_sk[crypto_sign_SECRETKEYBYTES],
                                         const uint8_t noise_sk[crypto_scalarmult_SCALARBYTES],
                                         const TOPRF_Update_Message *msg0,
                                         uint8_t keyid[toprf_keyid_SIZE],
                                         uint8_t stp_ltpk[crypto_sign_PUBLICKEYBYTES]);

/** This function sets all the variable sized buffers in the TOPRF_Update_PeerState structure.

  The buffer sizes depend on the n and t parameters to the DKG tese
  are announced by the STP in msg0, which is an input to the
  toprf_update_start_peer() function, after this function the
  peerstate is initialized and can be used to find out the n-t
  parameters and the keyid to load the corresponding data.

  Some of the buffers passed to this function must be initialized:

  @param [in] index: must be initialized based on the index of the k0
              share index. If no share is held by the peer the index
              must be established out of band. TODO fix this

  @param [in] n: must be initialized based on the info associated with
              the keyid returned by toprff_update_start_peer().

  @param [in] t: must be initialized based on the info associated with
              the keyid returned by toprff_update_start_peer().

  @param [in] k0: the shares of the old key held by the peer, pass
              NULL if this peer has no share.

  @param [in] k0_commitments: The commitments to the shares of the old
              key. Even if peer has no share, they must have these
              commitments. TODO check if this is actually true.

  @param [in] sig_pks: an array of all long-term signing public keys
              of all participants. The public key of the STP must be
              at 0th index, and all other keys must be in order of the
              indexes of the shares of k0 they hold. Peers that hold
              no k0 shares can be listed in arbitrary order following
              those with shares.

  @param [in] noise_pks: an array of all noise XK public keys of all
              peers, in the order of the indexes off the shares of k0.
              Peers that hold no k0 shares can be listed in arbitrary
              order following those with shares.

  If you want you can allocate all the buffers on the stack like this:

  @code
  Noise_XK_session_t *noise_outs[n];
  Noise_XK_session_t *noise_ins[n];

  TOPRF_Share pshares[n][2];
  uint8_t p_commitments[n*n][crypto_core_ristretto255_BYTES];
  uint8_t p_commitments_hashes[n][toprf_update_commitment_HASHBYTES];
  uint8_t peers_p_share_macs[n*n][crypto_auth_hmacsha256_BYTES];
  uint16_t peer_p_complaints[n*n];
  uint8_t peer_my_p_complaints[n];

  uint8_t encrypted_shares[n][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE];

  uint64_t peer_last_ts[n];
  uint8_t lambdas[dealers][crypto_core_ristretto255_SCALARBYTES];
  TOPRF_Share k0p_shares[dealers][2];
  uint8_t k0p_commitments[dealers*(n+1)][crypto_core_ristretto255_BYTES];
  uint8_t zk_challenge_nonce_commitments[n*2][crypto_scalarmult_ristretto255_BYTES];
  uint8_t zk_challenge_nonces[n*2][2][crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t zk_challenge_commitments[dealers*2][3][crypto_scalarmult_ristretto255_SCALARBYTES];
  uint8_t zk_challenge_e_i[2*dealers][crypto_scalarmult_ristretto255_SCALARBYTES];
  TOPRF_Update_Cheater peer_cheaters[n*n - 1];

  if(0!=toprf_update_peer_set_bufs(&peer, index, n, t, k0_share,
                                   &k0_commitments,
                                   &lt_pks, &peers_noise_pks, noise_sk,
                                   &noise_outs, &noise_ins,
                                   &pshares,
                                   &p_commitments,
                                   &p_commitments_hashes,
                                   &peers_p_share_macs,
                                   &encrypted_shares,
                                   &peer_cheaters, sizeof(peer_cheaters) / sizeof(TOPRF_Update_Cheater) / n,
                                   &lambdas,
                                   &k0p_shares,
                                   &k0p_commitments,
                                   &zk_challenge_nonce_commitments,
                                   &zk_challenge_nonces,
                                   &zk_challenge_commitments,
                                   &zk_challenge_e_i,
                                   peer_p_complaints,
                                   peer_my_p_complaints,
                                   peer_last_ts)) return 1;
  @endcode
*/
int toprf_update_peer_set_bufs(TOPRF_Update_PeerState *ctx,
                               const uint8_t self,
                               const uint8_t n, const uint8_t t,
                               const TOPRF_Share k0[2],
                               uint8_t (*k0_commitments)[][crypto_core_ristretto255_BYTES],
                               const uint8_t (*sig_pks)[][crypto_sign_PUBLICKEYBYTES],
                               uint8_t (*noise_pks)[][crypto_scalarmult_BYTES],
                               Noise_XK_session_t *(*noise_outs)[],
                               Noise_XK_session_t *(*noise_ins)[],
                               TOPRF_Share (*p_shares)[][2],
                               uint8_t (*p_commitments)[][crypto_core_ristretto255_BYTES],
                               uint8_t (*p_commitments_hashes)[][toprf_update_commitment_HASHBYTES],
                               uint8_t (*p_share_macs)[][crypto_auth_hmacsha256_BYTES],
                               uint8_t (*encrypted_shares)[][noise_xk_handshake3_SIZE + toprf_update_encrypted_shares_SIZE],
                               TOPRF_Update_Cheater (*cheaters)[], const size_t cheater_max,
                               uint8_t (*lambdas)[][crypto_core_ristretto255_SCALARBYTES],
                               TOPRF_Share (*k0p_shares)[][2],
                               uint8_t (*k0p_commitments)[][crypto_core_ristretto255_BYTES],
                               uint8_t (*zk_challenge_nonce_commitments)[][crypto_scalarmult_ristretto255_BYTES],
                               uint8_t (*zk_challenge_nonces)[][2][crypto_scalarmult_ristretto255_SCALARBYTES],
                               uint8_t (*zk_challenge_commitments)[][3][crypto_scalarmult_ristretto255_SCALARBYTES],
                               uint8_t (*zk_challenge_e_i)[][crypto_scalarmult_ristretto255_SCALARBYTES],
                               uint16_t *p_complaints,
                               uint8_t *my_p_complaints,
                               uint64_t *last_ts);

/**
   This function calculates the size of the buffer needed to hold all
   outputs from the peers serving as input to the next step of the STP.

   An implementer should allocate a buffer of this size, and
   concatenate all messages from all peers in the order of the peers.

   The allocated buffer is to be passed as an input to the
   toprf_update_stp_next() function, after this the buffer SHOULD be
   deallocated.

   @param [in] ctx: an initialized TOPRF_Update_STPState struct.
   @return 1 on error, otherwise the size to be allocated (can be 0)
 */
size_t toprf_update_stp_input_size(const TOPRF_Update_STPState *ctx);

/**
   This function calculates the size of the message from each peer to
   be received by the STP.

   @param [in] ctx: an initialized TOPRF_Update_STPState struct.
   @param [out] sizes: a array of type size_t with exactly N elements.

   @return 0 on if the sizes differ from peer to peer, otherwise all
           peers will be sending messages of equal size. In the latter
           case all items of the sizes array hold the same valid value.
 */
int toprf_update_stp_input_sizes(const TOPRF_Update_STPState *ctx, size_t *sizes);

/**
   This function calculates the size of the buffer needed to hold the
   output from the toprf_update_stp_next() function.

   An implementer should allocate a buffer of this size and pass it as
   parameter to toprf_update_stp_next().

   @param [in] ctx: an initialized TOPRF_Update_STPState struct.
   @return 1 on error, otherwise the size to be allocated (can be 0)
*/
size_t toprf_update_stp_output_size(const TOPRF_Update_STPState *ctx);

/**
   This function exeutes the next step of the TOPRF update protocol
   for the semi-trusted party.

   @param [in] ctx: pointer to a valid TOPRF_Update_STPState.
   @param [in] input: buffer to the input of the current step.
   @param [in] input_len: size of the input buffer.
   @param [out] output: buffer to the output of the current step.
   @param [in] output_len: size of the output buffer.
   @return 0 if no error

   An example of how to use this in concert with
   toprf_update_stp_input_size() and toprf_update_stp_output_size():

   @code
    uint8_t stp_out[toprf_update_stp_output_size(&tp)];
    uint8_t stp_in[toprf_update_stp_input_size(&tp)];
    recv(socket, stp_in, sizeof(stp_in));
    ret = toprf_update_stp_next(&stp,
                                stp_in, sizeof(stp_in),
                                stp_out, sizeof stp_out);
   @endcode
 */
int toprf_update_stp_next(TOPRF_Update_STPState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len);

/**
   This function "converts" the output of toprf_update_stp_next() into a
   message for the i-th peer.

   The outputs of steps of the protocol are sometimes broadcast
   messages where the output is the same for all peers, but some of
   the outputs are dedicated and unique messages for each peer. This
   function returns a pointer to a message and the size of the message
   to be sent for a particular peer specified as a parameter.

   @param [in] ctx: pointer to a valid TOPRF_Update_STPState.
   @param [in] base: a pointer to the output of the toprf_update_stp_next() function.
   @param [in] base_size: the size of the output of the toprf_update_stp_next() function.
   @param [in] peer: the index of the peer (starting with 0 for the first)
   @param [out] msg: pointer to a pointer to the message to be sent to the ith peer.
   @param [out] len: pointer to the length of the message to be sent to the ith peer.
   @return 0 if no error

   example how to use this in concert with toprf_update_stp_next():

   @code
    ret = toprf_update_stp_next(&stp, stp_in, sizeof(stp_in), stp_out, sizeof stp_out);

    for(int i=0;i<stp.n;i++) {
      const uint8_t *msg;
      size_t len;
      if(0!=toprf_update_stp_peer_msg(&stp, stp_out, sizeof stp_out, i, &msg, &len)) {
        return 1;
      }
      send(i, msg, len);
    }
    @endcode

 */
int toprf_update_stp_peer_msg(const TOPRF_Update_STPState *ctx, const uint8_t *base, const size_t base_size, const uint8_t peer, const uint8_t **msg, size_t *len);

/** This function checks if the protocol has finished for the STP or
    more toprf_update_stp_next() calls are necessary.

   @return 1 if more steps outstanding
 */
int toprf_update_stp_not_done(const TOPRF_Update_STPState *stp);

/**
   This function calculates the size of the buffer needed to hold the
   output from the STP serving as input to the next step of the peer.

   An implementer should allocate a buffer of this size.

   The allocated buffer is to be passed as an input to the
   toprf_update_peer_next() function, after this the buffer SHOULD be
   deallocated.

   @param [in] ctx: an initialized TOPRF_Update_PeerState struct.
   @return 1 on error, otherwise the size to be allocated (can be 0)
 */
size_t toprf_update_peer_input_size(const TOPRF_Update_PeerState *ctx);

/**
   This function calculates the size of the buffer needed to hold the
   output from the toprf_update_peer_next() function.

   An implementer should allocate a buffer of this size and pass it as
   parameter to toprf_update_peer_next().

   @param [in] ctx: an initialized TOPRF_Update_PeerState struct.
   @return 1 on error, otherwise the size to be allocated (can be 0)
*/
size_t toprf_update_peer_output_size(const TOPRF_Update_PeerState *ctx);

/**
   This function exeutes the next step of the TOPRF Update protocol
   for a peer.

   @param [in] ctx: pointer to a valid TOPRF_Update_PeerState.
   @param [in] input: buffer to the input of the current step.
   @param [in] input_len: size of the input buffer.
   @param [out] output: buffer to the output of the current step.
   @param [in] output_len: size of the output buffer.
   @return 0 if no error

   An example of how to use this in concert with
   toprf_update_peer_input_size() and toprf_update_peer_output_size()
   while allocating the buffers on the stack:

   @code
   uint8_t peers_out[toprf_update_peer_output_size(&peer)];

   uint8_t peer_in[toprf_update_peer_input_size(&peer)];
   recv(socket, peer_in, sizeof(peer_in));
   ret = toprf_update_peer_next(&peer,
                                peer_in, sizeof(peer_in),
                                peers_out, sizeof(peers_out));
   @endcode
 */
int toprf_update_peer_next(TOPRF_Update_PeerState *ctx, const uint8_t *input, const size_t input_len, uint8_t *output, const size_t output_len);

/**
   This function checks if the protocol has finished for the peer or
   more toprf_update_peer_next() calls are necessary.

   @return 1 if more steps outstanding
 */
int toprf_update_peer_not_done(const TOPRF_Update_PeerState *peer);

/**
   This function MUST be called before a peers state is deallocated.

   Unfortunately the underlying (but very cool and formally verified)
   Noise XK implementation does allocate a lot of internal state on
   the heap, and thus this must be freed manually.
 */
void toprf_update_peer_free(TOPRF_Update_PeerState *ctx);

/**
   log_file is a FILE* pointing to an open file or NULL.

   If the file is not NULL the implementation will emit log messages
   to this file.
 */
extern FILE* log_file;

#endif //TOPRF_UPDATE_H
