gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-exchange] branch master updated: fixing misc auditor


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated: fixing misc auditor issues
Date: Mon, 20 Mar 2017 02:29:36 +0100

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new a38fa32  fixing misc auditor issues
a38fa32 is described below

commit a38fa32484286d2895dca10d3f53d3c7599d2f3b
Author: Christian Grothoff <address@hidden>
AuthorDate: Mon Mar 20 02:29:33 2017 +0100

    fixing misc auditor issues
---
 src/auditor/taler-auditor.c               | 2773 ++++++++++++++++-------------
 src/auditordb/plugin_auditordb_postgres.c |   89 +-
 src/include/taler_amount_lib.h            |   12 +
 src/util/amount.c                         |   50 +-
 4 files changed, 1606 insertions(+), 1318 deletions(-)

diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 0427f12..971f6e5 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -23,6 +23,14 @@
  *   the wire transfers from the bank. This needs to be checked separately!
  * - Similarly, we do not check that the outgoing wire transfers match those
  *   given in the 'wire_out' table. This needs to be checked separately!
+ *
+ * KNOWN BUGS:
+ * - resolve HACK! -- need extra serial_id in 'pp' as we go over reserve_out 
twice!
+ * - risk is not calculated correctly
+ * - calculate, store and report aggregation fee balance!
+ * - error handling if denomination keys are used that are not known to the
+ *   auditor is, eh, awful / non-existent. We just throw the DB's constraint
+ *   violation back at the user. Great UX.
  */
 #include "platform.h"
 #include <gnunet/gnunet_util_lib.h>
@@ -251,20 +259,48 @@ static void
 report_reserve_balance (const struct TALER_Amount *total_balance,
                         const struct TALER_Amount *total_fee_balance)
 {
-  char *balance;
-  char *fees;
-
-  balance = TALER_amount_to_string (total_balance);
-  fees = TALER_amount_to_string (total_fee_balance);
   // TODO: implement proper reporting logic writing to file.
   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
               "Total escrow balance to be held for reserves: %s\n",
-              balance);
+              TALER_amount2s (total_balance));
   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
               "Total profits made from reserves: %s\n",
-              fees);
-  GNUNET_free (fees);
-  GNUNET_free (balance);
+              TALER_amount2s (total_fee_balance));
+}
+
+
+/**
+ * Report state of denomination processing.
+ *
+ * @param total_balance total value of outstanding coins
+ * @param total_risk total value of issued coins in active denominations
+ * @param deposit_fees total deposit fees collected
+ * @param melt_fees total melt fees collected
+ * @param refund_fees total refund fees collected
+ */
+static void
+report_denomination_balance (const struct TALER_Amount *total_balance,
+                             const struct TALER_Amount *total_risk,
+                             const struct TALER_Amount *deposit_fees,
+                             const struct TALER_Amount *melt_fees,
+                             const struct TALER_Amount *refund_fees)
+{
+  // TODO: implement proper reporting logic writing to file.
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Final balance for all denominations is %s\n",
+              TALER_amount2s (total_balance));
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Risk from active operations is %s\n",
+              TALER_amount2s (total_risk));
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Deposit fee profits are %s\n",
+              TALER_amount2s (deposit_fees));
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Melt fee profits are %s\n",
+              TALER_amount2s (melt_fees));
+  GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+              "Refund fee profits are %s\n",
+              TALER_amount2s (refund_fees));
 }
 
 
@@ -321,6 +357,16 @@ get_denomination_info (const struct 
TALER_DenominationPublicKey *denom_pub,
     *dki = NULL;
     return ret;
   }
+  {
+    struct TALER_Amount value;
+
+    TALER_amount_ntoh (&value,
+                       &dkip->properties.value);
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Tracking denomination `%s' (%s)\n",
+                GNUNET_h2s (dh),
+                TALER_amount2s (&value));
+  }
   *dki = dkip;
   GNUNET_assert (GNUNET_OK ==
                  GNUNET_CONTAINER_multihashmap_put (denominations,
@@ -346,6 +392,9 @@ free_dk_info (void *cls,
 {
   struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki = value;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Done with denomination `%s'\n",
+              GNUNET_h2s (key));
   GNUNET_free (dki);
   return GNUNET_OK;
 }
@@ -465,6 +514,10 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (rs->total_in.currency,
                                           &rs->a_withdraw_fee_balance));
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Creating fresh reserve `%s' with starting balance %s\n",
+                TALER_B2S (&rs->reserve_pub),
+                TALER_amount2s (&rs->a_balance));
     return GNUNET_OK;
   }
   rs->had_ri = GNUNET_YES;
@@ -482,6 +535,10 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Auditor remembers reserve `%s' has balance %s\n",
+              TALER_B2S (&rs->reserve_pub),
+              TALER_amount2s (&rs->a_balance));
   return GNUNET_OK;
 }
 
