gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 01/02: purse auditor helper theoretically complete (but


From: gnunet
Subject: [taler-exchange] 01/02: purse auditor helper theoretically complete (but DB logic still missing)
Date: Tue, 01 Nov 2022 16:44:06 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit 22357678a088836aca5cd2c7e78c2c6702b6092c
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Tue Nov 1 15:15:35 2022 +0100

    purse auditor helper theoretically complete (but DB logic still missing)
---
 src/auditor/.gitignore                             |   1 +
 src/auditor/taler-helper-auditor-purses.c          | 676 ++++++++++++++++-----
 src/exchange/taler-exchange-httpd_purses_create.c  |  18 +-
 src/exchange/taler-exchange-httpd_purses_deposit.c |   2 +
 src/exchange/taler-exchange-httpd_purses_get.c     |   2 +
 src/exchange/taler-exchange-httpd_purses_merge.c   |  18 +-
 src/exchange/taler-exchange-httpd_reserves_purse.c |   2 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 158 ++++-
 src/include/taler_auditordb_plugin.h               |  55 +-
 src/include/taler_crypto_lib.h                     |   4 +-
 src/include/taler_exchangedb_plugin.h              |  89 ++-
 src/util/wallet_signatures.c                       |   4 +-
 12 files changed, 834 insertions(+), 195 deletions(-)

diff --git a/src/auditor/.gitignore b/src/auditor/.gitignore
index 59088a1b..963062c1 100644
--- a/src/auditor/.gitignore
+++ b/src/auditor/.gitignore
@@ -23,3 +23,4 @@ auditor-basedb.sqlite3
 taler-auditor-test.sqlite3
 libeufin-nexus.pid
 libeufin-sandbox.pid
+taler-helper-auditor-purses
diff --git a/src/auditor/taler-helper-auditor-purses.c 
b/src/auditor/taler-helper-auditor-purses.c
index cfc345eb..6a101ee4 100644
--- a/src/auditor/taler-helper-auditor-purses.c
+++ b/src/auditor/taler-helper-auditor-purses.c
@@ -68,6 +68,11 @@ static json_t 
*report_purse_balance_insufficient_inconsistencies;
  */
 static struct TALER_Amount total_balance_insufficient_loss;
 
+/**
+ * Total amount purse decisions are delayed past deadline.
+ */
+static struct TALER_Amount total_delayed_decisions;
+
 /**
  * Array of reports about purses's not being closed inconsitencies.
  */
@@ -199,6 +204,54 @@ report_row_inconsistency (const char *table,
 }
 
 
+/**
+ * Obtain the purse fee for a purse created at @a time.
+ *
+ * @param atime when was the purse created
+ * @param[out] fee set to the purse fee
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+get_purse_fee (struct GNUNET_TIME_Timestamp atime,
+               struct TALER_Amount *fee)
+{
+  struct TALER_MasterSignatureP master_sig;
+  struct GNUNET_TIME_Timestamp start_date;
+  struct GNUNET_TIME_Timestamp end_date;
+  struct TALER_GlobalFeeSet fees;
+  struct GNUNET_TIME_Relative ptimeout;
+  struct GNUNET_TIME_Relative ktimeout;
+  struct GNUNET_TIME_Relative hexp;
+  uint32_t pacl;
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      TALER_ARL_edb->get_global_fee (TALER_ARL_edb->cls,
+                                     atime,
+                                     &start_date,
+                                     &end_date,
+                                     &fees,
+                                     &ptimeout,
+                                     &ktimeout,
+                                     &hexp,
+                                     &pacl,
+                                     &master_sig))
+  {
+    char *diag;
+
+    GNUNET_asprintf (&diag,
+                     "purse fee unavailable at %s\n",
+                     GNUNET_TIME_timestamp2s (atime));
+    report_row_inconsistency ("purse-fee",
+                              atime.abs_time.abs_value_us,
+                              diag);
+    GNUNET_free (diag);
+    return GNUNET_SYSERR;
+  }
+  *fee = fees.purse;
+  return GNUNET_OK;
+}
+
+
 /* ***************************** Analyze purses ************************ */
 /* This logic checks the purses_requests, purse_deposits,
    purse_refunds, purse_merges and account_merges */
@@ -215,17 +268,37 @@ struct PurseSummary
   struct TALER_PurseContractPublicKeyP purse_pub;
 
   /**
-   * Balance of the purse from deposits (excludes
-   * deposit fees).
-   * Updated only in #handle_purse_deposits().
+   * Balance of the purse from deposits (includes purse fee, excludes deposit
+   * fees), as calculated by auditor.
    */
   struct TALER_Amount balance;
 
   /**
-   * Expected value of the purse.
+   * Expected value of the purse, excludes purse fee.
    */
   struct TALER_Amount total_value;
 
+  /**
+   * Purse balance according to exchange DB.
+   */
+  struct TALER_Amount exchange_balance;
+
+  /**
+   * Contract terms of the purse.
+   */
+  struct TALER_PrivateContractHashP h_contract_terms;
+
+  /**
+   * Merge timestamp (as per exchange DB).
+   */
+  struct GNUNET_TIME_Timestamp merge_timestamp;
+
+  /**
+   * Purse creation date.  This is when the merge
+   * fee is applied.
+   */
+  struct GNUNET_TIME_Timestamp creation_date;
+
   /**
    * Purse expiration date.
    */
@@ -328,6 +401,21 @@ setup_purse (struct PurseContext *pc,
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_set_zero (TALER_ARL_currency,
                                         &ps->balance));
+  /* get purse meta-data from exchange DB */
+  qs = TALER_ARL_edb->select_purse (TALER_ARL_edb->cls,
+                                    purse_pub,
+                                    &ps->creation_date,
+                                    &ps->expiration_date,
+                                    &ps->total_value,
+                                    &ps->exchange_balance,
+                                    &ps->h_contract_terms,
+                                    &ps->merge_timestamp);
+  if (0 >= qs)
+  {
+    GNUNET_free (ps);
+    pc->qs = qs;
+    return NULL;
+  }
   if (0 > (qs = load_auditor_purse_summary (ps)))
   {
     GNUNET_free (ps);
@@ -344,33 +432,76 @@ setup_purse (struct PurseContext *pc,
 
 
 /**
- * Check that the purse summary matches what the exchange database
- * thinks about the purse, and update our own state of the purse.
- *
- * Remove all purses that we are happy with from the DB.
+ * Function called on purse requests.
  *
- * @param cls our `struct PurseContext`
- * @param key hash of the purse public key
- * @param value a `struct PurseSummary`
- * @return #GNUNET_OK to process more entries
- */
+ * @param cls closure
+ * @param purse_pub public key of the purse
+ * @param merge_pub public key representing the merge capability
+ * @param purse_expiration when would an unmerged purse expire
+ * @param h_contract_terms contract associated with the purse
+ * @param age_limit the age limit for deposits into the purse
+ * @param target_amount amount to be put into the purse
+ * @param purse_sig signature of the purse over the initialization data
+ * @return #GNUNET_OK to continue to iterate
+   */
 static enum GNUNET_GenericReturnValue
-verify_purse_balance (void *cls,
-                      const struct GNUNET_HashCode *key,
-                      void *value)
+handle_purse_requested (
+  void *cls,
+  uint64_t rowid,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_PurseMergePublicKeyP *merge_pub,
+  struct GNUNET_TIME_Timestamp purse_creation,
+  struct GNUNET_TIME_Timestamp purse_expiration,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
+  uint32_t age_limit,
+  const struct TALER_Amount *target_amount,
+  const struct TALER_PurseContractSignatureP *purse_sig)
 {
   struct PurseContext *pc = cls;
-  struct PurseSummary *ps = value;
-  enum GNUNET_GenericReturnValue ret;
+  struct PurseSummary *ps;
+  struct GNUNET_HashCode key;
 
-  ret = GNUNET_OK;
-  // FIXME: implement!
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_remove (pc->purses,
-                                                       key,
-                                                       ps));
-  GNUNET_free (ps);
-  return ret;
+  if (GNUNET_OK !=
+      TALER_wallet_purse_create_verify (purse_expiration,
+                                        h_contract_terms,
+                                        merge_pub,
+                                        age_limit,
+                                        target_amount,
+                                        purse_pub,
+                                        purse_sig))
+  {
+    TALER_ARL_report (report_bad_sig_losses,
+                      GNUNET_JSON_PACK (
+                        GNUNET_JSON_pack_string ("operation",
+                                                 "purse-reqeust"),
+                        GNUNET_JSON_pack_uint64 ("row",
+                                                 rowid),
+                        TALER_JSON_pack_amount ("loss",
+                                                target_amount),
+                        GNUNET_JSON_pack_data_auto ("key_pub",
+                                                    purse_pub)));
+    TALER_ARL_amount_add (&total_bad_sig_loss,
+                          &total_bad_sig_loss,
+                          target_amount);
+  }
+  GNUNET_CRYPTO_hash (purse_pub,
+                      sizeof (*purse_pub),
+                      &key);
+  ps = GNUNET_new (struct PurseSummary);
+  ps->purse_pub = *purse_pub;
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (TALER_ARL_currency,
+                                        &ps->balance));
+  ps->creation_date = purse_creation;
+  ps->expiration_date = purse_expiration;
+  ps->total_value = *target_amount;
+  ps->h_contract_terms = *h_contract_terms;
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CONTAINER_multihashmap_put (pc->purses,
+                                                    &key,
+                                                    ps,
+                                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  return GNUNET_OK;
 }
 
 
@@ -401,14 +532,12 @@ handle_purse_deposits (
   const struct TALER_DenominationPublicKey *denom_pub)
 {
   struct PurseContext *pc = cls;
+  struct TALER_Amount amount_minus_fee;
+  struct PurseSummary *ps;
   const char *base_url
     = (NULL == deposit->exchange_base_url)
     ? TALER_ARL_exchange_url
     : deposit->exchange_base_url;
-  enum GNUNET_DB_QueryStatus qs;
-  struct TALER_Amount amount_minus_fee;
-  struct TALER_Amount new_balance;
-  struct PurseSummary *rs;
   struct TALER_DenominationHashP h_denom_pub;
 
   /* should be monotonically increasing */
@@ -471,68 +600,42 @@ handle_purse_deposits (
     return GNUNET_OK;
   }
 
-  TALER_ARL_amount_add (&new_balance,
-                        auditor_balance,
-                        &amount_minus_fee);
-  qs = TALER_ARL_edb->set_purse_balance (TALER_ARL_edb->cls,
-                                         &deposit->purse_pub,
-                                         &new_balance);
-  GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
-  if (qs < 0)
-  {
-    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
-    pc->qs = qs;
-    return GNUNET_SYSERR;
-  }
-  if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE !=
-      (flags & TALER_WAMF_MERGE_MODE_MASK))
-  {
-    /* This just created the purse, actual credit to
-       the reserve will be done in handle_account_merged() */
-    return GNUNET_OK;
-  }
-  if ( (NULL != deposit->exchange_base_url) &&
-       (0 != strcmp (deposit->exchange_base_url,
-                     TALER_ARL_exchange_url)) )
-  {
-    /* credited reserve is at another exchange, do NOT credit here! */
-    return GNUNET_OK;
-  }
-
-  rs = setup_purse (pc,
+  ps = setup_purse (pc,
                     &deposit->purse_pub);
-  if (NULL == rs)
+  if (NULL == ps)
   {
-    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs)
+    {
+      report_row_inconsistency ("purse-deposit",
+                                rowid,
+                                "purse not found");
+    }
+    else
+    {
+      /* Database trouble!? */
+      GNUNET_break (0);
+    }
     return GNUNET_SYSERR;
   }
-  if ( (-1 != TALER_amount_cmp (&new_balance,
-                                purse_total)) &&
-       (-1 == TALER_amount_cmp (auditor_balance,
-                                purse_total)) )
-  {
-    /* new balance at or above purse_total
-       (and previous balance was below); thus
-       credit reserve with purse value! */
-    TALER_ARL_amount_add (&rs->balance,
-                          &rs->balance,
-                          purse_total);
-  }
+  TALER_ARL_amount_add (&ps->balance,
+                        &ps->balance,
+                        &amount_minus_fee);
+  TALER_ARL_amount_add (&balance.balance,
+                        &balance.balance,
+                        &amount_minus_fee);
   return GNUNET_OK;
 }
 
 
 /**
- * Function called with details about purse
- * merges that have been made, with
+ * Function called with details about purse merges that have been made, with
  * the goal of auditing the purse merge execution.
  *
  * @param cls closure
  * @param rowid unique serial ID for the deposit in our DB
  * @param partner_base_url where is the reserve, NULL for this exchange
  * @param amount total amount expected in the purse
- * @param balance current balance in the purse (according to the auditor)
+ * @param balance current balance in the purse
  * @param flags purse flags
  * @param merge_pub merge capability key
  * @param reserve_pub reserve the merge affects
@@ -557,97 +660,76 @@ handle_purse_merged (
 {
   struct PurseContext *pc = cls;
   struct PurseSummary *ps;
-  char *reserve_url;
 
   /* should be monotonically increasing */
   GNUNET_assert (rowid >= ppp.last_purse_merge_serial_id);
   ppp.last_purse_merge_serial_id = rowid + 1;
-  reserve_url
-    = TALER_reserve_make_payto (NULL == partner_base_url
+
+  {
+    char *reserve_url;
+
+    reserve_url
+      = TALER_reserve_make_payto (NULL == partner_base_url
                                 ? TALER_ARL_exchange_url
                                 : partner_base_url,
-                                reserve_pub);
-  if (GNUNET_OK !=
-      TALER_wallet_purse_merge_verify (reserve_url,
-                                       merge_timestamp,
-                                       purse_pub,
-                                       merge_pub,
-                                       merge_sig))
-  {
+                                  reserve_pub);
+    if (GNUNET_OK !=
+        TALER_wallet_purse_merge_verify (reserve_url,
+                                         merge_timestamp,
+                                         purse_pub,
+                                         merge_pub,
+                                         merge_sig))
+    {
+      GNUNET_free (reserve_url);
+      TALER_ARL_report (report_bad_sig_losses,
+                        GNUNET_JSON_PACK (
+                          GNUNET_JSON_pack_string ("operation",
+                                                   "merge-purse"),
+                          GNUNET_JSON_pack_uint64 ("row",
+                                                   rowid),
+                          TALER_JSON_pack_amount ("loss",
+                                                  amount),
+                          GNUNET_JSON_pack_data_auto ("key_pub",
+                                                      merge_pub)));
+      TALER_ARL_amount_add (&total_bad_sig_loss,
+                            &total_bad_sig_loss,
+                            amount);
+      return GNUNET_OK;
+    }
     GNUNET_free (reserve_url);
-    TALER_ARL_report (report_bad_sig_losses,
-                      GNUNET_JSON_PACK (
-                        GNUNET_JSON_pack_string ("operation",
-                                                 "merge-purse"),
-                        GNUNET_JSON_pack_uint64 ("row",
-                                                 rowid),
-                        TALER_JSON_pack_amount ("loss",
-                                                amount),
-                        GNUNET_JSON_pack_data_auto ("key_pub",
-                                                    merge_pub)));
-    TALER_ARL_amount_add (&total_bad_sig_loss,
-                          &total_bad_sig_loss,
-                          amount);
-    return GNUNET_OK;
-  }
-  GNUNET_free (reserve_url);
-  if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE ==
-      (flags & TALER_WAMF_MERGE_MODE_MASK))
-  {
-    /* This just created the purse, actual credit to
-       the reserve will be done in handle_purse_deposits() */
-    return GNUNET_OK;
-  }
-  if ( (NULL != partner_base_url) &&
-       (0 != strcmp (partner_base_url,
-                     TALER_ARL_exchange_url)) )
-  {
-    /* credited reserve is at another exchange, do NOT credit here! */
-    return GNUNET_OK;
   }
+
   ps = setup_purse (pc,
                     purse_pub);
   if (NULL == ps)
   {
-    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs)
+    {
+      report_row_inconsistency ("purse-merge",
+                                rowid,
+                                "purse not found");
+    }
+    else
+    {
+      /* Database trouble!? */
+      GNUNET_break (0);
+    }
     return GNUNET_SYSERR;
   }
