/*
   This file is part of TALER
   Copyright (C) 2022 Taler Systems SA

   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 3, or (at your option) any later version.

   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file free_coin_transaction_list.h
 * @brief implementation of the free_coin_transaction_list function for Postgres
 * @author Christian Grothoff
 */
#ifndef EXCHANGE_DATABASE_FREE_COIN_TRANSACTION_LIST_H
#define EXCHANGE_DATABASE_FREE_COIN_TRANSACTION_LIST_H

#include "exchangedb_lib.h"


/**
 * @brief Enumeration to classify the different types of transactions
 * that can be done with a coin.
 */
enum TALER_EXCHANGEDB_TransactionType
{

  /**
   * Deposit operation.
   */
  TALER_EXCHANGEDB_TT_DEPOSIT = 0,

  /**
   * Melt operation.
   */
  TALER_EXCHANGEDB_TT_MELT = 1,

  /**
   * Refund operation.
   */
  TALER_EXCHANGEDB_TT_REFUND = 2,

  /**
   * Recoup-refresh operation (on the old coin, adding to the old coin's value)
   */
  TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER = 3,

  /**
   * Recoup operation.
   */
  TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW = 4,

  /**
   * Recoup-refresh operation (on the new coin, eliminating its value)
   */
  TALER_EXCHANGEDB_TT_RECOUP_REFRESH = 5,

  /**
   * Purse deposit operation.
   */
  TALER_EXCHANGEDB_TT_PURSE_DEPOSIT = 6,

  /**
   * Purse deposit operation.
   */
  TALER_EXCHANGEDB_TT_PURSE_REFUND = 7,

  /**
   * Reserve open deposit operation.
   */
  TALER_EXCHANGEDB_TT_RESERVE_OPEN = 8

};


/**
 * @brief Specification for a deposit operation in the
 * `struct TALER_EXCHANGEDB_TransactionList`.
 */
struct TALER_EXCHANGEDB_DepositListEntry
{

  /**
   * ECDSA signature affirming that the customer intends
   * this coin to be deposited at the merchant identified
   * by @e h_wire in relation to the proposal data identified
   * by @e h_contract_terms.
   */
  struct TALER_CoinSpendSignatureP csig;

  /**
   * Public key of the merchant.  Enables later identification
   * of the merchant in case of a need to rollback transactions.
   */
  struct TALER_MerchantPublicKeyP merchant_pub;

  /**
   * Hash over the proposa data between merchant and customer
   * (remains unknown to the Exchange).
   */
  struct TALER_PrivateContractHashP h_contract_terms;

  /**
   * Hash over inputs from the wallet to customize the contract.
   */
  struct GNUNET_HashCode wallet_data_hash;

  /**
   * Hash of the public denomination key used to sign the coin.
   */
  struct TALER_DenominationHashP h_denom_pub;

  /**
   * Age commitment hash, if applicable to the denomination.  Should be all
   * zeroes if age commitment is not applicable to the denonimation.
   */
  struct TALER_AgeCommitmentHashP h_age_commitment;

  /**
   * Salt used to compute h_wire from the @e receiver_wire_account.
   */
  struct TALER_WireSaltP wire_salt;

  /**
   * Hash over the policy data for this deposit (remains unknown to the
   * Exchange).  Needed for the verification of the deposit's signature
   */
  struct TALER_ExtensionPolicyHashP h_policy;

  /**
   * Fraction of the coin's remaining value to be deposited, including
   * depositing fee (if any).  The coin is identified by @e coin_pub.
   */
  struct TALER_Amount amount_with_fee;

  /**
   * Depositing fee.
   */
  struct TALER_Amount deposit_fee;

  /**
   * Time when this request was generated.  Used, for example, to
   * assess when (roughly) the income was achieved for tax purposes.
   * Note that the Exchange will only check that the timestamp is not "too
   * far" into the future (i.e. several days).  The fact that the
   * timestamp falls within the validity period of the coin's
   * denomination key is irrelevant for the validity of the deposit
   * request, as obviously the customer and merchant could conspire to
   * set any timestamp.  Also, the Exchange must accept very old deposit
   * requests, as the merchant might have been unable to transmit the
   * deposit request in a timely fashion (so back-dating is not
   * prevented).
   */
  struct GNUNET_TIME_Timestamp timestamp;