@@ -573,6 +630,10 @@ handle_reserve_in (void *cls,
                                      &rs->total_in,
                                      credit));
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Additional incoming wire transfer for reserve `%s' of %s\n",
+              TALER_B2S (reserve_pub),
+              TALER_amount2s (credit));
   expiry = GNUNET_TIME_absolute_add (execution_date,
                                      TALER_IDLE_RESERVE_EXPIRATION_TIME);
   rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
@@ -705,7 +766,10 @@ handle_reserve_out (void *cls,
                                      &rs->total_out,
                                      amount_with_fee));
   }
-
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Reserve `%s' reduced by %s from withdraw\n",
+              TALER_B2S (reserve_pub),
+              TALER_amount2s (amount_with_fee));
   TALER_amount_ntoh (&withdraw_fee,
                      &dki->properties.fee_withdraw);
   GNUNET_assert (GNUNET_OK ==
@@ -794,6 +858,7 @@ verify_reserve_balance (void *cls,
   if (0 == GNUNET_TIME_absolute_get_remaining 
(rs->a_expiration_date).rel_value_us)
   {
     /* TODO: handle case where reserve is expired! (#4956) */
+    GNUNET_break (0); /* not implemented */
     /* NOTE: we may or may not have seen the wire-back transfer at this time,
        as the expiration may have just now happened.
        (That is, after we add the table structures and the logic to track
@@ -806,6 +871,10 @@ verify_reserve_balance (void *cls,
     /* TODO: balance is zero, drop reserve details (and then do not 
update/insert) */
     if (rs->had_ri)
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Final balance of reserve `%s' is %s, dropping it\n",
+                  TALER_B2S (&rs->reserve_pub),
+                  TALER_amount2s (&balance));
       ret = adb->del_reserve_info (adb->cls,
                                    asession,
                                    &rs->reserve_pub,
@@ -822,13 +891,21 @@ verify_reserve_balance (void *cls,
         goto cleanup;
       }
     }
+    else
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Final balance of reserve `%s' is %s, no need to remember 
it\n",
+                  TALER_B2S (&rs->reserve_pub),
+                  TALER_amount2s (&balance));
+    }
     ret = GNUNET_OK;
     goto cleanup;
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Reserve balance `%s' OK\n",
-              TALER_B2S (&rs->reserve_pub));
+              "Remembering final balance of reserve `%s' as %s\n",
+              TALER_B2S (&rs->reserve_pub),
+              TALER_amount2s (&balance));
 
   /* Add withdraw fees we encountered to totals */
   if (GNUNET_YES !=
@@ -898,6 +975,8 @@ analyze_reserves (void *cls)
   struct ReserveContext rc;
   int ret;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Analyzing reserves\n");
   ret = adb->get_reserve_summary (adb->cls,
                                   asession,
                                   &master_pub,
@@ -978,1557 +1057,1661 @@ analyze_reserves (void *cls)
 }
 
 
-/* ************************* Analyze coins ******************** */
-/* This logic checks that the exchange did the right thing for each
-   coin, checking deposits, refunds, refresh* and known_coins
-   tables */
+/* *********************** Analyze aggregations ******************** */
+/* This logic checks that the aggregator did the right thing
+   paying each merchant what they were due (and on time). */
 
 
 /**
- * Summary data we keep per denomination.
+ * Information we keep per loaded wire plugin.
  */
-struct DenominationSummary
+struct WirePlugin
 {
+
   /**
-   * Total value of outstanding (not deposited) coins issued with this
-   * denomination key.
+   * Kept in a DLL.
    */
-  struct TALER_Amount denom_balance;
+  struct WirePlugin *next;
 
   /**
-   * Total value of coins issued with this denomination key.
+   * Kept in a DLL.
    */
-  struct TALER_Amount denom_risk;
+  struct WirePlugin *prev;
 
   /**
-   * Denomination key information for this denomination.
+   * Name of the wire method.
    */
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  char *type;
 
   /**
-   * #GNUNET_YES if this record already existed in the DB.
-   * Used to decide between insert/update in
-   * #sync_denomination().
+   * Handle to the wire plugin.
    */
-  int in_db;
+  struct TALER_WIRE_Plugin *plugin;
+
 };
 
 
 /**
- * Closure for callbacks during #analyze_coins().
+ * Closure for callbacks during #analyze_merchants().
  */
-struct CoinContext
+struct AggregationContext
 {
 
   /**
-   * Map for tracking information about denominations.
+   * DLL of wire plugins encountered.
    */
-  struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
+  struct WirePlugin *wire_head;
 
   /**
-   * Total outstanding balances across all denomination keys.
+   * DLL of wire plugins encountered.
    */
-  struct TALER_Amount total_denom_balance;
+  struct WirePlugin *wire_tail;
+
+};
+
+
+/**
+ * Find the relevant wire plugin.
+ *
+ * @param ac context to search
+ * @param type type of the wire plugin to load
+ * @return NULL on error
+ */
+static struct TALER_WIRE_Plugin *
+get_wire_plugin (struct AggregationContext *ac,
+                 const char *type)
+{
+  struct WirePlugin *wp;
+  struct TALER_WIRE_Plugin *plugin;
+
+  for (wp = ac->wire_head; NULL != wp; wp = wp->next)
+    if (0 == strcmp (type,
+                     wp->type))
+      return wp->plugin;
+  plugin = TALER_WIRE_plugin_load (cfg,
+                                   type);
+  if (NULL == plugin)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Failed to locate wire plugin for `%s'\n",
+                type);
+    return NULL;
+  }
+  wp = GNUNET_new (struct WirePlugin);
+  wp->type = GNUNET_strdup (type);
+  wp->plugin = plugin;
+  GNUNET_CONTAINER_DLL_insert (ac->wire_head,
+                               ac->wire_tail,
+                               wp);
+  return plugin;
+}
+
+
+/**
+ * Closure for #wire_transfer_information_cb.
+ */
+struct WireCheckContext
+{
 
   /**
-   * Total deposit fees earned so far.
+   * Corresponding merchant context.
    */
-  struct TALER_Amount deposit_fee_balance;
+  struct AggregationContext *ac;
 
   /**
-   * Total melt fees earned so far.
+   * Total deposits claimed by all transactions that were aggregated
+   * under the given @e wtid.
    */
-  struct TALER_Amount melt_fee_balance;
+  struct TALER_Amount total_deposits;
 
   /**
-   * Total refund fees earned so far.
+   * Hash of the wire transfer details of the receiver.
    */
-  struct TALER_Amount refund_fee_balance;
+  struct GNUNET_HashCode h_wire;
 
   /**
-   * Current financial risk of the exchange operator with respect
-   * to key compromise.
-   *
-   * TODO: not yet properly used!
+   * Execution time of the wire transfer.
    */
-  struct TALER_Amount risk;
+  struct GNUNET_TIME_Absolute date;
 
   /**
-   * Current write/replace offset in the circular @e summaries buffer.
+   * Wire method used for the transfer.
    */
-  unsigned int summaries_off;
+  const char *method;
 
   /**
-   * #GNUNET_OK as long as we are fine to commit the result to the #adb.
+   * Set to #GNUNET_SYSERR if there are inconsistencies.
    */
-  int ret;
+  int ok;
 
 };
 
 
 /**
- * Initialize information about denomination from the database.
+ * Check coin's transaction history for plausibility.  Does NOT check
+ * the signatures (those are checked independently), but does calculate
+ * the amounts for the aggregation table and checks that the total
+ * claimed coin value is within the value of the coin's denomination.
  *
- * @param denom_hash hash of the public key of the denomination
- * @param[out] ds summary to initialize
+ * @param coin_pub public key of the coin (for reporting)
+ * @param h_proposal_data hash of the proposal for which we calculate the 
amount
+ * @param merchant_pub public key of the merchant (who is allowed to issue 
refunds)
+ * @param dki denomination information about the coin
+ * @param tl_head head of transaction history to verify
+ * @param[out] merchant_gain amount the coin contributes to the wire transfer 
to the merchant
+ * @param[out] merchant_fees fees the exchange charged the merchant for the 
transaction(s)
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
-init_denomination (const struct GNUNET_HashCode *denom_hash,
-                   struct DenominationSummary *ds)
+check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           const struct GNUNET_HashCode *h_proposal_data,
+                           const struct TALER_MerchantPublicKeyP *merchant_pub,
+                           const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
+                           const struct TALER_EXCHANGEDB_TransactionList 
*tl_head,
+                           struct TALER_Amount *merchant_gain,
+                           struct TALER_Amount *merchant_fees)
 {
-  int ret;
+  struct TALER_Amount expenditures;
+  struct TALER_Amount refunds;
+  struct TALER_Amount spent;
+  struct TALER_Amount value;
+  struct TALER_Amount merchant_loss;
 
-  ret = adb->get_denomination_balance (adb->cls,
-                                       asession,
-                                       denom_hash,
-                                       &ds->denom_balance,
-                                       &ds->denom_risk);
-  if (GNUNET_OK == ret)
-  {
-    ds->in_db = GNUNET_YES;
-    return GNUNET_OK;
-  }
-  if (GNUNET_SYSERR == ret)
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Checking transaction history of coin %s\n",
+              TALER_B2S (coin_pub));
+
+  GNUNET_assert (NULL != tl_head);
+  TALER_amount_get_zero (currency,
+                         &expenditures);
+  TALER_amount_get_zero (currency,
+                         &refunds);
+  TALER_amount_get_zero (currency,
+                         merchant_gain);
+  TALER_amount_get_zero (currency,
+                         merchant_fees);
+  TALER_amount_get_zero (currency,
+                         &merchant_loss);
+  /* Go over transaction history to compute totals; note that we do not
+     know the order, so instead of subtracting we compute positive
+     (deposit, melt) and negative (refund) values separately here,
+     and then subtract the negative from the positive after the loop. */
+  for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != 
tl;tl = tl->next)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &ds->denom_balance));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &ds->denom_risk));
-  return GNUNET_OK;
-}
+    const struct TALER_Amount *amount_with_fee;
+    const struct TALER_Amount *fee;
+    const struct TALER_AmountNBO *fee_dki;
+    struct TALER_Amount tmp;
 
+    switch (tl->type) {
+    case TALER_EXCHANGEDB_TT_DEPOSIT:
+      amount_with_fee = &tl->details.deposit->amount_with_fee;
+      fee = &tl->details.deposit->deposit_fee;
+      fee_dki = &dki->properties.fee_deposit;
+      if (GNUNET_OK !=
+          TALER_amount_add (&expenditures,
+                            &expenditures,
+                            amount_with_fee))
+      {
+        GNUNET_break (0);
+        return GNUNET_SYSERR;
+      }
+      /* Check if this deposit is within the remit of the aggregation
+         we are investigating, if so, include it in the totals. */
+      if ( (0 == memcmp (merchant_pub,
+                         &tl->details.deposit->merchant_pub,
+                         sizeof (struct TALER_MerchantPublicKeyP))) &&
+           (0 == memcmp (h_proposal_data,
+                         &tl->details.deposit->h_proposal_data,
+                         sizeof (struct GNUNET_HashCode))) )
+      {
+        struct TALER_Amount amount_without_fee;
 
-/**
- * Obtain the denomination summary for the given @a dh
- *
- * @param cc our execution context
- * @param dki denomination key information for @a dh
- * @param dh the denomination hash to use for the lookup
- * @return NULL on error
- */
-static struct DenominationSummary *
-get_denomination_summary (struct CoinContext *cc,
-                          const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
-                          const struct GNUNET_HashCode *dh)
-{
-  struct DenominationSummary *ds;
-
-  ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
-                                          dh);
-  if (NULL != ds)
-    return ds;
-  ds = GNUNET_new (struct DenominationSummary);
-  ds->dki = dki;
-  if (GNUNET_OK !=
-      init_denomination (dh,
-                         ds))
-  {
-    GNUNET_break (0);
-    GNUNET_free (ds);
-    return NULL;
-  }
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
-                                                    dh,
-                                                    ds,
-                                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  return ds;
-}
-
-
-/**
- * Write information about the current knowledge about a denomination key
- * back to the database and update our global reporting data about the
- * denomination.  Also remove and free the memory of @a value.
- *
- * @param cls the `struct CoinContext`
- * @param key the hash of the denomination key
- * @param value a `struct DenominationSummary`
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-sync_denomination (void *cls,
-                   const struct GNUNET_HashCode *denom_hash,
-                   void *value)
-{
-  struct CoinContext *cc = cls;
-  struct DenominationSummary *ds = value;
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki = ds->dki;
-  struct GNUNET_TIME_Absolute now;
-  struct GNUNET_TIME_Absolute expire_deposit;
-  struct GNUNET_TIME_Absolute expire_deposit_grace;
-  int ret;
-
-  now = GNUNET_TIME_absolute_get ();
-  expire_deposit = GNUNET_TIME_absolute_ntoh (dki->properties.expire_deposit);
-  /* add day grace period to deal with clocks not being perfectly synchronized 
*/
-  expire_deposit_grace = GNUNET_TIME_absolute_add (expire_deposit,
-                                                   DEPOSIT_GRACE_PERIOD);
-  if (now.abs_value_us > expire_deposit_grace.abs_value_us)
-  {
-    /* Denominationkey has expired, book remaining balance of
-       outstanding coins as revenue; and reduce cc->risk exposure. */
-    if (ds->in_db)
-      ret = adb->del_denomination_balance (adb->cls,
-                                           asession,
-                                           denom_hash);
-    else
-      ret = GNUNET_OK;
-    if ( (GNUNET_OK == ret) &&
-         ( (0 != ds->denom_risk.value) ||
-           (0 != ds->denom_risk.fraction) ) )
-    {
-      /* The denomination expired and carried a balance; we can now
-         book the remaining balance as profit, and reduce our risk
-         exposure by the accumulated risk of the denomination. */
-      if (GNUNET_SYSERR ==
-          TALER_amount_subtract (&cc->risk,
-                                 &cc->risk,
-                                 &ds->denom_risk))
-      {
-        /* Holy smokes, our risk assessment was inconsistent!
-           This is really, really bad. */
-        GNUNET_break (0);
-        cc->ret = GNUNET_SYSERR;
-        return GNUNET_OK;
-      }
-    }
-    if ( (GNUNET_OK == ret) &&
-         ( (0 != ds->denom_balance.value) ||
-           (0 != ds->denom_balance.fraction) ) )
-    {
-      /* book denom_balance coin expiration profits! */
-      if (GNUNET_OK !=
-          adb->insert_historic_denom_revenue (adb->cls,
-                                              asession,
-                                              &master_pub,
-                                              denom_hash,
-                                              expire_deposit,
-                                              &ds->denom_balance))
-      {
-        /* Failed to store profits? Bad database */
-        GNUNET_break (0);
-        cc->ret = GNUNET_SYSERR;
-        return GNUNET_OK;
-      }
-    }
-  }
-  else
-  {
-    if (ds->in_db)
-      ret = adb->update_denomination_balance (adb->cls,
-                                              asession,
-                                              denom_hash,
-                                              &ds->denom_balance,
-                                              &ds->denom_risk);
-    else
-      ret = adb->insert_denomination_balance (adb->cls,
-                                              asession,
-                                              denom_hash,
-                                              &ds->denom_balance,
-                                              &ds->denom_risk);
-  }
-  if (GNUNET_OK != ret)
-  {
-    GNUNET_break (0);
-    cc->ret = GNUNET_SYSERR;
-  }
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
-                                                       denom_hash,
-                                                       ds));
-  GNUNET_free (ds);
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about all withdraw operations.
- * Updates the denomination balance and the overall balance as
- * we now have additional coins that have been issued.
- *
- * Note that the signature was already checked in
- * #handle_reserve_out(), so we do not check it again here.
- *
- * @param cls our `struct CoinContext`
- * @param rowid unique serial ID for the refresh session in our DB
- * @param h_blind_ev blinded hash of the coin's public key
- * @param denom_pub public denomination key of the deposited coin
- * @param denom_sig signature over the deposited coin
- * @param reserve_pub public key of the reserve
- * @param reserve_sig signature over the withdraw operation (verified 
elsewhere)
- * @param execution_date when did the wallet withdraw the coin
- * @param amount_with_fee amount that was withdrawn
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-withdraw_cb (void *cls,
-             uint64_t rowid,
-             const struct GNUNET_HashCode *h_blind_ev,
-             const struct TALER_DenominationPublicKey *denom_pub,
-             const struct TALER_DenominationSignature *denom_sig,
-             const struct TALER_ReservePublicKeyP *reserve_pub,
-             const struct TALER_ReserveSignatureP *reserve_sig,
-             struct GNUNET_TIME_Absolute execution_date,
-             const struct TALER_Amount *amount_with_fee)
-{
-  struct CoinContext *cc = cls;
-  struct DenominationSummary *ds;
-  struct GNUNET_HashCode dh;
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_Amount value;
-
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             &dh))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  ds = get_denomination_summary (cc,
-                                 dki,
-                                 &dh);
-  TALER_amount_ntoh (&value,
-                     &dki->properties.value);
-  if (GNUNET_OK !=
-      TALER_amount_add (&ds->denom_balance,
-                        &ds->denom_balance,
-                        &value))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_amount_add (&cc->total_denom_balance,
-                        &cc->total_denom_balance,
-                        &value))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about coins that were melted, with the
- * goal of auditing the refresh's execution.  Verifies the signature
- * and updates our information about coins outstanding (the old coin's
- * denomination has less, the fresh coins increased outstanding
- * balances).
- *
- * @param cls closure
- * @param rowid unique serial ID for the refresh session in our DB
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param coin_sig signature from the coin
- * @param amount_with_fee amount that was deposited including fee
- * @param num_newcoins how many coins were issued
- * @param noreveal_index which index was picked by the exchange in 
cut-and-choose
- * @param session_hash what is the session hash
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-refresh_session_cb (void *cls,
-                    uint64_t rowid,
-                    const struct TALER_DenominationPublicKey *denom_pub,
-                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                    const struct TALER_CoinSpendSignatureP *coin_sig,
-                    const struct TALER_Amount *amount_with_fee,
-                    uint16_t num_newcoins,
-                    uint16_t noreveal_index,
-                    const struct GNUNET_HashCode *session_hash)
-{
-  struct CoinContext *cc = cls;
-  struct TALER_RefreshMeltCoinAffirmationPS rmc;
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct DenominationSummary *dso;
-  struct TALER_Amount amount_without_fee;
-  struct TALER_Amount tmp;
-
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             NULL))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* verify melt signature */
-  rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
-  rmc.purpose.size = htonl (sizeof (rmc));
-  rmc.session_hash = *session_hash;
-  TALER_amount_hton (&rmc.amount_with_fee,
-                     amount_with_fee);
-  rmc.melt_fee = dki->properties.fee_refresh;
-  rmc.coin_pub = *coin_pub;
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
-                                  &rmc.purpose,
-                                  &coin_sig->eddsa_signature,
-                                  &coin_pub->eddsa_pub))
-  {
-    report_row_inconsistency ("melt",
-                              rowid,
-                              "invalid signature for coin melt");
-    return GNUNET_OK;
-  }
-
-  {
-    struct TALER_DenominationPublicKey new_dp[num_newcoins];
-    const struct TALER_EXCHANGEDB_DenominationKeyInformationP 
*new_dki[num_newcoins];
-    struct TALER_Amount refresh_cost;
-    int err;
-
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (amount_with_fee->currency,
-                                          &refresh_cost));
-
-    if (GNUNET_OK !=
-        edb->get_refresh_order (edb->cls,
-                                esession,
-                                session_hash,
-                                num_newcoins,
-                                new_dp))
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-    /* Update outstanding amounts for all new coin's denominations, and check
-       that the resulting amounts are consistent with the value being 
refreshed. */
-    err = GNUNET_NO;
-    for (unsigned int i=0;i<num_newcoins;i++)
-    {
-      /* lookup new coin denomination key */
-      if (GNUNET_OK !=
-          get_denomination_info (&new_dp[i],
-                                 &new_dki[i],
-                                 NULL))
-      {
-        GNUNET_break (0);
-        err = GNUNET_YES;
-      }
-      GNUNET_CRYPTO_rsa_public_key_free (new_dp[i].rsa_public_key);
-      new_dp[i].rsa_public_key = NULL;
-    }
-    if (err)
-      return GNUNET_SYSERR;
-
-    for (unsigned int i=0;i<num_newcoins;i++)
-    {
-      /* update cost of refresh */
-      {
-        struct TALER_Amount fee;
-        struct TALER_Amount value;
-
-        TALER_amount_ntoh (&fee,
-                           &new_dki[i]->properties.fee_withdraw);
-        TALER_amount_ntoh (&value,
-                           &new_dki[i]->properties.value);
-        if ( (GNUNET_OK !=
-              TALER_amount_add (&refresh_cost,
-                                &refresh_cost,
-                                &fee)) ||
-             (GNUNET_OK !=
-              TALER_amount_add (&refresh_cost,
-                                &refresh_cost,
-                                &value)) )
+        if (GNUNET_OK !=
+            TALER_amount_subtract (&amount_without_fee,
+                                   amount_with_fee,
+                                   fee))
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+        if (GNUNET_OK !=
+            TALER_amount_add (merchant_gain,
+                              merchant_gain,
+                              &amount_without_fee))
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Detected applicable deposit of %s\n",
+                    TALER_amount2s (&amount_without_fee));
+        if (GNUNET_OK !=
+            TALER_amount_add (merchant_fees,
+                              merchant_fees,
+                              fee))
         {
           GNUNET_break (0);
           return GNUNET_SYSERR;
         }
       }
-    }
-
-    /* compute contribution of old coin */
-    {
-      struct TALER_Amount melt_fee;
-
-      TALER_amount_ntoh (&melt_fee,
-                         &dki->properties.fee_refresh);
+      break;
+    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+      amount_with_fee = &tl->details.melt->amount_with_fee;
+      fee = &tl->details.melt->melt_fee;
+      fee_dki = &dki->properties.fee_refresh;
       if (GNUNET_OK !=
-          TALER_amount_subtract (&amount_without_fee,
-                                 amount_with_fee,
-                                 &melt_fee))
+          TALER_amount_add (&expenditures,
+                            &expenditures,
+                            amount_with_fee))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
-    }
-
-    /* check old coin covers complete expenses */
-    if (1 == TALER_amount_cmp (&refresh_cost,
-                               &amount_without_fee))
-    {
-      /* refresh_cost > amount_without_fee */
-      report_row_inconsistency ("melt",
-                                rowid,
-                                "refresh costs exceed value of melt");
-      return GNUNET_OK;
-    }
-
-    /* update outstanding denomination amounts */
-    for (unsigned int i=0;i<num_newcoins;i++)
-    {
-      struct DenominationSummary *dsi;
-      struct TALER_Amount value;
-
-      dsi = get_denomination_summary (cc,
-                                      new_dki[i],
-                                      &new_dki[i]->properties.denom_hash);
-      TALER_amount_ntoh (&value,
-                         &new_dki[i]->properties.value);
+      break;
+    case TALER_EXCHANGEDB_TT_REFUND:
+      amount_with_fee = &tl->details.refund->refund_amount;
+      fee = &tl->details.refund->refund_fee;
+      fee_dki = &dki->properties.fee_refund;
       if (GNUNET_OK !=
-          TALER_amount_add (&dsi->denom_balance,
-                            &dsi->denom_balance,
-                            &value))
+          TALER_amount_add (&refunds,
+                            &refunds,
+                            amount_with_fee))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
       if (GNUNET_OK !=
-          TALER_amount_add (&cc->total_denom_balance,
-                            &cc->total_denom_balance,
-                            &value))
+          TALER_amount_add (&expenditures,
+                            &expenditures,
+                            fee))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
+      /* Check if this refund is within the remit of the aggregation
+         we are investigating, if so, include it in the totals. */
+      if ( (0 == memcmp (merchant_pub,
+                         &tl->details.refund->merchant_pub,
+                         sizeof (struct TALER_MerchantPublicKeyP))) &&
+           (0 == memcmp (h_proposal_data,
+                         &tl->details.refund->h_proposal_data,
+                         sizeof (struct GNUNET_HashCode))) )
+      {
+        if (GNUNET_OK !=
+            TALER_amount_add (&merchant_loss,
+                              &merchant_loss,
+                              amount_with_fee))
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Detected applicable refund of %s\n",
+                    TALER_amount2s (amount_with_fee));
+        if (GNUNET_OK !=
+            TALER_amount_add (merchant_fees,
+                              merchant_fees,
+                              fee))
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+      }
+      break;
     }
-  }
-
-  /* update old coin's denomination balance */
-  dso = get_denomination_summary (cc,
-                                  dki,
-                                  &dki->properties.denom_hash);
-  if (GNUNET_OK !=
-      TALER_amount_subtract (&tmp,
-                             &dso->denom_balance,
-                             amount_with_fee))
-  {
-    report_emergency (dki);
-    return GNUNET_SYSERR;
-  }
-  dso->denom_balance = tmp;
-
-  /* update global up melt fees */
-  {
-    struct TALER_Amount rfee;
-
-    TALER_amount_ntoh (&rfee,
-                       &dki->properties.fee_refresh);
-    if (GNUNET_OK !=
-        TALER_amount_add (&cc->melt_fee_balance,
-                          &cc->melt_fee_balance,
-                          &rfee))
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-  }
-
-  /* We're good! */
-  return GNUNET_OK;
-}
 
+    /* Check that the fees given in the transaction list and in dki match */
+    TALER_amount_ntoh (&tmp,
+                       fee_dki);
+    if (0 !=
+        TALER_amount_cmp (&tmp,
+                          fee))
+    {
+      /* Disagreement in fee structure within DB, should be impossible! */
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+  } /* for 'tl' */
 
-/**
- * Function called with details about deposits that have been made,
- * with the goal of auditing the deposit's execution.
- *
- * As a side-effect, #get_coin_summary will report
- * inconsistencies in the deposited coin's balance.
- *
- * @param cls closure
- * @param rowid unique serial ID for the deposit in our DB
- * @param timestamp when did the deposit happen
- * @param merchant_pub public key of the merchant
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param coin_sig signature from the coin
- * @param amount_with_fee amount that was deposited including fee
- * @param h_proposal_data hash of the proposal data known to merchant and 
customer
- * @param refund_deadline by which the merchant adviced that he might want
- *        to get a refund
- * @param wire_deadline by which the merchant adviced that he would like the
- *        wire transfer to be executed
- * @param receiver_wire_account wire details for the merchant, NULL from 
iterate_matching_deposits()
- * @param done flag set if the deposit was already executed (or not)
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-deposit_cb (void *cls,
-            uint64_t rowid,
-            struct GNUNET_TIME_Absolute timestamp,
-            const struct TALER_MerchantPublicKeyP *merchant_pub,
-            const struct TALER_DenominationPublicKey *denom_pub,
-            const struct TALER_CoinSpendPublicKeyP *coin_pub,
-            const struct TALER_CoinSpendSignatureP *coin_sig,
-            const struct TALER_Amount *amount_with_fee,
-            const struct GNUNET_HashCode *h_proposal_data,
-            struct GNUNET_TIME_Absolute refund_deadline,
-            struct GNUNET_TIME_Absolute wire_deadline,
-            const json_t *receiver_wire_account,
-            int done)
-{
-  struct CoinContext *cc = cls;
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct DenominationSummary *ds;
-  struct TALER_DepositRequestPS dr;
-  struct TALER_Amount tmp;
-
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             NULL))
+  /* Calculate total balance change, i.e. expenditures minus refunds */
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&spent,
+                             &expenditures,
+                             &refunds))
   {
-    GNUNET_break (0);
+    /* refunds above expenditures? Bad! */
+    report_coin_inconsistency (coin_pub,
+                               &expenditures,
+                               &refunds,
+                               "could not subtract refunded amount from 
expenditures");
     return GNUNET_SYSERR;
   }
 
-  /* Verify deposit signature */
-  dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
-  dr.purpose.size = htonl (sizeof (dr));
-  dr.h_proposal_data = *h_proposal_data;
-  if (GNUNET_OK !=
-      TALER_JSON_hash (receiver_wire_account,
-                       &dr.h_wire))
+  /* Now check that 'spent' is less or equal than total coin value */
+  TALER_amount_ntoh (&value,
+                     &dki->properties.value);
+  if (1 == TALER_amount_cmp (&spent,
+                             &value))
   {
-    GNUNET_break (0);
+    /* spent > value */
+    report_coin_inconsistency (coin_pub,
+                               &spent,
+                               &value,
+                               "accepted deposits (minus refunds) exceeds 
denomination value");
     return GNUNET_SYSERR;
   }
-  dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
-  TALER_amount_hton (&dr.amount_with_fee,
-                     amount_with_fee);
-  dr.deposit_fee = dki->properties.fee_deposit;
-  dr.merchant = *merchant_pub;
-  dr.coin_pub = *coin_pub;
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
-                                  &dr.purpose,
-                                  &coin_sig->eddsa_signature,
-                                  &coin_pub->eddsa_pub))
-  {
-    report_row_inconsistency ("deposit",
-                              rowid,
-                              "invalid signature for coin deposit");
-    return GNUNET_OK;
-  }
 
-  /* update old coin's denomination balance */
-  ds = get_denomination_summary (cc,
-                                 dki,
-                                 &dki->properties.denom_hash);
-  if (GNUNET_OK !=
-      TALER_amount_subtract (&tmp,
-                             &ds->denom_balance,
-                             amount_with_fee))
+  /* Finally, update @a merchant_gain by subtracting what he "lost" from 
refunds */
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (merchant_gain,
+                             merchant_gain,
+                             &merchant_loss))
   {
-    report_emergency (dki);
+    /* refunds above deposits? Bad! */
+    report_coin_inconsistency (coin_pub,
+                               merchant_gain,
+                               &merchant_loss,
+                               "merchant was granted more refunds than he 
deposited");
     return GNUNET_SYSERR;
   }
-  ds->denom_balance = tmp;
-
-  /* update global up melt fees */
-  {
-    struct TALER_Amount dfee;
-
-    TALER_amount_ntoh (&dfee,
-                       &dki->properties.fee_deposit);
-    if (GNUNET_OK !=
-        TALER_amount_add (&cc->deposit_fee_balance,
-                          &cc->deposit_fee_balance,
-                          &dfee))
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-  }
-
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Coin %s contributes %s to contract %s\n",
+              TALER_B2S (coin_pub),
+              TALER_amount2s (merchant_gain),
+              GNUNET_h2s (h_proposal_data));
   return GNUNET_OK;
 }
 
 
 /**
- * Function called with details about coins that were refunding,
- * with the goal of auditing the refund's execution.  Adds the
- * refunded amount back to the outstanding balance of the respective
- * denomination.
- *
- * As a side-effect, #get_coin_summary will report
- * inconsistencies in the refunded coin's balance.
+ * Function called with the results of the lookup of the
+ * transaction data associated with a wire transfer identifier.
  *
- * @param cls closure
- * @param rowid unique serial ID for the refund in our DB
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param merchant_pub public key of the merchant
- * @param merchant_sig signature of the merchant
- * @param h_proposal_data hash of the proposal data known to merchant and 
customer
- * @param rtransaction_id refund transaction ID chosen by the merchant
- * @param amount_with_fee amount that was deposited including fee
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @param cls a `struct WireCheckContext`
+ * @param rowid which row in the table is the information from (for 
diagnostics)
+ * @param merchant_pub public key of the merchant (should be same for all 
callbacks with the same @e cls)
+ * @param wire_method which wire plugin was used for the transfer?
+ * @param h_wire hash of wire transfer details of the merchant (should be same 
for all callbacks with the same @e cls)
+ * @param exec_time execution time of the wire transfer (should be same for 
all callbacks with the same @e cls)
+ * @param h_proposal_data which proposal was this payment about
+ * @param coin_pub which public key was this payment about
+ * @param coin_value amount contributed by this coin in total (with fee)
+ * @param coin_fee applicable fee for this coin
  */
-static int
-refund_cb (void *cls,
-           uint64_t rowid,
-           const struct TALER_DenominationPublicKey *denom_pub,
-           const struct TALER_CoinSpendPublicKeyP *coin_pub,
-           const struct TALER_MerchantPublicKeyP *merchant_pub,
-           const struct TALER_MerchantSignatureP *merchant_sig,
-           const struct GNUNET_HashCode *h_proposal_data,
-           uint64_t rtransaction_id,
-           const struct TALER_Amount *amount_with_fee)
+static void
+wire_transfer_information_cb (void *cls,
+                              uint64_t rowid,
+                              const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                              const char *wire_method,
+                              const struct GNUNET_HashCode *h_wire,
+                              struct GNUNET_TIME_Absolute exec_time,
+                              const struct GNUNET_HashCode *h_proposal_data,
+                              const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                              const struct TALER_Amount *coin_value,
+                              const struct TALER_Amount *coin_fee)
 {
-  struct CoinContext *cc = cls;
+  struct WireCheckContext *wcc = cls;
   const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct DenominationSummary *ds;
-  struct TALER_RefundRequestPS rr;
-  struct TALER_Amount amount_without_fee;
-  struct TALER_Amount refund_fee;
+  struct TALER_Amount contribution;
+  struct TALER_Amount computed_value;
+  struct TALER_Amount computed_fees;
+  struct TALER_Amount coin_value_without_fee;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  const struct TALER_CoinPublicInfo *coin;
+
+  /* Obtain coin's transaction history */
+  tl = edb->get_coin_transactions (edb->cls,
+                                   esession,
+                                   coin_pub);
+  if (NULL == tl)
+  {
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "no transaction history for coin claimed in 
aggregation");
+    return;
+  }
 
+  /* Obtain general denomination information about the coin */
+  coin = NULL;
+  switch (tl->type)
+  {
+  case TALER_EXCHANGEDB_TT_DEPOSIT:
+    coin = &tl->details.deposit->coin;
+    break;
+  case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+    coin = &tl->details.melt->coin;
+    break;
+  case TALER_EXCHANGEDB_TT_REFUND:
+    coin = &tl->details.refund->coin;
+    break;
+  }
+  GNUNET_assert (NULL != coin); /* hard check that switch worked */
   if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
+      get_denomination_info (&coin->denom_pub,
                              &dki,
                              NULL))
   {
+    /* This should be impossible from database constraints */
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    edb->free_coin_transaction_list (edb->cls,
+                                     tl);
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "could not find denomination key for coin 
claimed in aggregation");
+    return;
   }
 
-  /* verify refund signature */
-  rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
-  rr.purpose.size = htonl (sizeof (rr));
-  rr.h_proposal_data = *h_proposal_data;
-  rr.coin_pub = *coin_pub;
-  rr.merchant = *merchant_pub;
-  rr.rtransaction_id = GNUNET_htonll (rtransaction_id);
-  TALER_amount_hton (&rr.refund_amount,
-                     amount_with_fee);
-  rr.refund_fee = dki->properties.fee_refund;
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
-                                  &rr.purpose,
-                                  &merchant_sig->eddsa_sig,
-                                  &merchant_pub->eddsa_pub))
+  /* Check transaction history to see if it supports aggregate valuation */
+  check_transaction_history (coin_pub,
+                             h_proposal_data,
+                             merchant_pub,
+                             dki,
+                             tl,
+                             &computed_value,
+                             &computed_fees);
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&coin_value_without_fee,
+                             coin_value,
+                             coin_fee))
   {
-    report_row_inconsistency ("refund",
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
                               rowid,
-                              "invalid signature for refund");
-    return GNUNET_OK;
+                              "inconsistent coin value and fee claimed in 
aggregation");
+    return;
+  }
+  if (0 !=
+      TALER_amount_cmp (&computed_value,
+                        &coin_value_without_fee))
+  {
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "coin transaction history and aggregation 
disagree about coin's contribution");
+  }
+  if (0 !=
+      TALER_amount_cmp (&computed_fees,
+                        coin_fee))
+  {
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "coin transaction history and aggregation 
disagree about applicable fees");
+  }
+  edb->free_coin_transaction_list (edb->cls,
+                                   tl);
+
+  /* Check other details of wire transfer match */
+  if (0 != strcmp (wire_method,
+                   wcc->method))
+  {
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "wire method of aggregate do not match wire 
transfer");
   }
-
-  TALER_amount_ntoh (&refund_fee,
-                     &dki->properties.fee_refund);
-  if (GNUNET_OK !=
-      TALER_amount_subtract (&amount_without_fee,
-                             amount_with_fee,
-                             &refund_fee))
+  if (0 != memcmp (h_wire,
+                   &wcc->h_wire,
+                   sizeof (struct GNUNET_HashCode)))
   {
-    report_row_inconsistency ("refund",
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
                               rowid,
-                              "refunded amount smaller than refund fee");
-    return GNUNET_OK;
+                              "account details of aggregate do not match 
account details of wire transfer");
+    return;
   }
-
-  /* update coin's denomination balance */
-  ds = get_denomination_summary (cc,
-                                 dki,
-                                 &dki->properties.denom_hash);
-  if (GNUNET_OK !=
-      TALER_amount_add (&ds->denom_balance,
-                        &ds->denom_balance,
-                        &amount_without_fee))
+  if (exec_time.abs_value_us != wcc->date.abs_value_us)
   {
+    /* This should be impossible from database constraints */
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "date given in aggregate does not match wire 
transfer date");
+    return;
   }
-
-  /* update total refund fee balance */
-  if (GNUNET_OK !=
-      TALER_amount_add (&cc->refund_fee_balance,
-                        &cc->refund_fee_balance,
-                        &refund_fee))
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&contribution,
+                             coin_value,
+                             coin_fee))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    wcc->ok = GNUNET_SYSERR;
+    report_row_inconsistency ("aggregation",
+                              rowid,
+                              "could not calculate contribution of coin");
+    return;
   }
 
-  return GNUNET_OK;
+  /* Add coin's contribution to total aggregate value */
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_add (&wcc->total_deposits,
+                                   &wcc->total_deposits,
+                                   &contribution));
 }
 
 
 /**
- * Analyze the exchange's processing of coins.
+ * Check that a wire transfer made by the exchange is valid
+ * (has matching deposits).
  *
- * @param cls closure
- * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @param cls a `struct AggregationContext`
+ * @param rowid identifier of the respective row in the database
+ * @param date timestamp of the wire transfer (roughly)
+ * @param wtid wire transfer subject
+ * @param wire wire transfer details of the receiver
+ * @param amount amount that was wired
  */
-static int
-analyze_coins (void *cls)
+static void
+check_wire_out_cb (void *cls,
+                   uint64_t rowid,
+                   struct GNUNET_TIME_Absolute date,
+                   const struct TALER_WireTransferIdentifierRawP *wtid,
+                   const json_t *wire,
+                   const struct TALER_Amount *amount)
 {
-  struct CoinContext cc;
-  int dret;
+  struct AggregationContext *ac = cls;
+  struct WireCheckContext wcc;
+  json_t *method;
+  struct TALER_WIRE_Plugin *plugin;
 
-  /* setup 'cc' */
-  cc.ret = GNUNET_OK;
-  cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
-                                                           GNUNET_NO);
-  dret = adb->get_balance_summary (adb->cls,
-                                   asession,
-                                   &master_pub,
-                                   &cc.total_denom_balance,
-                                   &cc.deposit_fee_balance,
-                                   &cc.melt_fee_balance,
-                                   &cc.refund_fee_balance,
-                                   &cc.risk);
-  if (GNUNET_SYSERR == dret)
+  /* should be monotonically increasing */
+  GNUNET_assert (rowid >= pp.last_wire_out_serial_id);
+  pp.last_wire_out_serial_id = rowid + 1;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Checking wire transfer %s over %s performed on %s\n",
+              TALER_B2S (wtid),
+              TALER_amount2s (amount),
+              GNUNET_STRINGS_absolute_time_to_string (date));
+  wcc.ac = ac;
+  method = json_object_get (wire,
+                            "type");
+  if ( (NULL == method) ||
+       (! json_is_string (method)) )
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    report_row_inconsistency ("wire_out",
+                              rowid,
+                              "specified wire address lacks type");
+    return;
   }
-  if (GNUNET_NO == dret)
+  wcc.method = json_string_value (method);
+  wcc.ok = GNUNET_OK;
+  wcc.date = date;
+  TALER_amount_get_zero (amount->currency,
+                         &wcc.total_deposits);
+  TALER_JSON_hash (wire,
+                   &wcc.h_wire);
+  edb->lookup_wire_transfer (edb->cls,
+                             esession,
+                             wtid,
+                             &wire_transfer_information_cb,
+                             &wcc);
+  if (GNUNET_OK != wcc.ok)
   {
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (currency,
-                                          &cc.total_denom_balance));
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (currency,
-                                          &cc.deposit_fee_balance));
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (currency,
-                                          &cc.melt_fee_balance));
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (currency,
-                                          &cc.refund_fee_balance));
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_get_zero (currency,
-                                          &cc.risk));
+    report_row_inconsistency ("wire_out",
+                              rowid,
+                              "audit of associated transactions failed");
   }
-
-  /* process withdrawals */
-  if (GNUNET_SYSERR ==
-      edb->select_reserves_out_above_serial_id (edb->cls,
-                                                esession,
-                                                pp.last_reserve_out_serial_id,
-                                                &withdraw_cb,
-                                                &cc))
+  plugin = get_wire_plugin (ac,
+                            wcc.method);
+  if (NULL == plugin)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    report_row_inconsistency ("wire_out",
+                              rowid,
+                              "could not load required wire plugin to 
validate");
+    return;
   }
-
-  /* process refreshs */
   if (GNUNET_SYSERR ==
-      edb->select_refreshs_above_serial_id (edb->cls,
-                                            esession,
-                                            pp.last_melt_serial_id,
-                                            &refresh_session_cb,
-                                            &cc))
+      plugin->amount_round (plugin->cls,
+                            &wcc.total_deposits))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    report_row_minor_inconsistency ("wire_out",
+                                    rowid,
+                                    "wire plugin failed to round given 
amount");
   }
-
-  /* process deposits */
-  if (GNUNET_SYSERR ==
-      edb->select_deposits_above_serial_id (edb->cls,
-                                            esession,
-                                            pp.last_deposit_serial_id,
-                                            &deposit_cb,
-                                            &cc))
+  if (0 != TALER_amount_cmp (amount,
+                             &wcc.total_deposits))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    report_wire_out_inconsistency (wire,
+                                   rowid,
+                                   &wcc.total_deposits,
+                                   amount,
+                                   "computed amount inconsistent with wire 
amount");
+    return;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Wire transfer %s is OK\n",
+              TALER_B2S (wtid));
+}
 
-  /* process refunds */
+
+/**
+ * Analyze the exchange aggregator's payment processing.
+ *
+ * @param cls closure
+ * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ */
+static int
+analyze_aggregations (void *cls)
+{
+  struct AggregationContext ac;
+  struct WirePlugin *wc;
+  int ret;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Analyzing aggregations\n");
+  ret = GNUNET_OK;
+  ac.wire_head = NULL;
+  ac.wire_tail = NULL;
   if (GNUNET_SYSERR ==
-      edb->select_refunds_above_serial_id (edb->cls,
-                                           esession,
-                                           pp.last_refund_serial_id,
-                                           &refund_cb,
-                                           &cc))
+      edb->select_wire_out_above_serial_id (edb->cls,
+                                            esession,
+                                            pp.last_wire_out_serial_id,
+                                            &check_wire_out_cb,
+                                            &ac))
   {
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    ret = GNUNET_SYSERR;
   }
-
-  /* sync 'cc' back to disk */
-  GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
-                                         &sync_denomination,
-                                         &cc);
-  GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
-
-  if (GNUNET_YES == dret)
-      dret = adb->update_balance_summary (adb->cls,
-                                          asession,
-                                          &master_pub,
-                                          &cc.total_denom_balance,
-                                          &cc.deposit_fee_balance,
-                                          &cc.melt_fee_balance,
-                                          &cc.refund_fee_balance,
-                                          &cc.risk);
-  else
-    dret = adb->insert_balance_summary (adb->cls,
-                                        asession,
-                                        &master_pub,
-                                        &cc.total_denom_balance,
-                                        &cc.deposit_fee_balance,
-                                        &cc.melt_fee_balance,
-                                        &cc.refund_fee_balance,
-                                        &cc.risk);
-  if (GNUNET_OK != dret)
+  while (NULL != (wc = ac.wire_head))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_CONTAINER_DLL_remove (ac.wire_head,
+                                 ac.wire_tail,
+                                 wc);
+    TALER_WIRE_plugin_unload (wc->plugin);
+    GNUNET_free (wc->type);
+    GNUNET_free (wc);
   }
-
-  return cc.ret;
+  return ret;
 }
 
 
-/* ************************* Analyze merchants ******************** */
-/* This logic checks that the aggregator did the right thing
-   paying each merchant what they were due (and on time). */
+/* ************************* Analyze coins ******************** */
+/* This logic checks that the exchange did the right thing for each
+   coin, checking deposits, refunds, refresh* and known_coins
+   tables */
 
 
 /**
- * Information we keep per loaded wire plugin.
+ * Summary data we keep per denomination.
  */
-struct WirePlugin
+struct DenominationSummary
 {
-
   /**
-   * Kept in a DLL.
+   * Total value of outstanding (not deposited) coins issued with this
+   * denomination key.
    */
-  struct WirePlugin *next;
+  struct TALER_Amount denom_balance;
 
   /**
-   * Kept in a DLL.
+   * Total value of coins issued with this denomination key.
    */
-  struct WirePlugin *prev;
+  struct TALER_Amount denom_risk;
 
   /**
-   * Name of the wire method.
+   * Denomination key information for this denomination.
    */
-  char *type;
+  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
 
   /**
-   * Handle to the wire plugin.
+   * #GNUNET_YES if this record already existed in the DB.
+   * Used to decide between insert/update in
+   * #sync_denomination().
    */
-  struct TALER_WIRE_Plugin *plugin;
-
+  int in_db;
 };
 
 
 /**
- * Closure for callbacks during #analyze_merchants().
+ * Closure for callbacks during #analyze_coins().
  */
-struct AggregationContext
+struct CoinContext
 {
 
   /**
-   * DLL of wire plugins encountered.
+   * Map for tracking information about denominations.
    */
-  struct WirePlugin *wire_head;
+  struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
 
   /**
-   * DLL of wire plugins encountered.
+   * Total outstanding balances across all denomination keys.
    */
-  struct WirePlugin *wire_tail;
+  struct TALER_Amount total_denom_balance;
+
+  /**
+   * Total deposit fees earned so far.
+   */
+  struct TALER_Amount deposit_fee_balance;
+
+  /**
+   * Total melt fees earned so far.
+   */
+  struct TALER_Amount melt_fee_balance;
+
+  /**
+   * Total refund fees earned so far.
+   */
+  struct TALER_Amount refund_fee_balance;
+
+  /**
+   * Current financial risk of the exchange operator with respect
+   * to key compromise.
+   *
+   * TODO: not yet properly used!
+   */
+  struct TALER_Amount risk;
+
+  /**
+   * Current write/replace offset in the circular @e summaries buffer.
+   */
+  unsigned int summaries_off;
+
+  /**
+   * #GNUNET_OK as long as we are fine to commit the result to the #adb.
+   */
+  int ret;
 
 };
 
 
 /**
- * Find the relevant wire plugin.
+ * Initialize information about denomination from the database.
  *
- * @param ac context to search
- * @param type type of the wire plugin to load
+ * @param denom_hash hash of the public key of the denomination
+ * @param[out] ds summary to initialize
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ */
+static int
+init_denomination (const struct GNUNET_HashCode *denom_hash,
+                   struct DenominationSummary *ds)
+{
+  int ret;
+
+  ret = adb->get_denomination_balance (adb->cls,
+                                       asession,
+                                       denom_hash,
+                                       &ds->denom_balance,
+                                       &ds->denom_risk);
+  if (GNUNET_OK == ret)
+  {
+    ds->in_db = GNUNET_YES;
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Starting balance for denomination `%s' is %s\n",
+                GNUNET_h2s (denom_hash),
+                TALER_amount2s (&ds->denom_balance));
+    return GNUNET_OK;
+  }
+  if (GNUNET_SYSERR == ret)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &ds->denom_balance));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &ds->denom_risk));
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting balance for denomination `%s' is %s\n",
+              GNUNET_h2s (denom_hash),
+              TALER_amount2s (&ds->denom_balance));
+  return GNUNET_OK;
+}
+
+
+/**
+ * Obtain the denomination summary for the given @a dh
+ *
+ * @param cc our execution context
+ * @param dki denomination key information for @a dh
+ * @param dh the denomination hash to use for the lookup
  * @return NULL on error
  */
-static struct TALER_WIRE_Plugin *
-get_wire_plugin (struct AggregationContext *ac,
-                 const char *type)
+static struct DenominationSummary *
+get_denomination_summary (struct CoinContext *cc,
+                          const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
+                          const struct GNUNET_HashCode *dh)
 {
-  struct WirePlugin *wp;
-  struct TALER_WIRE_Plugin *plugin;
+  struct DenominationSummary *ds;
 
-  for (wp = ac->wire_head; NULL != wp; wp = wp->next)
-    if (0 == strcmp (type,
-                     wp->type))
-      return wp->plugin;
-  plugin = TALER_WIRE_plugin_load (cfg,
-                                   type);
-  if (NULL == plugin)
+  ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
+                                          dh);
+  if (NULL != ds)
+    return ds;
+  ds = GNUNET_new (struct DenominationSummary);
+  ds->dki = dki;
+  if (GNUNET_OK !=
+      init_denomination (dh,
+                         ds))
+  {
+    GNUNET_break (0);
+    GNUNET_free (ds);
+    return NULL;
+  }
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
+                                                    dh,
+                                                    ds,
+                                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  return ds;
+}
+
+
+/**
+ * Write information about the current knowledge about a denomination key
+ * back to the database and update our global reporting data about the
+ * denomination.  Also remove and free the memory of @a value.
+ *
+ * @param cls the `struct CoinContext`
+ * @param key the hash of the denomination key
+ * @param value a `struct DenominationSummary`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+sync_denomination (void *cls,
+                   const struct GNUNET_HashCode *denom_hash,
+                   void *value)
+{
+  struct CoinContext *cc = cls;
+  struct DenominationSummary *ds = value;
+  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki = ds->dki;
+  struct GNUNET_TIME_Absolute now;
+  struct GNUNET_TIME_Absolute expire_deposit;
+  struct GNUNET_TIME_Absolute expire_deposit_grace;
+  int ret;
+
+  now = GNUNET_TIME_absolute_get ();
+  expire_deposit = GNUNET_TIME_absolute_ntoh (dki->properties.expire_deposit);
+  /* add day grace period to deal with clocks not being perfectly synchronized 
*/
+  expire_deposit_grace = GNUNET_TIME_absolute_add (expire_deposit,
+                                                   DEPOSIT_GRACE_PERIOD);
+  if (now.abs_value_us > expire_deposit_grace.abs_value_us)
+  {
+    /* Denominationkey has expired, book remaining balance of
+       outstanding coins as revenue; and reduce cc->risk exposure. */
+    if (ds->in_db)
+      ret = adb->del_denomination_balance (adb->cls,
+                                           asession,
+                                           denom_hash);
+    else
+      ret = GNUNET_OK;
+    if ( (GNUNET_OK == ret) &&
+         ( (0 != ds->denom_risk.value) ||
+           (0 != ds->denom_risk.fraction) ) )
+    {
+      /* The denomination expired and carried a balance; we can now
+         book the remaining balance as profit, and reduce our risk
+         exposure by the accumulated risk of the denomination. */
+      if (GNUNET_SYSERR ==
+          TALER_amount_subtract (&cc->risk,
+                                 &cc->risk,
+                                 &ds->denom_risk))
+      {
+        /* Holy smokes, our risk assessment was inconsistent!
+           This is really, really bad. */
+        GNUNET_break (0);
+        cc->ret = GNUNET_SYSERR;
+        return GNUNET_OK;
+      }
+    }
+    if ( (GNUNET_OK == ret) &&
+         ( (0 != ds->denom_balance.value) ||
+           (0 != ds->denom_balance.fraction) ) )
+    {
+      /* book denom_balance coin expiration profits! */
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Denomination `%s' expired, booking %s in expiration 
profits\n",
+                  GNUNET_h2s (denom_hash),
+                  TALER_amount2s (&ds->denom_balance));
+      if (GNUNET_OK !=
+          adb->insert_historic_denom_revenue (adb->cls,
+                                              asession,
+                                              &master_pub,
+                                              denom_hash,
+                                              expire_deposit,
+                                              &ds->denom_balance))
+      {
+        /* Failed to store profits? Bad database */
+        GNUNET_break (0);
+        cc->ret = GNUNET_SYSERR;
+        return GNUNET_OK;
+      }
+    }
+  }
+  else
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Final balance for denomination `%s' is %s\n",
+                GNUNET_h2s (denom_hash),
+                TALER_amount2s (&ds->denom_balance));
+    if (ds->in_db)
+      ret = adb->update_denomination_balance (adb->cls,
+                                              asession,
+                                              denom_hash,
+                                              &ds->denom_balance,
+                                              &ds->denom_risk);
+    else
+      ret = adb->insert_denomination_balance (adb->cls,
+                                              asession,
+                                              denom_hash,
+                                              &ds->denom_balance,
+                                              &ds->denom_risk);
+  }
+  if (GNUNET_OK != ret)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to locate wire plugin for `%s'\n",
-                type);
-    return NULL;
+    GNUNET_break (0);
+    cc->ret = GNUNET_SYSERR;
   }