-  if (-1 == TALER_amount_cmp (balance,
-                              amount))
-  {
-    struct TALER_Amount loss;
-
-    TALER_ARL_amount_subtract (&loss,
-                               amount,
-                               balance);
-    /* illegal merge, balance is still below total purse value */
-    TALER_ARL_report (report_purse_balance_insufficient_inconsistencies,
-                      GNUNET_JSON_PACK (
-                        GNUNET_JSON_pack_string ("operation",
-                                                 "merge-purse"),
-                        GNUNET_JSON_pack_uint64 ("row",
-                                                 rowid),
-                        TALER_JSON_pack_amount ("loss",
-                                                &loss),
-                        GNUNET_JSON_pack_data_auto ("purse_pub",
-                                                    purse_pub)));
-    TALER_ARL_amount_add (&total_balance_insufficient_loss,
-                          &total_balance_insufficient_loss,
-                          &loss);
-    return GNUNET_OK;
-  }
+  GNUNET_break (0 ==
+                GNUNET_TIME_timestamp_cmp (merge_timestamp,
+                                           ==,
+                                           ps->merge_timestamp));
   TALER_ARL_amount_add (&ps->balance,
                         &ps->balance,
                         amount);
-  // ps->a_expiration_date = FIXME: do we care? If so, set to what (so that 
the auditor no longer complains about the reserve not being closed)
   return GNUNET_OK;
 }
 
 
 /**
- * Function called with details about
- * account merge requests that have been made, with
- * the goal of auditing the account merge execution.
+ * Function called with details about account merge requests that have been
+ * made, with the goal of auditing the account merge execution.
  *
  * @param cls closure
  * @param rowid unique serial ID for the deposit in our DB
@@ -715,20 +797,265 @@ handle_account_merged (
                     purse_pub);
   if (NULL == ps)
   {
-    GNUNET_break (0);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs)
+    {
+      report_row_inconsistency ("account-merge",
+                                rowid,
+                                "purse not found");
+    }
+    else
+    {
+      /* Database trouble!? */
+      GNUNET_break (0);
+    }
     return GNUNET_SYSERR;
   }