  /**
   * How much time does the merchant have to issue a refund request?
   * Zero if refunds are not allowed.  After this time, the coin
   * cannot be refunded.
   */
  struct GNUNET_TIME_Timestamp refund_deadline;

  /**
   * How much time does the merchant have to execute the wire transfer?
   * This time is advisory for aggregating transactions, not a hard
   * constraint (as the merchant can theoretically pick any time,
   * including one in the past).
   */
  struct GNUNET_TIME_Timestamp wire_deadline;

  /**
   * Detailed information about the receiver for executing the transaction.
   * URL in payto://-format.
   */
  struct TALER_FullPayto receiver_wire_account;

  /**
   * true, if age commitment is not applicable
   */
  bool no_age_commitment;

  /**
   * true, if wallet data hash is not present
   */
  bool no_wallet_data_hash;

  /**
   * True if a policy was provided with the deposit request
   */
  bool has_policy;

  /**
   * Has the deposit been wired?
   */
  bool done;

};


/**
 * @brief Specification for a refund operation in a coin's transaction list.
 */
struct TALER_EXCHANGEDB_RefundListEntry
{

  /**
   * Public key of the merchant.
   */
  struct TALER_MerchantPublicKeyP merchant_pub;

  /**
   * Signature from the merchant affirming the refund.
   */
  struct TALER_MerchantSignatureP merchant_sig;

  /**
   * Hash over the proposal data between merchant and customer
   * (remains unknown to the Exchange).
   */
  struct TALER_PrivateContractHashP h_contract_terms;

  /**
   * Merchant-generated REFUND transaction ID to detect duplicate
   * refunds.
   */
  uint64_t rtransaction_id;

  /**
   * Fraction of the original deposit's value to be refunded, including
   * refund fee (if any).  The coin is identified by @e coin_pub.
   */
  struct TALER_Amount refund_amount;

  /**
   * Refund fee to be covered by the customer.
   */
  struct TALER_Amount refund_fee;

};


/**
 * Information about a /coins/$COIN_PUB/melt operation in a coin transaction history.
 */
struct TALER_EXCHANGEDB_MeltListEntry
{

  /**
   * Signature over the melting operation.
   */
  struct TALER_CoinSpendSignatureP coin_sig;

  /**
   * Refresh commitment this coin is melted into.
   */
  struct TALER_RefreshCommitmentP rc;

  /**
   * Hash of the public denomination key used to sign the coin.
   */
  struct TALER_DenominationHashP h_denom_pub;

  /**
   * Hash of the age commitment used to sign the coin, if age restriction was
   * applicable to the denomination.  May be all zeroes if no age restriction
   * applies.
   */
  struct TALER_AgeCommitmentHashP h_age_commitment;

  /**
   * true, if no @e h_age_commitment is applicable
   */
  bool no_age_commitment;

  /**
   * How much value is being melted?  This amount includes the fees,
   * so the final amount contributed to the melt is this value minus
   * the fee for melting the coin.  We include the fee in what is
   * being signed so that we can verify a reserve's remaining total
   * balance without needing to access the respective denomination key
   * information each time.
   */
  struct TALER_Amount amount_with_fee;

  /**
   * Melt fee the exchange charged.
   */
  struct TALER_Amount melt_fee;

  /**
   * Index (smaller #TALER_CNC_KAPPA) which the exchange has chosen to not
   * have revealed during cut and choose.
   */
  uint32_t noreveal_index;

  /**
   * The refresh seed that was used for the melt operation
   */
  struct TALER_PublicRefreshMasterSeedP refresh_seed;

  /**
   * If false, @e blinding_seed is present
   */
  bool no_blinding_seed;

