#ifndef SECP256K1_SILENTPAYMENTS_H
#define SECP256K1_SILENTPAYMENTS_H

#include <stdint.h>
#include "secp256k1.h"
#include "secp256k1_extrakeys.h"

#ifdef __cplusplus
extern "C" {
#endif

/** This module provides an implementation for Silent Payments, as specified in
 *  BIP352. This particularly involves the creation of input tweak data by
 *  summing up secret or public keys and the derivation of a shared secret using
 *  Elliptic Curve Diffie-Hellman. Combined are either:
 *    - spender's secret keys and recipient's public key (a * B, sender side)
 *    - spender's public keys and recipient's secret key (A * b, recipient side)
 *  With this result, the necessary key material for ultimately creating/scanning
 *  or spending Silent Payments outputs can be determined.
 *
 *  Note that this module is _not_ a full implementation of BIP352, as it
 *  inherently doesn't deal with higher-level concepts like addresses, output
 *  script types or transactions. The intent is to provide a module for
 *  abstracting away the elliptic-curve operations required for the protocol. For
 *  any wallet software already using libsecp256k1, this API should provide all
 *  the functions needed for a Silent Payments implementation without requiring
 *  any further elliptic-curve operations from the wallet.
 */

/* Maximum number of Silent Payments recipients per group (i.e.
 * recipients sharing the same scan public key) as per BIP-352 */
#define SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT 2323

/** The data from a single recipient address
 *
 *  This struct serves as an input argument to `silentpayments_sender_create_outputs`.
 *
 *  `index` must be set to the position (starting with 0) of this recipient in the
 *  `recipients` array passed to `silentpayments_sender_create_outputs`. It is
 *  used to map the returned generated outputs back to the original recipient.
 *
 *  Note:
 *  The spend public key named `spend_pubkey` may have been optionally tweaked with
 *  a label by the recipient. Whether `spend_pubkey` has actually been tagged with
 *  a label is irrelevant for the sender. As a documentation convention in this API,
 *  `unlabeled_spend_pubkey` is used to indicate when the unlabeled spend public key
 *  must be used.
 */
typedef struct secp256k1_silentpayments_recipient {
    secp256k1_pubkey scan_pubkey;
    secp256k1_pubkey spend_pubkey;
    size_t index;
} secp256k1_silentpayments_recipient;

/** Create Silent Payments outputs for recipient(s).
 *
 *  Given a list of n secret keys a_1...a_n (one for each Silent Payments
 *  eligible input to spend), a serialized outpoint, and a list of recipients,
 *  create the taproot outputs. Inputs with conditional branches or multiple
 *  public keys are excluded from Silent Payments eligible inputs; see BIP352
 *  for more information.
 *
 *  `outpoint_smallest36` refers to the smallest outpoint lexicographically
 *  from the transaction inputs (both Silent Payments eligible and non-eligible
 *  inputs). This value MUST be the smallest outpoint out of all of the
 *  transaction inputs, otherwise the recipient will be unable to find the
 *  payment. Determining the smallest outpoint from the list of transaction
 *  inputs is the responsibility of the caller. It is strongly recommended
 *  that implementations ensure they are doing this correctly by using the
 *  test vectors from BIP352.
 *
 *  When creating more than one generated output, all of the generated outputs
 *  MUST be included in the final transaction. Dropping any of the generated
 *  outputs from the final transaction may make all or some of the outputs
 *  unfindable by the recipient.
 *
 *  Returns: 1 if creation of outputs was successful.
 *           0 on failure, i.e., when one of the following occurs:
 *             - The size of any group (i.e. recipients sharing the same scan public key)
 *               exceeds the protocol limit SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT.
 *             - The sum of all input secret keys is 0.
 *               (This occurs only with negligible probability if at least one of the
 *               input secret keys is uniformly random and independent of all other keys.)
 *             - An invalid output public key is created. (This can only happen for an
 *               adversarially chosen recipient spend public key.)
 *
 *  Args:                ctx: pointer to a context object
 *                            (not secp256k1_context_static).
 *  Out:   generated_outputs: pointer to an array of pointers to xonly public keys,
 *                            one per recipient.
 *                            The outputs are ordered to match the original
 *                            ordering of the recipient objects, i.e.,
 *                            `generated_outputs[0]` is the generated output
 *                            for the `secp256k1_silentpayments_recipient` object
 *                            with index = 0.
 *  In:           recipients: pointer to an array of pointers to Silent Payments
 *                            recipients, where each recipient is a scan public
 *                            key, a spend public key, and an index indicating
 *                            its position in the original ordering. This function
 *                            may reorder the pointers to the recipient objects
 *                            within the array, i.e., after the call (including on
 *                            failure), the index fields of the recipient objects
 *                            may no longer correspond to the positions in the
 *                            array. Multiple recipient objects with the same scan
 *                            public key and/or same spend public key can be passed
 *                            if they carry different indices.
 *              n_recipients: the size of the recipients array.
 *       outpoint_smallest36: serialized (36-byte) smallest outpoint
 *                            (lexicographically) from the transaction inputs
 *                  keypairs: pointer to an array of pointers to taproot
 *                            keypair inputs (can be NULL if no secret keys
 *                            of taproot inputs are used)
 *                n_keypairs: the size of the keypairs array.
 *                   seckeys: pointer to an array of pointers to 32-byte
 *                            secret keys of non-taproot inputs (can be NULL
 *                            if no secret keys of non-taproot inputs are
 *                            used)
 *                 n_seckeys: the size of the seckeys array.
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_sender_create_outputs(
    const secp256k1_context *ctx,
    secp256k1_xonly_pubkey **generated_outputs,
    const secp256k1_silentpayments_recipient **recipients,
    size_t n_recipients,
    const unsigned char *outpoint_smallest36,
    const secp256k1_keypair * const *keypairs,
    size_t n_keypairs,
    const unsigned char * const *seckeys,
    size_t n_seckeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);

/** Opaque data structure that holds a Silent Payments label.
 *
 *  Guaranteed to be 68 bytes in size. Serialized and parsed with
 *  `secp256k1_silentpayments_recipient_label_serialize` and
 *  `secp256k1_silentpayments_recipient_label_parse`.
 */
typedef struct secp256k1_silentpayments_label {
    unsigned char data[68];
} secp256k1_silentpayments_label;

/** Parse a Silent Payments label.
 *
 *  Returns: 1 when the label could be parsed, 0 otherwise.
 *  Args:    ctx: pointer to a context object
 *  Out:   label: pointer to a label object
 *  In:     in33: pointer to the 33-byte label to be parsed
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_label_parse(
    const secp256k1_context *ctx,
    secp256k1_silentpayments_label *label,
    const unsigned char *in33
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Serialize a Silent Payments label
 *
 *  Returns: 1 always
 *  Args:    ctx: pointer to a context object
 *  Out:   out33: pointer to a 33-byte array to store the serialized label
 *  In:    label: pointer to the label
 */
SECP256K1_API int secp256k1_silentpayments_recipient_label_serialize(
    const secp256k1_context *ctx,
    unsigned char *out33,
    const secp256k1_silentpayments_label *label
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Create Silent Payments label tweak and label.
 *
 *  Given a recipient's 32 byte scan key and a label integer m, calculate the
 *  corresponding label tweak and label:
 *
 *      label_tweak = hash(scan_key || m)
 *            label = label_tweak * G
 *
 *  Returns: 1 if label tweak and label creation was successful.
 *           0 if scan_key32 is invalid or the hash output label_tweak32 is
 *             not a valid scalar (negligible probability per hash evaluation).
 *
 * WARNING: Creating a large number of labels may significantly degrade
 * scanning performance in certain Silent Payments wallet implementations,
 * such as light clients. The scanning function provided in this module,
 * which is designed for full nodes, performs consistently even with hundreds
 * of thousands of labels. Other implementations may not share this property
 * or may be unable to use it due to lacking full transaction data.
 *
 * To maximize wallet interoperability, it is recommended to create only
 * the change label (m = 0) and avoid distributing labeled addresses.
 *
 *  Args:                ctx: pointer to a context object
 *                            (not secp256k1_context_static)
 *  Out:               label: pointer to the resulting label
 *             label_tweak32: pointer to the 32 byte label tweak
 *  In:           scan_key32: pointer to the recipient's 32 byte scan key
 *                         m: integer for the m-th label (0 is used for change outputs)
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_label_create(
    const secp256k1_context *ctx,
    secp256k1_silentpayments_label *label,
    unsigned char *label_tweak32,
    const unsigned char *scan_key32,
    uint32_t m
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Create Silent Payments labeled spend public key.
 *
 *  Given a recipient's spend public key and a label, calculate the
 *  corresponding labeled spend public key:
 *
 *      labeled_spend_pubkey = unlabeled_spend_pubkey + label
 *
 *  The result is used by the recipient to create a Silent Payments address,
 *  consisting of the serialized and concatenated scan public key and
 *  (labeled) spend public key.
 *
 *  Returns: 1 if labeled spend public key creation was successful.
 *           0 if spend pubkey and label sum to zero (negligible probability for
 *             labels created according to BIP352).
 *
 *  Args:                    ctx: pointer to a context object
 *  Out:    labeled_spend_pubkey: pointer to the resulting labeled spend public key
 *  In:   unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend public key
 *                         label: pointer to the recipient's label
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_create_labeled_spend_pubkey(
    const secp256k1_context *ctx,
    secp256k1_pubkey *labeled_spend_pubkey,
    const secp256k1_pubkey *unlabeled_spend_pubkey,
    const secp256k1_silentpayments_label *label
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Opaque data structure that holds Silent Payments prevouts summary data.
 *
 *  The exact representation of data inside is implementation defined and not
 *  guaranteed to be portable between different platforms or versions. It is
 *  however guaranteed to be 101 bytes in size, and can be safely copied/moved.
 *  This structure does not contain secret data. It can be created with
 *  `secp256k1_silentpayments_recipient_prevouts_summary_create`.
 */
typedef struct secp256k1_silentpayments_prevouts_summary {
    unsigned char data[101];
} secp256k1_silentpayments_prevouts_summary;

/** Compute Silent Payments prevouts summary from prevout public keys and transaction
 *  inputs.
 *
 *  Given a list of n public keys A_1...A_n (one for each Silent Payments
 *  eligible input to spend) and a serialized outpoint_smallest36, create a
 *  `prevouts_summary` object. This object summarizes the prevout data from the
 *  transaction inputs needed for scanning.
 *
 *  `outpoint_smallest36` refers to the smallest outpoint lexicographically
 *  from the transaction inputs (both Silent Payments eligible and non-eligible
 *  inputs). This value MUST be the smallest outpoint out of all of the
 *  transaction inputs, otherwise the recipient will be unable to find the
 *  payment.
 *
 *  The public keys have to be passed in via two different parameter pairs, one
 *  for regular and one for x-only public keys, in order to avoid the need of
 *  users converting to a common public key format before calling this function.
 *  The resulting data can be used for scanning on the recipient side.
 *
 *  Returns: 1 if prevouts summary creation was successful.
 *           0 if the transaction is not a Silent Payments transaction.
 *
 *  Args:                 ctx: pointer to a context object
 *  Out:     prevouts_summary: pointer to prevouts_summary object containing the
 *                             summed public key and input_hash.
 *  In:   outpoint_smallest36: serialized smallest outpoint (lexicographically)
 *                             from the transaction inputs
 *              xonly_pubkeys: pointer to an array of pointers to taproot
 *                             x-only public keys (can be NULL if no taproot
 *                             inputs are used)
 *            n_xonly_pubkeys: the size of the xonly_pubkeys array.
 *                    pubkeys: pointer to an array of pointers to non-taproot
 *                             public keys (can be NULL if no non-taproot
 *                             inputs are used)
 *                  n_pubkeys: the size of the pubkeys array.
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_prevouts_summary_create(
    const secp256k1_context *ctx,
    secp256k1_silentpayments_prevouts_summary *prevouts_summary,
    const unsigned char *outpoint_smallest36,
    const secp256k1_xonly_pubkey * const *xonly_pubkeys,
    size_t n_xonly_pubkeys,
    const secp256k1_pubkey * const *pubkeys,
    size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Type of callback function for label lookups
 *
 *  A function of this type will be used to retrieve the label tweak for a given
 *  label during scanning. A typical implementation will perform a lookup in a
 *  key-value store called the "label cache".
 *
 *  For creating the label cache data,
 *  `secp256k1_silentpayments_recipient_label_create` and
 *  `secp256k1_silentpayments_recipient_label_serialize` can be used.
 *
 *  Returns: pointer to the 32-byte label tweak if there is a match.
 *           NULL pointer if there is no match.
 *
 *  In:         label: pointer to the serialized 33-byte label to check
 *                     (computed during scanning)
 *      label_context: pointer to the recipient's label cache.
 */
typedef const unsigned char* (*secp256k1_silentpayments_label_lookup)(const unsigned char* label33, const void* label_context);

/** Found outputs struct
 *
 *  Struct for holding a found output along with data needed to spend it later.
 *
 *            output: the x-only public key for the taproot output
 *             tweak: the 32-byte tweak needed to spend the output
 *  found_with_label: boolean value to indicate if the output was sent to a
 *                    labeled address. If true, label will be set to a valid value.
 *             label: the label used. If found_with_label = false, this is set to
 *                    an invalid value.
 */
typedef struct secp256k1_silentpayments_found_output {
    secp256k1_xonly_pubkey output;
    unsigned char tweak[32];
    int found_with_label;
    secp256k1_silentpayments_label label;
} secp256k1_silentpayments_found_output;

/** Scan for Silent Payments transaction outputs.
 *
 *  Given a prevouts_summary object, a recipient's 32 byte scan key and spend public key,
 *  and the relevant transaction outputs, scan for outputs belonging to
 *  the recipient and return the tweak(s) needed for spending the output(s). An
 *  optional label_lookup callback function and label_context can be passed if
 *  the recipient uses labels. This allows for checking if a label exists in
 *  the recipients label cache and retrieving the label tweak during scanning.
 *
 *  If used, the `label_lookup` function must return a pointer to a 32-byte label
 *  tweak if the label is found, or NULL otherwise. The returned pointer must remain
 *  valid until the next call to `label_lookup` or until the function returns,
 *  whichever comes first. It is not retained beyond that.
 *
 *  For creating the label cache, `secp256k1_silentpayments_recipient_label_create`
 *  and `secp256k1_silentpayments_recipient_label_serialize` can be used.
 *
 *  Note:
 *  Scanning is bounded by SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT and may
 *  miss outputs if a transaction contains more outputs for a single scan public
 *  key group than this limit.
 *
 *  Returns: 1 if output scanning was successful.
 *           0 if the transaction is not a Silent Payments transaction,
 *             or if the arguments are invalid.
 *
 *  Args:                   ctx: pointer to a context object
 *  Out:          found_outputs: pointer to an array of pointers to found
 *                               output objects. The found outputs array MUST
 *                               have the same length as the tx_outputs array.
 *              n_found_outputs: pointer to an integer indicating the final
 *                               size of the found outputs array. This number
 *                               represents the number of outputs found while
 *                               scanning (0 if none are found). Can't be larger than
 *                               SECP256K1_SILENTPAYMENTS_RECIPIENT_GROUP_LIMIT.
 *  In:              tx_outputs: pointer to the transaction's x-only public key outputs,
 *                               in their original transaction (vout) order
 *                 n_tx_outputs: the size of the tx_outputs array.
 *                   scan_key32: pointer to the recipient's 32 byte scan key. The scan
 *                               key is valid if it passes secp256k1_ec_seckey_verify
 *             prevouts_summary: pointer to the transaction prevouts summary data (see
 *                               `secp256k1_silentpayments_recipient_prevouts_summary_create`).
 *       unlabeled_spend_pubkey: pointer to the recipient's unlabeled spend public key
 *                 label_lookup: pointer to a callback function for looking up
 *                               a label value. This function takes a serialized 33-byte
 *                               label as an argument and returns a pointer to the
 *                               32-byte label tweak if the label exists, otherwise
 *                               returns a NULL pointer (NULL if labels are not
 *                               used)
 *                label_context: pointer to a label context object (NULL if
 *                               labels are not used or context is not needed)
 */
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_silentpayments_recipient_scan_outputs(
    const secp256k1_context *ctx,
    secp256k1_silentpayments_found_output **found_outputs,
    uint32_t *n_found_outputs,
    const secp256k1_xonly_pubkey * const *tx_outputs,
    size_t n_tx_outputs,
    const unsigned char *scan_key32,
    const secp256k1_silentpayments_prevouts_summary *prevouts_summary,
    const secp256k1_pubkey *unlabeled_spend_pubkey,
    secp256k1_silentpayments_label_lookup label_lookup,
    const void *label_context
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8);

#ifdef __cplusplus
}
#endif

#endif /* SECP256K1_SILENTPAYMENTS_H */