-  wp = GNUNET_new (struct WirePlugin);
-  wp->type = GNUNET_strdup (type);
-  wp->plugin = plugin;
-  GNUNET_CONTAINER_DLL_insert (ac->wire_head,
-                               ac->wire_tail,
-                               wp);
-  return plugin;
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
+                                                       denom_hash,
+                                                       ds));
+  GNUNET_free (ds);
+  return GNUNET_OK;
 }
 
 
 /**
- * Closure for #wire_transfer_information_cb.
+ * Function called with details about all withdraw operations.
+ * Updates the denomination balance and the overall balance as
+ * we now have additional coins that have been issued.
+ *
+ * Note that the signature was already checked in
+ * #handle_reserve_out(), so we do not check it again here.
+ *
+ * @param cls our `struct CoinContext`
+ * @param rowid unique serial ID for the refresh session in our DB
+ * @param h_blind_ev blinded hash of the coin's public key
+ * @param denom_pub public denomination key of the deposited coin
+ * @param denom_sig signature over the deposited coin
+ * @param reserve_pub public key of the reserve
+ * @param reserve_sig signature over the withdraw operation (verified 
elsewhere)
+ * @param execution_date when did the wallet withdraw the coin
+ * @param amount_with_fee amount that was withdrawn
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
-struct WireCheckContext
+static int
+withdraw_cb (void *cls,
+             uint64_t rowid,
+             const struct GNUNET_HashCode *h_blind_ev,
+             const struct TALER_DenominationPublicKey *denom_pub,
+             const struct TALER_DenominationSignature *denom_sig,
+             const struct TALER_ReservePublicKeyP *reserve_pub,
+             const struct TALER_ReserveSignatureP *reserve_sig,
+             struct GNUNET_TIME_Absolute execution_date,
+             const struct TALER_Amount *amount_with_fee)
 {
+  struct CoinContext *cc = cls;
+  struct DenominationSummary *ds;
+  struct GNUNET_HashCode dh;
+  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  struct TALER_Amount value;
 
-  /**
-   * Corresponding merchant context.
-   */
-  struct AggregationContext *ac;
-
-  /**
-   * Total deposits claimed by all transactions that were aggregated
-   * under the given @e wtid.
-   */
-  struct TALER_Amount total_deposits;
-
-  /**
-   * Hash of the wire transfer details of the receiver.
-   */
-  struct GNUNET_HashCode h_wire;
-
-  /**
-   * Execution time of the wire transfer.
-   */
-  struct GNUNET_TIME_Absolute date;
-
-  /**
-   * Wire method used for the transfer.
-   */
-  const char *method;
-
-  /**
-   * Set to #GNUNET_SYSERR if there are inconsistencies.
-   */
-  int ok;
-
-};
+  if (GNUNET_OK !=
+      get_denomination_info (denom_pub,
+                             &dki,
+                             &dh))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  ds = get_denomination_summary (cc,
+                                 dki,
+                                 &dh);
+  TALER_amount_ntoh (&value,
+                     &dki->properties.value);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Issued coin in denomination `%s' of total value %s\n",
+              GNUNET_h2s (&dh),
+              TALER_amount2s (&value));
+  if (GNUNET_OK !=
+      TALER_amount_add (&ds->denom_balance,
+                        &ds->denom_balance,
+                        &value))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "New balance of denomination `%s' is %s\n",
+              GNUNET_h2s (&dh),
+              TALER_amount2s (&ds->denom_balance));
+  if (GNUNET_OK !=
+      TALER_amount_add (&cc->total_denom_balance,
+                        &cc->total_denom_balance,
+                        &value))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
 
 
 /**
- * Check coin's transaction history for plausibility.  Does NOT check
- * the signatures (those are checked independently), but does calculate
- * the amounts for the aggregation table and checks that the total
- * claimed coin value is within the value of the coin's denomination.
+ * Function called with details about coins that were melted, with the
+ * goal of auditing the refresh's execution.  Verifies the signature
+ * and updates our information about coins outstanding (the old coin's
+ * denomination has less, the fresh coins increased outstanding
+ * balances).
  *
- * @param coin_pub public key of the coin (for reporting)
- * @param h_proposal_data hash of the proposal for which we calculate the 
amount
- * @param merchant_pub public key of the merchant (who is allowed to issue 
refunds)
- * @param dki denomination information about the coin
- * @param tl_head head of transaction history to verify
- * @param[out] merchant_gain amount the coin contributes to the wire transfer 
to the merchant
- * @param[out] merchant_fees fees the exchange charged the merchant for the 
transaction(s)
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ * @param cls closure
+ * @param rowid unique serial ID for the refresh session in our DB
+ * @param denom_pub denomination public key of @a coin_pub
+ * @param coin_pub public key of the coin
+ * @param coin_sig signature from the coin
+ * @param amount_with_fee amount that was deposited including fee
+ * @param num_newcoins how many coins were issued
+ * @param noreveal_index which index was picked by the exchange in 
cut-and-choose
+ * @param session_hash what is the session hash
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
 static int
-check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                           const struct GNUNET_HashCode *h_proposal_data,
-                           const struct TALER_MerchantPublicKeyP *merchant_pub,
-                           const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
-                           const struct TALER_EXCHANGEDB_TransactionList 
*tl_head,
-                           struct TALER_Amount *merchant_gain,
-                           struct TALER_Amount *merchant_fees)
+refresh_session_cb (void *cls,
+                    uint64_t rowid,
+                    const struct TALER_DenominationPublicKey *denom_pub,
+                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    const struct TALER_CoinSpendSignatureP *coin_sig,
+                    const struct TALER_Amount *amount_with_fee,
+                    uint16_t num_newcoins,
+                    uint16_t noreveal_index,
+                    const struct GNUNET_HashCode *session_hash)
 {
-  struct TALER_Amount expenditures;
-  struct TALER_Amount refunds;
-  struct TALER_Amount spent;
-  struct TALER_Amount value;
-  struct TALER_Amount merchant_loss;
+  struct CoinContext *cc = cls;
+  struct TALER_RefreshMeltCoinAffirmationPS rmc;
+  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  struct DenominationSummary *dso;
+  struct TALER_Amount amount_without_fee;
+  struct TALER_Amount tmp;
 
-  GNUNET_assert (NULL != tl_head);
-  TALER_amount_get_zero (currency,
-                         &expenditures);
-  TALER_amount_get_zero (currency,
-                         &refunds);
-  TALER_amount_get_zero (currency,
-                         merchant_gain);
-  TALER_amount_get_zero (currency,
-                         merchant_fees);
-  TALER_amount_get_zero (currency,
-                         &merchant_loss);
-  /* Go over transaction history to compute totals; note that we do not
-     know the order, so instead of subtracting we compute positive
-     (deposit, melt) and negative (refund) values separately here,
-     and then subtract the negative from the positive after the loop. */
-  for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != 
tl;tl = tl->next)
+  if (GNUNET_OK !=
+      get_denomination_info (denom_pub,
+                             &dki,
+                             NULL))
   {
-    const struct TALER_Amount *amount_with_fee;
-    const struct TALER_Amount *fee;
-    const struct TALER_AmountNBO *fee_dki;
-    struct TALER_Amount tmp;
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
 
-    // FIXME:
-    // - for refunds/deposits that apply to this merchant and this contract
-    //   we need to update the total expenditures/refunds/fees
-    // - for all other operations, we need to update the per-coin totals
-    //   and at the end check that they do not exceed the value of the coin!
-    switch (tl->type) {
-    case TALER_EXCHANGEDB_TT_DEPOSIT:
-      amount_with_fee = &tl->details.deposit->amount_with_fee;
-      fee = &tl->details.deposit->deposit_fee;
-      fee_dki = &dki->properties.fee_deposit;
+  /* verify melt signature */
+  rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+  rmc.purpose.size = htonl (sizeof (rmc));
+  rmc.session_hash = *session_hash;
+  TALER_amount_hton (&rmc.amount_with_fee,
+                     amount_with_fee);
+  rmc.melt_fee = dki->properties.fee_refresh;
+  rmc.coin_pub = *coin_pub;
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
+                                  &rmc.purpose,
+                                  &coin_sig->eddsa_signature,
+                                  &coin_pub->eddsa_pub))
+  {
+    report_row_inconsistency ("melt",
+                              rowid,
+                              "invalid signature for coin melt");
+    return GNUNET_OK;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Melting coin %s in denomination `%s' of value %s\n",
+              TALER_B2S (coin_pub),
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (amount_with_fee));
+
+  {
+    struct TALER_DenominationPublicKey new_dp[num_newcoins];
+    const struct TALER_EXCHANGEDB_DenominationKeyInformationP 
*new_dki[num_newcoins];
+    struct TALER_Amount refresh_cost;
+    int err;
+
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (amount_with_fee->currency,
+                                          &refresh_cost));
+
+    if (GNUNET_OK !=
+        edb->get_refresh_order (edb->cls,
+                                esession,
+                                session_hash,
+                                num_newcoins,
+                                new_dp))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+    /* Update outstanding amounts for all new coin's denominations, and check
+       that the resulting amounts are consistent with the value being 
refreshed. */
+    err = GNUNET_NO;
+    for (unsigned int i=0;i<num_newcoins;i++)
+    {
+      /* lookup new coin denomination key */
       if (GNUNET_OK !=
-          TALER_amount_add (&expenditures,
-                            &expenditures,
-                            amount_with_fee))
+          get_denomination_info (&new_dp[i],
+                                 &new_dki[i],
+                                 NULL))
       {
         GNUNET_break (0);
-        return GNUNET_SYSERR;
+        err = GNUNET_YES;
       }
-      /* Check if this deposit is within the remit of the aggregation
-         we are investigating, if so, include it in the totals. */
-      if ( (0 == memcmp (merchant_pub,
-                         &tl->details.deposit->merchant_pub,
-                         sizeof (struct TALER_MerchantPublicKeyP))) &&
-           (0 == memcmp (h_proposal_data,
-                         &tl->details.deposit->h_proposal_data,
-                         sizeof (struct GNUNET_HashCode))) )
-      {
-        struct TALER_Amount amount_without_fee;
+      GNUNET_CRYPTO_rsa_public_key_free (new_dp[i].rsa_public_key);
+      new_dp[i].rsa_public_key = NULL;
+    }
+    if (err)
+      return GNUNET_SYSERR;
 
-        if (GNUNET_OK !=
-            TALER_amount_subtract (&amount_without_fee,
-                                   amount_with_fee,
-                                   fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
-        if (GNUNET_OK !=
-            TALER_amount_add (merchant_gain,
-                              merchant_gain,
-                              &amount_without_fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
-        if (GNUNET_OK !=
-            TALER_amount_add (merchant_fees,
-                              merchant_fees,
-                              fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      amount_with_fee = &tl->details.melt->amount_with_fee;
-      fee = &tl->details.melt->melt_fee;
-      fee_dki = &dki->properties.fee_refresh;
-      if (GNUNET_OK !=
-          TALER_amount_add (&expenditures,
-                            &expenditures,
-                            amount_with_fee))
+    /* calculate total refresh cost */
+    for (unsigned int i=0;i<num_newcoins;i++)
+    {
+      /* update cost of refresh */
+      struct TALER_Amount fee;
+      struct TALER_Amount value;
+
+      TALER_amount_ntoh (&fee,
+                         &new_dki[i]->properties.fee_withdraw);
+      TALER_amount_ntoh (&value,
+                         &new_dki[i]->properties.value);
+      if ( (GNUNET_OK !=
+            TALER_amount_add (&refresh_cost,
+                              &refresh_cost,
+                              &fee)) ||
+           (GNUNET_OK !=
+            TALER_amount_add (&refresh_cost,
+                              &refresh_cost,
+                              &value)) )
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
-      break;
-    case TALER_EXCHANGEDB_TT_REFUND:
-      amount_with_fee = &tl->details.refund->refund_amount;
-      fee = &tl->details.refund->refund_fee;
-      fee_dki = &dki->properties.fee_refund;
+    }
+
+    /* compute contribution of old coin */
+    {
+      struct TALER_Amount melt_fee;
+
+      TALER_amount_ntoh (&melt_fee,
+                         &dki->properties.fee_refresh);
       if (GNUNET_OK !=
-          TALER_amount_add (&refunds,
-                            &refunds,
-                            amount_with_fee))
+          TALER_amount_subtract (&amount_without_fee,
+                                 amount_with_fee,
+                                 &melt_fee))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
+    }
+
+    /* check old coin covers complete expenses */
+    if (1 == TALER_amount_cmp (&refresh_cost,
+                               &amount_without_fee))
+    {
+      /* refresh_cost > amount_without_fee */
+      report_row_inconsistency ("melt",
+                                rowid,
+                                "refresh costs exceed value of melt");
+      return GNUNET_OK;
+    }
+
+    /* update outstanding denomination amounts */
+    for (unsigned int i=0;i<num_newcoins;i++)
+    {
+      struct DenominationSummary *dsi;
+      struct TALER_Amount value;
+
+      dsi = get_denomination_summary (cc,
+                                      new_dki[i],
+                                      &new_dki[i]->properties.denom_hash);
+      TALER_amount_ntoh (&value,
+                         &new_dki[i]->properties.value);
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Created fresh coin in denomination `%s' of value %s\n",
+                  GNUNET_h2s (&new_dki[i]->properties.denom_hash),
+                  TALER_amount2s (&value));
       if (GNUNET_OK !=
-          TALER_amount_add (&expenditures,
-                            &expenditures,
-                            fee))
+          TALER_amount_add (&dsi->denom_balance,
+                            &dsi->denom_balance,
+                            &value))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
       }
