diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/exchange_api_common.c | 368 | ||||
| -rw-r--r-- | src/lib/exchange_api_common.h | 123 | ||||
| -rw-r--r-- | src/lib/exchange_api_deposit.c | 101 | ||||
| -rw-r--r-- | src/lib/exchange_api_melt.c | 187 | ||||
| -rw-r--r-- | src/lib/exchange_api_purse_create_with_deposit.c | 203 | ||||
| -rw-r--r-- | src/lib/exchange_api_purse_deposit.c | 172 | ||||
| -rw-r--r-- | src/lib/exchange_api_recoup.c | 106 | ||||
| -rw-r--r-- | src/lib/exchange_api_recoup_refresh.c | 117 | ||||
| -rw-r--r-- | src/lib/exchange_api_refund.c | 32 | 
9 files changed, 807 insertions, 602 deletions
| diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 567239ee..59f5bab8 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -22,6 +22,7 @@  #include "platform.h"  #include "taler_json_lib.h"  #include <gnunet/gnunet_curl_lib.h> +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h" @@ -685,11 +686,6 @@ struct CoinHistoryParseContext    const struct TALER_CoinSpendPublicKeyP *coin_pub;    /** -   * Hash of @e dk, set from parsing. -   */ -  struct TALER_DenominationHashP *h_denom_pub; - -  /**     * Where to sum up total refunds.     */    struct TALER_Amount rtotal; @@ -749,8 +745,6 @@ help_deposit (struct CoinHistoryParseContext *pc,                                   &h_contract_terms),      GNUNET_JSON_spec_fixed_auto ("h_wire",                                   &h_wire), -    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                 pc->h_denom_pub),      GNUNET_JSON_spec_mark_optional (        GNUNET_JSON_spec_fixed_auto ("h_age_commitment",                                     &hac), @@ -784,7 +778,7 @@ help_deposit (struct CoinHistoryParseContext *pc,          &h_contract_terms,          no_hac ? NULL : &hac,          NULL /* h_extensions! */, -        pc->h_denom_pub, +        &pc->dk->h_key,          wallet_timestamp,          &merchant_pub,          refund_deadline, @@ -836,8 +830,6 @@ help_melt (struct CoinHistoryParseContext *pc,                                   &sig),      GNUNET_JSON_spec_fixed_auto ("rc",                                   &rc), -    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                 pc->h_denom_pub),      GNUNET_JSON_spec_mark_optional (        GNUNET_JSON_spec_fixed_auto ("h_age_commitment",                                     &h_age_commitment), @@ -876,7 +868,7 @@ help_melt (struct CoinHistoryParseContext *pc,          amount,          &melt_fee,          &rc, -        pc->h_denom_pub, +        &pc->dk->h_key,          no_hac          ? NULL          : &h_age_commitment, @@ -1008,8 +1000,6 @@ help_recoup (struct CoinHistoryParseContext *pc,                                   &coin_sig),      GNUNET_JSON_spec_fixed_auto ("coin_blind",                                   &coin_bks), -    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                 pc->h_denom_pub),      GNUNET_JSON_spec_timestamp ("timestamp",                                  ×tamp),      GNUNET_JSON_spec_end () @@ -1036,7 +1026,7 @@ help_recoup (struct CoinHistoryParseContext *pc,      return GNUNET_SYSERR;    }    if (GNUNET_OK != -      TALER_wallet_recoup_verify (pc->h_denom_pub, +      TALER_wallet_recoup_verify (&pc->dk->h_key,                                    &coin_bks,                                    pc->coin_pub,                                    &coin_sig)) @@ -1081,8 +1071,6 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,                                   &old_coin_pub),      GNUNET_JSON_spec_fixed_auto ("coin_blind",                                   &coin_bks), -    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                 pc->h_denom_pub),      GNUNET_JSON_spec_timestamp ("timestamp",                                  ×tamp),      GNUNET_JSON_spec_end () @@ -1109,7 +1097,7 @@ help_recoup_refresh (struct CoinHistoryParseContext *pc,      return GNUNET_SYSERR;    }    if (GNUNET_OK != -      TALER_wallet_recoup_verify (pc->h_denom_pub, +      TALER_wallet_recoup_verify (&pc->dk->h_key,                                    &coin_bks,                                    pc->coin_pub,                                    &coin_sig)) @@ -1250,12 +1238,11 @@ help_purse_deposit (struct CoinHistoryParseContext *pc,  enum GNUNET_GenericReturnValue  TALER_EXCHANGE_verify_coin_history (    const struct TALER_EXCHANGE_DenomPublicKey *dk, -  const char *currency,    const struct TALER_CoinSpendPublicKeyP *coin_pub,    json_t *history, -  struct TALER_DenominationHashP *h_denom_pub,    struct TALER_Amount *total)  { +  const char *currency = dk->value.currency;    const struct    {      const char *type; @@ -1273,8 +1260,7 @@ TALER_EXCHANGE_verify_coin_history (    struct CoinHistoryParseContext pc = {      .dk = dk,      .coin_pub = coin_pub, -    .total = total, -    .h_denom_pub = h_denom_pub +    .total = total    };    size_t len; @@ -1528,11 +1514,10 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (  {    struct TALER_PurseMergeSignatureP merge_sig;    struct GNUNET_TIME_Timestamp merge_timestamp; -  const char *partner_url = exchange_url; +  const char *partner_url = NULL;    struct TALER_ReservePublicKeyP reserve_pub;    struct GNUNET_JSON_Specification spec[] = {      GNUNET_JSON_spec_mark_optional ( -      // FIXME: partner_url or partner_base_url?        GNUNET_JSON_spec_string ("partner_url",                                 &partner_url),        NULL), @@ -1554,6 +1539,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (      GNUNET_break_op (0);      return GNUNET_SYSERR;    } +  if (NULL == partner_url) +    partner_url = exchange_url;    payto_uri = make_payto (partner_url,                            &reserve_pub);    if (GNUNET_OK != @@ -1581,18 +1568,55 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (  } -/** - * Check proof of a contract conflict. - * - * DESIGN-FIXME: this 'proof' doesn't really proof a conflict! - * - * @param ccontract_sig conflicting signature (must - *        not match the signature from the proof) - * @param purse_pub public key of the purse - * @param exchange_url the base URL of this exchange - * @param proof the proof to check - * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig - */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_coin_conflict_ ( +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  const char *exchange_url, +  const json_t *proof, +  struct TALER_CoinSpendPublicKeyP *coin_pub, +  struct TALER_CoinSpendSignatureP *coin_sig) +{ +  const char *partner_url = NULL; +  struct TALER_Amount amount; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("coin_sig", +                                 coin_sig), +    GNUNET_JSON_spec_fixed_auto ("coin_pub", +                                 coin_pub), +    GNUNET_JSON_spec_mark_optional ( +      GNUNET_JSON_spec_string ("partner_url", +                               &partner_url), +      NULL), +    TALER_JSON_spec_amount_any ("amount", +                                &amount), +    GNUNET_JSON_spec_end () +  }; + +  if (GNUNET_OK != +      GNUNET_JSON_parse (proof, +                         spec, +                         NULL, NULL)) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  if (NULL == partner_url) +    partner_url = exchange_url; +  if (GNUNET_OK != +      TALER_wallet_purse_deposit_verify ( +        partner_url, +        purse_pub, +        &amount, +        coin_pub, +        coin_sig)) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + +  enum GNUNET_GenericReturnValue  TALER_EXCHANGE_check_purse_econtract_conflict_ (    const struct TALER_PurseContractSignatureP *ccontract_sig, @@ -1642,4 +1666,278 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (  } +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_amount_conflict_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  const json_t *proof, +  struct TALER_CoinSpendPublicKeyP *coin_pub, +  struct TALER_Amount *remaining) +{ +  json_t *history; +  struct TALER_Amount total; +  struct TALER_DenominationHashP h_denom_pub; +  const struct TALER_EXCHANGE_DenomPublicKey *dki; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("coin_pub", +                                 coin_pub), +    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", +                                 &h_denom_pub), +    GNUNET_JSON_spec_json ("history", +                           &history), +    GNUNET_JSON_spec_end () +  }; + +  if (GNUNET_OK != +      GNUNET_JSON_parse (proof, +                         spec, +                         NULL, NULL)) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  dki = TALER_EXCHANGE_get_denomination_key_by_hash ( +    keys, +    &h_denom_pub); +  if (NULL == dki) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  if (GNUNET_OK != +      TALER_EXCHANGE_verify_coin_history (dki, +                                          coin_pub, +                                          history, +                                          &total)) +  { +    GNUNET_break_op (0); +    json_decref (history); +    return GNUNET_SYSERR; +  } +  json_decref (history); +  if (0 > +      TALER_amount_subtract (remaining, +                             &dki->value, +                             &total)) +  { +    /* Strange 'proof': coin was double-spent +       before our transaction?! */ +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + + +/** + * Verify that @a coin_sig does NOT appear in + * the history of @a proof and thus whatever transaction + * is authorized by @a coin_sig is a conflict with + * @a proof. + * + * @param proof a proof to check + * @param coin_sig signature that must not be in @a proof + * @return #GNUNET_OK if @a coin_sig is not in @a proof + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_signature_conflict_ ( +  const json_t *proof, +  const struct TALER_CoinSpendSignatureP *coin_sig) +{ +  json_t *history; +  size_t off; +  json_t *entry; + +  history = json_object_get (proof, +                             "history"); +  if (NULL == history) +  { +    GNUNET_break (0); +    return GNUNET_SYSERR; +  } +  json_array_foreach (history, off, entry) +  { +    struct TALER_CoinSpendSignatureP cs; +    struct GNUNET_JSON_Specification spec[] = { +      GNUNET_JSON_spec_fixed_auto ("coin_sig", +                                   &cs), +      GNUNET_JSON_spec_end () +    }; + +    if (GNUNET_OK != +        GNUNET_JSON_parse (entry, +                           spec, +                           NULL, NULL)) +      continue; /* entry without coin signature */ +    if (0 == +        GNUNET_memcmp (&cs, +                       coin_sig)) +    { +      GNUNET_break_op (0); +      return GNUNET_SYSERR; +    } +  } +  return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_denomination_conflict_ ( +  const json_t *proof, +  const struct TALER_DenominationHashP *ch_denom_pub) +{ +  struct TALER_DenominationHashP h_denom_pub; +  struct GNUNET_JSON_Specification spec[] = { +    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", +                                 &h_denom_pub), +    GNUNET_JSON_spec_end () +  }; + +  if (GNUNET_OK != +      GNUNET_JSON_parse (proof, +                         spec, +                         NULL, NULL)) +  { +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  if (0 == +      GNUNET_memcmp (ch_denom_pub, +                     &h_denom_pub)) +  { +    GNUNET_break_op (0); +    return GNUNET_OK; +  } +  /* indeed, proof with different denomination key provided */ +  return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_conflict_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  const json_t *proof, +  const struct TALER_EXCHANGE_DenomPublicKey *dk, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_CoinSpendSignatureP *coin_sig, +  const struct TALER_Amount *required) +{ +  enum TALER_ErrorCode ec; + +  ec = TALER_JSON_get_error_code (proof); +  switch (ec) +  { +  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: +    { +      struct TALER_Amount left; +      struct TALER_CoinSpendPublicKeyP pcoin_pub; + +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_amount_conflict_ ( +            keys, +            proof, +            &pcoin_pub, +            &left)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (0 != +          GNUNET_memcmp (&pcoin_pub, +                         coin_pub)) +      { +        /* conflict is for a different coin! */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (-1 != +          TALER_amount_cmp (&left, +                            required)) +      { +        /* Balance was sufficient after all; recoup MAY have still been possible */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_signature_conflict_ ( +            proof, +            coin_sig)) +      { +        /* Not a conflicting transaction: ours is included! */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      break; +    } +  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: +    { +      struct TALER_Amount left; +      struct TALER_CoinSpendPublicKeyP pcoin_pub; + +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_amount_conflict_ ( +            keys, +            proof, +            &pcoin_pub, +            &left)) +      { +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (0 != +          GNUNET_memcmp (&pcoin_pub, +                         coin_pub)) +      { +        /* conflict is for a different coin! */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_denomination_conflict_ ( +            proof, +            &dk->h_key)) +      { +        /* Eh, same denomination, hence no conflict */ +        GNUNET_break_op (0); +        return GNUNET_SYSERR; +      } +      break; +    } +  default: +    GNUNET_break_op (0); +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_min_denomination_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  struct TALER_Amount *min) +{ +  bool have_min = false; +  for (unsigned int i = 0; i<keys->num_denom_keys; i++) +  { +    const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i]; + +    if (! have_min) +    { +      *min = dk->value; +      have_min = true; +      continue; +    } +    if (1 != TALER_amount_cmp (min, +                               &dk->value)) +      continue; +    *min = dk->value; +  } +  if (! have_min) +  { +    GNUNET_break (0); +    return GNUNET_SYSERR; +  } +  return GNUNET_OK; +} + +  /* end of exchange_api_common.c */ diff --git a/src/lib/exchange_api_common.h b/src/lib/exchange_api_common.h index 9c6d4502..9cbdf547 100644 --- a/src/lib/exchange_api_common.h +++ b/src/lib/exchange_api_common.h @@ -23,6 +23,7 @@  #define EXCHANGE_API_COMMON_H  #include "taler_json_lib.h" +#include "taler_exchange_service.h"  /** @@ -33,7 +34,7 @@   * @param purse_pub the public key (must match   *        the signature from the proof)   * @param proof the proof to check - * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig + * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a cpurse_sig   */  enum GNUNET_GenericReturnValue  TALER_EXCHANGE_check_purse_create_conflict_ ( @@ -51,7 +52,7 @@ TALER_EXCHANGE_check_purse_create_conflict_ (   *        the signature from the proof)   * @param exchange_url the base URL of this exchange   * @param proof the proof to check - * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig + * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and @a merge_pub and conflicts with @a cmerge_sig   */  enum GNUNET_GenericReturnValue  TALER_EXCHANGE_check_purse_merge_conflict_ ( @@ -63,16 +64,38 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (  /** - * Check proof of a contract conflict. + * Check @a proof that claims this coin was spend + * differently on the same purse already. Note that + * the caller must still check that @a coin_pub is + * in the list of coins that were used, and that + * @a coin_sig is different from the signature the + * caller used.   * - * DESIGN-FIXME: this 'proof' doesn't really proof a conflict! + * @param purse_pub the public key of the purse + * @param exchange_url base URL of our exchange + * @param proof the proof to check + * @param[out] coin_pub set to the conflicting coin + * @param[out] coin_sig set to the conflicting signature + * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and showing that @a coin_pub was spent using @a coin_sig. + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_purse_coin_conflict_ ( +  const struct TALER_PurseContractPublicKeyP *purse_pub, +  const char *exchange_url, +  const json_t *proof, +  struct TALER_CoinSpendPublicKeyP *coin_pub, +  struct TALER_CoinSpendSignatureP *coin_sig); + + +/** + * Check proof of a contract conflict.   *   * @param ccontract_sig conflicting signature (must   *        not match the signature from the proof)   * @param purse_pub public key of the purse   * @param exchange_url the base URL of this exchange   * @param proof the proof to check - * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a purse_sig + * @return #GNUNET_OK if the @a proof is OK for @a purse_pub and conflicts with @a ccontract_sig   */  enum GNUNET_GenericReturnValue  TALER_EXCHANGE_check_purse_econtract_conflict_ ( @@ -81,4 +104,94 @@ TALER_EXCHANGE_check_purse_econtract_conflict_ (    const json_t *proof); +/** + * Check proof of a coin spend value conflict. + * + * @param keys exchange /keys structure + * @param proof the proof to check + * @param[out] coin_pub set to the public key of the + *        coin that is claimed to have an insufficient + *        balance + * @param[out] remaining set to the remaining balance + *        of the coin as provided by the proof + * @return #GNUNET_OK if the @a proof is OK for @a purse_pub demonstrating that @a coin_pub has only @a remaining balance. + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_amount_conflict_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  const json_t *proof, +  struct TALER_CoinSpendPublicKeyP *coin_pub, +  struct TALER_Amount *remaining); + + +/** + * Verify that @a proof contains a coin history + * that demonstrates that @a coin_pub was previously + * used with a denomination key that is different + * from @a ch_denom_pub.  Note that the coin history + * MUST have been checked before using + * #TALER_EXCHANGE_check_coin_amount_conflict_(). + * + * @param proof a proof to check + * @param ch_denom_pub hash of the conflicting denomination + * @return #GNUNET_OK if @a ch_denom_pub differs from the + *         denomination hash given by the history of the coin + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_denomination_conflict_ ( +  const json_t *proof, +  const struct TALER_DenominationHashP *ch_denom_pub); + + +/** + * Verify that @a coin_sig does NOT appear in + * the history of @a proof and thus whatever transaction + * is authorized by @a coin_sig is a conflict with + * @a proof. + * + * @param proof a proof to check + * @param coin_sig signature that must not be in @a proof + * @return #GNUNET_OK if @a coin_sig is not in @a proof + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_signature_conflict_ ( +  const json_t *proof, +  const struct TALER_CoinSpendSignatureP *coin_sig); + + +/** + * Check that the provided @a proof indeeds indicates + * a conflict for @a coin_pub. + * + * @param keys exchange keys + * @param proof provided conflict proof + * @param dk denomination of @a coin_pub that the client + *           used + * @param coin_pub public key of the coin + * @param coin_sig signature over operation that conflicted + * @param required balance required on the coin for the operation + * @return #GNUNET_OK if @a proof holds + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_check_coin_conflict_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  const json_t *proof, +  const struct TALER_EXCHANGE_DenomPublicKey *dk, +  const struct TALER_CoinSpendPublicKeyP *coin_pub, +  const struct TALER_CoinSpendSignatureP *coin_sig, +  const struct TALER_Amount *required); + + +/** + * Find the smallest denomination amount in @e keys. + * + * @param keys keys to search + * @param[out] min set to the smallest amount + * @return #GNUNET_SYSERR if there are no denominations in @a keys + */ +enum GNUNET_GenericReturnValue +TALER_EXCHANGE_get_min_denomination_ ( +  const struct TALER_EXCHANGE_Keys *keys, +  struct TALER_Amount *min); +  #endif diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c index 67f595bf..58b1c3a4 100644 --- a/src/lib/exchange_api_deposit.c +++ b/src/lib/exchange_api_deposit.c @@ -29,6 +29,7 @@  #include "taler_json_lib.h"  #include "taler_auditor_service.h"  #include "taler_exchange_service.h" +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h"  #include "exchange_api_curl_defaults.h" @@ -129,6 +130,11 @@ struct TALER_EXCHANGE_DepositHandle    struct TALER_CoinSpendPublicKeyP coin_pub;    /** +   * Our signature for the deposit operation. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; + +  /**     * The Merchant's public key.  Allows the merchant to later refund     * the transaction or to inquire about the wire transfer identifier.     */ @@ -227,77 +233,6 @@ auditor_cb (void *cls,  /** - * Verify that the signatures on the "403 FORBIDDEN" response from the - * exchange demonstrating customer double-spending are valid. - * - * @param dh deposit handle - * @param json json reply with the signature(s) and transaction history - * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_deposit_signature_conflict ( -  const struct TALER_EXCHANGE_DepositHandle *dh, -  const json_t *json) -{ -  json_t *history; -  struct TALER_Amount total; -  enum TALER_ErrorCode ec; -  struct TALER_DenominationHashP h_denom_pub; - -  memset (&h_denom_pub, -          0, -          sizeof (h_denom_pub)); -  history = json_object_get (json, -                             "history"); -  if (GNUNET_OK != -      TALER_EXCHANGE_verify_coin_history (&dh->dki, -                                          dh->dki.value.currency, -                                          &dh->coin_pub, -                                          history, -                                          &h_denom_pub, -                                          &total)) -  { -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  } -  ec = TALER_JSON_get_error_code (json); -  switch (ec) -  { -  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: -    if (0 > -        TALER_amount_add (&total, -                          &total, -                          &dh->amount_with_fee)) -    { -      /* clearly not OK if our transaction would have caused -         the overflow... */ -      return GNUNET_OK; -    } - -    if (0 >= TALER_amount_cmp (&total, -                               &dh->dki.value)) -    { -      /* transaction should have still fit */ -      GNUNET_break (0); -      return GNUNET_SYSERR; -    } -    /* everything OK, proof of double-spending was provided */ -    return GNUNET_OK; -  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: -    if (0 != GNUNET_memcmp (&dh->dki.h_key, -                            &h_denom_pub)) -      return GNUNET_OK; /* indeed, proof with different denomination key provided */ -    /* invalid proof provided */ -    return GNUNET_SYSERR; -  default: -    /* unexpected error code */ -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  } -} - - -/**   * Function called when we're done processing the   * HTTP /deposit request.   * @@ -316,8 +251,10 @@ handle_deposit_finished (void *cls,      .hr.reply = j,      .hr.http_status = (unsigned int) response_code    }; +  const struct TALER_EXCHANGE_Keys *keys;    dh->job = NULL; +  keys = TALER_EXCHANGE_get_keys (dh->exchange);    switch (response_code)    {    case 0: @@ -409,19 +346,21 @@ handle_deposit_finished (void *cls,         happen, we should pass the JSON reply to the application */      break;    case MHD_HTTP_CONFLICT: -    /* Double spending; check signatures on transaction history */ +    dr.hr.ec = TALER_JSON_get_error_code (j); +    dr.hr.hint = TALER_JSON_get_error_hint (j);      if (GNUNET_OK != -        verify_deposit_signature_conflict (dh, -                                           j)) +        TALER_EXCHANGE_check_coin_conflict_ ( +          keys, +          j, +          &dh->dki, +          &dh->coin_pub, +          &dh->coin_sig, +          &dh->amount_with_fee))      {        GNUNET_break_op (0);        dr.hr.http_status = 0; -      dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE; -    } -    else -    { -      dr.hr.ec = TALER_JSON_get_error_code (j); -      dr.hr.hint = TALER_JSON_get_error_hint (j); +      dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +      break;      }      break;    case MHD_HTTP_GONE: @@ -692,6 +631,8 @@ TALER_EXCHANGE_deposit (    dh->exchange = exchange;    dh->cb = cb;    dh->cb_cls = cb_cls; +  dh->coin_sig = *coin_sig; +  dh->coin_pub = *coin_pub;    dh->url = TEAH_path_to_url (exchange,                                arg_str);    if (NULL == dh->url) diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c index 80c75970..ff720d2f 100644 --- a/src/lib/exchange_api_melt.c +++ b/src/lib/exchange_api_melt.c @@ -27,6 +27,7 @@  #include <gnunet/gnunet_curl_lib.h>  #include "taler_json_lib.h"  #include "taler_exchange_service.h" +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h"  #include "exchange_api_curl_defaults.h" @@ -102,6 +103,11 @@ struct TALER_EXCHANGE_MeltHandle    struct TALER_CoinSpendPublicKeyP coin_pub;    /** +   * Signature affirming the melt. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; + +  /**     * @brief Public information about the coin's denomination key     */    const struct TALER_EXCHANGE_DenomPublicKey *dki; @@ -184,143 +190,6 @@ verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,  /** - * Verify that the signatures on the "409 CONFLICT" response from the - * exchange demonstrating customer denomination key differences - * resulting from coin private key reuse are valid. - * - * @param mh melt handle - * @param json json reply with the signature(s) and transaction history - * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_melt_signature_denom_conflict (struct TALER_EXCHANGE_MeltHandle *mh, -                                      const json_t *json) - -{ -  json_t *history; -  struct TALER_Amount total; -  struct TALER_DenominationHashP h_denom_pub; - -  memset (&h_denom_pub, -          0, -          sizeof (h_denom_pub)); -  history = json_object_get (json, -                             "history"); -  if (GNUNET_OK != -      TALER_EXCHANGE_verify_coin_history (mh->dki, -                                          mh->dki->value.currency, -                                          &mh->coin_pub, -                                          history, -                                          &h_denom_pub, -                                          &total)) -  { -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  } -  if (0 != GNUNET_memcmp (&mh->dki->h_key, -                          &h_denom_pub)) -    return GNUNET_OK; /* indeed, proof with different denomination key provided */ -  /* invalid proof provided */ -  return GNUNET_SYSERR; -} - - -/** - * Verify that the signatures on the "409 CONFLICT" response from the - * exchange demonstrating customer double-spending are valid. - * - * @param mh melt handle - * @param json json reply with the signature(s) and transaction history - * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not - */ -static enum GNUNET_GenericReturnValue -verify_melt_signature_spend_conflict (struct TALER_EXCHANGE_MeltHandle *mh, -                                      const json_t *json) -{ -  json_t *history; -  struct TALER_Amount total; -  struct GNUNET_JSON_Specification spec[] = { -    GNUNET_JSON_spec_json ("history", -                           &history), -    GNUNET_JSON_spec_end () -  }; -  const struct MeltedCoin *mc; -  enum TALER_ErrorCode ec; -  struct TALER_DenominationHashP h_denom_pub; - -  /* parse JSON reply */ -  if (GNUNET_OK != -      GNUNET_JSON_parse (json, -                         spec, -                         NULL, NULL)) -  { -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  } - -  /* Find out which coin was deemed problematic by the exchange */ -  mc = &mh->md.melted_coin; -  /* verify coin history */ -  memset (&h_denom_pub, -          0, -          sizeof (h_denom_pub)); -  history = json_object_get (json, -                             "history"); -  if (GNUNET_OK != -      TALER_EXCHANGE_verify_coin_history (mh->dki, -                                          mc->original_value.currency, -                                          &mh->coin_pub, -                                          history, -                                          &h_denom_pub, -                                          &total)) -  { -    GNUNET_break_op (0); -    json_decref (history); -    return GNUNET_SYSERR; -  } -  json_decref (history); - -  ec = TALER_JSON_get_error_code (json); -  switch (ec) -  { -  case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: -    /* check if melt operation was really too expensive given history */ -    if (0 > -        TALER_amount_add (&total, -                          &total, -                          &mc->melt_amount_with_fee)) -    { -      /* clearly not OK if our transaction would have caused -         the overflow... */ -      return GNUNET_OK; -    } - -    if (0 >= TALER_amount_cmp (&total, -                               &mc->original_value)) -    { -      /* transaction should have still fit */ -      GNUNET_break (0); -      return GNUNET_SYSERR; -    } - -    /* everything OK, valid proof of double-spending was provided */ -    return GNUNET_OK; -  case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: -    if (0 != GNUNET_memcmp (&mh->dki->h_key, -                            &h_denom_pub)) -      return GNUNET_OK; /* indeed, proof with different denomination key provided */ -    /* invalid proof provided */ -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  default: -    /* unexpected error code */ -    GNUNET_break_op (0); -    return GNUNET_SYSERR; -  } -} - - -/**   * Function called when we're done processing the   * HTTP /coins/$COIN_PUB/melt request.   * @@ -339,8 +208,10 @@ handle_melt_finished (void *cls,      .hr.reply = j,      .hr.http_status = (unsigned int) response_code    }; +  const struct TALER_EXCHANGE_Keys *keys;    mh->job = NULL; +  keys = TALER_EXCHANGE_get_keys (mh->exchange);    switch (response_code)    {    case 0: @@ -372,36 +243,19 @@ handle_melt_finished (void *cls,      break;    case MHD_HTTP_CONFLICT:      mr.hr.ec = TALER_JSON_get_error_code (j); -    switch (mr.hr.ec) +    mr.hr.hint = TALER_JSON_get_error_hint (j); +    if (GNUNET_OK != +        TALER_EXCHANGE_check_coin_conflict_ ( +          keys, +          j, +          mh->dki, +          &mh->coin_pub, +          &mh->coin_sig, +          &mh->md.melted_coin.melt_amount_with_fee))      { -    case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: -      /* Double spending; check signatures on transaction history */ -      if (GNUNET_OK != -          verify_melt_signature_spend_conflict (mh, -                                                j)) -      { -        GNUNET_break_op (0); -        mr.hr.http_status = 0; -        mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; -        mr.hr.hint = TALER_JSON_get_error_hint (j); -      } -      break; -    case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: -      if (GNUNET_OK != -          verify_melt_signature_denom_conflict (mh, -                                                j)) -      { -        GNUNET_break_op (0); -        mr.hr.http_status = 0; -        mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; -        mr.hr.hint = TALER_JSON_get_error_hint (j); -      } -      break; -    default:        GNUNET_break_op (0);        mr.hr.http_status = 0; -      mr.hr.ec = TALER_EC_EXCHANGE_MELT_INVALID_SIGNATURE_BY_EXCHANGE; -      mr.hr.hint = TALER_JSON_get_error_hint (j); +      mr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;        break;      }      break; @@ -456,7 +310,6 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)    json_t *melt_obj;    CURL *eh;    struct GNUNET_CURL_Context *ctx; -  struct TALER_CoinSpendSignatureP confirm_sig;    char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];    struct TALER_DenominationHashP h_denom_pub;    struct TALER_ExchangeWithdrawValues alg_values[mh->rd->fresh_pks_len]; @@ -480,7 +333,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)                            &h_denom_pub,                            mh->md.melted_coin.h_age_commitment,                            &mh->md.melted_coin.coin_priv, -                          &confirm_sig); +                          &mh->coin_sig);    GNUNET_CRYPTO_eddsa_key_get_public (&mh->md.melted_coin.coin_priv.eddsa_priv,                                        &mh->coin_pub.eddsa_pub);    melt_obj = GNUNET_JSON_PACK ( @@ -489,7 +342,7 @@ start_melt (struct TALER_EXCHANGE_MeltHandle *mh)      TALER_JSON_pack_denom_sig ("denom_sig",                                 &mh->md.melted_coin.sig),      GNUNET_JSON_pack_data_auto ("confirm_sig", -                                &confirm_sig), +                                &mh->coin_sig),      TALER_JSON_pack_amount ("value_with_fee",                              &mh->md.melted_coin.melt_amount_with_fee),      GNUNET_JSON_pack_data_auto ("rc", diff --git a/src/lib/exchange_api_purse_create_with_deposit.c b/src/lib/exchange_api_purse_create_with_deposit.c index f21b7d31..f2d80b94 100644 --- a/src/lib/exchange_api_purse_create_with_deposit.c +++ b/src/lib/exchange_api_purse_create_with_deposit.c @@ -35,6 +35,33 @@  /** + * Information we track per deposited coin. + */ +struct Deposit +{ +  /** +   * Coin's public key. +   */ +  struct TALER_CoinSpendPublicKeyP coin_pub; + +  /** +   * Signature made with the coin. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; + +  /** +   * Coin's denomination. +   */ +  struct TALER_DenominationHashP h_denom_pub; + +  /** +   * How much did we say the coin contributed. +   */ +  struct TALER_Amount contribution; +}; + + +/**   * @brief A purse create with deposit handle   */  struct TALER_EXCHANGE_PurseCreateDepositHandle @@ -106,6 +133,16 @@ struct TALER_EXCHANGE_PurseCreateDepositHandle     */    struct GNUNET_TIME_Timestamp purse_expiration; +  /** +   * Array of @e num_deposit deposits. +   */ +  struct Deposit *deposits; + +  /** +   * How many deposits did we make? +   */ +  unsigned int num_deposits; +  }; @@ -128,8 +165,10 @@ handle_purse_create_deposit_finished (void *cls,      .hr.reply = j,      .hr.http_status = (unsigned int) response_code    }; +  const struct TALER_EXCHANGE_Keys *keys;    pch->job = NULL; +  keys = TALER_EXCHANGE_get_keys (pch->exchange);    switch (response_code)    {    case 0: @@ -232,43 +271,151 @@ handle_purse_create_deposit_finished (void *cls,          break;        case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:          { -#if FIXME -          struct TALER_Amount total; -          struct TALER_DenominationHashP h_denom_pub; -          const struct TALER_EXCHANGE_DenomPublicKey *dk = NULL; +          struct TALER_Amount left; +          struct TALER_CoinSpendPublicKeyP pcoin_pub; +          bool found = false; -          // FIXME: parse coin_pub -          // FIXME: lookup dk for that coin from our deposits array! - -          if (NULL == dk) +          if (GNUNET_OK != +              TALER_EXCHANGE_check_coin_amount_conflict_ ( +                keys, +                j, +                &pcoin_pub, +                &left))            { -            /* not one of our coins */              GNUNET_break_op (0);              dr.hr.http_status = 0;              dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;              break;            } +          for (unsigned int i = 0; i<pch->num_deposits; i++) +          { +            struct Deposit *deposit = &pch->deposits[i]; + +            if (0 != GNUNET_memcmp (&pcoin_pub, +                                    &deposit->coin_pub)) +              continue; +            if (-1 != +                TALER_amount_cmp (&left, +                                  &deposit->contribution)) +            { +              /* Balance was sufficient after all; recoup MAY have still been possible */ +              GNUNET_break_op (0); +              continue; +            } +            if (GNUNET_OK != +                TALER_EXCHANGE_check_coin_signature_conflict_ ( +                  j, +                  &deposit->coin_sig)) +            { +              GNUNET_break_op (0); +              continue; +            } +            found = true; +            break; +          } +          if (! found) +          { +            /* conflict is for a different coin! */ +            GNUNET_break_op (0); +            dr.hr.http_status = 0; +            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +            break; +          } +          break; +        } +      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: +        { +          struct TALER_Amount left; +          struct TALER_CoinSpendPublicKeyP pcoin_pub; +          bool found = false; +            if (GNUNET_OK != -              TALER_EXCHANGE_verify_coin_history (dk, -                                                  pch->exchange->currency, -                                                  &coin_pub, -                                                  history, -                                                  &h_denom_pub, -                                                  &total)) +              TALER_EXCHANGE_check_coin_amount_conflict_ ( +                keys, +                j, +                &pcoin_pub, +                &left))            {              GNUNET_break_op (0);              dr.hr.http_status = 0;              dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;              break;            } -          // FIXME: check total is too high for the request... -#endif +          for (unsigned int i = 0; i<pch->num_deposits; i++) +          { +            struct Deposit *deposit = &pch->deposits[i]; + +            if (0 != +                GNUNET_memcmp (&pcoin_pub, +                               &deposit->coin_pub)) +              continue; +            if (GNUNET_OK != +                TALER_EXCHANGE_check_coin_denomination_conflict_ ( +                  j, +                  &deposit->h_denom_pub)) +            { +              /* Eh, same denomination, hence no conflict */ +              GNUNET_break_op (0); +              continue; +            } +            found = true; +          } +          if (! found) +          { +            /* conflict is for a different coin! */ +            GNUNET_break_op (0); +            dr.hr.http_status = 0; +            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +            break; +          } +          /* meta data conflict is real! */            break;          }        case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:          { -          // FIXME! -          break; +          struct TALER_CoinSpendPublicKeyP coin_pub; +          struct TALER_CoinSpendSignatureP coin_sig; +          bool found = false; + +          if (GNUNET_OK != +              TALER_EXCHANGE_check_purse_coin_conflict_ ( +                &pch->purse_pub, +                pch->exchange->url, +                j, +                &coin_pub, +                &coin_sig)) +          { +            GNUNET_break_op (0); +            dr.hr.http_status = 0; +            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +            break; +          } +          for (unsigned int i = 0; i<pch->num_deposits; i++) +          { +            struct Deposit *deposit = &pch->deposits[i]; + +            if (0 != +                GNUNET_memcmp (&coin_pub, +                               &deposit->coin_pub)) +              continue; +            if (0 == +                GNUNET_memcmp (&coin_sig, +                               &deposit->coin_sig)) +            { +              GNUNET_break_op (0); +              continue; +            } +            found = true; +            break; +          } +          if (! found) +          { +            /* conflict is for a different coin! */ +            GNUNET_break_op (0); +            dr.hr.http_status = 0; +            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +            break; +          }          }        case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:          if (GNUNET_OK != @@ -408,6 +555,9 @@ TALER_EXCHANGE_purse_create_with_deposit (      GNUNET_free (pch);      return NULL;    } +  pch->num_deposits = num_deposits; +  pch->deposits = GNUNET_new_array (num_deposits, +                                    struct Deposit);    deposit_arr = json_array ();    GNUNET_assert (NULL != deposit_arr);    url = TEAH_path_to_url (exchange, @@ -418,9 +568,8 @@ TALER_EXCHANGE_purse_create_with_deposit (    for (unsigned int i = 0; i<num_deposits; i++)    {      const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; +    struct Deposit *d = &pch->deposits[i];      json_t *jdeposit; -    struct TALER_CoinSpendSignatureP coin_sig; -    struct TALER_CoinSpendPublicKeyP coin_pub;  #if FIXME_OEC      struct TALER_AgeCommitmentHash agh;      struct TALER_AgeCommitmentHash *aghp = NULL; @@ -441,14 +590,16 @@ TALER_EXCHANGE_purse_create_with_deposit (        return NULL;      }  #endif +    d->contribution = deposit->amount; +    d->h_denom_pub = deposit->h_denom_pub;      GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv, -                                        &coin_pub.eddsa_pub); +                                        &d->coin_pub.eddsa_pub);      TALER_wallet_purse_deposit_sign (        url,        &pch->purse_pub,        &deposit->amount,        &deposit->coin_priv, -      &coin_sig); +      &d->coin_sig);      jdeposit = GNUNET_JSON_PACK (  #if FIXME_OEC        GNUNET_JSON_pack_allow_null ( @@ -465,9 +616,9 @@ TALER_EXCHANGE_purse_create_with_deposit (        TALER_JSON_pack_denom_sig ("ub_sig",                                   &deposit->denom_sig),        GNUNET_JSON_pack_data_auto ("coin_sig", -                                  &coin_sig), +                                  &d->coin_sig),        GNUNET_JSON_pack_data_auto ("coin_pub", -                                  &coin_pub)); +                                  &d->coin_pub));      GNUNET_assert (0 ==                     json_array_append_new (deposit_arr,                                            jdeposit)); @@ -529,6 +680,7 @@ TALER_EXCHANGE_purse_create_with_deposit (        curl_easy_cleanup (eh);      json_decref (create_obj);      GNUNET_free (pch->econtract.econtract); +    GNUNET_free (pch->deposits);      GNUNET_free (pch->url);      GNUNET_free (pch);      return NULL; @@ -558,6 +710,7 @@ TALER_EXCHANGE_purse_create_with_deposit_cancel (    }    GNUNET_free (pch->econtract.econtract);    GNUNET_free (pch->url); +  GNUNET_free (pch->deposits);    TALER_curl_easy_post_finished (&pch->ctx);    GNUNET_free (pch);  } diff --git a/src/lib/exchange_api_purse_deposit.c b/src/lib/exchange_api_purse_deposit.c index 2027ca0d..6946419d 100644 --- a/src/lib/exchange_api_purse_deposit.c +++ b/src/lib/exchange_api_purse_deposit.c @@ -28,6 +28,7 @@  #include <gnunet/gnunet_curl_lib.h>  #include "taler_json_lib.h"  #include "taler_exchange_service.h" +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h"  #include "exchange_api_curl_defaults.h" @@ -44,6 +45,11 @@ struct Coin    struct TALER_CoinSpendPublicKeyP coin_pub;    /** +   * Signature made with the coin. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; + +  /**     * Coin's denomination.     */    struct TALER_DenominationHashP h_denom_pub; @@ -226,30 +232,17 @@ handle_purse_deposit_finished (void *cls,      {      case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA:        { -        const char *partner_url = NULL;          struct TALER_CoinSpendPublicKeyP coin_pub;          struct TALER_CoinSpendSignatureP coin_sig; -        struct TALER_Amount amount; -        struct GNUNET_JSON_Specification spec[] = { -          GNUNET_JSON_spec_fixed_auto ("coin_sig", -                                       &coin_sig), -          GNUNET_JSON_spec_fixed_auto ("coin_pub", -                                       &coin_pub), -          GNUNET_JSON_spec_mark_optional ( -            GNUNET_JSON_spec_string ("partner_url", -                                     &partner_url), -            NULL), -          TALER_JSON_spec_amount ("amount", -                                  keys->currency, -                                  &amount), -          GNUNET_JSON_spec_end () -        };          bool found = false;          if (GNUNET_OK != -            GNUNET_JSON_parse (j, -                               spec, -                               NULL, NULL)) +            TALER_EXCHANGE_check_purse_coin_conflict_ ( +              &pch->purse_pub, +              pch->base_url, +              j, +              &coin_pub, +              &coin_sig))          {            GNUNET_break_op (0);            dr.hr.http_status = 0; @@ -257,29 +250,21 @@ handle_purse_deposit_finished (void *cls,            break;          }          for (unsigned int i = 0; i<pch->num_deposits; i++) +        {            if (0 == GNUNET_memcmp (&coin_pub,                                    &pch->coins[i].coin_pub))            { +            if (0 == GNUNET_memcmp (&coin_sig, +                                    &pch->coins[i].coin_sig)) +            { +              /* identical signature => not a conflict */ +              continue; +            }              found = true;              break;            } -        if (! found) -        { -          /* proof is about a coin we did not even deposit */ -          GNUNET_break_op (0); -          dr.hr.http_status = 0; -          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          break;          } -        if (NULL == partner_url) -          partner_url = pch->base_url; -        if (GNUNET_OK != -            TALER_wallet_purse_deposit_verify ( -              partner_url, -              &pch->purse_pub, -              &amount, -              &coin_pub, -              &coin_sig)) +        if (! found)          {            GNUNET_break_op (0);            dr.hr.http_status = 0; @@ -291,27 +276,18 @@ handle_purse_deposit_finished (void *cls,        }      case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS:        { -        json_t *history; -        struct TALER_Amount total; -        struct TALER_DenominationHashP h_denom_pub; -        const struct TALER_EXCHANGE_DenomPublicKey *dki;          struct TALER_CoinSpendPublicKeyP coin_pub; -        struct GNUNET_JSON_Specification spec[] = { -          GNUNET_JSON_spec_fixed_auto ("coin_pub", -                                       &coin_pub), -          GNUNET_JSON_spec_json ("history", -                                 &history), -          GNUNET_JSON_spec_end () -        }; +        struct TALER_Amount remaining;          bool found = false;          const struct Coin *my_coin;          if (GNUNET_OK != -            GNUNET_JSON_parse (j, -                               spec, -                               NULL, NULL)) +            TALER_EXCHANGE_check_coin_amount_conflict_ ( +              keys, +              j, +              &coin_pub, +              &remaining))          { -          GNUNET_break_op (0);            dr.hr.http_status = 0;            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;            break; @@ -334,45 +310,22 @@ handle_purse_deposit_finished (void *cls,            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;            break;          } -        dki = TALER_EXCHANGE_get_denomination_key_by_hash ( -          keys, -          &my_coin->h_denom_pub); -        if (NULL == dki) -        { -          dr.hr.http_status = 0; -          dr.hr.ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN; -          GNUNET_break_op (0); -          break; -        } -        if (GNUNET_OK != -            TALER_EXCHANGE_verify_coin_history (dki, -                                                dki->value.currency, -                                                &coin_pub, -                                                history, -                                                &h_denom_pub, -                                                &total)) +        if (1 == TALER_amount_cmp (&remaining, +                                   &my_coin->contribution))          { +          /* transaction should have still fit */            GNUNET_break_op (0);            dr.hr.http_status = 0;            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          json_decref (history);            break;          } -        json_decref (history); -        if (0 > -            TALER_amount_add (&total, -                              &total, -                              &my_coin->contribution)) -        { -          /* clearly not OK if our transaction would have caused -             the overflow... */ -          break; -        } -        if (0 >= TALER_amount_cmp (&total, -                                   &dki->value)) +        if (GNUNET_OK != +            TALER_EXCHANGE_check_coin_signature_conflict_ ( +              j, +              &my_coin->coin_sig))          { -          /* transaction should have still fit */ -          GNUNET_break (0); +          /* THIS transaction must not be in the conflicting history */ +          GNUNET_break_op (0);            dr.hr.http_status = 0;            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;            break; @@ -382,27 +335,18 @@ handle_purse_deposit_finished (void *cls,        }      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY:        { -        json_t *history; -        struct TALER_Amount total; -        struct TALER_DenominationHashP h_denom_pub; -        const struct Coin *my_coin; -        const struct TALER_EXCHANGE_DenomPublicKey *dki;          struct TALER_CoinSpendPublicKeyP coin_pub; -        struct GNUNET_JSON_Specification spec[] = { -          GNUNET_JSON_spec_fixed_auto ("coin_pub", -                                       &coin_pub), -          GNUNET_JSON_spec_json ("history", -                                 &history), -          GNUNET_JSON_spec_end () -        }; +        struct TALER_Amount remaining;          bool found = false; +        const struct Coin *my_coin;          if (GNUNET_OK != -            GNUNET_JSON_parse (j, -                               spec, -                               NULL, NULL)) +            TALER_EXCHANGE_check_coin_amount_conflict_ ( +              keys, +              j, +              &coin_pub, +              &remaining))          { -          GNUNET_break_op (0);            dr.hr.http_status = 0;            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;            break; @@ -425,31 +369,12 @@ handle_purse_deposit_finished (void *cls,            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;            break;          } -        dki = TALER_EXCHANGE_get_denomination_key_by_hash ( -          keys, -          &my_coin->h_denom_pub); -        memset (&h_denom_pub, -                0, -                sizeof (h_denom_pub));          if (GNUNET_OK != -            TALER_EXCHANGE_verify_coin_history (dki, -                                                dki->value.currency, -                                                &coin_pub, -                                                history, -                                                &h_denom_pub, -                                                &total)) -        { -          GNUNET_break_op (0); -          dr.hr.http_status = 0; -          dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          json_decref (history); -          break; -        } -        json_decref (history); -        if (0 == GNUNET_memcmp (&dki->h_key, -                                &h_denom_pub)) +            TALER_EXCHANGE_check_coin_denomination_conflict_ ( +              j, +              &my_coin->h_denom_pub))          { -          /* sorry, this proves nothing */ +          /* no conflicting denomination detected */            GNUNET_break_op (0);            dr.hr.http_status = 0;            dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; @@ -562,7 +487,6 @@ TALER_EXCHANGE_purse_deposit (      const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i];      struct Coin *coin = &pch->coins[i];      json_t *jdeposit; -    struct TALER_CoinSpendSignatureP coin_sig;  #if FIXME_OEC      struct TALER_AgeCommitmentHash agh;      struct TALER_AgeCommitmentHash *aghp = NULL; @@ -593,7 +517,7 @@ TALER_EXCHANGE_purse_deposit (        &pch->purse_pub,        &deposit->amount,        &deposit->coin_priv, -      &coin_sig); +      &coin->coin_sig);      jdeposit = GNUNET_JSON_PACK (  #if FIXME_OEC        GNUNET_JSON_pack_allow_null ( @@ -612,7 +536,7 @@ TALER_EXCHANGE_purse_deposit (        GNUNET_JSON_pack_data_auto ("coin_pub",                                    &coin->coin_pub),        GNUNET_JSON_pack_data_auto ("coin_sig", -                                  &coin_sig)); +                                  &coin->coin_sig));      GNUNET_assert (0 ==                     json_array_append_new (deposit_arr,                                            jdeposit)); diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c index 5c197e2f..49fb6fd5 100644 --- a/src/lib/exchange_api_recoup.c +++ b/src/lib/exchange_api_recoup.c @@ -1,6 +1,6 @@  /*    This file is part of TALER -  Copyright (C) 2017-2021 Taler Systems SA +  Copyright (C) 2017-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 @@ -27,6 +27,7 @@  #include <gnunet/gnunet_curl_lib.h>  #include "taler_json_lib.h"  #include "taler_exchange_service.h" +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h"  #include "exchange_api_curl_defaults.h" @@ -60,6 +61,11 @@ struct TALER_EXCHANGE_RecoupHandle    struct TALER_EXCHANGE_DenomPublicKey pk;    /** +   * Our signature requesting the recoup. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; + +  /**     * Handle for the request.     */    struct GNUNET_CURL_Job *job; @@ -139,8 +145,10 @@ handle_recoup_finished (void *cls,      .reply = j,      .http_status = (unsigned int) response_code    }; +  const struct TALER_EXCHANGE_Keys *keys;    ph->job = NULL; +  keys = TALER_EXCHANGE_get_keys (ph->exchange);    switch (response_code)    {    case 0: @@ -166,76 +174,34 @@ handle_recoup_finished (void *cls,      break;    case MHD_HTTP_CONFLICT:      { -      /* Insufficient funds, proof attached */ -      json_t *history; -      struct TALER_Amount total; -      struct TALER_DenominationHashP h_denom_pub; -      const struct TALER_EXCHANGE_DenomPublicKey *dki; -      enum TALER_ErrorCode ec; - -      dki = &ph->pk; -      history = json_object_get (j, -                                 "history"); +      struct TALER_Amount min_key; + +      hr.ec = TALER_JSON_get_error_code (j); +      hr.hint = TALER_JSON_get_error_hint (j);        if (GNUNET_OK != -          TALER_EXCHANGE_verify_coin_history (dki, -                                              dki->fees.deposit.currency, -                                              &ph->coin_pub, -                                              history, -                                              &h_denom_pub, -                                              &total)) +          TALER_EXCHANGE_get_min_denomination_ (keys, +                                                &min_key))        { -        GNUNET_break_op (0); -        hr.http_status = 0; +        GNUNET_break (0);          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +        hr.http_status = 0; +        break;        } -      else -      { -        hr.ec = TALER_JSON_get_error_code (j); -        hr.hint = TALER_JSON_get_error_hint (j); -      } -      ec = TALER_JSON_get_error_code (j); -      switch (ec) +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_conflict_ ( +            keys, +            j, +            &ph->pk, +            &ph->coin_pub, +            &ph->coin_sig, +            &min_key))        { -      case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: -        if (0 > TALER_amount_cmp (&total, -                                  &dki->value)) -        { -          /* recoup MAY have still been possible */ -          /* FIXME: This code may falsely complain, as we do not -             know that the smallest denomination offered by the -             exchange is here. We should look at the key -             structure of ph->exchange, and find the smallest -             _currently withdrawable_ denomination and check -             if the value remaining would suffice... */ -          GNUNET_break_op (0); -          hr.http_status = 0; -          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          break; -        } -        break; -      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: -        if (0 == GNUNET_memcmp (&ph->pk.h_key, -                                &h_denom_pub)) -        { -          /* invalid proof provided */ -          GNUNET_break_op (0); -          hr.http_status = 0; -          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          break; -        } -        /* valid error from exchange */ -        break; -      default: -        GNUNET_break_op (0); -        hr.http_status = 0; +        GNUNET_break (0);          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +        hr.http_status = 0;          break;        } -      ph->cb (ph->cb_cls, -              &hr, -              NULL); -      TALER_EXCHANGE_recoup_cancel (ph); -      return; +      break;      }    case MHD_HTTP_FORBIDDEN:      /* Nothing really to verify, exchange says one of the signatures is @@ -291,8 +257,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,  {    struct TALER_EXCHANGE_RecoupHandle *ph;    struct GNUNET_CURL_Context *ctx; -  struct TALER_CoinSpendSignatureP coin_sig; -  struct TALER_CoinSpendPublicKeyP coin_pub;    struct TALER_DenominationHashP h_denom_pub;    json_t *recoup_obj;    CURL *eh; @@ -302,7 +266,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,    GNUNET_assert (GNUNET_YES ==                   TEAH_handle_is_ready (exchange)); - +  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);    TALER_planchet_setup_coin_priv (ps,                                    exchange_vals,                                    &coin_priv); @@ -310,13 +274,13 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,                                           exchange_vals,                                           &bks);    GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, -                                      &coin_pub.eddsa_pub); +                                      &ph->coin_pub.eddsa_pub);    TALER_denom_pub_hash (&pk->key,                          &h_denom_pub);    TALER_wallet_recoup_sign (&h_denom_pub,                              &bks,                              &coin_priv, -                            &coin_sig); +                            &ph->coin_sig);    recoup_obj = GNUNET_JSON_PACK (      GNUNET_JSON_pack_data_auto ("denom_pub_hash",                                  &h_denom_pub), @@ -325,7 +289,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,      TALER_JSON_pack_exchange_withdraw_values ("ewv",                                                exchange_vals),      GNUNET_JSON_pack_data_auto ("coin_sig", -                                &coin_sig), +                                &ph->coin_sig),      GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",                                  &bks));    if (TALER_DENOMINATION_CS == denom_sig->cipher) @@ -352,7 +316,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,      char *end;      end = GNUNET_STRINGS_data_to_string ( -      &coin_pub, +      &ph->coin_pub,        sizeof (struct TALER_CoinSpendPublicKeyP),        pub_str,        sizeof (pub_str)); @@ -363,8 +327,6 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle *exchange,                       pub_str);    } -  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle); -  ph->coin_pub = coin_pub;    ph->exchange = exchange;    ph->pk = *pk;    memset (&ph->pk.key, diff --git a/src/lib/exchange_api_recoup_refresh.c b/src/lib/exchange_api_recoup_refresh.c index 8ae8f976..d551df74 100644 --- a/src/lib/exchange_api_recoup_refresh.c +++ b/src/lib/exchange_api_recoup_refresh.c @@ -27,6 +27,7 @@  #include <gnunet/gnunet_curl_lib.h>  #include "taler_json_lib.h"  #include "taler_exchange_service.h" +#include "exchange_api_common.h"  #include "exchange_api_handle.h"  #include "taler_signatures.h"  #include "exchange_api_curl_defaults.h" @@ -79,6 +80,11 @@ struct TALER_EXCHANGE_RecoupRefreshHandle     */    struct TALER_CoinSpendPublicKeyP coin_pub; +  /** +   * Signature affirming the recoup-refresh operation. +   */ +  struct TALER_CoinSpendSignatureP coin_sig; +  }; @@ -140,8 +146,10 @@ handle_recoup_refresh_finished (void *cls,      .reply = j,      .http_status = (unsigned int) response_code    }; +  const struct TALER_EXCHANGE_Keys *keys;    ph->job = NULL; +  keys = TALER_EXCHANGE_get_keys (ph->exchange);    switch (response_code)    {    case 0: @@ -180,76 +188,34 @@ handle_recoup_refresh_finished (void *cls,      break;    case MHD_HTTP_CONFLICT:      { -      /* Insufficient funds, proof attached */ -      json_t *history; -      struct TALER_Amount total; -      struct TALER_DenominationHashP h_denom_pub; -      const struct TALER_EXCHANGE_DenomPublicKey *dki; -      enum TALER_ErrorCode ec; - -      dki = &ph->pk; -      history = json_object_get (j, -                                 "history"); +      struct TALER_Amount min_key; + +      hr.ec = TALER_JSON_get_error_code (j); +      hr.hint = TALER_JSON_get_error_hint (j);        if (GNUNET_OK != -          TALER_EXCHANGE_verify_coin_history (dki, -                                              dki->fees.deposit.currency, -                                              &ph->coin_pub, -                                              history, -                                              &h_denom_pub, -                                              &total)) +          TALER_EXCHANGE_get_min_denomination_ (keys, +                                                &min_key))        { -        GNUNET_break_op (0); -        hr.http_status = 0; +        GNUNET_break (0);          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +        hr.http_status = 0; +        break;        } -      else -      { -        hr.ec = TALER_JSON_get_error_code (j); -        hr.hint = TALER_JSON_get_error_hint (j); -      } -      ec = TALER_JSON_get_error_code (j); -      switch (ec) +      if (GNUNET_OK != +          TALER_EXCHANGE_check_coin_conflict_ ( +            keys, +            j, +            &ph->pk, +            &ph->coin_pub, +            &ph->coin_sig, +            &min_key))        { -      case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: -        if (0 > TALER_amount_cmp (&total, -                                  &dki->value)) -        { -          /* recoup MAY have still been possible */ -          /* FIXME: This code may falsely complain, as we do not -             know that the smallest denomination offered by the -             exchange is here. We should look at the key -             structure of ph->exchange, and find the smallest -             _currently withdrawable_ denomination and check -             if the value remaining would suffice... */ -          GNUNET_break_op (0); -          hr.http_status = 0; -          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          break; -        } -        break; -      case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: -        if (0 == GNUNET_memcmp (&ph->pk.h_key, -                                &h_denom_pub)) -        { -          /* invalid proof provided */ -          GNUNET_break_op (0); -          hr.http_status = 0; -          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; -          break; -        } -        /* valid error from exchange */ -        break; -      default: -        GNUNET_break_op (0); -        hr.http_status = 0; +        GNUNET_break (0);          hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; +        hr.http_status = 0;          break;        } -      ph->cb (ph->cb_cls, -              &hr, -              NULL); -      TALER_EXCHANGE_recoup_refresh_cancel (ph); -      return; +      break;      }    case MHD_HTTP_GONE:      /* Kind of normal: the money was already sent to the merchant @@ -295,8 +261,6 @@ TALER_EXCHANGE_recoup_refresh (  {    struct TALER_EXCHANGE_RecoupRefreshHandle *ph;    struct GNUNET_CURL_Context *ctx; -  struct TALER_CoinSpendSignatureP coin_sig; -  struct TALER_CoinSpendPublicKeyP coin_pub;    struct TALER_DenominationHashP h_denom_pub;    json_t *recoup_obj;    CURL *eh; @@ -307,6 +271,14 @@ TALER_EXCHANGE_recoup_refresh (    GNUNET_assert (NULL != recoup_cb);    GNUNET_assert (GNUNET_YES ==                   TEAH_handle_is_ready (exchange)); +  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle); +  ph->exchange = exchange; +  ph->pk = *pk; +  memset (&ph->pk.key, +          0, +          sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ +  ph->cb = recoup_cb; +  ph->cb_cls = recoup_cb_cls;    TALER_planchet_setup_coin_priv (ps,                                    exchange_vals,                                    &coin_priv); @@ -314,13 +286,13 @@ TALER_EXCHANGE_recoup_refresh (                                           exchange_vals,                                           &bks);    GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv, -                                      &coin_pub.eddsa_pub); +                                      &ph->coin_pub.eddsa_pub);    TALER_denom_pub_hash (&pk->key,                          &h_denom_pub);    TALER_wallet_recoup_refresh_sign (&h_denom_pub,                                      &bks,                                      &coin_priv, -                                    &coin_sig); +                                    &ph->coin_sig);    recoup_obj = GNUNET_JSON_PACK (      GNUNET_JSON_pack_data_auto ("denom_pub_hash",                                  &h_denom_pub), @@ -329,7 +301,7 @@ TALER_EXCHANGE_recoup_refresh (      TALER_JSON_pack_exchange_withdraw_values ("ewv",                                                exchange_vals),      GNUNET_JSON_pack_data_auto ("coin_sig", -                                &coin_sig), +                                &ph->coin_sig),      GNUNET_JSON_pack_data_auto ("coin_blind_key_secret",                                  &bks)); @@ -358,7 +330,7 @@ TALER_EXCHANGE_recoup_refresh (      char *end;      end = GNUNET_STRINGS_data_to_string ( -      &coin_pub, +      &ph->coin_pub,        sizeof (struct TALER_CoinSpendPublicKeyP),        pub_str,        sizeof (pub_str)); @@ -369,15 +341,6 @@ TALER_EXCHANGE_recoup_refresh (                       pub_str);    } -  ph = GNUNET_new (struct TALER_EXCHANGE_RecoupRefreshHandle); -  ph->coin_pub = coin_pub; -  ph->exchange = exchange; -  ph->pk = *pk; -  memset (&ph->pk.key, -          0, -          sizeof (ph->pk.key)); /* zero out, as lifetime cannot be warranted */ -  ph->cb = recoup_cb; -  ph->cb_cls = recoup_cb_cls;    ph->url = TEAH_path_to_url (exchange,                                arg_str);    if (NULL == ph->url) diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c index 004661b0..09a21883 100644 --- a/src/lib/exchange_api_refund.c +++ b/src/lib/exchange_api_refund.c @@ -173,9 +173,12 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,                              const json_t *json)  {    json_t *history; +  struct TALER_DenominationHashP h_denom_pub;    struct GNUNET_JSON_Specification spec[] = {      GNUNET_JSON_spec_json ("history",                             &history), +    GNUNET_JSON_spec_fixed_auto ("h_denom_pub", +                                 &h_denom_pub),      GNUNET_JSON_spec_end ()    };    size_t len; @@ -234,7 +237,6 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,        struct TALER_AgeCommitmentHash h_age_commitment;        bool no_hac;        // struct TALER_ExtensionContractHashP h_extensions; // FIXME! -      struct TALER_DenominationHashP h_denom_pub;        struct GNUNET_TIME_Timestamp wallet_timestamp;        struct TALER_MerchantPublicKeyP merchant_pub;        struct GNUNET_TIME_Timestamp refund_deadline; @@ -246,8 +248,6 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,                                       &h_contract_terms),          GNUNET_JSON_spec_fixed_auto ("h_wire",                                       &h_wire), -        GNUNET_JSON_spec_fixed_auto ("h_denom_pub", -                                     &h_denom_pub),          GNUNET_JSON_spec_mark_optional (            GNUNET_JSON_spec_fixed_auto ("h_age_commitment",                                         &h_age_commitment), @@ -429,24 +429,22 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,      }    } +  if (have_refund)    { -    if (have_refund) +    if (0 > +        TALER_amount_add (&rtotal, +                          &rtotal, +                          &rh->refund_amount))      { -      if (0 > -          TALER_amount_add (&rtotal, -                            &rtotal, -                            &rh->refund_amount)) -      { -        GNUNET_break (0); -        GNUNET_JSON_parse_free (spec); -        return GNUNET_SYSERR; -      } -    } -    else -    { -      rtotal = rh->refund_amount; +      GNUNET_break (0); +      GNUNET_JSON_parse_free (spec); +      return GNUNET_SYSERR;      }    } +  else +  { +    rtotal = rh->refund_amount; +  }    if (-1 == TALER_amount_cmp (&dtotal,                                &rtotal))    { | 
