#ifndef SECP256K1_RANGEPROOF_H
# define SECP256K1_RANGEPROOF_H

# include "secp256k1.h"
# include "secp256k1_generator.h"

# ifdef __cplusplus
extern "C" {
# endif

#include <stdint.h>

/** This module implements a variant of Back-Maxwell range proofs as described
 * in the Confidential Assets paper (https://blockstream.com/bitcoin17-final41.pdf).
 * The construction is based on Borromean ring signatures.
 * (https://nt4tn.net/papers/borromean_draft_0.01_34241bb.pdf)
 *
 * This implementation differs from the variant in the paper mainly in that it
 * omits an optimization that saves one scalar per ring. This optimization complicates
 * the protocol and security analysis, as it requires differentiating cases where
 * the i-th bit v_i = 0 versus otherwise, and makes calculating response points R_i less
 * straightforward. The implemented version uses Borromean ring signatures in
 * an unmodified way.
 *
 * Another difference is that the implementation omits the last ring's commitment
 * from the proof, which is recovered by the verifier by subtracting all other digit
 * commitments from the total, reducing proof size by one group element.
 *
 * Furthermore, in the implementation every hash calculation includes a message
 * m=SHA256(C||H||header||C_0||...||C_(n-2)||extra_commit), binding the commitment C,
 * generator H, proof header, the n-1 explicit digit commitments, and any extra data.
 * This prevents an attack that would compromise non-malleability. In the paper's
 * version of the protocol, a prover could pick distinct indices i, j and a scalar y,
 * and modify digit commitments in the original proof by setting C'_i = C_i + yG and
 * C'_j = C_j - yG, obtaining a different valid proof for the same commitment and
 * witness.
 *
 * In the current implementation, up to 3968 bytes of message data can be
 * embedded and recovered within maximally-sized proofs. The implemented embedding
 * method using the forged parts of ring signatures could also be applied to the
 * construction in the paper, but is not mentioned there. Message embedding is used
 * in Confidential Assets to transmit values and blinding factors of the corresponding
 * commitments. This is possible because randomness is generated by seeding HMAC-DRBG
 * with the shared ECDH key, allowing the receiver to rewind the proof using the same
 * random values the sender used.
 */

/** Length of a message that can be embedded into a maximally-sized rangeproof
 *
 * It is not be possible to fit a message of this size into a non-maximally-sized
 * rangeproof, but it is guaranteed that any embeddable message can fit into an
 * array of this size. This constant is intended to be used for memory allocations
 * and sanity checks.
 */
#define SECP256K1_RANGEPROOF_MAX_MESSAGE_LEN 3968

/** Verify a proof that a committed value is within a range.
 * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs.
 *         0: Proof failed or other error.
 * In:   ctx: pointer to a context object (not secp256k1_context_static)
 *       commit: the commitment being proved. (cannot be NULL)
 *       proof: pointer to character array with the proof. (cannot be NULL)
 *       plen: length of proof in bytes.
 *       extra_commit: additional data covered in rangeproof signature
 *       extra_commit_len: length of extra_commit byte array (0 if NULL)
 *       gen: additional generator 'h'
 * Out:  min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL)
 *       max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL)
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify(
  const secp256k1_context *ctx,
  uint64_t *min_value,
  uint64_t *max_value,
  const secp256k1_pedersen_commitment *commit,
  const unsigned char *proof,
  size_t plen,
  const unsigned char *extra_commit,
  size_t extra_commit_len,
  const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(9);

/** Verify a range proof proof and rewind the proof to recover information sent by its author.
 *  Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered.
 *          0: Proof failed, rewind failed, or other error.
 *  In:   ctx: pointer to a context object (not secp256k1_context_static)
 *        commit: the commitment being proved. (cannot be NULL)
 *        proof: pointer to character array with the proof. (cannot be NULL)
 *        plen: length of proof in bytes.
 *        nonce: 32-byte secret nonce used by the prover (cannot be NULL)
 *        extra_commit: additional data covered in rangeproof signature
 *        extra_commit_len: length of extra_commit byte array (0 if NULL)
 *        gen: additional generator 'h'
 *  In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment
 *        value_out: pointer to an unsigned int64 which has the exact value of the commitment.
 *        message_out: pointer to a 4096 byte character array to receive message data from the proof author.
 *        outlen: length of message data written to message_out. This is generally not equal to the
 *                msg_len used by the signer. However, for all i with msg_len <= i < outlen, it is
 *                guaranteed that message_out[i] == 0.
 *        min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL)
 *        max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL)
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind(
  const secp256k1_context *ctx,
  unsigned char *blind_out,
  uint64_t *value_out,
  unsigned char *message_out,
  size_t *outlen,
  const unsigned char *nonce,
  uint64_t *min_value,
  uint64_t *max_value,
  const secp256k1_pedersen_commitment *commit,
  const unsigned char *proof,
  size_t plen,
  const unsigned char *extra_commit,
  size_t extra_commit_len,
  const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10) SECP256K1_ARG_NONNULL(14);

/** Author a proof that a committed value is within a range.
 *  Returns 1: Proof successfully created.
 *          0: Error
 *  In:     ctx:    pointer to a context object (not secp256k1_context_static)
 *          proof:  pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL)
 *          min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount.
 *          commit: the commitment being proved.
 *          blind:  32-byte blinding factor used by commit. The blinding factor may be all-zeros as long as min_bits is set to 3 or greater.
 *                  This is a side-effect of the underlying crypto, not a deliberate API choice, but it may be useful when balancing CT transactions.
 *          nonce:  32-byte secret nonce used to initialize the proof.
 *
 *                  Each call to this function must have a UNIQUE nonce that
 *                  MUST NOT BE REUSED in subsequent calls. The nonce must be
 *                  KEPT SECRET except from parties authorized to rewind the
 *                  proof. Anyone who knows the nonce can recover `value` and
 *                  `blind` from the proof. Reusing the nonce may expose `blind`
 *                  even to parties that do not know the nonce.
 *          exp:    Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18.
 *                  (-1 is a special case that makes the value public. 0 is the most private.)
 *          min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64).
 *          value:  Actual value of the commitment.
 *          message: pointer to a byte array of data to be embedded in the rangeproof that can be recovered by rewinding the proof
 *          msg_len: size of the message to be embedded in the rangeproof
 *          extra_commit: additional data to be covered in rangeproof signature
 *          extra_commit_len: length of extra_commit byte array (0 if NULL)
 *          gen: additional generator 'h'
 *  In/out: plen:   point to an integer with the size of the proof buffer and the size of the constructed proof.
 *
 *  If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64.
 *
 *  If exp is -1 the value is revealed by the proof (e.g. it proves that the proof is a blinding of a specific value, without revealing the blinding key.)
 *
 *  This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding.
 *
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign(
  const secp256k1_context *ctx,
  unsigned char *proof,
  size_t *plen,
  uint64_t min_value,
  const secp256k1_pedersen_commitment *commit,
  const unsigned char *blind,
  const unsigned char *nonce,
  int exp,
  int min_bits,
  uint64_t value,
  const unsigned char *message,
  size_t msg_len,
  const unsigned char *extra_commit,
  size_t extra_commit_len,
  const secp256k1_generator *gen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(15);

/** Extract some basic information from a range-proof.
 *  Returns 1: Information successfully extracted.
 *          0: Decode failed.
 *  In:   ctx: pointer to a context object
 *        proof: pointer to character array with the proof.
 *        plen: length of proof in bytes.
 *  Out:  exp: Exponent used in the proof (-1 means the value isn't private).
 *        mantissa: Number of bits covered by the proof.
 *        min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL)
 *        max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL)
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info(
  const secp256k1_context *ctx,
  int *exp,
  int *mantissa,
  uint64_t *min_value,
  uint64_t *max_value,
  const unsigned char *proof,
  size_t plen
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Returns an upper bound on the size of a rangeproof with the given parameters
 *
 * An actual rangeproof may be smaller, for example if the actual value
 * is less than both the provided `max_value` and 2^`min_bits`, or if
 * the `exp` parameter to `secp256k1_rangeproof_sign` is set such that
 * the proven range is compressed. In particular this function will always
 * overestimate the size of single-value proofs. Also, if `min_value`
 * is set to 0 in the proof, the result will usually, but not always,
 * be 8 bytes smaller than if a nonzero value had been passed.
 *
 * The goal of this function is to provide a useful upper bound for
 * memory allocation or fee estimation purposes, without requiring
 * too many parameters be fixed in advance.
 *
 * To obtain the size of largest possible proof, set `max_value` to
 * `UINT64_MAX` (and `min_bits` to any valid value such as 0).
 *
 *  In:       ctx: pointer to a context object
 *      max_value: the maximum value that might be passed for `value` for the proof.
 *       min_bits: the value that will be passed as `min_bits` for the proof.
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT size_t secp256k1_rangeproof_max_size(
  const secp256k1_context *ctx,
  uint64_t max_value,
  int min_bits
) SECP256K1_ARG_NONNULL(1);

# ifdef __cplusplus
}
# endif

#endif