-      /* Check if this refund is within the remit of the aggregation
-         we are investigating, if so, include it in the totals. */
-      if ( (0 == memcmp (merchant_pub,
-                         &tl->details.refund->merchant_pub,
-                         sizeof (struct TALER_MerchantPublicKeyP))) &&
-           (0 == memcmp (h_proposal_data,
-                         &tl->details.refund->h_proposal_data,
-                         sizeof (struct GNUNET_HashCode))) )
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "New balance of denomination `%s' is %s\n",
+                  GNUNET_h2s (&new_dki[i]->properties.denom_hash),
+                  TALER_amount2s (&dsi->denom_balance));
+      if (GNUNET_OK !=
+          TALER_amount_add (&cc->total_denom_balance,
+                            &cc->total_denom_balance,
+                            &value))
       {
-        if (GNUNET_OK !=
-            TALER_amount_add (&merchant_loss,
-                              &merchant_loss,
-                              amount_with_fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
-        if (GNUNET_OK !=
-            TALER_amount_add (merchant_fees,
-                              merchant_fees,
-                              fee))
-        {
-          GNUNET_break (0);
-          return GNUNET_SYSERR;
-        }
+        GNUNET_break (0);
+        return GNUNET_SYSERR;
       }
-      break;
     }
+  }
 
