aboutsummaryrefslogtreecommitdiff
path: root/src/auditor/taler-helper-auditor-wire-debit.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/auditor/taler-helper-auditor-wire-debit.c')
-rw-r--r--src/auditor/taler-helper-auditor-wire-debit.c1082
1 files changed, 362 insertions, 720 deletions
diff --git a/src/auditor/taler-helper-auditor-wire-debit.c b/src/auditor/taler-helper-auditor-wire-debit.c
index c9b1157c8..105867bf2 100644
--- a/src/auditor/taler-helper-auditor-wire-debit.c
+++ b/src/auditor/taler-helper-auditor-wire-debit.c
@@ -21,9 +21,14 @@
* @author Özgür Kesim
*
* - We check that the outgoing wire transfers match those
- * given in the 'wire_out' and 'reserve_closures' tables
- * - Finally, we check that all wire transfers that should have been made,
- * were actually made
+ * given in the 'wire_out' and 'reserve_closures' tables;
+ * any outgoing transfer MUST have a prior justification,
+ * so if one is missing we flag it (and never remove it).
+ * - We check that all wire transfers that should
+ * have been made, were actually made. If any were not made,
+ * we flag those, but may remove those flags if we later
+ * find that the wire transfers were made (wire transfers
+ * could be delayed due to AML/KYC or core-banking issues).
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@@ -45,6 +50,12 @@
#define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
/**
+ * Maximum number of wire transfers we process per
+ * (database) transaction.
+ */
+#define MAX_PER_TRANSACTION 1024
+
+/**
* How much do we allow the bank and the exchange to disagree about
* timestamps? Should be sufficiently large to avoid bogus reports from deltas
* created by imperfect clock synchronization and network delay.
@@ -54,6 +65,13 @@
/**
+ * How long do we long-poll for bank wire transfers?
+ */
+#define LONG_POLL_MAX GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, \
+ 1)
+
+
+/**
* Run in test mode. Exit when idle instead of
* going to sleep and waiting for more work.
*/
@@ -181,8 +199,6 @@ static struct WireAccount *wa_tail;
* Last reserve_out / wire_out serial IDs seen.
*/
static TALER_ARL_DEF_PP (wire_reserve_close_id);
-static TALER_ARL_DEF_PP (wire_batch_deposit_id);
-static TALER_ARL_DEF_PP (wire_aggregation_id);
/**
* Amount that is considered "tiny"
@@ -210,9 +226,10 @@ static TALER_ARL_DEF_AB (total_amount_lag);
static TALER_ARL_DEF_AB (total_closure_amount_lag);
/**
- * Total amount affected by wire format troubles.
+ * Total amount affected by duplicate wire transfer
+ * subjects.
*/
-static TALER_ARL_DEF_AB (total_wire_format_amount);
+static TALER_ARL_DEF_AB (wire_debit_duplicate_transfer_subject_total);
/**
* Total amount debited to exchange accounts.
@@ -502,7 +519,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
TALER_ARL_SET_AB (total_bad_amount_out_minus),
TALER_ARL_SET_AB (total_amount_lag),
TALER_ARL_SET_AB (total_closure_amount_lag),
- TALER_ARL_SET_AB (total_wire_format_amount),
+ TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
TALER_ARL_SET_AB (total_wire_out),
NULL);
if (0 > qs)
@@ -515,7 +532,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
TALER_ARL_SET_AB (total_bad_amount_out_minus),
TALER_ARL_SET_AB (total_amount_lag),
TALER_ARL_SET_AB (total_closure_amount_lag),
- TALER_ARL_SET_AB (total_wire_format_amount),
+ TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
TALER_ARL_SET_AB (total_wire_out),
NULL);
if (0 > qs)
@@ -554,23 +571,18 @@ commit (enum GNUNET_DB_QueryStatus qs)
qs = TALER_ARL_adb->update_auditor_progress (
TALER_ARL_adb->cls,
TALER_ARL_SET_PP (wire_reserve_close_id),
- TALER_ARL_SET_PP (wire_batch_deposit_id),
- TALER_ARL_SET_PP (wire_aggregation_id),
NULL);
if (0 > qs)
goto handle_db_error;
qs = TALER_ARL_adb->insert_auditor_progress (
TALER_ARL_adb->cls,
TALER_ARL_SET_PP (wire_reserve_close_id),
- TALER_ARL_SET_PP (wire_batch_deposit_id),
- TALER_ARL_SET_PP (wire_aggregation_id),
NULL);
if (0 > qs)
goto handle_db_error;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Concluded audit step at %llu/%llu\n",
- (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
- (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id));
+ "Concluded audit step at %llu\n",
+ (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
qs = TALER_ARL_edb->commit (TALER_ARL_edb->cls);
if (0 > qs)
goto handle_db_error;
@@ -599,440 +611,335 @@ handle_db_error:
}
-/* ******************** Analyze required outgoing transfers ******************** */
-
-/**
- * Closure for import_wire_missing_cb().
- */
-struct ImportMissingWireContext
-{
- /**
- * Set to maximum row ID encountered.
- */
- uint64_t max_batch_deposit_uuid;
-
- /**
- * Set to database errors in callback.
- */
- enum GNUNET_DB_QueryStatus err;
-};
-
-
/**
- * Function called on deposits that need to be checked for their
- * wire transfer.
+ * Check that @a want is within #TIME_TOLERANCE of @a have.
+ * Otherwise report an inconsistency in row @a rowid of @a table.
*
- * @param cls closure, points to a `struct ImportMissingWireContext`
- * @param batch_deposit_serial_id serial of the entry in the batch deposits table
- * @param total_amount value of the missing deposits, including fee
- * @param wire_target_h_payto where should the funds be wired
- * @param deadline what was the earliest requested wire transfer deadline
+ * @param table where is the inconsistency (if any)
+ * @param rowid what is the row
+ * @param want what is the expected time
+ * @param have what is the time we got
+ * @return true on success, false to abort
*/
-static void
-import_wire_missing_cb (
- void *cls,
- uint64_t batch_deposit_serial_id,
- const struct TALER_Amount *total_amount,
- const struct TALER_PaytoHashP *wire_target_h_payto,
- struct GNUNET_TIME_Timestamp deadline)
+static bool
+check_time_difference (const char *table,
+ uint64_t rowid,
+ struct GNUNET_TIME_Timestamp want,
+ struct GNUNET_TIME_Timestamp have)
{
- struct ImportMissingWireContext *wc = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- if (wc->err < 0)
- return; /* already failed */
- GNUNET_assert (batch_deposit_serial_id > wc->max_batch_deposit_uuid);
- wc->max_batch_deposit_uuid = batch_deposit_serial_id;
- qs = TALER_ARL_adb->insert_pending_deposit (
- TALER_ARL_adb->cls,
- batch_deposit_serial_id,
- wire_target_h_payto,
- total_amount,
- deadline);
- if (qs < 0)
- wc->err = qs;
-}
-
+ struct GNUNET_TIME_Relative delta;
+ char *details;
-/**
- * Information about a delayed wire transfer and the possible
- * reasons for the delay.
- */
-struct ReasonDetail
-{
- /**
- * Batch deposit that may be lacking a wire transfer.
- */
- uint64_t batch_deposit_serial_id;
+ if (GNUNET_TIME_timestamp_cmp (have, >, want))
+ delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
+ have.abs_time);
+ else
+ delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
+ want.abs_time);
+ if (GNUNET_TIME_relative_cmp (delta,
+ <=,
+ TIME_TOLERANCE))
+ return true;
- /**
- * Total amount that should have been transferred.
- */
- struct TALER_Amount total_amount;
+ GNUNET_asprintf (&details,
+ "execution date mismatch (%s)",
+ GNUNET_TIME_relative2s (delta,
+ true));
+ {
+ struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
+ .row_id = rowid,
+ .diagnostic = details,
+ .row_table = (char *) table
+ };
+ enum GNUNET_DB_QueryStatus qs;
- /**
- * Earliest deadline for an expected transfer to the account.
- */
- struct GNUNET_TIME_Timestamp deadline;
+ qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
+ TALER_ARL_adb->cls,
+ &rmi);
- /**
- * Target account hash.
- */
- struct TALER_PaytoHashP wire_target_h_payto;
+ if (qs < 0)
+ {
+ global_qs = qs;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_free (details);
+ return false;
+ }
+ }
+ GNUNET_free (details);
+ return true;
+}
-};
/**
- * Closure for report_wire_missing_cb().
+ * Closure for #check_rc_matches
*/
-struct ReportMissingWireContext
+struct CheckMatchContext
{
- /**
- * Map from wire_target_h_payto to `struct ReasonDetail`.
- */
- struct GNUNET_CONTAINER_MultiShortmap *map;
/**
- * Set to database errors in callback.
- */
- enum GNUNET_DB_QueryStatus err;
-};
-
-
-/**
- * Closure for #clear_finished_transfer_cb().
- */
-struct AggregationContext
-{
- /**
- * Set to maximum row ID encountered.
+ * Reserve operation looking for a match
*/
- uint64_t max_aggregation_serial;
+ const struct ReserveOutInfo *roi;
/**
- * Set to database errors in callback.
+ * Set to true if we found a match.
*/
- enum GNUNET_DB_QueryStatus err;
+ bool found;
};
/**
- * Free memory allocated in @a value.
- *
- * @param cls unused
- * @param key unused
- * @param value must be a `struct ReasonDetail`
- * @return #GNUNET_YES if we should continue to
- * iterate,
- * #GNUNET_NO if not.
- */
-static enum GNUNET_GenericReturnValue
-free_report_entry (void *cls,
- const struct GNUNET_ShortHashCode *key,
- void *value)
-{
- struct ReasonDetail *rd = value;
-
- GNUNET_free (rd);
- return GNUNET_YES;
-}
-
-
-/**
- * We had an entry in our map of wire transfers that
- * should have been performed. Generate report.
+ * Check if any of the reserve closures match the given wire transfer.
*
- * @param cls unused
- * @param key unused
- * @param value must be a `struct ReasonDetail`
- * @return #GNUNET_YES if we should continue to
- * iterate,
- * #GNUNET_NO if not.
+ * @param[in,out] cls a `struct CheckMatchContext`
+ * @param key key of @a value in #reserve_closures
+ * @param value a `struct ReserveClosure`
*/
static enum GNUNET_GenericReturnValue
-generate_report (void *cls,
- const struct GNUNET_ShortHashCode *key,
- void *value)
+check_rc_matches (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
{
- struct ReasonDetail *rd = value;
-
-
- /* For now, we simplify and only check that the
- amount was tiny */
- if (0 > TALER_amount_cmp (&rd->total_amount,
- &tiny_amount))
- return free_report_entry (cls,
- key,
- value); /* acceptable, amount was tiny */
+ struct CheckMatchContext *ctx = cls;
+ struct ReserveClosure *rc = value;
- // TODO: maybe split total_amount_lag up by category below?
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag),
- &TALER_ARL_USE_AB (total_amount_lag),
- &rd->total_amount);
+ if ((0 == GNUNET_memcmp (&ctx->roi->details.wtid,
+ &rc->wtid)) &&
+ (0 == strcasecmp (rc->receiver_account,
+ ctx->roi->details.credit_account_uri)) &&
+ (0 == TALER_amount_cmp (&rc->amount,
+ &ctx->roi->details.amount)))
{
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TALER_ARL_adb->insert_pending_deposit (
- TALER_ARL_adb->cls,
- rd->batch_deposit_serial_id,
- &rd->wire_target_h_payto,
- &rd->total_amount,
- rd->deadline);
- if (qs < 0)
+ if (! check_time_difference ("reserves_closures",
+ rc->rowid,
+ rc->execution_date,
+ ctx->roi->details.execution_date))
{
- global_qs = qs;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ free_rc (NULL,
+ key,
+ rc);
return GNUNET_SYSERR;
}
+ ctx->found = true;
+ free_rc (NULL,
+ key,
+ rc);
+ return GNUNET_NO;
}
- return free_report_entry (cls,
- key,
- value);
-}
-
-
-/**
- * Function called on deposits that are past their due date
- * and have not yet seen a wire transfer.
- *
- * @param cls closure, points to a `struct ReportMissingWireContext`
- * @param batch_deposit_serial_id row in the database for which the wire transfer is missing
- * @param total_amount value of the missing deposits, including fee
- * @param wire_target_h_payto hash of payto-URI where the funds should have been wired
- * @param deadline what was the earliest requested wire transfer deadline
- */
-static void
-report_wire_missing_cb (
- void *cls,
- uint64_t batch_deposit_serial_id,
- const struct TALER_Amount *total_amount,
- const struct TALER_PaytoHashP *wire_target_h_payto,
- struct GNUNET_TIME_Timestamp deadline)
-{
- struct ReportMissingWireContext *rc = cls;
- struct ReasonDetail *rd;
-
- rd = GNUNET_CONTAINER_multishortmap_get (rc->map,
- &wire_target_h_payto->hash);
- if (NULL == rd)
- {
- rd = GNUNET_new (struct ReasonDetail);
- rd->batch_deposit_serial_id = batch_deposit_serial_id;
- rd->wire_target_h_payto = *wire_target_h_payto;
- rd->total_amount = *total_amount;
- rd->deadline = deadline;
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multishortmap_put (
- rc->map,
- &wire_target_h_payto->hash,
- rd,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- else
- {
- TALER_ARL_amount_add (&rd->total_amount,
- &rd->total_amount,
- total_amount);
- rd->deadline = GNUNET_TIME_timestamp_min (rd->deadline,
- deadline);
- }
+ return GNUNET_OK;
}
/**
- * Function called on aggregations that were done for
- * a (batch) deposit.
+ * Check if a profit drain operation justified the @a roi
*
- * @param cls closure
- * @param tracking_serial_id where in the table are we
- * @param batch_deposit_serial_id which batch deposit was aggregated
- */
-static void
-clear_finished_transfer_cb (
- void *cls,
- uint64_t tracking_serial_id,
- uint64_t batch_deposit_serial_id)
-{
- struct AggregationContext *ac = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- if (0 > ac->err)
- return; /* already failed */
- GNUNET_assert (ac->max_aggregation_serial < tracking_serial_id);
- ac->max_aggregation_serial = tracking_serial_id;
- qs = TALER_ARL_adb->delete_pending_deposit (
- TALER_ARL_adb->cls,
- batch_deposit_serial_id);
- if (0 == qs)
- {
- /* Aggregated something twice or other error, report! */
- GNUNET_break (0);
- // FIXME: report more nicely!
- }
- if (0 > qs)
- ac->err = qs;
-}
-
-
-/**
- * Checks that all wire transfers that should have happened
- * (based on deposits) have indeed happened.
+ * @param roi reserve out operation to check
+ * @return #GNUNET_YES if @a roi was justified by a profit drain,
+ * #GNUNET_NO of @a roi was not justified by a proft drain
+ * #GNUNET_SYSERR on database trouble
*/
-static void
-check_for_required_transfers (void)
+static enum GNUNET_GenericReturnValue
+check_profit_drain (struct ReserveOutInfo *roi)
{
- struct ImportMissingWireContext wc = {
- .max_batch_deposit_uuid = TALER_ARL_USE_PP (wire_batch_deposit_id),
- .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
- };
- struct GNUNET_TIME_Absolute deadline;
enum GNUNET_DB_QueryStatus qs;
- struct ReportMissingWireContext rc = {
- .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
- };
- struct AggregationContext ac = {
- .max_aggregation_serial = TALER_ARL_USE_PP (wire_aggregation_id),
- .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
- };
+ uint64_t serial;
+ char *account_section;
+ char *payto_uri;
+ struct GNUNET_TIME_Timestamp request_timestamp;
+ struct TALER_Amount amount;
+ struct TALER_MasterSignatureP master_sig;
- qs = TALER_ARL_edb->select_batch_deposits_missing_wire (
+ qs = TALER_ARL_edb->get_drain_profit (
TALER_ARL_edb->cls,
- TALER_ARL_USE_PP (wire_batch_deposit_id),
- &import_wire_missing_cb,
- &wc);
- if ((0 > qs) || (0 > wc.err))
+ &roi->details.wtid,
+ &serial,
+ &account_section,
+ &payto_uri,
+ &request_timestamp,
+ &amount,
+ &master_sig);
+ switch (qs)
{
+ case GNUNET_DB_STATUS_HARD_ERROR:
GNUNET_break (0);
- GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
- (GNUNET_DB_STATUS_SOFT_ERROR == wc.err));
global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown ();
- return;
- }
- TALER_ARL_USE_PP (wire_batch_deposit_id) = wc.max_batch_deposit_uuid;
- qs = TALER_ARL_edb->select_aggregations_above_serial (
- TALER_ARL_edb->cls,
- TALER_ARL_USE_PP (wire_aggregation_id),
- &clear_finished_transfer_cb,
- &ac);
- if ((0 > qs) || (0 > ac.err))
- {
+ return GNUNET_SYSERR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ /* should fail on commit later ... */
GNUNET_break (0);
- GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
- (GNUNET_DB_STATUS_SOFT_ERROR == ac.err));
- global_ret = EXIT_FAILURE;
- GNUNET_SCHEDULER_shutdown ();
- return;
+ return GNUNET_SYSERR;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* not a profit drain */
+ return GNUNET_NO;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
}
- TALER_ARL_USE_PP (wire_aggregation_id) = ac.max_aggregation_serial;
- /* Subtract #GRACE_PERIOD, so we can be a bit behind in processing
- without immediately raising undue concern */
- deadline = GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
- GRACE_PERIOD);
- rc.map = GNUNET_CONTAINER_multishortmap_create (1024,
- GNUNET_NO);
- qs = TALER_ARL_adb->select_pending_deposits (
- TALER_ARL_adb->cls,
- deadline,
- &report_wire_missing_cb,
- &rc);
- if ((0 > qs) || (0 > rc.err))
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Profit drain of %s to %s found!\n",
+ TALER_amount2s (&amount),
+ payto_uri);
+ if (GNUNET_OK !=
+ TALER_exchange_offline_profit_drain_verify (
+ &roi->details.wtid,
+ request_timestamp,
+ &amount,
+ account_section,
+ payto_uri,
+ &TALER_ARL_master_pub,
+ &master_sig))
{
+ struct TALER_AUDITORDB_RowInconsistency ri = {
+ .row_id = roi->details.serial_id,
+ .row_table = "profit_drains",
+ .diagnostic = "invalid signature"
+ };
+
GNUNET_break (0);
- GNUNET_break ((GNUNET_DB_STATUS_SOFT_ERROR == qs) ||
- (GNUNET_DB_STATUS_SOFT_ERROR == rc.err));
- GNUNET_CONTAINER_multishortmap_iterate (rc.map,
- &free_report_entry,
- NULL);
- GNUNET_CONTAINER_multishortmap_destroy (rc.map);
- global_ret = EXIT_FAILURE;
- GNUNET_SCHEDULER_shutdown ();
- return;
+ qs = TALER_ARL_adb->insert_row_inconsistency (
+ TALER_ARL_adb->cls,
+ &ri);
+ GNUNET_free (payto_uri);
+ GNUNET_free (account_section);
+ if (qs < 0)
+ {
+ global_qs = qs;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_NO;
}
- GNUNET_CONTAINER_multishortmap_iterate (rc.map,
- &generate_report,
- NULL);
- GNUNET_CONTAINER_multishortmap_destroy (rc.map);
- /* conclude with success */
- commit (global_qs);
- if (test_mode)
+ GNUNET_free (account_section);
+ if (0 !=
+ strcasecmp (payto_uri,
+ roi->details.credit_account_uri))
{
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
-}
-
+ struct TALER_AUDITORDB_WireOutInconsistency woi = {
+ .row_id = serial,
+ .destination_account = (char *) roi->details.credit_account_uri,
+ .diagnostic = "profit drain wired to invalid account",
+ .expected = roi->details.amount,
+ .claimed = zero,
+ };
-/* ***************************** Analyze reserves_out ************************ */
+ qs = TALER_ARL_adb->insert_wire_out_inconsistency (
+ TALER_ARL_adb->cls,
+ &woi);
+ if (qs < 0)
+ {
+ global_qs = qs;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ GNUNET_free (payto_uri);
+ return GNUNET_SYSERR;
+ }
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &amount);
+ GNUNET_free (payto_uri);
+ return GNUNET_YES; /* justified, kind-of */
+ }
+ GNUNET_free (payto_uri);
+ if (0 !=
+ TALER_amount_cmp (&amount,
+ &roi->details.amount))
+ {
+ struct TALER_AUDITORDB_WireOutInconsistency woi = {
+ .row_id = roi->details.serial_id,
+ .destination_account = (char *) roi->details.credit_account_uri,
+ .diagnostic = "incorrect amount drained to correct account",
+ .expected = roi->details.amount,
+ .claimed = amount,
+ };
-/**
- * Clean up after processing wire out data.
- */
-static void
-conclude_wire_out (void)
-{
- GNUNET_CONTAINER_multihashmap_destroy (out_map);
- out_map = NULL;
- check_for_required_transfers ();
+ qs = TALER_ARL_adb->insert_wire_out_inconsistency (
+ TALER_ARL_adb->cls,
+ &woi);
+ if (qs < 0)
+ {
+ global_qs = qs;
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return GNUNET_SYSERR;
+ }
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
+ &TALER_ARL_USE_AB (total_bad_amount_out_minus),
+ &roi->details.amount);
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &amount);
+ return GNUNET_YES; /* justified, kind-of */
+ }
+ /* profit drain was correct */
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
+ &TALER_ARL_USE_AB (total_drained),
+ &amount);
+ return GNUNET_YES;
}
/**
- * Check that @a want is within #TIME_TOLERANCE of @a have.
- * Otherwise report an inconsistency in row @a rowid of @a table.
+ * Check whether the given transfer was justified by a reserve closure or
+ * profit drain. If not, complain that we failed to match an entry from
+ * #out_map. This means a wire transfer was made without proper
+ * justification.
*
- * @param table where is the inconsistency (if any)
- * @param rowid what is the row
- * @param want what is the expected time
- * @param have what is the time we got
- * @return true on success, false to abort
+ * @param cls a `struct WireAccount`
+ * @param key unused key
+ * @param value the `struct ReserveOutInfo` to report
+ * @return #GNUNET_OK on success
*/
-static bool
-check_time_difference (const char *table,
- uint64_t rowid,
- struct GNUNET_TIME_Timestamp want,
- struct GNUNET_TIME_Timestamp have)
+static enum GNUNET_GenericReturnValue
+complain_out_not_found (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
{
- struct GNUNET_TIME_Relative delta;
- char *details;
+ // struct WireAccount *wa = cls;
+ struct ReserveOutInfo *roi = value;
+ struct GNUNET_HashCode rkey;
+ struct CheckMatchContext ctx = {
+ .roi = roi,
+ .found = false
+ };
+ enum GNUNET_GenericReturnValue ret;
- if (GNUNET_TIME_timestamp_cmp (have, >, want))
- delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
- have.abs_time);
- else
- delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
- want.abs_time);
- if (GNUNET_TIME_relative_cmp (delta,
- <=,
- TIME_TOLERANCE))
- return true;
+ (void) cls;
+ (void) key;
+ hash_rc (roi->details.credit_account_uri,
+ &roi->details.wtid,
+ &rkey);
+ GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
+ &rkey,
+ &check_rc_matches,
+ &ctx);
+ if (ctx.found)
+ return GNUNET_OK;
+ ret = check_profit_drain (roi);
+ if (GNUNET_NO != ret)
+ return ret;
- GNUNET_asprintf (&details,
- "execution date mismatch (%s)",
- GNUNET_TIME_relative2s (delta,
- true));
{
- struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
- .row_id = rowid,
- .diagnostic = details,
- .row_table = (char *) table
+ struct TALER_AUDITORDB_WireOutInconsistency woi = {
+ .row_id = roi->details.serial_id,
+ .destination_account = (char *) roi->details.credit_account_uri,
+ .diagnostic = "missing justification for outgoing wire transfer",
+ .expected = zero,
+ .claimed = roi->details.amount
};
enum GNUNET_DB_QueryStatus qs;
- qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
+ qs = TALER_ARL_adb->insert_wire_out_inconsistency (
TALER_ARL_adb->cls,
- &rmi);
-
+ &woi);
if (qs < 0)
{
global_qs = qs;
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_free (details);
- return false;
+ return GNUNET_SYSERR;
}
}
- GNUNET_free (details);
- return true;
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &TALER_ARL_USE_AB (total_bad_amount_out_plus),
+ &roi->details.amount);
+ return GNUNET_OK;
}
@@ -1049,17 +956,17 @@ check_time_difference (const char *table,
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static enum GNUNET_GenericReturnValue
-wire_out_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Timestamp date,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const char *payto_uri,
- const struct TALER_Amount *amount)
+wire_out_cb (
+ void *cls,
+ uint64_t rowid,
+ struct GNUNET_TIME_Timestamp date,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ const char *payto_uri,
+ const struct TALER_Amount *amount)
{
struct WireAccount *wa = cls;
struct GNUNET_HashCode key;
struct ReserveOutInfo *roi;
- enum GNUNET_GenericReturnValue ret = GNUNET_OK;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Exchange wire OUT #%llu at %s of %s with WTID %s\n",
@@ -1084,7 +991,7 @@ wire_out_cb (void *cls,
struct TALER_AUDITORDB_WireOutInconsistency woi = {
.row_id = rowid,
.destination_account = (char *) payto_uri,
- .diagnostic = "expected wire transfer missing",
+ .diagnostic = "expected outgoing wire transfer missing",
.expected = *amount,
.claimed = zero,
};
@@ -1140,9 +1047,9 @@ wire_out_cb (void *cls,
amount))
{
struct TALER_AUDITORDB_WireOutInconsistency woi = {
- .row_id = rowid,
.destination_account = (char *) payto_uri,
.diagnostic = "wire amount does not match",
+ .wire_out_row_id = rowid,
.expected = *amount,
.claimed = zero,
};
@@ -1185,268 +1092,28 @@ wire_out_cb (void *cls,
return GNUNET_OK;
}
- if (! check_time_difference ("wire_out",
- rowid,
- date,
- roi->details.execution_date))
- ret = GNUNET_SYSERR;
- GNUNET_assert (GNUNET_OK ==
- free_roi (NULL,
- &key,
- roi));
- wa->last_wire_out_serial_id = rowid + 1;
- return ret;
-}
-
-
-/**
- * Closure for #check_rc_matches
- */
-struct CheckMatchContext
-{
-
- /**
- * Reserve operation looking for a match
- */
- const struct ReserveOutInfo *roi;
-
- /**
- * Set to true if we found a match.
- */
- bool found;
-};
-
-
-/**
- * Check if any of the reserve closures match the given wire transfer.
- *
- * @param[in,out] cls a `struct CheckMatchContext`
- * @param key key of @a value in #reserve_closures
- * @param value a `struct ReserveClosure`
- */
-static enum GNUNET_GenericReturnValue
-check_rc_matches (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- struct CheckMatchContext *ctx = cls;
- struct ReserveClosure *rc = value;
-
- if ((0 == GNUNET_memcmp (&ctx->roi->details.wtid,
- &rc->wtid)) &&
- (0 == strcasecmp (rc->receiver_account,
- ctx->roi->details.credit_account_uri)) &&
- (0 == TALER_amount_cmp (&rc->amount,
- &ctx->roi->details.amount)))
{
- if (! check_time_difference ("reserves_closures",
- rc->rowid,
- rc->execution_date,
- ctx->roi->details.execution_date))
- {
- free_rc (NULL,
- key,
- rc);
- return GNUNET_SYSERR;
- }
- ctx->found = true;
- free_rc (NULL,
- key,
- rc);
- return GNUNET_NO;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Check whether the given transfer was justified by a reserve closure or
- * profit drain. If not, complain that we failed to match an entry from
- * #out_map. This means a wire transfer was made without proper
- * justification.
- *
- * @param cls a `struct WireAccount`
- * @param key unused key
- * @param value the `struct ReserveOutInfo` to report
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-complain_out_not_found (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- // struct WireAccount *wa = cls;
- struct ReserveOutInfo *roi = value;
- struct GNUNET_HashCode rkey;
- struct CheckMatchContext ctx = {
- .roi = roi,
- .found = false
- };
+ enum GNUNET_GenericReturnValue ret;
- (void) key;
- hash_rc (roi->details.credit_account_uri,
- &roi->details.wtid,
- &rkey);
- GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
- &rkey,
- &check_rc_matches,
- &ctx);
- if (ctx.found)
- return GNUNET_OK;
- /* check for profit drain */
- {
- enum GNUNET_DB_QueryStatus qs;
- uint64_t serial;
- char *account_section;
- char *payto_uri;
- struct GNUNET_TIME_Timestamp request_timestamp;
- struct TALER_Amount amount;
- struct TALER_MasterSignatureP master_sig;
-
- qs = TALER_ARL_edb->get_drain_profit (TALER_ARL_edb->cls,
- &roi->details.wtid,
- &serial,
- &account_section,
- &payto_uri,
- &request_timestamp,
- &amount,
- &master_sig);
- switch (qs)
+ if (! check_time_difference ("wire_out",
+ rowid,
+ date,
+ roi->details.execution_date))
{
- case GNUNET_DB_STATUS_HARD_ERROR:
- GNUNET_break (0);
- global_ret = EXIT_FAILURE;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_SYSERR;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- /* should fail on commit later ... */
- GNUNET_break (0);
- return GNUNET_NO;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- /* not a profit drain */
- break;
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Profit drain of %s to %s found!\n",
- TALER_amount2s (&amount),
- payto_uri);
- if (GNUNET_OK !=
- TALER_exchange_offline_profit_drain_verify (
- &roi->details.wtid,
- request_timestamp,
- &amount,
- account_section,
- payto_uri,
- &TALER_ARL_master_pub,
- &master_sig))
- {
- struct TALER_AUDITORDB_RowInconsistency ri = {
- .row_id = roi->details.serial_id,
- .row_table = "profit_drains",
- .diagnostic = "invalid signature"
- };
-
- GNUNET_break (0);
- qs = TALER_ARL_adb->insert_row_inconsistency (
- TALER_ARL_adb->cls,
- &ri);
- if (qs < 0)
- {
- global_qs = qs;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return GNUNET_SYSERR;
- }
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &amount);
- }
- else if (0 !=
- strcasecmp (payto_uri,
- roi->details.credit_account_uri))
- {
- struct TALER_AUDITORDB_WireOutInconsistency woi = {
- .row_id = serial,
- .destination_account = (char *) roi->details.credit_account_uri,
- .diagnostic = "amount wired to invalid account",
- .expected = roi->details.amount,
- .claimed = zero,
- };
-
- qs = TALER_ARL_adb->insert_wire_out_inconsistency (
- TALER_ARL_adb->cls,
- &woi);
- if (qs < 0)
- {
- global_qs = qs;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return GNUNET_SYSERR;
- }
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &amount);
- }
- else if (0 !=
- TALER_amount_cmp (&amount,
- &roi->details.amount))
- {
- struct TALER_AUDITORDB_WireOutInconsistency woi = {
- .row_id = roi->details.serial_id,
- .destination_account = (char *) roi->details.credit_account_uri,
- .diagnostic = "incorrect amount to correct account",
- .expected = roi->details.amount,
- .claimed = amount,
- };
-
- qs = TALER_ARL_adb->insert_wire_out_inconsistency (
- TALER_ARL_adb->cls,
- &woi);
- if (qs < 0)
- {
- global_qs = qs;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return GNUNET_SYSERR;
- }
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
- &TALER_ARL_USE_AB (total_bad_amount_out_minus),
- &roi->details.amount);
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &amount);
- }
- GNUNET_free (account_section);
- GNUNET_free (payto_uri);
- /* profit drain was correct */
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
- &TALER_ARL_USE_AB (total_drained),
- &amount);
- return GNUNET_OK;
+ /* We had a database error, fail */
+ ret = GNUNET_SYSERR;
}
- }
-
- {
- struct TALER_AUDITORDB_WireOutInconsistency woi = {
- .row_id = roi->details.serial_id,
- .destination_account = (char *) roi->details.credit_account_uri,
- .diagnostic = "missing justification for outgoing wire transfer",
- .expected = zero,
- .claimed =roi->details.amount
- };
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TALER_ARL_adb->insert_wire_out_inconsistency (
- TALER_ARL_adb->cls,
- &woi);
- if (qs < 0)
+ else
{
- global_qs = qs;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return GNUNET_SYSERR;
+ ret = GNUNET_OK;
}
+ GNUNET_assert (GNUNET_OK ==
+ free_roi (NULL,
+ &key,
+ roi));
+ wa->last_wire_out_serial_id = rowid + 1;
+ return ret;
}
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &TALER_ARL_USE_AB (total_bad_amount_out_plus),
- &roi->details.amount);
- return GNUNET_OK;
}
@@ -1455,17 +1122,17 @@ complain_out_not_found (void *cls,
* the DEBIT transactions this time, and then verify that all of them are
* justified by 'reserves_out'.
*
- * @param cls `struct WireAccount` with a wire account list to process
+ * @param[in,out] wa wire account list to process
*/
static void
-process_debits (void *cls);
+process_debits (struct WireAccount *wa);
/**
* Go over the "wire_out" table of the exchange and
* verify that all wire outs are in that table.
*
- * @param wa wire account we are processing
+ * @param[in,out] wa wire account we are processing
*/
static void
check_exchange_wire_out (struct WireAccount *wa)
@@ -1509,8 +1176,9 @@ check_exchange_wire_out (struct WireAccount *wa)
* @param dhr HTTP response details
*/
static void
-history_debit_cb (void *cls,
- const struct TALER_BANK_DebitHistoryResponse *dhr)
+history_debit_cb (
+ void *cls,
+ const struct TALER_BANK_DebitHistoryResponse *dhr)
{
struct WireAccount *wa = cls;
struct ReserveOutInfo *roi;
@@ -1524,23 +1192,22 @@ history_debit_cb (void *cls,
{
const struct TALER_BANK_DebitDetails *dd
= &dhr->details.ok.details[i];
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
GNUNET_TIME_timestamp2s (dd->execution_date),
TALER_amount2s (&dd->amount),
TALER_B2S (&dd->wtid));
- /* Update offset */
wa->wire_off_out = dd->serial_id;
slen = strlen (dd->credit_account_uri) + 1;
- roi = GNUNET_malloc (sizeof (struct ReserveOutInfo)
- + slen);
GNUNET_CRYPTO_hash (&dd->wtid,
sizeof (dd->wtid),
&roi->subject_hash);
- roi->details.amount = dd->amount;
- roi->details.execution_date = dd->execution_date;
- roi->details.wtid = dd->wtid;
- roi->details.credit_account_uri = (const char *) &roi[1];
+ roi = GNUNET_malloc (sizeof (struct ReserveOutInfo)
+ + slen);
+ roi->details = *dd;
+ roi->details.credit_account_uri
+ = (const char *) &roi[1];
GNUNET_memcpy (&roi[1],
dd->credit_account_uri,
slen);
@@ -1551,17 +1218,15 @@ history_debit_cb (void *cls,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{
struct TALER_AUDITORDB_WireFormatInconsistency wfi = {
- // fixme: rowid!
- .diagnostic = "duplicate subject hash",
.amount = dd->amount,
- .wire_offset = dd->serial_id
+ .wire_offset = dd->serial_id,
+ .diagnostic = "duplicate outgoing wire transfer subject"
};
enum GNUNET_DB_QueryStatus qs;
qs = TALER_ARL_adb->insert_wire_format_inconsistency (
TALER_ARL_adb->cls,
&wfi);
-
if (qs < 0)
{
global_qs = qs;
@@ -1569,8 +1234,10 @@ history_debit_cb (void *cls,
commit (qs);
return;
}
- TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_format_amount),
- &TALER_ARL_USE_AB (total_wire_format_amount),
+ TALER_ARL_amount_add (&TALER_ARL_USE_AB (
+ wire_debit_duplicate_transfer_subject_total),
+ &TALER_ARL_USE_AB (
+ wire_debit_duplicate_transfer_subject_total),
&dd->amount);
}
}
@@ -1600,42 +1267,31 @@ history_debit_cb (void *cls,
}
-/**
- * Main function for processing 'reserves_out' data. We start by going over
- * the DEBIT transactions this time, and then verify that all of them are
- * justified by 'reserves_out'.
- *
- * @param cls `struct WireAccount` with a wire account list to process
- */
static void
-process_debits (void *cls)
+process_debits (struct WireAccount *wa)
{
- struct WireAccount *wa = cls;
-
/* skip accounts where DEBIT is not enabled */
while ( (NULL != wa) &&
- (GNUNET_NO == wa->ai->debit_enabled))
+ (! wa->ai->debit_enabled) )
wa = wa->next;
if (NULL == wa)
{
- /* end of iteration, now check wire_out to see
- if it matches #out_map */
- conclude_wire_out ();
+ /* end of iteration */
+ commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking bank DEBIT records of account `%s'\n",
wa->ai->section_name);
GNUNET_assert (NULL == wa->dhh);
- // FIXME: handle the case where more than INT32_MAX transactions exist.
- // (CG: used to be INT64_MAX, changed by MS to INT32_MAX, why? To be discussed with him!)
- wa->dhh = TALER_BANK_debit_history (ctx,
- wa->ai->auth,
- wa->wire_off_out,
- INT32_MAX,
- GNUNET_TIME_UNIT_ZERO,
- &history_debit_cb,
- wa);
+ wa->dhh = TALER_BANK_debit_history (
+ ctx,
+ wa->ai->auth,
+ wa->wire_off_out,
+ MAX_PER_TRANSACTION,
+ GNUNET_TIME_UNIT_ZERO,
+ &history_debit_cb,
+ wa);
if (NULL == wa->dhh)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1650,24 +1306,9 @@ process_debits (void *cls)
/**
- * Begin analyzing wire_out.
- */
-static void
-begin_debit_audit (void)
-{
- GNUNET_assert (NULL == out_map);
- out_map = GNUNET_CONTAINER_multihashmap_create (1024,
- true);
- process_debits (wa_head);
-}
-
-
-/* ***************************** Setup logic ************************ */
-
-/**
* Function called about reserve closing operations the aggregator triggered.
*
- * @param cls closure
+ * @param cls closure; NULL
* @param rowid row identifier used to uniquely identify the reserve closing operation
* @param execution_date when did we execute the close operation
* @param amount_with_fee how much did we debit the reserve
@@ -1680,15 +1321,16 @@ begin_debit_audit (void)
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static enum GNUNET_GenericReturnValue
-reserve_closed_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Timestamp execution_date,
- const struct TALER_Amount *amount_with_fee,
- const struct TALER_Amount *closing_fee,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const char *receiver_account,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- uint64_t close_request_row)
+reserve_closed_cb (
+ void *cls,
+ uint64_t rowid,
+ struct GNUNET_TIME_Timestamp execution_date,
+ const struct TALER_Amount *amount_with_fee,
+ const struct TALER_Amount *closing_fee,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *receiver_account,
+ const struct TALER_WireTransferIdentifierRawP *wtid,
+ uint64_t close_request_row)
{
struct ReserveClosure *rc;
struct GNUNET_HashCode key;
@@ -1702,8 +1344,9 @@ reserve_closed_cb (void *cls,
closing_fee))
{
struct TALER_AUDITORDB_RowInconsistency ri = {
+ .row_id = rowid,
.row_table = "reserves_closures",
- .diagnostic = "closing fee above total amount"
+ .diagnostic = "closing fee above reserve balance (and closed anyway)"
};
enum GNUNET_DB_QueryStatus qs;
@@ -1729,10 +1372,11 @@ reserve_closed_cb (void *cls,
hash_rc (receiver_account,
wtid,
&key);
- (void) GNUNET_CONTAINER_multihashmap_put (reserve_closures,
- &key,
- rc,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
+ (void) GNUNET_CONTAINER_multihashmap_put (
+ reserve_closures,
+ &key,
+ rc,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
return GNUNET_OK;
}
@@ -1784,7 +1428,7 @@ begin_transaction (void)
TALER_ARL_GET_AB (total_bad_amount_out_minus),
TALER_ARL_GET_AB (total_amount_lag),
TALER_ARL_GET_AB (total_closure_amount_lag),
- TALER_ARL_GET_AB (total_wire_format_amount),
+ TALER_ARL_GET_AB (wire_debit_duplicate_transfer_subject_total),
TALER_ARL_GET_AB (total_wire_out),
NULL);
switch (qs)
@@ -1828,8 +1472,6 @@ begin_transaction (void)
qs = TALER_ARL_adb->get_auditor_progress (
TALER_ARL_adb->cls,
TALER_ARL_GET_PP (wire_reserve_close_id),
- TALER_ARL_GET_PP (wire_batch_deposit_id),
- TALER_ARL_GET_PP (wire_aggregation_id),
NULL);
if (0 > qs)
{
@@ -1844,10 +1486,8 @@ begin_transaction (void)
else
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Resuming wire audit at %llu / %llu / %llu\n",
- (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id),
- (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
- (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id));
+ "Resuming wire debit audit at %llu\n",
+ (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
}
qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
@@ -1860,7 +1500,7 @@ begin_transaction (void)
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- begin_debit_audit ();
+ process_debits (wa_head);
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
@@ -1880,8 +1520,8 @@ process_account_cb (void *cls,
struct WireAccount *wa;
(void) cls;
- if ((! ai->debit_enabled) &&
- (! ai->credit_enabled))
+ if ( (! ai->debit_enabled) &&
+ (! ai->credit_enabled) )
return; /* not an active exchange account */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Found exchange account `%s'\n",
@@ -1980,6 +1620,8 @@ run (void *cls,
}
reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024,
GNUNET_NO);
+ out_map = GNUNET_CONTAINER_multihashmap_create (1024,
+ true);
if (GNUNET_OK !=
TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
TALER_EXCHANGEDB_ALO_DEBIT