/*
   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_reserve_history.h
 * @brief implementation of the free_reserve_history function for Postgres
 * @author Christian Grothoff
 */
#ifndef EXCHANGE_DATABASE_FREE_RESERVE_HISTORY_H
#define EXCHANGE_DATABASE_FREE_RESERVE_HISTORY_H

#include "taler/taler_util.h"
#include "taler/taler_json_lib.h"
#include "exchangedb_lib.h"


/**
 * @brief Information we keep on bank transfer(s) that established a reserve.
 */
struct TALER_EXCHANGEDB_BankTransfer
{

  /**
   * Public key of the reserve that was filled.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Amount that was transferred to the exchange.
   */
  struct TALER_Amount amount;

  /**
   * When did the exchange receive the incoming transaction?
   * (This is the execution date of the exchange's database,
   * the execution date of the bank should be in @e wire).
   */
  struct GNUNET_TIME_Timestamp execution_date;

  /**
   * Detailed wire information about the sending account
   * in "payto://" format.
   */
  struct TALER_FullPayto sender_account_details;

  /**
   * Data uniquely identifying the wire transfer (wire transfer-type specific)
   */
  uint64_t wire_reference;

};


/**
 * @brief Information we keep for a withdraw request
 * to reproduce the /withdraw operation if needed, and to have proof
 * that a reserve was drained by this amount.
 */
struct TALER_EXCHANGEDB_Withdraw
{
  /**
   * Total amount (with fee) committed to withdraw
   */
  struct TALER_Amount amount_with_fee;

  /**
   * true, if a proof of age was required following this withdraw,
   * in a subsequent call to /reveal-withdraw.
   * In this case, @e max_age, @e h_commitment and
   * @e noreveal_index are to be taken into account
   */
  bool age_proof_required;

  /**
   * Maximum age (in years) that the coins are restricted to,
   * if ``age_proof_required`` is true.
   */
  uint16_t max_age;

  /**
   * If ``age_proof_required`` is true, index (smaller #TALER_CNC_KAPPA)
   * which the exchange has chosen to keep unrevealed
   * during the next cut and choose (aka /reveal-age) step.
   * This value applies to all n coins in the commitment.
   */
  uint16_t noreveal_index;

  /**
   * If @e age_proof_required is true, the running hash over all blinded coin
   * envelope's TALER_BlindedCoinHashP values.
   * It runs over ``kappa*num_coins``, starting with the hashes for the coins
   * for kappa index=0, then index=1 etc.,
   * i.e. h[0][0]...h[0][n-1]h[1][0]...h[1][n-1]...h[κ-1][0]...h[κ-1][n-1]
   */
  struct TALER_HashBlindedPlanchetsP planchets_h;

  /**
   * Public key of the reserve that was drained.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Signature confirming the withdrawal commitment
   */
  struct TALER_ReserveSignatureP reserve_sig;

  /**
   * Number of coins to be withdrawn.
   */
  size_t num_coins;

  /**
   * The hash of the blinded coin envelopes which are signed by the exchange.
   * In case of @e age_proof_required = true, this is the hash over the chosen coins'
   * envelopes (according to @e noreveal_index) from the request, which contained
   * kappa*num_coins blinded coins envelopes.
   */
  struct TALER_HashBlindedPlanchetsP selected_h;

  /**
   * Array of @a num_coins denomination signatures of the blinded coins @a
   * h_coin_evs.
   */
  struct TALER_BlindedDenominationSignature *denom_sigs;

  /**
   * Array of @a num_coins serial id's of the denominations, corresponding to
   * the coins in @a h_coin_evs.
   * If @e age_proof_required is true, the denominations MUST support age restriction.
   */
  uint64_t *denom_serials;

  /**
   * If true, no @e blinding_seed is set and @e num_cs_r_values is 0.
   */
  bool no_blinding_seed;

  /**
   * If @e no_blinding_seed is false, the blinding seed for the nonces needed for
   * blind CS signatures.
   */
  struct TALER_BlindingMasterSeedP blinding_seed;

  /**
   * Number of elements in @e cs_r_values.
   * Only non-zero IF @e  age_proof_required is true AND any of the denomination
   * has a cipher of type CS.
   */
  size_t num_cs_r_values;