-    /* Check that the fees given in the transaction list and in dki match */
-    TALER_amount_ntoh (&tmp,
-                       fee_dki);
-    if (0 !=
-        TALER_amount_cmp (&tmp,
-                          fee))
+  /* update old coin's denomination balance */
+  dso = get_denomination_summary (cc,
+                                  dki,
+                                  &dki->properties.denom_hash);
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&tmp,
+                             &dso->denom_balance,
+                             amount_with_fee))
+  {
+    report_emergency (dki);
+    return GNUNET_SYSERR;
+  }
+  dso->denom_balance = tmp;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "New balance of denomination `%s' after melt is %s\n",
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (&dso->denom_balance));
+
+  /* update global up melt fees */
+  {
+    struct TALER_Amount rfee;
+
+    TALER_amount_ntoh (&rfee,
+                       &dki->properties.fee_refresh);
+    if (GNUNET_OK !=
+        TALER_amount_add (&cc->melt_fee_balance,
+                          &cc->melt_fee_balance,
+                          &rfee))
     {
-      /* Disagreement in fee structure within DB, should be impossible! */
       GNUNET_break (0);
       return GNUNET_SYSERR;
     }
-  } /* for 'tl' */
+  }
 
-  /* Calculate total balance change, i.e. expenditures minus refunds */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&spent,
-                             &expenditures,
-                             &refunds))
+  /* We're good! */
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called with details about deposits that have been made,
+ * with the goal of auditing the deposit's execution.
+ *
+ * As a side-effect, #get_coin_summary will report
+ * inconsistencies in the deposited coin's balance.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the deposit in our DB
+ * @param timestamp when did the deposit happen
+ * @param merchant_pub public key of the merchant
+ * @param denom_pub denomination public key of @a coin_pub
+ * @param coin_pub public key of the coin
+ * @param coin_sig signature from the coin
+ * @param amount_with_fee amount that was deposited including fee
+ * @param h_proposal_data hash of the proposal data known to merchant and 
customer
+ * @param refund_deadline by which the merchant adviced that he might want
+ *        to get a refund
+ * @param wire_deadline by which the merchant adviced that he would like the
+ *        wire transfer to be executed
+ * @param receiver_wire_account wire details for the merchant, NULL from 
iterate_matching_deposits()
+ * @param done flag set if the deposit was already executed (or not)
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+deposit_cb (void *cls,
+            uint64_t rowid,
+            struct GNUNET_TIME_Absolute timestamp,
+            const struct TALER_MerchantPublicKeyP *merchant_pub,
+            const struct TALER_DenominationPublicKey *denom_pub,
+            const struct TALER_CoinSpendPublicKeyP *coin_pub,
+            const struct TALER_CoinSpendSignatureP *coin_sig,
+            const struct TALER_Amount *amount_with_fee,
+            const struct GNUNET_HashCode *h_proposal_data,
+            struct GNUNET_TIME_Absolute refund_deadline,
+            struct GNUNET_TIME_Absolute wire_deadline,
+            const json_t *receiver_wire_account,
+            int done)
+{
+  struct CoinContext *cc = cls;
+  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  struct DenominationSummary *ds;
+  struct TALER_DepositRequestPS dr;
+  struct TALER_Amount tmp;
+
+  if (GNUNET_OK !=
+      get_denomination_info (denom_pub,
+                             &dki,
+                             NULL))
   {
-    /* refunds above expenditures? Bad! */
-    report_coin_inconsistency (coin_pub,
-                               &expenditures,
-                               &refunds,
-                               "could not subtract refunded amount from 
expenditures");
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* Verify deposit signature */
+  dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
+  dr.purpose.size = htonl (sizeof (dr));
+  dr.h_proposal_data = *h_proposal_data;
+  if (GNUNET_OK !=
+      TALER_JSON_hash (receiver_wire_account,
+                       &dr.h_wire))
+  {
+    GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-
-  /* Now check that 'spent' is less or equal than total coin value */
-  TALER_amount_ntoh (&value,
-                     &dki->properties.value);
-  if (1 == TALER_amount_cmp (&spent,
-                             &value))
+  dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+  dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+  TALER_amount_hton (&dr.amount_with_fee,
+                     amount_with_fee);
+  dr.deposit_fee = dki->properties.fee_deposit;
+  dr.merchant = *merchant_pub;
+  dr.coin_pub = *coin_pub;
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
+                                  &dr.purpose,
+                                  &coin_sig->eddsa_signature,
+                                  &coin_pub->eddsa_pub))
   {
-    /* spent > value */
-    report_coin_inconsistency (coin_pub,
-                               &spent,
-                               &value,
-                               "accepted deposits (minus refunds) exceeds 
denomination value");
-    return GNUNET_SYSERR;
+    report_row_inconsistency ("deposit",
+                              rowid,
+                              "invalid signature for coin deposit");
+    return GNUNET_OK;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Deposited coin %s in denomination `%s' of value %s\n",
+              TALER_B2S (coin_pub),
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (amount_with_fee));
 
-  /* Finally, update @a merchant_gain by subtracting what he "lost" from 
refunds */
+  /* update old coin's denomination balance */
+  ds = get_denomination_summary (cc,
+                                 dki,
+                                 &dki->properties.denom_hash);
   if (GNUNET_SYSERR ==
-      TALER_amount_subtract (merchant_gain,
-                             merchant_gain,
-                             &merchant_loss))
+      TALER_amount_subtract (&tmp,
+                             &ds->denom_balance,
+                             amount_with_fee))
   {
-    /* refunds above deposits? Bad! */
-    report_coin_inconsistency (coin_pub,
-                               merchant_gain,
-                               &merchant_loss,
-                               "merchant was granted more refunds than he 
deposited");
+    report_emergency (dki);
     return GNUNET_SYSERR;
   }
+  ds->denom_balance = tmp;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "New balance of denomination `%s' after deposit is %s\n",
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (&ds->denom_balance));
+
+  /* update global up melt fees */
+  {
+    struct TALER_Amount dfee;
+
+    TALER_amount_ntoh (&dfee,
+                       &dki->properties.fee_deposit);
+    if (GNUNET_OK !=
+        TALER_amount_add (&cc->deposit_fee_balance,
+                          &cc->deposit_fee_balance,
+                          &dfee))
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+  }
+
   return GNUNET_OK;
 }
 
 
 /**
- * Function called with the results of the lookup of the
- * transaction data associated with a wire transfer identifier.
+ * Function called with details about coins that were refunding,
+ * with the goal of auditing the refund's execution.  Adds the
+ * refunded amount back to the outstanding balance of the respective
+ * denomination.
  *
- * @param cls a `struct WireCheckContext`
- * @param rowid which row in the table is the information from (for 
diagnostics)
- * @param merchant_pub public key of the merchant (should be same for all 
callbacks with the same @e cls)
- * @param wire_method which wire plugin was used for the transfer?
- * @param h_wire hash of wire transfer details of the merchant (should be same 
for all callbacks with the same @e cls)
- * @param exec_time execution time of the wire transfer (should be same for 
all callbacks with the same @e cls)
- * @param h_proposal_data which proposal was this payment about
- * @param coin_pub which public key was this payment about
- * @param coin_value amount contributed by this coin in total (with fee)
- * @param coin_fee applicable fee for this coin
+ * As a side-effect, #get_coin_summary will report
+ * inconsistencies in the refunded coin's balance.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the refund in our DB
+ * @param denom_pub denomination public key of @a coin_pub
+ * @param coin_pub public key of the coin
+ * @param merchant_pub public key of the merchant
+ * @param merchant_sig signature of the merchant
+ * @param h_proposal_data hash of the proposal data known to merchant and 
customer
+ * @param rtransaction_id refund transaction ID chosen by the merchant
+ * @param amount_with_fee amount that was deposited including fee
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
-static void
-wire_transfer_information_cb (void *cls,
-                              uint64_t rowid,
-                              const struct TALER_MerchantPublicKeyP 
*merchant_pub,
-                              const char *wire_method,
-                              const struct GNUNET_HashCode *h_wire,
-                              struct GNUNET_TIME_Absolute exec_time,
-                              const struct GNUNET_HashCode *h_proposal_data,
-                              const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                              const struct TALER_Amount *coin_value,
-                              const struct TALER_Amount *coin_fee)
+static int
+refund_cb (void *cls,
+           uint64_t rowid,
+           const struct TALER_DenominationPublicKey *denom_pub,
+           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+           const struct TALER_MerchantPublicKeyP *merchant_pub,
+           const struct TALER_MerchantSignatureP *merchant_sig,
+           const struct GNUNET_HashCode *h_proposal_data,
+           uint64_t rtransaction_id,
+           const struct TALER_Amount *amount_with_fee)
 {
-  struct WireCheckContext *wcc = cls;
+  struct CoinContext *cc = cls;
   const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_Amount contribution;
-  struct TALER_Amount computed_value;
-  struct TALER_Amount computed_fees;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  const struct TALER_CoinPublicInfo *coin;
-
-  /* Obtain coin's transaction history */
-  tl = edb->get_coin_transactions (edb->cls,
-                                   esession,
-                                   coin_pub);
-  if (NULL == tl)
-  {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "no transaction history for coin claimed in 
aggregation");
-    return;
-  }
+  struct DenominationSummary *ds;
+  struct TALER_RefundRequestPS rr;
+  struct TALER_Amount amount_without_fee;
+  struct TALER_Amount refund_fee;
 
-  /* Obtain general denomination information about the coin */
-  coin = NULL;
-  switch (tl->type)
-  {
-  case TALER_EXCHANGEDB_TT_DEPOSIT:
-    coin = &tl->details.deposit->coin;
-    break;
-  case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-    coin = &tl->details.melt->coin;
-    break;
-  case TALER_EXCHANGEDB_TT_REFUND:
-    coin = &tl->details.refund->coin;
-    break;
-  }
-  GNUNET_assert (NULL != coin); /* hard check that switch worked */
   if (GNUNET_OK !=
-      get_denomination_info (&coin->denom_pub,
+      get_denomination_info (denom_pub,
                              &dki,
                              NULL))
   {
-    /* This should be impossible from database constraints */
     GNUNET_break (0);
-    edb->free_coin_transaction_list (edb->cls,
-                                     tl);
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "could not find denomination key for coin 
claimed in aggregation");
-    return;
+    return GNUNET_SYSERR;
   }
 
-  /* Check transaction history to see if it supports aggregate valuation */
-  check_transaction_history (coin_pub,
-                             h_proposal_data,
-                             merchant_pub,
-                             dki,
-                             tl,
-                             &computed_value,
-                             &computed_fees);
-  if (0 !=
-      TALER_amount_cmp (&computed_value,
-                        coin_value))
-  {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "coin transaction history and aggregation 
disagree about coin's contribution");
-  }
-  if (0 !=
-      TALER_amount_cmp (&computed_fees,
-                        coin_fee))
+  /* verify refund signature */
+  rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
+  rr.purpose.size = htonl (sizeof (rr));
+  rr.h_proposal_data = *h_proposal_data;
+  rr.coin_pub = *coin_pub;
+  rr.merchant = *merchant_pub;
+  rr.rtransaction_id = GNUNET_htonll (rtransaction_id);
+  TALER_amount_hton (&rr.refund_amount,
+                     amount_with_fee);
+  rr.refund_fee = dki->properties.fee_refund;
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
+                                  &rr.purpose,
+                                  &merchant_sig->eddsa_sig,
+                                  &merchant_pub->eddsa_pub))
   {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
+    report_row_inconsistency ("refund",
                               rowid,
-                              "coin transaction history and aggregation 
disagree about applicable fees");
+                              "invalid signature for refund");
+    return GNUNET_OK;
   }
-  edb->free_coin_transaction_list (edb->cls,
-                                   tl);
 
-  /* Check other details of wire transfer match */
-  if (0 != strcmp (wire_method,
-                   wcc->method))
-  {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "wire method of aggregate do not match wire 
transfer");
-  }
-  if (0 != memcmp (h_wire,
-                   &wcc->h_wire,
-                   sizeof (struct GNUNET_HashCode)))
+  TALER_amount_ntoh (&refund_fee,
+                     &dki->properties.fee_refund);
+  if (GNUNET_OK !=
+      TALER_amount_subtract (&amount_without_fee,
+                             amount_with_fee,
+                             &refund_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
+    report_row_inconsistency ("refund",
                               rowid,
-                              "account details of aggregate do not match 
account details of wire transfer");
-    return;
+                              "refunded amount smaller than refund fee");
+    return GNUNET_OK;
   }
-  if (exec_time.abs_value_us != wcc->date.abs_value_us)
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Refunding coin %s in denomination `%s' value %s\n",
+              TALER_B2S (coin_pub),
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (amount_with_fee));
+
+  /* update coin's denomination balance */
+  ds = get_denomination_summary (cc,
+                                 dki,
+                                 &dki->properties.denom_hash);
+  if (GNUNET_OK !=
+      TALER_amount_add (&ds->denom_balance,
+                        &ds->denom_balance,
+                        &amount_without_fee))
   {
-    /* This should be impossible from database constraints */
     GNUNET_break (0);
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "date given in aggregate does not match wire 
transfer date");
-    return;
+    return GNUNET_SYSERR;
   }
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&contribution,
-                             coin_value,
-                             coin_fee))
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "New balance of denomination `%s' after refund is %s\n",
+              GNUNET_h2s (&dki->properties.denom_hash),
+              TALER_amount2s (&ds->denom_balance));
+
+  /* update total refund fee balance */
+  if (GNUNET_OK !=
+      TALER_amount_add (&cc->refund_fee_balance,
+                        &cc->refund_fee_balance,
+                        &refund_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
-    report_row_inconsistency ("aggregation",
-                              rowid,
-                              "could not calculate contribution of coin");
-    return;
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
 
-  /* Add coin's contribution to total aggregate value */
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_add (&wcc->total_deposits,
-                                   &wcc->total_deposits,
-                                   &contribution));
+  return GNUNET_OK;
 }
 
 
 /**
- * Check that a wire transfer made by the exchange is valid
- * (has matching deposits).
+ * Analyze the exchange's processing of coins.
  *
- * @param cls a `struct AggregationContext`
- * @param rowid identifier of the respective row in the database
- * @param date timestamp of the wire transfer (roughly)
- * @param wtid wire transfer subject
- * @param wire wire transfer details of the receiver
- * @param amount amount that was wired
+ * @param cls closure
+ * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
  */
-static void
-check_wire_out_cb (void *cls,
-                   uint64_t rowid,
-                   struct GNUNET_TIME_Absolute date,
-                   const struct TALER_WireTransferIdentifierRawP *wtid,
-                   const json_t *wire,
-                   const struct TALER_Amount *amount)
+static int
+analyze_coins (void *cls)
 {
-  struct AggregationContext *ac = cls;
-  struct WireCheckContext wcc;
-  json_t *method;
-  struct TALER_WIRE_Plugin *plugin;
+  struct CoinContext cc;
+  int dret;
 
-  wcc.ac = ac;
-  method = json_object_get (wire,
-                            "type");
-  if ( (NULL == method) ||
-       (! json_is_string (method)) )
+  pp.last_reserve_out_serial_id = 0; // HACK! FIXME!
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Analyzing coins\n");
+  /* setup 'cc' */
+  cc.ret = GNUNET_OK;
+  cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
+                                                           GNUNET_NO);
+  dret = adb->get_balance_summary (adb->cls,
+                                   asession,
+                                   &master_pub,
+                                   &cc.total_denom_balance,
+                                   &cc.deposit_fee_balance,
+                                   &cc.melt_fee_balance,
+                                   &cc.refund_fee_balance,
+                                   &cc.risk);
+  if (GNUNET_SYSERR == dret)
   {
-    report_row_inconsistency ("wire_out",
-                              rowid,
-                              "specified wire address lacks type");
-    return;
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
-  wcc.method = json_string_value (method);
-  wcc.ok = GNUNET_OK;
-  wcc.date = date;
-  TALER_amount_get_zero (amount->currency,
-                         &wcc.total_deposits);
-  TALER_JSON_hash (wire,
-                   &wcc.h_wire);
-  edb->lookup_wire_transfer (edb->cls,
-                             esession,
-                             wtid,
-                             &wire_transfer_information_cb,
-                             &wcc);
-  if (GNUNET_OK != wcc.ok)
+  if (GNUNET_NO == dret)
   {
-    report_row_inconsistency ("wire_out",
-                              rowid,
-                              "audit of associated transactions failed");
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (currency,
+                                          &cc.total_denom_balance));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (currency,
+                                          &cc.deposit_fee_balance));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (currency,
+                                          &cc.melt_fee_balance));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (currency,
+                                          &cc.refund_fee_balance));
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_get_zero (currency,
+                                          &cc.risk));
   }
