summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/auditor/taler-wire-auditor.c284
1 files changed, 276 insertions, 8 deletions
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 7183c33bc..840d446f7 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -21,7 +21,7 @@
* - First, this auditor verifies that 'reserves_in' actually matches
* the incoming wire transfers from the bank.
* - Second, we check that the outgoing wire transfers match those
- * given in the 'wire_out' table.
+ * given in the 'wire_out' table (TODO!)
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@@ -63,6 +63,12 @@ static char *currency;
static const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
+ * Map with information about incoming wire transfers.
+ * Maps hashes of the wire offsets to `struct ReserveInInfo`s.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *in_map;
+
+/**
* Our session with the #edb.
*/
static struct TALER_EXCHANGEDB_Session *esession;
@@ -120,6 +126,61 @@ static size_t wire_off_size;
/* ***************************** Shutdown **************************** */
+/**
+ * Entry in map with wire information we expect to obtain from the
+ * #edb later.
+ */
+struct ReserveInInfo
+{
+
+ /**
+ * Hash of expected row offset.
+ */
+ struct GNUNET_HashCode row_off_hash;
+
+ /**
+ * Number of bytes in @e row_off.
+ */
+ size_t row_off_size;
+
+ /**
+ * Expected details about the wire transfer.
+ */
+ struct TALER_WIRE_TransferDetails details;
+
+ /**
+ * RowID in reserves_in table.
+ */
+ uint64_t rowid;
+
+};
+
+
+/**
+ * Free entry in #in_map.
+ *
+ * @param cls NULL
+ * @param key unused key
+ * @param value the `struct ReserveInInfo` to free
+ * @return #GNUNET_OK
+ */
+static int
+free_rii (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct ReserveInInfo *rii = value;
+
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap_remove (in_map,
+ key,
+ rii));
+ json_decref (rii->details.account_details);
+ GNUNET_free (rii);
+ return GNUNET_OK;
+}
+
+
/**
* Task run on shutdown.
*
@@ -134,6 +195,14 @@ do_shutdown (void *cls)
hh);
hh = NULL;
}
+ if (NULL != in_map)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (in_map,
+ &free_rii,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (in_map);
+ in_map = NULL;
+ }
if (NULL != wp)
{
TALER_WIRE_plugin_unload (wp);
@@ -154,7 +223,6 @@ do_shutdown (void *cls)
/* ***************************** Report logic **************************** */
-#if 0
/**
* Report a (serious) inconsistency in the exchange's database.
*
@@ -196,7 +264,6 @@ report_row_minor_inconsistency (const char *table,
(unsigned long long) rowid,
diagnostic);
}
-#endif
/* *************************** General transaction logic ****************** */
@@ -291,10 +358,103 @@ commit (enum GNUNET_DB_QueryStatus qs)
}
+/* ***************************** Analyze reserves_out ************************ */
+
+
+/**
+ * Main functin for processing reserves_out data.
+ */
+static void
+process_debits ()
+{
+ /* TODO: also check DEBITs! */
+
+ /* conclude with: */
+ commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
/* ***************************** Analyze reserves_in ************************ */
/**
+ * Function called with details about incoming wire transfers
+ * as claimed by the exchange DB.
+ *
+ * @param cls NULL
+ * @param rowid unique serial ID for the refresh session in our DB
+ * @param reserve_pub public key of the reserve (also the WTID)
+ * @param credit amount that was received
+ * @param sender_account_details information about the sender's bank account
+ * @param wire_reference unique identifier for the wire transfer (plugin-specific format)
+ * @param wire_reference_size number of bytes in @a wire_reference
+ * @param execution_date when did we receive the funds
+ * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ */
+static int
+reserve_in_cb (void *cls,
+ uint64_t rowid,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *credit,
+ const json_t *sender_account_details,
+ const void *wire_reference,
+ size_t wire_reference_size,
+ struct GNUNET_TIME_Absolute execution_date)
+
+{
+ struct ReserveInInfo *rii;
+
+ rii = GNUNET_new (struct ReserveInInfo);
+ GNUNET_CRYPTO_hash (wire_reference,
+ wire_reference_size,
+ &rii->row_off_hash);
+ rii->row_off_size = wire_reference_size;
+ rii->details.amount = *credit;
+ rii->details.execution_date = execution_date;
+ rii->details.reserve_pub = *reserve_pub;
+ rii->details.account_details = json_incref ((json_t *) sender_account_details);
+ rii->rowid = rowid;
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_multihashmap_put (in_map,
+ &rii->row_off_hash,
+ rii,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ {
+ GNUNET_break_op (0); /* duplicate wire offset is not allowed! */
+ report_row_inconsistency ("reserves_in",
+ rowid,
+ "duplicate wire offset");
+ return GNUNET_SYSERR;
+ }
+ pp.last_reserve_in_serial_id = rowid + 1;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Complain that we failed to match an entry from #in_map.
+ *
+ * @param cls NULL
+ * @param key unused key
+ * @param value the `struct ReserveInInfo` to free
+ * @return #GNUNET_OK
+ */
+static int
+complain_not_found (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *value)
+{
+ struct ReserveInInfo *rii = value;
+
+ report_row_inconsistency ("reserves_in",
+ rii->rowid,
+ "matching wire transfer not found");
+ return GNUNET_OK;
+}
+
+
+/**
* Callbacks of this type are used to serve the result of asking
* the bank for the transaction history.
*
@@ -312,21 +472,107 @@ history_credit_cb (void *cls,
size_t row_off_size,
const struct TALER_WIRE_TransferDetails *details)
{
- if (NULL == details)
+ struct ReserveInInfo *rii;
+ struct GNUNET_HashCode key;
+
+ if (TALER_BANK_DIRECTION_NONE == dir)
{
/* end of operation */
hh = NULL;
- /* TODO: also check DEBITs! */
- commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
+ GNUNET_CONTAINER_multihashmap_iterate (in_map,
+ &complain_not_found,
+ NULL);
+ /* clean up before 2nd phase */
+ GNUNET_CONTAINER_multihashmap_iterate (in_map,
+ &free_rii,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (in_map);
+ in_map = NULL;
+ process_debits ();
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_hash (row_off,
+ row_off_size,
+ &key);
+ rii = GNUNET_CONTAINER_multihashmap_get (in_map,
+ &key);
+ if (NULL == rii)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n",
+ GNUNET_STRINGS_absolute_time_to_string (details->execution_date));
+ return GNUNET_SYSERR;
+ }
+
+ /* Update offset */
+ if (NULL == in_wire_off)
+ {
+ wire_off_size = row_off_size;
+ in_wire_off = GNUNET_malloc (row_off_size);
+ }
+ if (wire_off_size != row_off_size)
+ {
+ GNUNET_break (0);
+ commit (GNUNET_DB_STATUS_HARD_ERROR);
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
- /* TODO: implement actual checks! */
+ memcpy (in_wire_off,
+ row_off,
+ row_off_size);
+
+ /* compare records with expected data */
+ if (row_off_size != rii->row_off_size)
+ {
+ GNUNET_break (0);
+ report_row_inconsistency ("reserves_in",
+ rii->rowid,
+ "wire reference size missmatch");
+ return GNUNET_OK;
+ }
+ if (0 != TALER_amount_cmp (&rii->details.amount,
+ &details->amount))
+ {
+ report_row_inconsistency ("reserves_in",
+ rii->rowid,
+ "wire amount missmatch");
+ return GNUNET_OK;
+ }
+ if (details->execution_date.abs_value_us !=
+ rii->details.execution_date.abs_value_us)
+ {
+ report_row_minor_inconsistency ("reserves_in",
+ rii->rowid,
+ "execution date missmatch");
+ }
+ if (0 != memcmp (&details->reserve_pub,
+ &rii->details.reserve_pub,
+ sizeof (struct TALER_ReservePublicKeyP)))
+ {
+ report_row_inconsistency ("reserves_in",
+ rii->rowid,
+ "reserve public key / wire subject missmatch");
+ return GNUNET_OK;
+ }
+ if (! json_equal (details->account_details,
+ rii->details.account_details))
+ {
+ report_row_minor_inconsistency ("reserves_in",
+ rii->rowid,
+ "sender account missmatch");
+ }
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (in_map,
+ &key,
+ rii));
+ GNUNET_assert (GNUNET_OK ==
+ free_rii (NULL,
+ &key,
+ rii));
return GNUNET_OK;
}
-
/* ***************************** Setup logic ************************ */
@@ -344,6 +590,7 @@ run (void *cls,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *c)
{
+ enum GNUNET_DB_QueryStatus qs;
int ret;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -475,6 +722,27 @@ run (void *cls,
(unsigned long long) pp.last_reserve_out_serial_id);
}
+ in_map = GNUNET_CONTAINER_multihashmap_create (1024,
+ GNUNET_YES);
+ qs = edb->select_reserves_in_above_serial_id (edb->cls,
+ esession,
+ pp.last_reserve_in_serial_id,
+ &reserve_in_cb,
+ NULL);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ global_ret = 1;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ "No new incoming transactions available, skipping CREDIT phase\n");
+ process_debits ();
+ return;
+ }
hh = wp->get_history (wp->cls,
TALER_BANK_DIRECTION_CREDIT,
in_wire_off,