/*
  This file is part of TALER
  Copyright (C) 2021-2025 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 exchange_signatures.c
 * @brief Utility functions for Taler security module signatures
 * @author Christian Grothoff
 */
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format used to generate the signature on a confirmation
 * from the exchange that a deposit request succeeded.
 */
struct TALER_DepositConfirmationPS
{
  /**
   * Purpose must be #TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT.  Signed
   * by a `struct TALER_ExchangePublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash over the contract for which this deposit is made.
   */
  struct TALER_PrivateContractHashP h_contract_terms GNUNET_PACKED;

  /**
   * Hash over the wiring information of the merchant.
   */
  struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;

  /**
   * Hash over the optional policy extension of the deposit, 0 if there
   * was no policy.
   */
  struct TALER_ExtensionPolicyHashP h_policy GNUNET_PACKED;

  /**
   * Time when this confirmation was generated / when the exchange received
   * the deposit request.
   */
  struct GNUNET_TIME_TimestampNBO exchange_timestamp;

  /**
   * By when does the exchange expect to pay the merchant
   * (as per the merchant's request).
   */
  struct GNUNET_TIME_TimestampNBO wire_deadline;

  /**
   * How much time does the @e merchant have to issue a refund
   * request?  Zero if refunds are not allowed.  After this time, the
   * coin cannot be refunded.  Note that the wire transfer will not be
   * performed by the exchange until the refund deadline.  This value
   * is taken from the original deposit request.
   */
  struct GNUNET_TIME_TimestampNBO refund_deadline;

  /**
   * Amount to be deposited, excluding fee.  Calculated from the
   * amount with fee and the fee from the deposit request.
   */
  struct TALER_AmountNBO total_without_fee;

  /**
   * Hash over all of the coin signatures.
   */
  struct GNUNET_HashCode h_coin_sigs;

  /**
   * The Merchant's public key.  Allows the merchant to later refund
   * the transaction or to inquire about the wire transfer identifier.
   */
  struct TALER_MerchantPublicKeyP merchant_pub;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_deposit_confirmation_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_MerchantWireHashP *h_wire,
  const struct TALER_ExtensionPolicyHashP *h_policy,
  struct GNUNET_TIME_Timestamp exchange_timestamp,
  struct GNUNET_TIME_Timestamp wire_deadline,
  struct GNUNET_TIME_Timestamp refund_deadline,
  const struct TALER_Amount *total_without_fee,
  unsigned int num_coins,
  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
  const struct TALER_MerchantPublicKeyP *merchant_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_DepositConfirmationPS dcs = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT),
    .purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)),
    .h_contract_terms = *h_contract_terms,
    .h_wire = *h_wire,
    .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp),
    .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline),
    .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
    .merchant_pub = *merchant_pub,
    .h_policy = {{{0}}}
  };
  struct GNUNET_HashContext *hc;

  hc = GNUNET_CRYPTO_hash_context_start ();
  for (unsigned int i = 0; i<num_coins; i++)
    GNUNET_CRYPTO_hash_context_read (hc,
                                     coin_sigs[i],
                                     sizeof (*coin_sigs[i]));
  GNUNET_CRYPTO_hash_context_finish (hc,
                                     &dcs.h_coin_sigs);
  if (NULL != h_policy)
    dcs.h_policy = *h_policy;
  TALER_amount_hton (&dcs.total_without_fee,
                     total_without_fee);
  return scb (&dcs.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_deposit_confirmation_verify (
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_MerchantWireHashP *h_wire,
  const struct TALER_ExtensionPolicyHashP *h_policy,
  struct GNUNET_TIME_Timestamp exchange_timestamp,
  struct GNUNET_TIME_Timestamp wire_deadline,
  struct GNUNET_TIME_Timestamp refund_deadline,
  const struct TALER_Amount *total_without_fee,
  unsigned int num_coins,
  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
  const struct TALER_MerchantPublicKeyP *merchant_pub,
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_ExchangeSignatureP *exchange_sig)
{
  struct TALER_DepositConfirmationPS dcs = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT),
    .purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)),
    .h_contract_terms = *h_contract_terms,
    .h_wire = *h_wire,
    .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp),
    .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline),
    .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
    .merchant_pub = *merchant_pub
  };
  struct GNUNET_HashContext *hc;

  hc = GNUNET_CRYPTO_hash_context_start ();
  for (unsigned int i = 0; i<num_coins; i++)
    GNUNET_CRYPTO_hash_context_read (hc,
                                     coin_sigs[i],
                                     sizeof (*coin_sigs[i]));
  GNUNET_CRYPTO_hash_context_finish (hc,
                                     &dcs.h_coin_sigs);
  if (NULL != h_policy)
    dcs.h_policy = *h_policy;
  TALER_amount_hton (&dcs.total_without_fee,
                     total_without_fee);
  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT,
                                  &dcs,
                                  &exchange_sig->eddsa_signature,
                                  &exchange_pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format used to generate the signature on a request to refund
 * a coin into the account of the customer.
 */