-  plugin = get_wire_plugin (ac,
-                            wcc.method);
-  if (NULL == plugin)
+
+  /* process withdrawals */
+  if (GNUNET_SYSERR ==
+      edb->select_reserves_out_above_serial_id (edb->cls,
+                                                esession,
+                                                pp.last_reserve_out_serial_id,
+                                                &withdraw_cb,
+                                                &cc))
   {
-    report_row_inconsistency ("wire_out",
-                              rowid,
-                              "could not load required wire plugin to 
validate");
-    return;
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
+
+  /* process refunds */
   if (GNUNET_SYSERR ==
-      plugin->amount_round (plugin->cls,
-                            &wcc.total_deposits))
+      edb->select_refunds_above_serial_id (edb->cls,
+                                           esession,
+                                           pp.last_refund_serial_id,
+                                           &refund_cb,
+                                           &cc))
   {
-    report_row_minor_inconsistency ("wire_out",
-                                    rowid,
-                                    "wire plugin failed to round given 
amount");
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
-  if (0 != TALER_amount_cmp (amount,
-                             &wcc.total_deposits))
+
+  /* process refreshs */
+  if (GNUNET_SYSERR ==
+      edb->select_refreshs_above_serial_id (edb->cls,
+                                            esession,
+                                            pp.last_melt_serial_id,
+                                            &refresh_session_cb,
+                                            &cc))
   {
-    report_wire_out_inconsistency (wire,
-                                   rowid,
-                                   &wcc.total_deposits,
-                                   amount,
-                                   "computed amount inconsistent with wire 
amount");
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
-}
-
-
-/**
- * Analyze the exchange aggregator's payment processing.
- *
- * @param cls closure
- * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
- */
-static int
-analyze_aggregations (void *cls)
-{
-  struct AggregationContext ac;
-  struct WirePlugin *wc;
-  int ret;
 
-  ret = GNUNET_OK;
-  ac.wire_head = NULL;
-  ac.wire_tail = NULL;
+  /* process deposits */
   if (GNUNET_SYSERR ==
-      edb->select_wire_out_above_serial_id (edb->cls,
+      edb->select_deposits_above_serial_id (edb->cls,
                                             esession,
-                                            pp.last_wire_out_serial_id,
-                                            &check_wire_out_cb,
-                                            &ac))
+                                            pp.last_deposit_serial_id,
+                                            &deposit_cb,
+                                            &cc))
   {
     GNUNET_break (0);
-    ret = GNUNET_SYSERR;
+    return GNUNET_SYSERR;
   }
-  while (NULL != (wc = ac.wire_head))
+
+  /* sync 'cc' back to disk */
+  GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
+                                         &sync_denomination,
+                                         &cc);
+  GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
+
+  if (GNUNET_YES == dret)
+      dret = adb->update_balance_summary (adb->cls,
+                                          asession,
+                                          &master_pub,
+                                          &cc.total_denom_balance,
+                                          &cc.deposit_fee_balance,
+                                          &cc.melt_fee_balance,
+                                          &cc.refund_fee_balance,
+                                          &cc.risk);
+  else
+    dret = adb->insert_balance_summary (adb->cls,
+                                        asession,
+                                        &master_pub,
+                                        &cc.total_denom_balance,
+                                        &cc.deposit_fee_balance,
+                                        &cc.melt_fee_balance,
+                                        &cc.refund_fee_balance,
+                                        &cc.risk);
+  report_denomination_balance (&cc.total_denom_balance,
+                               &cc.risk,
+                               &cc.deposit_fee_balance,
+                               &cc.melt_fee_balance,
+                               &cc.refund_fee_balance);
+  if (GNUNET_OK != dret)
   {
-    GNUNET_CONTAINER_DLL_remove (ac.wire_head,
-                                 ac.wire_tail,
-                                 wc);
-    TALER_WIRE_plugin_unload (wc->plugin);
-    GNUNET_free (wc->type);
-    GNUNET_free (wc);
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
   }
-  return ret;
+
+  return cc.ret;
 }
 
 
@@ -2559,28 +2742,18 @@ incremental_processing (Analysis analysis,
                         void *analysis_cls)
 {
   int ret;
+  int have_pp;
 
-  if (! restart)
-  {
-    ret = adb->get_auditor_progress (adb->cls,
-                                     asession,
-                                     &master_pub,
-                                     &pp);
-  }
-  else
-  {
-    ret = GNUNET_NO;
-    GNUNET_break (GNUNET_OK ==
-                  adb->drop_tables (adb->cls));
-    GNUNET_break (GNUNET_OK ==
-                  adb->create_tables (adb->cls));
-  }
-  if (GNUNET_SYSERR == ret)
+  have_pp = adb->get_auditor_progress (adb->cls,
+                                       asession,
+                                       &master_pub,
+                                       &pp);
+  if (GNUNET_SYSERR == have_pp)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_NO == ret)
+  if (GNUNET_NO == have_pp)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
                 _("First analysis using this auditor, starting audit from 
scratch\n"));
@@ -2588,7 +2761,7 @@ incremental_processing (Analysis analysis,
   else
   {
     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
-                _("Resuming audit at %llu/%llu/%llu/%llu/%llu/%llu\n\n"),
+                _("Resuming audit at %llu/%llu/%llu/%llu/%llu/%llu\n"),
                 (unsigned long long) pp.last_reserve_in_serial_id,
                 (unsigned long long) pp.last_reserve_out_serial_id,
                 (unsigned long long) pp.last_deposit_serial_id,
@@ -2603,17 +2776,23 @@ incremental_processing (Analysis analysis,
                 "Analysis phase failed, not recording progress\n");
     return GNUNET_SYSERR;
   }
-  ret = adb->update_auditor_progress (adb->cls,
-                                      asession,
-                                      &master_pub,
-                                      &pp);
+  if (GNUNET_YES == have_pp)
+    ret = adb->update_auditor_progress (adb->cls,
+                                        asession,
+                                        &master_pub,
+                                        &pp);
+  else
+    ret = adb->insert_auditor_progress (adb->cls,
+                                        asession,
+                                        &master_pub,
+                                        &pp);
   if (GNUNET_OK != ret)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
-              _("Resuming audit at %llu/%llu/%llu/%llu/%llu/%llu\n\n"),
+              _("Concluded audit step at %llu/%llu/%llu/%llu/%llu/%llu\n\n"),
               (unsigned long long) pp.last_reserve_in_serial_id,
               (unsigned long long) pp.last_reserve_out_serial_id,
               (unsigned long long) pp.last_deposit_serial_id,
@@ -2717,10 +2896,10 @@ setup_sessions_and_run ()
 
   transact (&analyze_reserves,
             NULL);
-  transact (&analyze_coins,
-            NULL);
   transact (&analyze_aggregations,
             NULL);
+  transact (&analyze_coins,
+            NULL);
 }
 
 
@@ -2738,6 +2917,8 @@ run (void *cls,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle *c)
 {
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Launching auditor\n");
   cfg = c;
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_string (cfg,
@@ -2768,7 +2949,31 @@ run (void *cls,
     TALER_EXCHANGEDB_plugin_unload (edb);
     return;
   }
+  if (restart)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Full audit restart requested, dropping old audit data.\n");
+    GNUNET_break (GNUNET_OK ==
+                  adb->drop_tables (adb->cls));
+    TALER_AUDITORDB_plugin_unload (adb);
+    if (NULL ==
+        (adb = TALER_AUDITORDB_plugin_load (cfg)))
+    {
+      fprintf (stderr,
+               "Failed to initialize auditor database plugin after drop.\n");
+      global_ret = 1;
+      TALER_EXCHANGEDB_plugin_unload (edb);
+      return;
+    }
+    GNUNET_break (GNUNET_OK ==
+                  adb->create_tables (adb->cls));
+  }
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Starting audit\n");
   setup_sessions_and_run ();
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Audit complete\n");
   TALER_AUDITORDB_plugin_unload (adb);
   TALER_EXCHANGEDB_plugin_unload (edb);
 }
diff --git a/src/auditordb/plugin_auditordb_postgres.c 
b/src/auditordb/plugin_auditordb_postgres.c
index 73ec92d..74dff92 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -26,13 +26,17 @@
 #include <pthread.h>
 #include <libpq-fe.h>
 
+
+#define LOG(kind,...) GNUNET_log_from (kind, "taler-auditordb-postgres", 
__VA_ARGS__)
+
+
 /**
  * Log a query error.
  *
  * @param result PQ result object of the query that failed
  */
 #define QUERY_ERR(result)                          \
-  GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s (%s)\n", 
__FILE__, __LINE__, PQresultErrorMessage (result), PQresStatus (PQresultStatus 
(result)))
+  LOG (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s (%s)\n", __FILE__, 
__LINE__, PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)))
 
 
 /**
@@ -42,7 +46,7 @@
  */
 #define BREAK_DB_ERR(result) do { \
     GNUNET_break (0); \
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s (%s)\n", 
PQresultErrorMessage (result), PQresStatus (PQresultStatus (result))); \
+    LOG (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s (%s)\n", 
PQresultErrorMessage (result), PQresStatus (PQresultStatus (result))); \
   } while (0)
 
 