-
   TALER_ARL_amount_add (&balance.balance,
                         &balance.balance,
                         purse_fee);
   TALER_ARL_amount_add (&ps->balance,
                         &ps->balance,
-                        amount);
+                        purse_fee);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called with details about purse decisions that have been made.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the deposit in our DB
+ * @param purse_pub which purse was the decision made on
+ * @param refunded true if decision was to refund
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static enum GNUNET_GenericReturnValue
+handle_purse_decision (
+  void *cls,
+  uint64_t rowid,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  bool refunded)
+{
+  struct PurseContext *pc = cls;
+  struct PurseSummary *ps;
+  struct GNUNET_HashCode key;
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_Amount purse_fee;
+  struct TALER_Amount balance_without_purse_fee;
+
+  ps = setup_purse (pc,
+                    purse_pub);
+  if (NULL == ps)
+  {
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs)
+    {
+      report_row_inconsistency ("purse-decision",
+                                rowid,
+                                "purse not found");
+    }
+    else
+    {
+      /* Database trouble!? */
+      GNUNET_break (0);
+    }
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      get_purse_fee (ps->creation_date,
+                     &purse_fee))
+  {
+    report_row_inconsistency ("purse-request",
+                              rowid,
+                              "purse fee unavailable");
+  }
+  if (0 >
+      TALER_amount_subtract (&balance_without_purse_fee,
+                             &ps->balance,
+                             &purse_fee))
+  {
+    report_row_inconsistency ("purse-request",
+                              rowid,
+                              "purse fee higher than balance");
+    TALER_amount_set_zero (TALER_ARL_currency,
+                           &balance_without_purse_fee);
+  }
+
+  if (refunded)
+  {
+    if (-1 != TALER_amount_cmp (&balance_without_purse_fee,
+                                &ps->total_value))
+    {
+      report_amount_arithmetic_inconsistency ("purse-decision: refund",
+                                              rowid,
+                                              &balance_without_purse_fee,
+                                              &ps->total_value,
+                                              0);
+    }
+  }
+  else
+  {
+    if (-1 == TALER_amount_cmp (&balance_without_purse_fee,
+                                &ps->total_value))
+    {
+      report_amount_arithmetic_inconsistency ("purse-decision: merge",
+                                              rowid,
+                                              &ps->total_value,
+                                              &balance_without_purse_fee,
+                                              0);
+      TALER_ARL_amount_add (&total_balance_insufficient_loss,
+                            &total_balance_insufficient_loss,
+                            &ps->total_value);
+    }
+  }
+
+  qs = TALER_ARL_adb->delete_purse_info (TALER_ARL_adb->cls,
+                                         purse_pub,
+                                         &TALER_ARL_master_pub);
+  GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+    pc->qs = qs;
+    return GNUNET_SYSERR;
+  }
+  GNUNET_CRYPTO_hash (purse_pub,
+                      sizeof (*purse_pub),
+                      &key);
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (pc->purses,
+                                                       &key,
+                                                       ps));
+  GNUNET_free (ps);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called on expired purses.
+ *
+ * @param cls closure
+ * @param purse_pub public key of the purse
+ * @param balance amount of money in the purse
+ * @param expiration_date when did the purse expire?
+ * @return #GNUNET_OK to continue to iterate
+ */
+static enum GNUNET_GenericReturnValue
+handle_purse_expired (
+  void *cls,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_Amount *balance,
+  struct GNUNET_TIME_Timestamp expiration_date)
+{
+  struct PurseContext *pc = cls;
+
+  (void) pc;
+  TALER_ARL_report (report_purse_not_closed_inconsistencies,
+                    GNUNET_JSON_PACK (
+                      GNUNET_JSON_pack_data_auto ("purse_pub",
+                                                  purse_pub),
+                      TALER_JSON_pack_amount ("balance",
+                                              balance),
+                      TALER_JSON_pack_time_abs_human ("expired",
+                                                      
expiration_date.abs_time)));
+  TALER_ARL_amount_add (&total_delayed_decisions,
+                        &total_delayed_decisions,
+                        balance);
   return GNUNET_OK;
 }
 
 