struct TALER_RefundConfirmationPS
{
  /**
   * Purpose must be #TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash over the proposal data to identify the contract
   * which is being refunded.
   */
  struct TALER_PrivateContractHashP h_contract_terms GNUNET_PACKED;

  /**
   * The coin's public key.  This is the value that must have been
   * signed (blindly) by the Exchange.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * The Merchant's public key.  Allows the merchant to later refund
   * the transaction or to inquire about the wire transfer identifier.
   */
  struct TALER_MerchantPublicKeyP merchant;

  /**
   * Merchant-generated transaction ID for the refund.
   */
  uint64_t rtransaction_id GNUNET_PACKED;

  /**
   * Amount to be refunded, including refund fee charged by the
   * exchange to the customer.
   */
  struct TALER_AmountNBO refund_amount;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_refund_confirmation_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_MerchantPublicKeyP *merchant,
  uint64_t rtransaction_id,
  const struct TALER_Amount *refund_amount,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RefundConfirmationPS rc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND),
    .purpose.size = htonl (sizeof (rc)),
    .h_contract_terms = *h_contract_terms,
    .coin_pub = *coin_pub,
    .merchant = *merchant,
    .rtransaction_id = GNUNET_htonll (rtransaction_id)
  };

  TALER_amount_hton (&rc.refund_amount,
                     refund_amount);
  return scb (&rc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_refund_confirmation_verify (
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_MerchantPublicKeyP *merchant,
  uint64_t rtransaction_id,
  const struct TALER_Amount *refund_amount,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RefundConfirmationPS rc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND),
    .purpose.size = htonl (sizeof (rc)),
    .h_contract_terms = *h_contract_terms,
    .coin_pub = *coin_pub,
    .merchant = *merchant,
    .rtransaction_id = GNUNET_htonll (rtransaction_id)
  };

  TALER_amount_hton (&rc.refund_amount,
                     refund_amount);
  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND,
                                  &rc,
                                  &sig->eddsa_signature,
                                  &pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format of the block signed by the Exchange in response to a successful
 * "/refresh/melt" request.  Hereby the exchange affirms that all of the
 * coins were successfully melted.  This also commits the exchange to a
 * particular index to not be revealed during the refresh.
 */
struct TALER_RefreshMeltConfirmationPS
{
  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT.   Signed
   * by a `struct TALER_ExchangePublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Commitment made in the /refresh/melt.
   */
  struct TALER_RefreshCommitmentP rc GNUNET_PACKED;

  /**
   * Index that the client will not have to reveal, in NBO.
   * Must be smaller than #TALER_CNC_KAPPA.
   */
  uint32_t noreveal_index GNUNET_PACKED;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_melt_confirmation_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_RefreshCommitmentP *rc,
  uint32_t noreveal_index,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RefreshMeltConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT),
    .purpose.size = htonl (sizeof (confirm)),
    .rc = *rc,
    .noreveal_index = htonl (noreveal_index)
  };

  return scb (&confirm.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_melt_confirmation_verify (
  const struct TALER_RefreshCommitmentP *rc,
  uint32_t noreveal_index,
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_ExchangeSignatureP *exchange_sig)
{
  struct TALER_RefreshMeltConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT),
    .purpose.size = htonl (sizeof (confirm)),
    .rc = *rc,
    .noreveal_index = htonl (noreveal_index)
  };

  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
                                &confirm,
                                &exchange_sig->eddsa_signature,
                                &exchange_pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format of the block signed by the Exchange in response to a
 * successful "/withdraw" request.
 * If age restriction is set, the exchange hereby also
 * affirms that the commitment along with the maximum age group and
 * the amount were accepted.  This also commits the exchange to a particular
 * index to not be revealed during the reveal.
 */
struct TALER_WithdrawConfirmationPS
{
  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW.   Signed by a
   * `struct TALER_ExchangePublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Commitment made in the /withdraw call.
   */
  struct TALER_HashBlindedPlanchetsP h_planchets GNUNET_PACKED;

  /**
   * If age restriction does not apply to this withdrawal,
   * (i.e. max_age was not set during the request)
   * MUST be 0xFFFFFFFF.
   * Otherwise (i.e. age restriction applies):
   * index that the client will not have to reveal, in NBO,
   * MUST be smaller than #TALER_CNC_KAPPA.
   */
  uint32_t noreveal_index GNUNET_PACKED;

};