  /**
   * Array @e num_r_pubs of public R-value pairs for CS that were generated from the
   * @e blinding_seed, a coin's index and the denomination's private key during the
   * the /withdraw request, to ensure idempotency in case of expiration of a denomination.
   * NULL if @e num_r_pub is 0 (or @e age_proof_required is false).
   */
  struct GNUNET_CRYPTO_CSPublicRPairP *cs_r_values;

  /**
   * The bitvector encoding the choices per coin, made by the exchange,
   * for the R-values in @e cs_r_values.  The value is encoded in NBO
   * and the lowest bit corresponds to the pair at index 0 in @e  cs_r_values.
   */
  uint64_t cs_r_choices;

  /**
   * [out]-Array of @a num_coins hashes of the public keys of the denominations
   * identified by @e denom_serials.  This field is only set when calling
   * get_reserve_history().
   */
  struct TALER_DenominationHashP *denom_pub_hashes;
};


/**
 * Information the exchange records about a recoup request
 * in a reserve history.
 */
struct TALER_EXCHANGEDB_Recoup
{

  /**
   * Information about the coin that was paid back.
   */
  struct TALER_CoinPublicInfo coin;

  /**
   * 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;

  /**
   * 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;

};


/**
 * @brief Information we keep on bank transfer(s) that
 * closed a reserve.
 */
struct TALER_EXCHANGEDB_ClosingTransfer
{

  /**
   * Public key of the reserve that was depleted.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Amount that was transferred from the exchange.
   */
  struct TALER_Amount amount;

  /**
   * Amount that was charged by the exchange.
   */
  struct TALER_Amount closing_fee;

  /**
   * When did the exchange execute the transaction?
   */
  struct GNUNET_TIME_Timestamp execution_date;

  /**
   * Detailed wire information about the receiving account
   * in payto://-format.
   */
  struct TALER_FullPayto receiver_account_details;

  /**
   * Detailed wire transfer information that uniquely identifies the
   * wire transfer.
   */
  struct TALER_WireTransferIdentifierRawP wtid;

};


/**
 * Details about a purse merge operation.
 */
struct TALER_EXCHANGEDB_PurseMerge
{

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

  /**
   * Amount in the purse, with fees.
   */
  struct TALER_Amount amount_with_fee;

  /**
   * Fee paid for the purse.
   */
  struct TALER_Amount purse_fee;

  /**
   * Hash over the contract.
   */
  struct TALER_PrivateContractHashP h_contract_terms;

  /**
   * Merge capability key.
   */
  struct TALER_PurseMergePublicKeyP merge_pub;

  /**
   * Purse public key.
   */
  struct TALER_PurseContractPublicKeyP purse_pub;

  /**
   * Signature by the reserve approving the merge.
   */
  struct TALER_ReserveSignatureP reserve_sig;

  /**
   * When was the merge made.
   */
  struct GNUNET_TIME_Timestamp merge_timestamp;

  /**
   * When was the purse set to expire.
   */
  struct GNUNET_TIME_Timestamp purse_expiration;

  /**
   * Minimum age required for depositing into the purse.
   */
  uint32_t min_age;

  /**
   * Flags of the purse.
   */
  enum TALER_WalletAccountMergeFlags flags;

  /**
   * true if the purse was actually successfully merged,
   * false if the @e purse_fee was charged but the
   * @e amount was not credited to the reserve.
   */
  bool merged;
};


/**
 * Details about a (paid for) reserve history request.
 */
struct TALER_EXCHANGEDB_HistoryRequest
{
  /**
   * Public key of the reserve the history request was for.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Fee paid for the request.
   */
  struct TALER_Amount history_fee;

  /**
   * When was the request made.
   */
  struct GNUNET_TIME_Timestamp request_timestamp;

  /**
   * Signature by the reserve approving the history request.
   */
  struct TALER_ReserveSignatureP reserve_sig;
};


/**
 * Details about a (paid for) reserve open request.
 */
struct TALER_EXCHANGEDB_OpenRequest
{
  /**
   * Public key of the reserve the open request was for.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Fee paid for the request from the reserve.
   */
  struct TALER_Amount open_fee;

  /**
   * When was the request made.
   */
  struct GNUNET_TIME_Timestamp request_timestamp;

  /**
   * How long was the reserve supposed to be open.
   */
  struct GNUNET_TIME_Timestamp reserve_expiration;