+/**
+ * Check that the purse summary matches what the exchange database
+ * thinks about the purse, and update our own state of the purse.
+ *
+ * Remove all purses that we are happy with from the DB.
+ *
+ * @param cls our `struct PurseContext`
+ * @param key hash of the purse public key
+ * @param value a `struct PurseSummary`
+ * @return #GNUNET_OK to process more entries
+ */
+static enum GNUNET_GenericReturnValue
+verify_purse_balance (void *cls,
+                      const struct GNUNET_HashCode *key,
+                      void *value)
+{
+  struct PurseContext *pc = cls;
+  struct PurseSummary *ps = value;
+  enum GNUNET_GenericReturnValue ret;
+  enum GNUNET_DB_QueryStatus qs;
+
+  ret = GNUNET_OK;
+  if (internal_checks)
+  {
+    struct TALER_Amount pf;
+    struct TALER_Amount balance_without_purse_fee;
+
+    /* subtract purse fee from ps->balance to get actual balance we expect, as
+       we track the balance including purse fee, while the exchange subtracts
+       the purse fee early on. */
+    if (GNUNET_OK !=
+        get_purse_fee (ps->creation_date,
+                       &pf))
+    {
+      GNUNET_break (0);
+      report_row_inconsistency ("purse",
+                                0,
+                                "purse fee unavailable");
+    }
+    if (0 >
+        TALER_amount_subtract (&balance_without_purse_fee,
+                               &ps->balance,
+                               &pf))
+    {
+      report_row_inconsistency ("purse",
+                                0,
+                                "purse fee higher than balance");
+      TALER_amount_set_zero (TALER_ARL_currency,
+                             &balance_without_purse_fee);
+    }
+
+    if (0 != TALER_amount_cmp (&ps->exchange_balance,
+                               &balance_without_purse_fee))
+    {
+      report_amount_arithmetic_inconsistency ("purse-balance",
+                                              0,
+                                              &ps->exchange_balance,
+                                              &balance_without_purse_fee,
+                                              0);
+    }
+  }
+
+  if (ps->had_pi)
+    qs = TALER_ARL_adb->update_purse_info (TALER_ARL_adb->cls,
+                                           &ps->purse_pub,
+                                           &TALER_ARL_master_pub,
+                                           &ps->balance);
+  else
+    qs = TALER_ARL_adb->insert_purse_info (TALER_ARL_adb->cls,
+                                           &ps->purse_pub,
+                                           &TALER_ARL_master_pub,
+                                           &ps->balance,
+                                           ps->expiration_date);
+  GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+    pc->qs = qs;
+    return GNUNET_SYSERR;
+  }
+
+  GNUNET_assert (GNUNET_YES ==
+                 GNUNET_CONTAINER_multihashmap_remove (pc->purses,
+                                                       key,
+                                                       ps));
+  GNUNET_free (ps);
+  return ret;
+}
+
+
 /**
  * Analyze purses for being well-formed.
  *
@@ -762,9 +1089,10 @@ analyze_purses (void *cls)
   else
   {
     ppp_start = ppp;
-    // FIXME: add other values!
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Resuming purse audit at %llu/%llu/%llu\n",
+                "Resuming purse audit at %llu/%llu/%llu/%llu/%llu\n",
+                (unsigned long long) ppp.last_purse_request_serial_id,
+                (unsigned long long) ppp.last_purse_decision_serial_id,
                 (unsigned long long) ppp.last_purse_merge_serial_id,
                 (unsigned long long) ppp.last_purse_deposits_serial_id,
                 (unsigned long long) ppp.last_account_merge_serial_id);
@@ -780,6 +1108,18 @@ analyze_purses (void *cls)
   }
   pc.purses = GNUNET_CONTAINER_multihashmap_create (512,
                                                     GNUNET_NO);
+
+  qs = TALER_ARL_edb->select_purse_requests_above_serial_id (
+    TALER_ARL_edb->cls,
+    ppp.last_purse_request_serial_id,
+    &handle_purse_requested,
+    &pc);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+
   qs = TALER_ARL_edb->select_purse_merges_above_serial_id (
     TALER_ARL_edb->cls,
     ppp.last_purse_merge_serial_id,
@@ -812,6 +1152,28 @@ analyze_purses (void *cls)
     return qs;
   }
 
+  qs = TALER_ARL_edb->select_all_purse_decisions_above_serial_id (
+    TALER_ARL_edb->cls,
+    ppp.last_purse_decision_serial_id,
+    &handle_purse_decision,
+    &pc);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+
+  qs = TALER_ARL_adb->select_purse_expired (
+    TALER_ARL_adb->cls,
+    &TALER_ARL_master_pub,
+    &handle_purse_expired,
+    &pc);
+  if (qs < 0)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+
   GNUNET_CONTAINER_multihashmap_iterate (pc.purses,
                                          &verify_purse_balance,
                                          &pc);
@@ -853,7 +1215,9 @@ analyze_purses (void *cls)
     return qs;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Concluded purse audit step at %llu/%llu/%llu\n",
+              "Concluded purse audit step at %llu/%llu/%llu/%llu/%llu\n",
+              (unsigned long long) ppp.last_purse_request_serial_id,
+              (unsigned long long) ppp.last_purse_decision_serial_id,
               (unsigned long long) ppp.last_purse_merge_serial_id,
               (unsigned long long) ppp.last_purse_deposits_serial_id,
               (unsigned long long) ppp.last_account_merge_serial_id);
@@ -889,6 +1253,12 @@ run (void *cls,
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_set_zero (TALER_ARL_currency,
                                         &balance.balance));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (TALER_ARL_currency,
+                                        &total_balance_insufficient_loss));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (TALER_ARL_currency,
+                                        &total_delayed_decisions));
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_set_zero (TALER_ARL_currency,
                                         &total_arithmetic_delta_plus));
@@ -927,6 +1297,8 @@ run (void *cls,
       /* Globals (REVIEW!) */
       TALER_JSON_pack_amount ("total_balance_insufficient",
                               &total_balance_insufficient_loss),