GNUNET_NETWORK_STRUCT_END

enum TALER_ErrorCode
TALER_exchange_online_withdraw_age_confirmation_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_HashBlindedPlanchetsP *h_planchets,
  uint32_t noreveal_index,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{

  struct TALER_WithdrawConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW),
    .purpose.size = htonl (sizeof (confirm)),
    .h_planchets = *h_planchets,
    .noreveal_index = htonl (noreveal_index)
  };

  return scb (&confirm.purpose,
              pub,
              sig);
}


enum TALER_ErrorCode
TALER_exchange_online_withdraw_confirmation_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_HashBlindedPlanchetsP *h_planchets,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{

  struct TALER_WithdrawConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW),
    .purpose.size = htonl (sizeof (confirm)),
    .h_planchets = *h_planchets,
    .noreveal_index = htonl (0xFFFFFFFF)
  };

  return scb (&confirm.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_withdraw_age_confirmation_verify (
  const struct TALER_HashBlindedPlanchetsP *h_planchets,
  uint32_t noreveal_index,
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_ExchangeSignatureP *exchange_sig)
{
  struct TALER_WithdrawConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW),
    .purpose.size = htonl (sizeof (confirm)),
    .h_planchets = *h_planchets,
    .noreveal_index = htonl (noreveal_index)
  };

  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (
        TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW,
        &confirm,
        &exchange_sig->eddsa_signature,
        &exchange_pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_withdraw_confirmation_verify (
  const struct TALER_HashBlindedPlanchetsP *h_planchets,
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_ExchangeSignatureP *exchange_sig)
{
  struct TALER_WithdrawConfirmationPS confirm = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW),
    .purpose.size = htonl (sizeof (confirm)),
    .h_planchets = *h_planchets,
    .noreveal_index = htonl (0xFFFFFFFF)
  };

  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (
        TALER_SIGNATURE_EXCHANGE_CONFIRM_WITHDRAW,
        &confirm,
        &exchange_sig->eddsa_signature,
        &exchange_pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Signature made by the exchange over the full set of keys, used
 * to detect cheating exchanges that give out different sets to
 * different users.
 */
struct TALER_ExchangeKeySetPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_KEY_SET.   Signed
   * by a `struct TALER_ExchangePublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time of the key set issue.
   */
  struct GNUNET_TIME_TimestampNBO list_issue_date;

  /**
   * Hash over the various denomination signing keys returned.
   */
  struct GNUNET_HashCode hc GNUNET_PACKED;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_key_set_sign (
  TALER_ExchangeSignCallback2 scb,
  void *cls,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct GNUNET_HashCode *hc,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ExchangeKeySetPS ks = {
    .purpose.size = htonl (sizeof (ks)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET),
    .list_issue_date = GNUNET_TIME_timestamp_hton (timestamp),
    .hc = *hc
  };

  return scb (cls,
              &ks.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_key_set_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct GNUNET_HashCode *hc,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ExchangeKeySetPS ks = {
    .purpose.size = htonl (sizeof (ks)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET),
    .list_issue_date = GNUNET_TIME_timestamp_hton (timestamp),
    .hc = *hc
  };

  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_KEY_SET,
                                &ks,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format internally used for packing the detailed information
 * to generate the signature for /track/transfer signatures.
 */
struct TALER_WireDepositDetailP
{

  /**
   * Hash of the contract
   */
  struct TALER_PrivateContractHashP h_contract_terms;

  /**
   * Time when the wire transfer was performed by the exchange.
   */
  struct GNUNET_TIME_TimestampNBO execution_time;

  /**
   * Coin's public key.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * Total value of the coin.
   */
  struct TALER_AmountNBO deposit_value;

  /**
   * Fees charged by the exchange for the deposit.
   */
  struct TALER_AmountNBO deposit_fee;

};

GNUNET_NETWORK_STRUCT_END


void
TALER_exchange_online_wire_deposit_append (
  struct GNUNET_HashContext *hash_context,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  struct GNUNET_TIME_Timestamp execution_time,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_Amount *deposit_value,
  const struct TALER_Amount *deposit_fee)
{
  struct TALER_WireDepositDetailP dd = {
    .h_contract_terms = *h_contract_terms,
    .execution_time = GNUNET_TIME_timestamp_hton (execution_time),
    .coin_pub = *coin_pub
  };
  TALER_amount_hton (&dd.deposit_value,
                     deposit_value);
  TALER_amount_hton (&dd.deposit_fee,
                     deposit_fee);
  GNUNET_CRYPTO_hash_context_read (hash_context,
                                   &dd,
                                   sizeof (dd));
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format used to generate the signature for /wire/deposit
 * replies.
 */
struct TALER_WireDepositDataPS
{
  /**
   * Purpose header for the signature over the contract with
   * purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Total amount that was transferred.
   */
  struct TALER_AmountNBO total;

  /**
   * Wire fee that was charged.
   */
  struct TALER_AmountNBO wire_fee;

  /**
   * Public key of the merchant (for all aggregated transactions).
   */
  struct TALER_MerchantPublicKeyP merchant_pub;

  /**
   * Hash of bank account of the merchant.
   */
  struct TALER_FullPaytoHashP h_payto;

  /**
   * Hash of the individual deposits that were aggregated,
   * each in the format of a `struct TALER_WireDepositDetailP`.
   */
  struct GNUNET_HashCode h_details;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_wire_deposit_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_Amount *total,
  const struct TALER_Amount *wire_fee,
  const struct TALER_MerchantPublicKeyP *merchant_pub,
  const struct TALER_FullPayto payto,
  const struct GNUNET_HashCode *h_details,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_WireDepositDataPS wdp = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT),
    .purpose.size = htonl (sizeof (wdp)),
    .merchant_pub = *merchant_pub,
    .h_details = *h_details
  };

  TALER_amount_hton (&wdp.total,
                     total);
  TALER_amount_hton (&wdp.wire_fee,
                     wire_fee);
  TALER_full_payto_hash (payto,
                         &wdp.h_payto);
  return scb (&wdp.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_wire_deposit_verify (
  const struct TALER_Amount *total,
  const struct TALER_Amount *wire_fee,
  const struct TALER_MerchantPublicKeyP *merchant_pub,
  const struct TALER_FullPaytoHashP *h_payto,
  const struct GNUNET_HashCode *h_details,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_WireDepositDataPS wdp = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT),
    .purpose.size = htonl (sizeof (wdp)),
    .merchant_pub = *merchant_pub,
    .h_details = *h_details,
    .h_payto = *h_payto
  };

  TALER_amount_hton (&wdp.total,
                     total);
  TALER_amount_hton (&wdp.wire_fee,
                     wire_fee);
  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT,
    &wdp,
    &sig->eddsa_signature,
    &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Details affirmed by the exchange about a wire transfer the exchange
 * claims to have done with respect to a deposit operation.
 */
struct TALER_ConfirmWirePS
{
  /**
   * Purpose header for the signature over the contract with
   * purpose #TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Hash over the wiring information of the merchant.
   */
  struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;

  /**
   * Hash over the contract for which this deposit is made.
   */
  struct TALER_PrivateContractHashP h_contract_terms GNUNET_PACKED;

  /**
   * Raw value (binary encoding) of the wire transfer subject.
   */
  struct TALER_WireTransferIdentifierRawP wtid;

  /**
   * The coin's public key.  This is the value that must have been
   * signed (blindly) by the Exchange.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * When did the exchange execute this transfer? Note that the
   * timestamp may not be exactly the same on the wire, i.e.
   * because the wire has a different timezone or resolution.
   */
  struct GNUNET_TIME_TimestampNBO execution_time;

  /**
   * The contribution of @e coin_pub to the total transfer volume.
   * This is the value of the deposit minus the fee.
   */
  struct TALER_AmountNBO coin_contribution;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_confirm_wire_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_MerchantWireHashP *h_wire,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_WireTransferIdentifierRawP *wtid,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  struct GNUNET_TIME_Timestamp execution_time,
  const struct TALER_Amount *coin_contribution,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)

{
  struct TALER_ConfirmWirePS cw = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE),
    .purpose.size = htonl (sizeof (cw)),
    .h_wire = *h_wire,
    .h_contract_terms = *h_contract_terms,
    .wtid = *wtid,
    .coin_pub = *coin_pub,
    .execution_time = GNUNET_TIME_timestamp_hton (execution_time)
  };

  TALER_amount_hton (&cw.coin_contribution,
                     coin_contribution);
  return scb (&cw.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_confirm_wire_verify (
  const struct TALER_MerchantWireHashP *h_wire,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_WireTransferIdentifierRawP *wtid,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  struct GNUNET_TIME_Timestamp execution_time,
  const struct TALER_Amount *coin_contribution,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ConfirmWirePS cw = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE),
    .purpose.size = htonl (sizeof (cw)),
    .h_wire = *h_wire,
    .h_contract_terms = *h_contract_terms,
    .wtid = *wtid,
    .coin_pub = *coin_pub,
    .execution_time = GNUNET_TIME_timestamp_hton (execution_time)
  };

  TALER_amount_hton (&cw.coin_contribution,
                     coin_contribution);
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE,
                                &cw,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it will
 * refund a coin as part of the emergency /recoup
 * protocol.  The recoup will go back to the bank
 * account that created the reserve.
 */
struct TALER_RecoupConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange receive the recoup request?
   * Indirectly determines when the wire transfer is (likely)
   * to happen.
   */
  struct GNUNET_TIME_TimestampNBO timestamp;

  /**
   * How much of the coin's value will the exchange transfer?
   * (Needed in case the coin was partially spent.)
   */
  struct TALER_AmountNBO recoup_amount;

  /**
   * Public key of the coin.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * Public key of the reserve that will receive the recoup.
   */
  struct TALER_ReservePublicKeyP reserve_pub;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_confirm_recoup_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *recoup_amount,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RecoupConfirmationPS pc = {
    .purpose.size = htonl (sizeof (pc)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .coin_pub = *coin_pub,
    .reserve_pub = *reserve_pub
  };

  TALER_amount_hton (&pc.recoup_amount,
                     recoup_amount);
  return scb (&pc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_confirm_recoup_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *recoup_amount,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RecoupConfirmationPS pc = {
    .purpose.size = htonl (sizeof (pc)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .coin_pub = *coin_pub,
    .reserve_pub = *reserve_pub
  };

  TALER_amount_hton (&pc.recoup_amount,
                     recoup_amount);
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
                                &pc,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it will refund a refreshed coin
 * as part of the emergency /recoup protocol.  The recoup will go back to the
 * old coin's balance.
 */
struct TALER_RecoupRefreshConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange receive the recoup request?
   * Indirectly determines when the wire transfer is (likely)
   * to happen.
   */
  struct GNUNET_TIME_TimestampNBO timestamp;

  /**
   * How much of the coin's value will the exchange transfer?
   * (Needed in case the coin was partially spent.)
   */
  struct TALER_AmountNBO recoup_amount;

  /**
   * Public key of the refreshed coin.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * Public key of the old coin that will receive the recoup.
   */
  struct TALER_CoinSpendPublicKeyP old_coin_pub;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_confirm_recoup_refresh_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *recoup_amount,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RecoupRefreshConfirmationPS pc = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
    .purpose.size = htonl (sizeof (pc)),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .coin_pub = *coin_pub,
    .old_coin_pub = *old_coin_pub
  };

  TALER_amount_hton (&pc.recoup_amount,
                     recoup_amount);
  return scb (&pc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_confirm_recoup_refresh_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *recoup_amount,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_RecoupRefreshConfirmationPS pc = {
    .purpose.purpose = htonl (
      TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH),
    .purpose.size = htonl (sizeof (pc)),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .coin_pub = *coin_pub,
    .old_coin_pub = *old_coin_pub
  };

  TALER_amount_hton (&pc.recoup_amount,
                     recoup_amount);

  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP_REFRESH,
                                &pc,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it does not
 * currently know a denomination by the given hash.
 */
struct TALER_DenominationUnknownAffirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange sign this message.
   */
  struct GNUNET_TIME_TimestampNBO timestamp;

  /**
   * Hash of the public denomination key we do not know.
   */
  struct TALER_DenominationHashP h_denom_pub;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_denomination_unknown_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_DenominationHashP *h_denom_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_DenominationUnknownAffirmationPS dua = {
    .purpose.size = htonl (sizeof (dua)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .h_denom_pub = *h_denom_pub,
  };

  return scb (&dua.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_denomination_unknown_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_DenominationHashP *h_denom_pub,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_DenominationUnknownAffirmationPS dua = {
    .purpose.size = htonl (sizeof (dua)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .h_denom_pub = *h_denom_pub,
  };

  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_UNKNOWN,
                                &dua,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it does not
 * currently consider the given denomination to be valid
 * for the requested operation.
 */
struct TALER_DenominationExpiredAffirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange sign this message.
   */
  struct GNUNET_TIME_TimestampNBO timestamp;

  /**
   * Name of the operation that is not allowed at this time.  Might NOT be 0-terminated, but is padded with 0s.
   */
  char operation[8];

  /**
   * Hash of the public denomination key we do not know.
   */
  struct TALER_DenominationHashP h_denom_pub;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_denomination_expired_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_DenominationHashP *h_denom_pub,
  const char *op,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_DenominationExpiredAffirmationPS dua = {
    .purpose.size = htonl (sizeof (dua)),
    .purpose.purpose = htonl (
      TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .h_denom_pub = *h_denom_pub,
  };

  /* strncpy would create a compiler warning */
  GNUNET_memcpy (dua.operation,
                 op,
                 GNUNET_MIN (sizeof (dua.operation),
                             strlen (op)));
  return scb (&dua.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_denomination_expired_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_DenominationHashP *h_denom_pub,
  const char *op,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_DenominationExpiredAffirmationPS dua = {
    .purpose.size = htonl (sizeof (dua)),
    .purpose.purpose = htonl (
      TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED),
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp),
    .h_denom_pub = *h_denom_pub,
  };

  /* strncpy would create a compiler warning */
  GNUNET_memcpy (dua.operation,
                 op,
                 GNUNET_MIN (sizeof (dua.operation),
                             strlen (op)));
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_AFFIRM_DENOM_EXPIRED,
                                &dua,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it has
 * closed a reserve and send back the funds.
 */
struct TALER_ReserveCloseConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange initiate the wire transfer.
   */
  struct GNUNET_TIME_TimestampNBO timestamp;

  /**
   * How much did the exchange send?
   */
  struct TALER_AmountNBO closing_amount;

  /**
   * How much did the exchange charge for closing the reserve?
   */
  struct TALER_AmountNBO closing_fee;

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

  /**
   * Hash of the receiver's bank account.
   */
  struct TALER_FullPaytoHashP h_payto;

  /**
   * Wire transfer subject.
   */
  struct TALER_WireTransferIdentifierRawP wtid;
};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_reserve_closed_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *closing_amount,
  const struct TALER_Amount *closing_fee,
  const struct TALER_FullPayto payto,
  const struct TALER_WireTransferIdentifierRawP *wtid,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ReserveCloseConfirmationPS rcc = {
    .purpose.size = htonl (sizeof (rcc)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED),
    .wtid = *wtid,
    .reserve_pub = *reserve_pub,
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp)
  };

  TALER_amount_hton (&rcc.closing_amount,
                     closing_amount);
  TALER_amount_hton (&rcc.closing_fee,
                     closing_fee);
  TALER_full_payto_hash (payto,
                         &rcc.h_payto);
  return scb (&rcc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_reserve_closed_verify (
  struct GNUNET_TIME_Timestamp timestamp,
  const struct TALER_Amount *closing_amount,
  const struct TALER_Amount *closing_fee,
  const struct TALER_FullPayto payto,
  const struct TALER_WireTransferIdentifierRawP *wtid,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ReserveCloseConfirmationPS rcc = {
    .purpose.size = htonl (sizeof (rcc)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED),
    .wtid = *wtid,
    .reserve_pub = *reserve_pub,
    .timestamp = GNUNET_TIME_timestamp_hton (timestamp)
  };

  TALER_amount_hton (&rcc.closing_amount,
                     closing_amount);
  TALER_amount_hton (&rcc.closing_fee,
                     closing_fee);
  TALER_full_payto_hash (payto,
                         &rcc.h_payto);
  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
    &rcc,
    &sig->eddsa_signature,
    &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it has
 * received funds deposited into a purse.
 */
struct TALER_PurseCreateDepositConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange receive the deposits.
   */
  struct GNUNET_TIME_TimestampNBO exchange_time;

  /**
   * When will the purse expire?
   */
  struct GNUNET_TIME_TimestampNBO purse_expiration;

  /**
   * How much should the purse ultimately contain.
   */
  struct TALER_AmountNBO amount_without_fee;

  /**
   * How much was deposited so far.
   */
  struct TALER_AmountNBO total_deposited;

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

  /**
   * Hash of the contract of the purse.
   */
  struct TALER_PrivateContractHashP h_contract_terms;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_purse_created_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp exchange_time,
  struct GNUNET_TIME_Timestamp purse_expiration,
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_Amount *total_deposited,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_PurseCreateDepositConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION),
    .purpose.size = htonl (sizeof (dc)),
    .h_contract_terms = *h_contract_terms,
    .purse_pub = *purse_pub,
    .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
    .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
  };

  TALER_amount_hton (&dc.amount_without_fee,
                     amount_without_fee);
  TALER_amount_hton (&dc.total_deposited,
                     total_deposited);
  return scb (&dc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_purse_created_verify (
  struct GNUNET_TIME_Timestamp exchange_time,
  struct GNUNET_TIME_Timestamp purse_expiration,
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_Amount *total_deposited,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_PurseCreateDepositConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION),
    .purpose.size = htonl (sizeof (dc)),
    .h_contract_terms = *h_contract_terms,
    .purse_pub = *purse_pub,
    .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
    .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
  };

  TALER_amount_hton (&dc.amount_without_fee,
                     amount_without_fee);
  TALER_amount_hton (&dc.total_deposited,
                     total_deposited);
  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION,
    &dc,
    &sig->eddsa_signature,
    &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it has
 * received funds deposited into a purse.
 */
struct TALER_CoinPurseRefundConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

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

  /**
   * Public key of the coin.
   */
  struct TALER_CoinSpendPublicKeyP coin_pub;

  /**
   * How much will be refunded to the purse.
   */
  struct TALER_AmountNBO refunded_amount;

  /**
   * How much was the refund fee.
   */
  struct TALER_AmountNBO refund_fee;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_purse_refund_sign (
  TALER_ExchangeSignCallback scb,
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_Amount *refund_fee,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_CoinPurseRefundConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND),
    .purpose.size = htonl (sizeof (dc)),
    .coin_pub = *coin_pub,
    .purse_pub = *purse_pub,
  };

  TALER_amount_hton (&dc.refunded_amount,
                     amount_without_fee);
  TALER_amount_hton (&dc.refund_fee,
                     refund_fee);
  return scb (&dc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_purse_refund_verify (
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_Amount *refund_fee,
  const struct TALER_CoinSpendPublicKeyP *coin_pub,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_CoinPurseRefundConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND),
    .purpose.size = htonl (sizeof (dc)),
    .coin_pub = *coin_pub,
    .purse_pub = *purse_pub,
  };

  TALER_amount_hton (&dc.refunded_amount,
                     amount_without_fee);
  TALER_amount_hton (&dc.refund_fee,
                     refund_fee);
  return GNUNET_CRYPTO_eddsa_verify (
    TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_REFUND,
    &dc,
    &sig->eddsa_signature,
    &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Response by which the exchange affirms that it has
 * merged a purse into a reserve.
 */
struct TALER_PurseMergedConfirmationPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_MERGED
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * When did the exchange receive the deposits.
   */
  struct GNUNET_TIME_TimestampNBO exchange_time;

  /**
   * When will the purse expire?
   */
  struct GNUNET_TIME_TimestampNBO purse_expiration;

  /**
   * How much should the purse ultimately contain.
   */
  struct TALER_AmountNBO amount_without_fee;

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

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

  /**
   * Hash of the contract of the purse.
   */
  struct TALER_PrivateContractHashP h_contract_terms;

  /**
   * Hash of the provider URL hosting the reserve.
   */
  struct GNUNET_HashCode h_provider_url;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_purse_merged_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp exchange_time,
  struct GNUNET_TIME_Timestamp purse_expiration,
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const char *exchange_url,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_PurseMergedConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_MERGED),
    .purpose.size = htonl (sizeof (dc)),
    .h_contract_terms = *h_contract_terms,
    .purse_pub = *purse_pub,
    .reserve_pub = *reserve_pub,
    .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
    .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
  };

  TALER_amount_hton (&dc.amount_without_fee,
                     amount_without_fee);
  GNUNET_CRYPTO_hash (exchange_url,
                      strlen (exchange_url) + 1,
                      &dc.h_provider_url);
  return scb (&dc.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_purse_merged_verify (
  struct GNUNET_TIME_Timestamp exchange_time,
  struct GNUNET_TIME_Timestamp purse_expiration,
  const struct TALER_Amount *amount_without_fee,
  const struct TALER_PurseContractPublicKeyP *purse_pub,
  const struct TALER_PrivateContractHashP *h_contract_terms,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const char *exchange_url,
  const struct TALER_ExchangePublicKeyP *pub,
  const struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_PurseMergedConfirmationPS dc = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_MERGED),
    .purpose.size = htonl (sizeof (dc)),
    .h_contract_terms = *h_contract_terms,
    .purse_pub = *purse_pub,
    .reserve_pub = *reserve_pub,
    .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
    .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
  };

  TALER_amount_hton (&dc.amount_without_fee,
                     amount_without_fee);
  GNUNET_CRYPTO_hash (exchange_url,
                      strlen (exchange_url) + 1,
                      &dc.h_provider_url);
  return
    GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_MERGED,
                                &dc,
                                &sig->eddsa_signature,
                                &pub->eddsa_pub);
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * @brief Format used to generate the signature on a purse status
 * from the exchange.
 */
struct TALER_PurseStatusPS
{
  /**
   * Purpose must be #TALER_SIGNATURE_EXCHANGE_PURSE_STATUS.  Signed
   * by a `struct TALER_ExchangePublicKeyP` using EdDSA.
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time when the purse was merged, possibly 'never'.
   */
  struct GNUNET_TIME_TimestampNBO merge_timestamp;

  /**
   * Time when the purse was deposited last, possibly 'never'.
   */
  struct GNUNET_TIME_TimestampNBO deposit_timestamp;

  /**
   * Amount deposited in total in the purse without fees.
   * May be possibly less than the target amount.
   */
  struct TALER_AmountNBO balance;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_purse_status_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp merge_timestamp,
  struct GNUNET_TIME_Timestamp deposit_timestamp,
  const struct TALER_Amount *balance,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_PurseStatusPS dcs = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_PURSE_STATUS),
    .purpose.size = htonl (sizeof (dcs)),
    .merge_timestamp = GNUNET_TIME_timestamp_hton (merge_timestamp),
    .deposit_timestamp = GNUNET_TIME_timestamp_hton (deposit_timestamp)
  };

  TALER_amount_hton (&dcs.balance,
                     balance);
  return scb (&dcs.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_purse_status_verify (
  struct GNUNET_TIME_Timestamp merge_timestamp,
  struct GNUNET_TIME_Timestamp deposit_timestamp,
  const struct TALER_Amount *balance,
  const struct TALER_ExchangePublicKeyP *exchange_pub,
  const struct TALER_ExchangeSignatureP *exchange_sig)
{
  struct TALER_PurseStatusPS dcs = {
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_PURSE_STATUS),
    .purpose.size = htonl (sizeof (dcs)),
    .merge_timestamp = GNUNET_TIME_timestamp_hton (merge_timestamp),
    .deposit_timestamp = GNUNET_TIME_timestamp_hton (deposit_timestamp)
  };

  TALER_amount_hton (&dcs.balance,
                     balance);
  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_PURSE_STATUS,
                                  &dcs,
                                  &exchange_sig->eddsa_signature,
                                  &exchange_pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


GNUNET_NETWORK_STRUCT_BEGIN

/**
 * Message signed by the exchange to affirm that the
 * owner of a reserve has certain attributes.
 */
struct TALER_ExchangeAttestPS
{

  /**
   * Purpose is #TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS
   */
  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

  /**
   * Time when the attestation was made.
   */
  struct GNUNET_TIME_TimestampNBO attest_timestamp;

  /**
   * Time when the attestation expires.
   */
  struct GNUNET_TIME_TimestampNBO expiration_time;

  /**
   * Public key of the reserve for which the attributes
   * are attested.
   */
  struct TALER_ReservePublicKeyP reserve_pub;

  /**
   * Hash over the attributes.
   */
  struct GNUNET_HashCode h_attributes;

};

GNUNET_NETWORK_STRUCT_END


enum TALER_ErrorCode
TALER_exchange_online_reserve_attest_details_sign (
  TALER_ExchangeSignCallback scb,
  struct GNUNET_TIME_Timestamp attest_timestamp,
  struct GNUNET_TIME_Timestamp expiration_time,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const json_t *attributes,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ExchangeAttestPS rap = {
    .purpose.size = htonl (sizeof (rap)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS),
    .attest_timestamp = GNUNET_TIME_timestamp_hton (attest_timestamp),
    .expiration_time = GNUNET_TIME_timestamp_hton (expiration_time),
    .reserve_pub = *reserve_pub
  };

  TALER_json_hash (attributes,
                   &rap.h_attributes);
  return scb (&rap.purpose,
              pub,
              sig);
}


enum GNUNET_GenericReturnValue
TALER_exchange_online_reserve_attest_details_verify (
  struct GNUNET_TIME_Timestamp attest_timestamp,
  struct GNUNET_TIME_Timestamp expiration_time,
  const struct TALER_ReservePublicKeyP *reserve_pub,
  const json_t *attributes,
  struct TALER_ExchangePublicKeyP *pub,
  struct TALER_ExchangeSignatureP *sig)
{
  struct TALER_ExchangeAttestPS rap = {
    .purpose.size = htonl (sizeof (rap)),
    .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS),
    .attest_timestamp = GNUNET_TIME_timestamp_hton (attest_timestamp),
    .expiration_time = GNUNET_TIME_timestamp_hton (expiration_time),
    .reserve_pub = *reserve_pub
  };

  TALER_json_hash (attributes,
                   &rap.h_attributes);
  if (GNUNET_OK !=
      GNUNET_CRYPTO_eddsa_verify (
        TALER_SIGNATURE_EXCHANGE_RESERVE_ATTEST_DETAILS,
        &rap,
        &sig->eddsa_signature,
        &pub->eddsa_pub))
  {
    GNUNET_break_op (0);
    return GNUNET_SYSERR;
  }
  return GNUNET_OK;
}


/* end of exchange_signatures.c */