@@ -152,10 +156,9 @@ static void
 pq_notice_processor_cb (void *arg,
                         const char *message)
 {
-  GNUNET_log_from (GNUNET_ERROR_TYPE_INFO,
-                   "pq",
-                   "%s",
-                   message);
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "%s",
+       message);
 }
 
 
@@ -205,10 +208,30 @@ postgres_drop_tables (void *cls)
   conn = connect_to_postgres (pc);
   if (NULL == conn)
     return GNUNET_SYSERR;
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Dropping ALL tables\n");
+  LOG (GNUNET_ERROR_TYPE_INFO,
+       "Dropping ALL tables\n");
+  /* TODO: we probably need a bit more fine-grained control
+     over drops for the '-r' option of taler-auditor; also,
+     for the testcase, we currently fail to drop the
+     auditor_denominations table... */
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS predicted_result;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS historic_ledger;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS historic_losses;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS historic_denomination_revenue;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS balance_summary;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS denomination_pending;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS auditor_reserve_balance;");
+  SQLEXEC_ (conn,
+            "DROP TABLE IF EXISTS auditor_reserves;");
   SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS test;");
+            "DROP TABLE IF EXISTS auditor_progress;");
   PQfinish (conn);
   return GNUNET_OK;
  SQLEXEC_fail:
@@ -944,7 +967,7 @@ postgres_start (void *cls,
       PQresultStatus (result))
   {
     TALER_LOG_ERROR ("Failed to start transaction: %s\n",
-               PQresultErrorMessage (result));
+                     PQresultErrorMessage (result));
     GNUNET_break (0);
     PQclear (result);
     return GNUNET_SYSERR;
@@ -1016,9 +1039,9 @@ postgres_commit (void *cls,
       PQclear (result);
       return GNUNET_NO;
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Database commit failure: %s\n",
-                sqlstate);
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         "Database commit failure: %s\n",
+         sqlstate);
     PQclear (result);
     return GNUNET_SYSERR;
   }
@@ -1175,8 +1198,8 @@ postgres_select_denomination_info (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_select_denomination_info() returned 0 matching 
rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_select_denomination_info() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -1357,8 +1380,8 @@ postgres_get_auditor_progress (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_auditor_progress() returned 0 matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_auditor_progress() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -1574,8 +1597,8 @@ postgres_get_reserve_info (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_reserve_info() returned 0 matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_reserve_info() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -1732,8 +1755,8 @@ postgres_get_reserve_summary (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_reserve_summary() returned 0 matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_reserve_summary() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -1882,8 +1905,8 @@ postgres_get_denomination_balance (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_denomination_balance() returned 0 matching 
rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_denomination_balance() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -2068,8 +2091,8 @@ postgres_get_balance_summary (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_balance_summary() returned 0 matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_balance_summary() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -2183,8 +2206,8 @@ postgres_select_historic_denom_revenue (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_select_historic_denom_revenue() returned 0 matching 
rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_select_historic_denom_revenue() returned 0 matching 
rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -2315,8 +2338,8 @@ postgres_select_historic_losses (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_select_historic_losses() returned 0 matching 
rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_select_historic_losses() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -2444,8 +2467,8 @@ postgres_select_historic_reserve_revenue (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_select_historic_reserve_revenue() returned 0 
matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_select_historic_reserve_revenue() returned 0 matching 
rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
@@ -2605,8 +2628,8 @@ postgres_get_predicted_balance (void *cls,
   int nrows = PQntuples (result);
   if (0 == nrows)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "postgres_get_predicted_balance() returned 0 matching rows\n");
+    LOG (GNUNET_ERROR_TYPE_DEBUG,
+         "postgres_get_predicted_balance() returned 0 matching rows\n");
     PQclear (result);
     return GNUNET_NO;
   }
diff --git a/src/include/taler_amount_lib.h b/src/include/taler_amount_lib.h
index e6c36fe..ef323f8 100644
--- a/src/include/taler_amount_lib.h
+++ b/src/include/taler_amount_lib.h
@@ -297,6 +297,18 @@ TALER_amount_normalize (struct TALER_Amount *amount);
 char *
 TALER_amount_to_string (const struct TALER_Amount *amount);
 
+
+/**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return statically allocated buffer with string representation,
+ *         NULL if the @a amount was invalid
+ */
+const char *
+TALER_amount2s (const struct TALER_Amount *amount);
+
+
 #if 0                           /* keep Emacsens' auto-indent happy */
 {
 #endif
diff --git a/src/util/amount.c b/src/util/amount.c
index 44eefe6..e066485 100644
--- a/src/util/amount.c
+++ b/src/util/amount.c
@@ -529,9 +529,9 @@ char *
 TALER_amount_to_string (const struct TALER_Amount *amount)
 {
   char *result;
+  unsigned int i;
   uint32_t n;
   char tail[TALER_AMOUNT_FRAC_LEN + 1];
-  unsigned int i;
   struct TALER_Amount norm;
 
   if (GNUNET_YES != TALER_amount_is_valid (amount))
@@ -565,6 +565,54 @@ TALER_amount_to_string (const struct TALER_Amount *amount)
 
 
 /**
+ * Convert amount to string.
+ *
+ * @param amount amount to convert to string
+ * @return statically allocated buffer with string representation,
+ *         NULL if the @a amount was invalid
+ */
+const char *
+TALER_amount2s (const struct TALER_Amount *amount)
+{
+  static char result[TALER_AMOUNT_FRAC_LEN + TALER_CURRENCY_LEN + 3 + 12];
+  unsigned int i;
+  uint32_t n;
+  char tail[TALER_AMOUNT_FRAC_LEN + 1];
+  struct TALER_Amount norm;
+
+  if (GNUNET_YES != TALER_amount_is_valid (amount))
+    return NULL;
+  norm = *amount;
+  GNUNET_break (GNUNET_SYSERR !=
+                TALER_amount_normalize (&norm));
+  if (0 != (n = norm.fraction))
+  {
+    for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
+    {
+      tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
+      n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
+    }
+    tail[i] = '\0';
+    GNUNET_snprintf (result,
+                     sizeof (result),
+                     "%s:%llu.%s",
+                     norm.currency,
+                     (unsigned long long) norm.value,
+                     tail);
+  }
+  else
+  {
+    GNUNET_snprintf (result,
+                     sizeof (result),
+                     "%s:%llu",
+                     norm.currency,
+                     (unsigned long long) norm.value);
+  }
+  return result;
+}
+
+
+/**
  * Divide an amount by a float.  Note that this function
  * may introduce a rounding error!
  *

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]