+      TALER_JSON_pack_amount ("total_delayed_purse_decisions",
+                              &total_delayed_decisions),
       GNUNET_JSON_pack_array_steal (
         "purse_balance_insufficient_inconsistencies",
         report_purse_balance_insufficient_inconsistencies),
diff --git a/src/exchange/taler-exchange-httpd_purses_create.c 
b/src/exchange/taler-exchange-httpd_purses_create.c
index 2bccd11f..f3da2c85 100644
--- a/src/exchange/taler-exchange-httpd_purses_create.c
+++ b/src/exchange/taler-exchange-httpd_purses_create.c
@@ -156,15 +156,15 @@ create_transaction (void *cls,
     uint32_t min_age;
 
     TEH_plugin->rollback (TEH_plugin->cls);
-    qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
-                                           &pcc->pd.purse_pub,
-                                           &merge_pub,
-                                           &purse_expiration,
-                                           &h_contract_terms,
-                                           &min_age,
-                                           &target_amount,
-                                           &balance,
-                                           &purse_sig);
+    qs = TEH_plugin->get_purse_request (TEH_plugin->cls,
+                                        &pcc->pd.purse_pub,
+                                        &merge_pub,
+                                        &purse_expiration,
+                                        &h_contract_terms,
+                                        &min_age,
+                                        &target_amount,
+                                        &balance,
+                                        &purse_sig);
     if (qs < 0)
     {
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
diff --git a/src/exchange/taler-exchange-httpd_purses_deposit.c 
b/src/exchange/taler-exchange-httpd_purses_deposit.c
index 581abe90..dc1a256d 100644
--- a/src/exchange/taler-exchange-httpd_purses_deposit.c
+++ b/src/exchange/taler-exchange-httpd_purses_deposit.c
@@ -359,11 +359,13 @@ TEH_handler_purses_deposit (
 
   {
     enum GNUNET_DB_QueryStatus qs;
+    struct GNUNET_TIME_Timestamp create_timestamp;
     struct GNUNET_TIME_Timestamp merge_timestamp;
 
     qs = TEH_plugin->select_purse (
       TEH_plugin->cls,
       pcc.purse_pub,
+      &create_timestamp,
       &pcc.purse_expiration,
       &pcc.amount,
       &pcc.deposit_total,
diff --git a/src/exchange/taler-exchange-httpd_purses_get.c 
b/src/exchange/taler-exchange-httpd_purses_get.c
index 3261ed34..8384086b 100644
--- a/src/exchange/taler-exchange-httpd_purses_get.c
+++ b/src/exchange/taler-exchange-httpd_purses_get.c
@@ -303,9 +303,11 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc,
 
   {
     enum GNUNET_DB_QueryStatus qs;
+    struct GNUNET_TIME_Timestamp create_timestamp;
 
     qs = TEH_plugin->select_purse (TEH_plugin->cls,
                                    &gc->purse_pub,
+                                   &create_timestamp,
                                    &gc->purse_expiration,
                                    &gc->amount,
                                    &gc->deposited,
diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c 
b/src/exchange/taler-exchange-httpd_purses_merge.c
index c1582948..0bb8a186 100644
--- a/src/exchange/taler-exchange-httpd_purses_merge.c
+++ b/src/exchange/taler-exchange-httpd_purses_merge.c
@@ -426,15 +426,15 @@ TEH_handler_purses_merge (
   }
 
   /* Fetch purse details */
-  qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
-                                         pcc.purse_pub,
-                                         &pcc.merge_pub,
-                                         &pcc.purse_expiration,
-                                         &pcc.h_contract_terms,
-                                         &pcc.min_age,
-                                         &pcc.target_amount,
-                                         &pcc.balance,
-                                         &purse_sig);
+  qs = TEH_plugin->get_purse_request (TEH_plugin->cls,
+                                      pcc.purse_pub,
+                                      &pcc.merge_pub,
+                                      &pcc.purse_expiration,
+                                      &pcc.h_contract_terms,
+                                      &pcc.min_age,
+                                      &pcc.target_amount,
+                                      &pcc.balance,
+                                      &purse_sig);
   switch (qs)
   {
   case GNUNET_DB_STATUS_HARD_ERROR:
diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c 
b/src/exchange/taler-exchange-httpd_reserves_purse.c
index 79625a39..cc3ffeb1 100644
--- a/src/exchange/taler-exchange-httpd_reserves_purse.c
+++ b/src/exchange/taler-exchange-httpd_reserves_purse.c
@@ -252,7 +252,7 @@ purse_transaction (void *cls,
       uint32_t min_age;
 
       TEH_plugin->rollback (TEH_plugin->cls);
-      qs = TEH_plugin->select_purse_request (
+      qs = TEH_plugin->get_purse_request (
         TEH_plugin->cls,
         &rpc->pd.purse_pub,
         &merge_pub,
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index c4957c91..5ccb7112 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2259,6 +2259,7 @@ prepare_statements (struct PostgresClosure *pg)
       "select_purse",
       "SELECT "
       " merge_pub"
+      ",purse_creation"
       ",purse_expiration"
       ",h_contract_terms"
       ",amount_with_fee_val"
@@ -2269,9 +2270,9 @@ prepare_statements (struct PostgresClosure *pg)
       " FROM purse_requests"
       " LEFT JOIN purse_merges USING (purse_pub)"
       " WHERE purse_pub=$1;"),
-    /* Used in #postgres_select_purse_request */
+    /* Used in #postgres_get_purse_request */
     GNUNET_PQ_make_prepare (
-      "select_purse_request",
+      "get_purse_request",
       "SELECT "
       " merge_pub"
       ",purse_expiration"
@@ -8001,6 +8002,127 @@ postgres_select_purse_decisions_above_serial_id (
 }
 
 
+/**
+ * Closure for #all_purse_decision_serial_helper_cb().
+ */
+struct AllPurseDecisionSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_AllPurseDecisionCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Plugin context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */
+  enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct PurseRefundSerialContext`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+all_purse_decision_serial_helper_cb (void *cls,
+                                     PGresult *result,
+                                     unsigned int num_results)
+{
+  struct AllPurseDecisionSerialContext *dsc = cls;
+
+  for (unsigned int i = 0; i<num_results; i++)
+  {
+    struct TALER_PurseContractPublicKeyP purse_pub;
+    bool refunded;
+    uint64_t rowid;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
+                                            &purse_pub),
+      GNUNET_PQ_result_spec_bool ("refunded",
+                                  &refunded),
+      GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id",
+                                    &rowid),
+      GNUNET_PQ_result_spec_end
+    };
+    enum GNUNET_GenericReturnValue ret;
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      dsc->status = GNUNET_SYSERR;
+      return;
+    }
+    ret = dsc->cb (dsc->cb_cls,
+                   rowid,
+                   &purse_pub,
+                   refunded);
+    GNUNET_PQ_cleanup_result (rs);
+    if (GNUNET_OK != ret)
+      break;
+  }
+}
+
+
+/**
+ * Select purse decisions above @a serial_id in monotonically increasing
+ * order.
+ *
+ * @param cls closure
+ * @param serial_id highest serial ID to exclude (select strictly larger)
+ * @param cb function to call on each result
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_all_purse_decisions_above_serial_id (
+  void *cls,
+  uint64_t serial_id,
+  TALER_EXCHANGEDB_AllPurseDecisionCallback cb,
+  void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&serial_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct AllPurseDecisionSerialContext dsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             
"audit_get_all_purse_decision_incr",
+                                             params,
+                                             &
+                                             
all_purse_decision_serial_helper_cb,
+                                             &dsc);
+  if (GNUNET_OK != dsc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
 /**
  * Closure for #purse_refund_coin_helper_cb().
  */
@@ -11355,7 +11477,7 @@ postgres_insert_contract (
  * @return transaction status code
  */
 static enum GNUNET_DB_QueryStatus
-postgres_select_purse_request (
+postgres_get_purse_request (
   void *cls,
   const struct TALER_PurseContractPublicKeyP *purse_pub,
   struct TALER_PurseMergePublicKeyP *merge_pub,
@@ -11389,7 +11511,7 @@ postgres_select_purse_request (
     GNUNET_PQ_result_spec_end
   };
   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "select_purse_request",
+                                                   "get_purse_request",
                                                    params,
                                                    rs);
 }
@@ -11464,15 +11586,15 @@ postgres_insert_purse_request (
     struct TALER_Amount balance;
     struct TALER_PurseContractSignatureP purse_sig2;
 
-    qs = postgres_select_purse_request (pg,
-                                        purse_pub,
-                                        &merge_pub2,
-                                        &purse_expiration2,
-                                        &h_contract_terms2,
-                                        &age_limit2,
-                                        &amount2,
-                                        &balance,
-                                        &purse_sig2);
+    qs = postgres_get_purse_request (pg,
+                                     purse_pub,
+                                     &merge_pub2,
+                                     &purse_expiration2,
+                                     &h_contract_terms2,
+                                     &age_limit2,
+                                     &amount2,
+                                     &balance,
+                                     &purse_sig2);
     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     {
       GNUNET_break (0);
@@ -11542,6 +11664,7 @@ postgres_expire_purse (
  *
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param purse_pub public key of the new purse
+ * @param[out] purse_creation set to time when the purse was created
  * @param[out] purse_expiration set to time when the purse will expire
  * @param[out] amount set to target amount (with fees) to be put into the purse
  * @param[out] deposited set to actual amount put into the purse so far
@@ -11553,6 +11676,7 @@ static enum GNUNET_DB_QueryStatus
 postgres_select_purse (
   void *cls,
   const struct TALER_PurseContractPublicKeyP *purse_pub,
+  struct GNUNET_TIME_Timestamp *purse_creation,
   struct GNUNET_TIME_Timestamp *purse_expiration,
   struct TALER_Amount *amount,
   struct TALER_Amount *deposited,
@@ -11567,6 +11691,8 @@ postgres_select_purse (
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_timestamp ("purse_expiration",
                                      purse_expiration),
+    GNUNET_PQ_result_spec_timestamp ("purse_creation",
+                                     purse_creation),
     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
                                  amount),
     TALER_PQ_RESULT_SPEC_AMOUNT ("balance",
@@ -13042,6 +13168,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &postgres_select_history_requests_above_serial_id;
   plugin->select_purse_decisions_above_serial_id
     = &postgres_select_purse_decisions_above_serial_id;
+  plugin->select_all_purse_decisions_above_serial_id
+    = &postgres_select_all_purse_decisions_above_serial_id;
   plugin->select_purse_deposits_by_purse
     = &postgres_select_purse_deposits_by_purse;
   plugin->select_refreshes_above_serial_id
@@ -13136,8 +13264,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
     = &postgres_select_contract_by_purse;
   plugin->insert_purse_request
     = &postgres_insert_purse_request;
-  plugin->select_purse_request
-    = &postgres_select_purse_request;
+  plugin->get_purse_request
+    = &postgres_get_purse_request;
   plugin->expire_purse
     = &postgres_expire_purse;
   plugin->select_purse
diff --git a/src/include/taler_auditordb_plugin.h 
b/src/include/taler_auditordb_plugin.h
index 129b5171..709a956c 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -604,6 +604,23 @@ typedef enum GNUNET_GenericReturnValue
   const struct TALER_AUDITORDB_DepositConfirmation *dc);
 
 
+/**
+ * Function called on expired purses.
+ *
+ * @param cls closure
+ * @param purse_pub public key of the purse
+ * @param balance amount of money in the purse
+ * @param expiration_date when did the purse expire?
+ * @return #GNUNET_OK to continue to iterate
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TALER_AUDITORDB_ExpiredPurseCallback)(
+  void *cls,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_Amount *balance,
+  struct GNUNET_TIME_Timestamp expiration_date);
+
+
 /**
  * @brief The plugin API, returned from the plugin's "init" function.
  * The argument given to "init" is simply a configuration handle.
@@ -1220,7 +1237,7 @@ struct TALER_AUDITORDB_Plugin
    * @param purse_pub public key of the purse
    * @param master_pub master public key of the exchange
    * @param balance balance of the purse
-   * @param expiration_date expiration date of the reserve
+   * @param expiration_date expiration date of the purse
    * @return transaction status code
    */
   enum GNUNET_DB_QueryStatus
@@ -1245,7 +1262,7 @@ struct TALER_AUDITORDB_Plugin
   enum GNUNET_DB_QueryStatus
   (*update_purse_info)(
     void *cls,
-    const struct TALER_ReservePublicKeyP *reserve_pub,
+    const struct TALER_PurseContractPublicKeyP *purse_pub,
     const struct TALER_MasterPublicKeyP *master_pub,
     const struct TALER_Amount *balance);
 
@@ -1254,7 +1271,7 @@ struct TALER_AUDITORDB_Plugin
    * Get information about a purse.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
-   * @param reserve_pub public key of the reserve
+   * @param purse_pub public key of the purse
    * @param master_pub master public key of the exchange
    * @param[out] rowid which row did we get the information from
    * @param[out] balance set to balance of the purse
@@ -1271,6 +1288,38 @@ struct TALER_AUDITORDB_Plugin
     struct GNUNET_TIME_Timestamp *expiration_date);
 
 
+  /**
+   * Delete information about a purse.
+   *
+   * @param cls the @e cls of this struct with the plugin-specific state
+   * @param purse_pub public key of the reserve
+   * @param master_pub master public key of the exchange
+   * @return transaction status code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*delete_purse_info)(
+    void *cls,
+    const struct TALER_PurseContractPublicKeyP *purse_pub,
+    const struct TALER_MasterPublicKeyP *master_pub);
+
+
+  /**
+   * Get information about expired purses.
+   *
+   * @param cls the @e cls of this struct with the plugin-specific state
+   * @param master_pub master public key of the exchange
+   * @param cb function to call on expired purses
+   * @param cb_cls closure for @a cb
+   * @return transaction status code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_purse_expired)(
+    void *cls,
+    const struct TALER_MasterPublicKeyP *master_pub,
+    TALER_AUDITORDB_ExpiredPurseCallback cb,
+    void *cb_cls);
+
+
   /**
    * Delete information about a purse.
    *
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index df992c6e..2e85f6d9 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -2771,7 +2771,7 @@ TALER_CRYPTO_helper_esign_disconnect (
 void
 TALER_wallet_purse_create_sign (
   struct GNUNET_TIME_Timestamp purse_expiration,
-  struct TALER_PrivateContractHashP *h_contract_terms,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_PurseMergePublicKeyP *merge_pub,
   uint32_t min_age,
   const struct TALER_Amount *amount,
@@ -2794,7 +2794,7 @@ TALER_wallet_purse_create_sign (
 enum GNUNET_GenericReturnValue
 TALER_wallet_purse_create_verify (
   struct GNUNET_TIME_Timestamp purse_expiration,
-  struct TALER_PrivateContractHashP *h_contract_terms,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_PurseMergePublicKeyP *merge_pub,
   uint32_t min_age,
   const struct TALER_Amount *amount,
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 4965a27b..de4babfd 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2356,7 +2356,7 @@ typedef enum GNUNET_GenericReturnValue
  * @param cls closure
  * @param rowid unique serial ID for the deposit in our DB
  * @param purse_pub public key of the purse
- * @param reserve_pub public key of the target reserve, NULL if not known
+ * @param reserve_pub public key of the target reserve, NULL if not known / 
refunded
  * @param purse_value what is the (target) value of the purse
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
@@ -2369,6 +2369,24 @@ typedef enum GNUNET_GenericReturnValue
   const struct TALER_Amount *purse_value);
 
 
+/**
+ * Function called with details about purse decisions that have been made, with
+ * the goal of auditing the purse's execution.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the deposit in our DB
+ * @param purse_pub public key of the purse
+ * @param refunded true if decision was to refund
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+typedef enum GNUNET_GenericReturnValue
+(*TALER_EXCHANGEDB_AllPurseDecisionCallback)(
+  void *cls,
+  uint64_t rowid,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  bool refunded);
+
+
 /**
  * Function called with details about purse refunds that have been made, with
  * the goal of auditing the purse refund's execution.
@@ -2993,6 +3011,34 @@ typedef void
   bool done);
 
 
+/**
+ * Function called on purse requests.
+ *
+ * @param cls closure
+ * @param purse_pub public key of the purse
+ * @param merge_pub public key representing the merge capability
+ * @param purse_creation when was the purse created?
+ * @param purse_expiration when would an unmerged purse expire
+ * @param h_contract_terms contract associated with the purse
+ * @param age_limit the age limit for deposits into the purse
+ * @param target_amount amount to be put into the purse
+ * @param purse_sig signature of the purse over the initialization data
+ * @return #GNUNET_OK to continue to iterate
+   */
+typedef enum GNUNET_GenericReturnValue
+(*TALER_EXCHANGEDB_PurseRequestCallback)(
+  void *cls,
+  uint64_t rowid,
+  const struct TALER_PurseContractPublicKeyP *purse_pub,
+  const struct TALER_PurseMergePublicKeyP *merge_pub,
+  struct GNUNET_TIME_Timestamp purse_creation,
+  struct GNUNET_TIME_Timestamp purse_expiration,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
+  uint32_t age_limit,
+  const struct TALER_Amount *target_amount,
+  const struct TALER_PurseContractSignatureP *purse_sig);
+
+
 /**
  * Function called with information about the exchange's denomination keys.
  * Note that the 'master' field in @a issue will not yet be initialized when
@@ -4594,6 +4640,25 @@ struct TALER_EXCHANGEDB_Plugin
                                      TALER_EXCHANGEDB_DepositCallback cb,
                                      void *cb_cls);
 
+
+  /**
+   * Function called to return meta data about a purses
+   * above a certain serial ID.
+   *
+   * @param cls the @e cls of this struct with the plugin-specific state
+   * @param serial_id number to select requests by
+   * @param cb function to call on each request
+   * @param cb_cls closure for @a cb
+   * @return transaction status code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_purse_requests_above_serial_id)(
+    void *cls,
+    uint64_t serial_id,
+    TALER_EXCHANGEDB_PurseRequestCallback cb,
+    void *cb_cls);
+
+
   /**
    * Select purse deposits above @a serial_id in monotonically increasing
    * order.
@@ -4686,6 +4751,24 @@ struct TALER_EXCHANGEDB_Plugin
     void *cb_cls);
 
 
+  /**
+   * Select all purse refunds above @a serial_id in monotonically increasing
+   * order.
+   *
+   * @param cls closure
+   * @param serial_id highest serial ID to exclude (select strictly larger)
+   * @param cb function to call on each result
+   * @param cb_cls closure for @a cb
+   * @return transaction status code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_all_purse_decisions_above_serial_id)(
+    void *cls,
+    uint64_t serial_id,
+    TALER_EXCHANGEDB_AllPurseDecisionCallback cb,
+    void *cb_cls);
+
+
   /**
    * Select coins deposited into a purse.
    *
@@ -5647,6 +5730,7 @@ struct TALER_EXCHANGEDB_Plugin
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param purse_pub public key of the new purse
+   * @param[out] purse_creation set to time when the purse was created
    * @param[out] purse_expiration set to time when the purse will expire
    * @param[out] amount set to target amount (with fees) to be put into the 
purse
    * @param[out] deposited set to actual amount put into the purse so far
@@ -5658,6 +5742,7 @@ struct TALER_EXCHANGEDB_Plugin
   (*select_purse)(
     void *cls,
     const struct TALER_PurseContractPublicKeyP *purse_pub,
+    struct GNUNET_TIME_Timestamp *purse_creation,
     struct GNUNET_TIME_Timestamp *purse_expiration,
     struct TALER_Amount *amount,
     struct TALER_Amount *deposited,
@@ -5681,7 +5766,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @return transaction status code
    */
   enum GNUNET_DB_QueryStatus
-  (*select_purse_request)(
+  (*get_purse_request)(
     void *cls,
     const struct TALER_PurseContractPublicKeyP *purse_pub,
     struct TALER_PurseMergePublicKeyP *merge_pub,
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 19291439..6c8124d0 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -821,7 +821,7 @@ GNUNET_NETWORK_STRUCT_END
 void
 TALER_wallet_purse_create_sign (
   struct GNUNET_TIME_Timestamp purse_expiration,
-  struct TALER_PrivateContractHashP *h_contract_terms,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_PurseMergePublicKeyP *merge_pub,
   uint32_t min_age,
   const struct TALER_Amount *amount,
@@ -848,7 +848,7 @@ TALER_wallet_purse_create_sign (
 enum GNUNET_GenericReturnValue
 TALER_wallet_purse_create_verify (
   struct GNUNET_TIME_Timestamp purse_expiration,
-  struct TALER_PrivateContractHashP *h_contract_terms,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
   const struct TALER_PurseMergePublicKeyP *merge_pub,
   uint32_t min_age,
   const struct TALER_Amount *amount,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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