  /**
   * If @e no_blinding_seed it false, the blinding seed that was used
   * for the melt operation, in case of CS denominations.
   */
  struct TALER_BlindingMasterSeedP blinding_seed;

};


/**
 * Information the exchange records about a recoup request
 * in a coin history.
 */
struct TALER_EXCHANGEDB_RecoupListEntry
{

  /**
   * Blinding factor supplied to prove to the exchange that
   * the coin came from this reserve.
   */
  union GNUNET_CRYPTO_BlindingSecretP coin_blind;

  /**
   * Signature of the coin of type
   * #TALER_SIGNATURE_WALLET_COIN_RECOUP.
   */
  struct TALER_CoinSpendSignatureP coin_sig;

  /**
   * Hash of the public denomination key used to sign the coin.
   */
  struct TALER_DenominationHashP h_denom_pub;

  /**
   * Public key of the reserve the coin was paid back into.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * How much was the coin still worth at this time?
   */
  struct TALER_Amount value;

  /**
   * When did the /recoup operation happen?
   */
  struct GNUNET_TIME_Timestamp timestamp;

};


/**
 * Information the exchange records about a recoup-refresh request in
 * a coin transaction history.
 */
struct TALER_EXCHANGEDB_RecoupRefreshListEntry
{

  /**
   * Information about the coin that was paid back
   * (NOT the coin we are considering the history of!)
   */
  struct TALER_CoinPublicInfo coin;

  /**
   * Blinding factor supplied to prove to the exchange that
   * the coin came from this @e old_coin_pub.
   */
  union GNUNET_CRYPTO_BlindingSecretP coin_blind;

  /**
   * Signature of the coin of type
   * #TALER_SIGNATURE_WALLET_COIN_RECOUP.
   */
  struct TALER_CoinSpendSignatureP coin_sig;

  /**
   * Public key of the old coin that the refreshed coin was paid back to.
   */
  struct TALER_CoinSpendPublicKeyP old_coin_pub;

  /**
   * How much was the coin still worth at this time?
   */
  struct TALER_Amount value;

  /**
   * When did the recoup operation happen?
   */
  struct GNUNET_TIME_Timestamp timestamp;

};


/**
 * Information about a /purses/$PID/deposit operation in a coin transaction history.
 */
struct TALER_EXCHANGEDB_PurseDepositListEntry
{

  /**
   * Exchange hosting the purse, NULL for this exchange.
   */
  char *exchange_base_url;

  /**
   * Public key of the purse.
   */
  struct TALER_PurseContractPublicKeyP purse_pub;

  /**
   * Contribution of the coin to the purse, including
   * deposit fee.
   */
  struct TALER_Amount amount;

  /**
   * Depositing fee.
   */
  struct TALER_Amount deposit_fee;

  /**
   * Signature by the coin affirming the deposit.
   */
  struct TALER_CoinSpendSignatureP coin_sig;

  /**
   * Hash of the age commitment used to sign the coin, if age restriction was
   * applicable to the denomination.
   */
  struct TALER_AgeCommitmentHashP h_age_commitment;

  /**
   * Hash of the public denomination key used to sign the coin.
   */
  struct TALER_DenominationHashP h_denom_pub;

  /**
   * Set to true if the coin was refunded.
   */
  bool refunded;

  /**
   * Set to true if there was no age commitment.
   */
  bool no_age_commitment;

};


/**
 * @brief Specification for a purse refund operation in a coin's transaction list.
 */
struct TALER_EXCHANGEDB_PurseRefundListEntry
{

  /**
   * Public key of the purse.
   */
  struct TALER_PurseContractPublicKeyP purse_pub;

  /**
   * Fraction of the original deposit's value to be refunded, including
   * refund fee (if any).  The coin is identified by @e coin_pub.
   */
  struct TALER_Amount refund_amount;

  /**
   * Refund fee to be covered by the customer.
   */
  struct TALER_Amount refund_fee;

};


/**
 * Information about a /reserves/$RID/open operation in a coin transaction history.
 */
struct TALER_EXCHANGEDB_ReserveOpenListEntry
{