  /**
   * Signature by the reserve approving the open request,
   * with purpose #TALER_SIGNATURE_WALLET_RESERVE_OPEN.
   */
  struct TALER_ReserveSignatureP reserve_sig;

  /**
   * How many open purses should be included with the
   * open reserve?
   */
  uint32_t purse_limit;

};


/**
 * Details about an (explicit) reserve close request.
 */
struct TALER_EXCHANGEDB_CloseRequest
{
  /**
   * Public key of the reserve the history request was for.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * When was the request made.
   */
  struct GNUNET_TIME_Timestamp request_timestamp;

  /**
   * Hash of the payto://-URI of the target account
   * for the closure, or all zeros for the reserve
   * origin account.
   */
  struct TALER_FullPaytoHashP target_account_h_payto;

  /**
   * Signature by the reserve approving the history request.
   */
  struct TALER_ReserveSignatureP reserve_sig;

};


/**
 * @brief Types of operations on a reserve.
 */
enum TALER_EXCHANGEDB_ReserveOperation
{
  /**
   * Money was deposited into the reserve via a bank transfer.
   * This is how customers establish a reserve at the exchange.
   */
  TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE = 0,

  /**
   * A batch of coins was withdrawn from the reserve using /withdraw.
   */
  TALER_EXCHANGEDB_RO_WITHDRAW_COINS = 1,

  /**
   * A coin was returned to the reserve using /recoup.
   */
  TALER_EXCHANGEDB_RO_RECOUP_COIN = 2,

  /**
   * The exchange send inactive funds back from the reserve to the
   * customer's bank account.  This happens when the exchange
   * closes a reserve with a non-zero amount left in it.
   */
  TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK = 3,

  /**
   * Event where a purse was merged into a reserve.
   */
  TALER_EXCHANGEDB_RO_PURSE_MERGE = 4,

  /**
   * Event where a wallet paid for a full reserve history.
   */
  TALER_EXCHANGEDB_RO_HISTORY_REQUEST = 5,

  /**
   * Event where a wallet paid to open a reserve for longer.
   */
  TALER_EXCHANGEDB_RO_OPEN_REQUEST = 6,

  /**
   * Event where a wallet requested a reserve to be closed.
   */
  TALER_EXCHANGEDB_RO_CLOSE_REQUEST = 7,

};


/**
 * @brief Reserve history as a linked list.  Lists all of the transactions
 * associated with this reserve (such as the bank transfers that
 * established the reserve and all /withdraw operations we have done
 * since).
 */
struct TALER_EXCHANGEDB_ReserveHistory
{

  /**
   * Next entry in the reserve history.
   */
  struct TALER_EXCHANGEDB_ReserveHistory *next;

  /**
   * Offset of this entry in the reserve history.
   * Corresponds to the reserve_history_serial_id in the database.
   */
  uint64_t history_offset;

  /**
   * Type of the event, determines @e details.
   */
  enum TALER_EXCHANGEDB_ReserveOperation type;

  /**
   * Details of the operation, depending on @e type.
   */
  union
  {

    /**
     * Details about a bank transfer to the exchange (reserve
     * was established).
     */
    struct TALER_EXCHANGEDB_BankTransfer *bank;

    /**
     * Details about a /withdraw operation.
     */
    struct TALER_EXCHANGEDB_Withdraw *withdraw;

    /**
     * Details about a /recoup operation.
     */
    struct TALER_EXCHANGEDB_Recoup *recoup;

    /**
     * Details about a bank transfer from the exchange (reserve
     * was closed).
     */
    struct TALER_EXCHANGEDB_ClosingTransfer *closing;

    /**
     * Details about a purse merge operation.
     */
    struct TALER_EXCHANGEDB_PurseMerge *merge;

    /**
     * Details about a (paid for) reserve history request.
     */
    struct TALER_EXCHANGEDB_HistoryRequest *history;

    /**
     * Details about a (paid for) open reserve request.
     */
    struct TALER_EXCHANGEDB_OpenRequest *open_request;

    /**
     * Details about an (explicit) reserve close request.
     */
    struct TALER_EXCHANGEDB_CloseRequest *close_request;

  } details;

};


/**
 * Free memory associated with the given reserve history.
 *
 * @param[in] rh history to free.
 */
void
TALER_EXCHANGEDB_free_reserve_history (
  struct TALER_EXCHANGEDB_ReserveHistory *rh);


#endif