  /**
   * Signature of the reserve.
   */
  struct TALER_ReserveSignatureP reserve_sig;

  /**
   * Contribution of the coin to the open fee, including
   * deposit fee.
   */
  struct TALER_Amount coin_contribution;

  /**
   * Signature by the coin affirming the open deposit.
   */
  struct TALER_CoinSpendSignatureP coin_sig;

  /**
   * Hash of the age commitment used to sign the coin, if age restriction was
   * applicable to the denomination.
   */
  struct TALER_AgeCommitmentHashP h_age_commitment;

  /**
   * Set to true if there was no age commitment.
   */
  bool no_age_commitment;

};


/**
 * @brief List of transactions we performed for a particular coin.
 */
struct TALER_EXCHANGEDB_TransactionList
{

  /**
   * Next pointer in the NULL-terminated linked list.
   */
  struct TALER_EXCHANGEDB_TransactionList *next;

  /**
   * Type of the transaction, determines what is stored in @e details.
   */
  enum TALER_EXCHANGEDB_TransactionType type;

  /**
   * Serial ID of this entry in the @e type-specific table.
   */
  uint64_t serial_id;

  /**
   * Serial ID of this entry in the coin history table.
   */
  uint64_t coin_history_id;

  /**
   * Details about the transaction, depending on @e type.
   */
  union
  {

    /**
     * Details if transaction was a deposit operation.
     * (#TALER_EXCHANGEDB_TT_DEPOSIT)
     */
    struct TALER_EXCHANGEDB_DepositListEntry *deposit;

    /**
     * Details if transaction was a melt operation.
     * (#TALER_EXCHANGEDB_TT_MELT)
     */
    struct TALER_EXCHANGEDB_MeltListEntry *melt;

    /**
     * Details if transaction was a refund operation.
     * (#TALER_EXCHANGEDB_TT_REFUND)
     */
    struct TALER_EXCHANGEDB_RefundListEntry *refund;

    /**
     * Details if transaction was a recoup-refund operation where
     * this coin was the OLD coin.
     * (#TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER).
     */
    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *old_coin_recoup;

    /**
     * Details if transaction was a recoup operation.
     * (#TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW)
     */
    struct TALER_EXCHANGEDB_RecoupListEntry *recoup;

    /**
     * Details if transaction was a recoup-refund operation where
     * this coin was the REFRESHED coin.
     * (#TALER_EXCHANGEDB_TT_RECOUP_REFRESH)
     */
    struct TALER_EXCHANGEDB_RecoupRefreshListEntry *recoup_refresh;

    /**
     * Coin was deposited into a purse.
     * (#TALER_EXCHANGEDB_TT_PURSE_DEPOSIT)
     */
    struct TALER_EXCHANGEDB_PurseDepositListEntry *purse_deposit;

    /**
     * Coin was refunded upon purse expiration
     * (#TALER_EXCHANGEDB_TT_PURSE_REFUND)
     */
    struct TALER_EXCHANGEDB_PurseRefundListEntry *purse_refund;

    /**
     * Coin was used to pay to open a reserve.
     * (#TALER_EXCHANGEDB_TT_RESERVE_OPEN)
     */
    struct TALER_EXCHANGEDB_ReserveOpenListEntry *reserve_open;

  } details;

};

/**
 * Calculate the total value of all transactions performed.
 * Stores @a off plus the cost of all transactions in @a tl
 * in @a ret.
 *
 * @param tl transaction list to process
 * @param off offset to use as the starting value
 * @param[out] ret where the resulting total is to be stored
 * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
 */
enum GNUNET_GenericReturnValue
TALER_EXCHANGEDB_calculate_transaction_list_totals (
  struct TALER_EXCHANGEDB_TransactionList *tl,
  const struct TALER_Amount *off,
  struct TALER_Amount *ret);


/**
 * Free linked list of transactions.
 *
 * @param[in] tl list to free
 */
void
TALER_EXCHANGEDB_free_coin_transaction_list (
  struct TALER_EXCHANGEDB_TransactionList *tl);


#endif
