aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/auditor/taler-auditor.c227
-rw-r--r--src/auditordb/plugin_auditordb_postgres.c159
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c3
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c24
-rw-r--r--src/include/taler_auditordb_plugin.h35
-rw-r--r--src/include/taler_exchangedb_plugin.h4
6 files changed, 435 insertions, 17 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 8c98c3c22..c1aaa614a 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -183,6 +183,11 @@ static struct TALER_Amount total_balance_reserve_not_closed;
static json_t *report_wire_out_inconsistencies;
/**
+ * Array of reports about missing deposit confirmations.
+ */
+static json_t *report_deposit_confirmation_inconsistencies;
+
+/**
* Total delta between calculated and stored wire out transfers,
* for positive deltas.
*/
@@ -236,6 +241,16 @@ static struct TALER_Amount total_arithmetic_delta_plus;
static struct TALER_Amount total_arithmetic_delta_minus;
/**
+ * Total number of deposit confirmations that we did not get.
+ */
+static json_int_t number_missed_deposit_confirmations;
+
+/**
+ * Total amount involved in deposit confirmations that we did not get.
+ */
+static struct TALER_Amount total_missed_deposit_confirmations;
+
+/**
* Total amount reported in all calls to #report_emergency().
*/
static struct TALER_Amount reported_emergency_sum;
@@ -4036,6 +4051,202 @@ analyze_coins (void *cls)
}
+/* *************************** Analysis of deposit-confirmations ********** */
+
+/**
+ * Closure for #test_dc.
+ */
+struct DepositConfirmationContext
+{
+
+ /**
+ * How many deposit confirmations did we NOT find in the #edb?
+ */
+ unsigned long long missed_count;
+
+ /**
+ * What is the total amount missing?
+ */
+ struct TALER_Amount missed_amount;
+
+ /**
+ * Lowest SerialID of the first coin we missed? (This is where we
+ * should resume next time).
+ */
+ uint64_t first_missed_coin_serial;
+
+ /**
+ * Lowest SerialID of the first coin we missed? (This is where we
+ * should resume next time).
+ */
+ uint64_t last_seen_coin_serial;
+
+ /**
+ * Success or failure of (exchange) database operations within
+ * #test_dc.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+
+};
+
+
+/**
+ * Given a deposit confirmation from #adb, check that it is also
+ * in #edb. Update the deposit confirmation context accordingly.
+ *
+ * @param cls our `struct DepositConfirmationContext`
+ * @param serial_id row of the @a dc in the database
+ * @param dc the deposit confirmation we know
+ */
+static void
+test_dc (void *cls,
+ uint64_t serial_id,
+ const struct TALER_AUDITORDB_DepositConfirmation *dc)
+{
+ struct DepositConfirmationContext *dcc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_EXCHANGEDB_Deposit dep;
+
+ dcc->last_seen_coin_serial = serial_id;
+ memset (&dep,
+ 0,
+ sizeof (dep));
+ dep.coin.coin_pub = dc->coin_pub;
+ dep.h_contract_terms = dc->h_contract_terms;
+ dep.merchant_pub = dc->merchant;
+ dep.h_wire = dc->h_wire;
+ dep.refund_deadline = dc->refund_deadline;
+
+ qs = edb->have_deposit (edb->cls,
+ esession,
+ &dep,
+ GNUNET_NO /* do not check refund deadline */);
+ if (qs > 0)
+ return; /* found, all good */
+ if (qs < 0)
+ {
+ GNUNET_break (0); /* DB error, complain */
+ dcc->qs = qs;
+ return;
+ }
+ /* deposit confirmation missing! report! */
+ report (report_deposit_confirmation_inconsistencies,
+ json_pack ("{s:s, s:o, s:I, s:o}",
+ "timestamp",
+ GNUNET_STRINGS_absolute_time_to_string (dc->timestamp),
+ "amount",
+ TALER_JSON_from_amount (&dc->amount_without_fee),
+ "rowid",
+ (json_int_t) serial_id,
+ "account",
+ GNUNET_JSON_from_data_auto (&dc->h_wire)));
+ dcc->first_missed_coin_serial = GNUNET_MIN (dcc->first_missed_coin_serial,
+ serial_id);
+ dcc->missed_count++;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_add (&dcc->missed_amount,
+ &dcc->missed_amount,
+ &dc->amount_without_fee));
+}
+
+
+/**
+ * Check that the deposit-confirmations that were reported to
+ * us by merchants are also in the exchange's database.
+ *
+ * @param cls closure
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+analyze_deposit_confirmations (void *cls)
+{
+ struct TALER_AUDITORDB_ProgressPointDepositConfirmation ppdc;
+ struct DepositConfirmationContext dcc;
+ enum GNUNET_DB_QueryStatus qs;
+ enum GNUNET_DB_QueryStatus qsx;
+ enum GNUNET_DB_QueryStatus qsp;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Analyzing deposit confirmations\n");
+ ppdc.last_deposit_confirmation_serial_id = 0;
+ qsp = adb->get_auditor_progress_deposit_confirmation (adb->cls,
+ asession,
+ &master_pub,
+ &ppdc);
+ if (0 > qsp)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp);
+ return qsp;
+ }
+ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsp)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
+ _("First analysis using this auditor, starting audit from scratch\n"));
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Resuming deposit confirmation audit at %llu\n"),
+ (unsigned long long) ppdc.last_deposit_confirmation_serial_id);
+ }
+
+ /* setup 'cc' */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_get_zero (currency,
+ &dcc.missed_amount));
+ dcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+ dcc.missed_count = 0LLU;
+ dcc.first_missed_coin_serial = UINT64_MAX;
+ qsx = adb->get_deposit_confirmations (adb->cls,
+ asession,
+ &master_pub,
+ ppdc.last_deposit_confirmation_serial_id,
+ &test_dc,
+ &dcc);
+ if (0 > qsx)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+ return qsx;
+ }
+ if (0 > dcc.qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == dcc.qs);
+ return dcc.qs;
+ }
+ if (UINT64_MAX == dcc.first_missed_coin_serial)
+ ppdc.last_deposit_confirmation_serial_id = dcc.last_seen_coin_serial;
+ else
+ ppdc.last_deposit_confirmation_serial_id = dcc.first_missed_coin_serial - 1;
+
+ /* sync 'cc' back to disk */
+ if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
+ qs = adb->update_auditor_progress_deposit_confirmation (adb->cls,
+ asession,
+ &master_pub,
+ &ppdc);
+ else
+ qs = adb->insert_auditor_progress_deposit_confirmation (adb->cls,
+ asession,
+ &master_pub,
+ &ppdc);
+ if (0 >= qs)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Failed to update auditor DB, not recording progress\n");
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ number_missed_deposit_confirmations = (json_int_t) dcc.missed_count;
+ total_missed_deposit_confirmations = dcc.missed_amount;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Concluded deposit confirmation audit step at %llu\n"),
+ (unsigned long long) ppdc.last_deposit_confirmation_serial_id);
+ return qs;
+}
+
+
+
/* *************************** General transaction logic ****************** */
/**
@@ -4151,6 +4362,8 @@ setup_sessions_and_run ()
NULL);
transact (&analyze_coins,
NULL);
+ transact (&analyze_deposit_confirmations,
+ NULL);
}
@@ -4348,6 +4561,8 @@ run (void *cls,
GNUNET_assert (NULL !=
(report_wire_out_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
+ (report_deposit_confirmation_inconsistencies = json_array ()));
+ GNUNET_assert (NULL !=
(report_coin_inconsistencies = json_array ()));
GNUNET_assert (NULL !=
(report_aggregation_fee_balances = json_array ()));
@@ -4383,7 +4598,8 @@ run (void *cls,
" s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o,"
" s:o, s:o, s:o, s:o, s:o,"
- " s:o, s:o, s:o }",
+ " s:o, s:o, s:o, s:o, s:I,"
+ " s:o }",
/* blocks of 5 for easier counting/matching to format string */
/* block */
"reserve_balance_insufficient_inconsistencies",
@@ -4457,7 +4673,14 @@ run (void *cls,
"total_refresh_hanging",
TALER_JSON_from_amount (&total_refresh_hanging),
"refresh_hanging",
- report_refreshs_hanging);
+ report_refreshs_hanging,
+ "deposit_confirmation_inconsistencies",
+ report_deposit_confirmation_inconsistencies,
+ "missing_deposit_confirmation_count",
+ (json_int_t) number_missed_deposit_confirmations,
+ /* block */
+ "missing_deposit_confirmation_total",
+ TALER_JSON_from_amount (&total_missed_deposit_confirmations));
GNUNET_break (NULL != report);
json_dumpf (report,
stdout,
diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c
index 6f2aa2909..aeb96df5d 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -390,6 +390,7 @@ postgres_create_tables (void *cls)
we must check that the exchange reported these properly. */
GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS deposit_confirmations "
"(master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE"
+ ",serial_id BIGSERIAL UNIQUE"
",h_contract_terms BYTEA CHECK (LENGTH(h_contract_terms)=64)"
",h_wire BYTEA CHECK (LENGTH(h_wire)=64)"
",timestamp INT8 NOT NULL"
@@ -567,6 +568,25 @@ postgres_prepare (PGconn *db_conn)
",master_sig" /* master_sig could be normalized... */
") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);",
11),
+ /* Used in #postgres_get_deposit_confirmations() */
+ GNUNET_PQ_make_prepare ("auditor_deposit_confirmation_select",
+ "SELECT"
+ " h_contract_terms"
+ ",h_wire"
+ ",timestamp"
+ ",refund_deadline"
+ ",amount_without_fee_val"
+ ",amount_without_fee_frac"
+ ",amount_without_fee_curr"
+ ",coin_pub"
+ ",merchant_pub"
+ ",exchange_sig"
+ ",exchange_pub"
+ ",master_sig" /* master_sig could be normalized... */
+ " FROM deposit_confirmations"
+ " WHERE master_pub=$1"
+ " AND serial_id>$2",
+ 2),
/* Used in #postgres_update_auditor_progress_reserve() */
GNUNET_PQ_make_prepare ("auditor_progress_update_reserve",
"UPDATE auditor_progress_reserve SET "
@@ -1417,6 +1437,144 @@ postgres_insert_deposit_confirmation (void *cls,
/**
+ * Closure for #deposit_confirmation_cb().
+ */
+struct DepositConfirmationContext
+{
+
+ /**
+ * Master public key that is being used.
+ */
+ const struct TALER_MasterPublicKeyP *master_pub;
+
+ /**
+ * Function to call for each deposit confirmation.
+ */
+ TALER_AUDITORDB_DepositConfirmationCallback cb;
+
+ /**
+ * Closure for @e cb
+ */
+ void *cb_cls;
+
+ /**
+ * Query status to return.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_get_deposit_confirmations().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct DepositConfirmationContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+deposit_confirmation_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct DepositConfirmationContext *dcc = cls;
+
+ for (unsigned int i = 0; i < num_results; i++)
+ {
+ uint64_t serial_id;
+ struct TALER_AUDITORDB_DepositConfirmation dc = {
+ .master_public_key = *dcc->master_pub
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("serial_id",
+ &serial_id),
+ GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+ &dc.h_contract_terms),
+ GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+ &dc.h_wire),
+ GNUNET_PQ_result_spec_absolute_time ("timetamp",
+ &dc.timestamp),
+ GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+ &dc.refund_deadline),
+ TALER_PQ_result_spec_amount ("amount_without_fee",
+ &dc.amount_without_fee),
+ GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+ &dc.coin_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+ &dc.merchant),
+ GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
+ &dc.exchange_sig),
+ GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
+ &dc.exchange_pub),
+ GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+ &dc.master_sig),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ dcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+ return;
+ }
+ dcc->qs = i + 1;
+ dcc->cb (dcc->cb_cls,
+ serial_id,
+ &dc);
+ }
+}
+
+
+/**
+ * Get information about deposit confirmations from the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param master_pub for which exchange do we want to get deposit confirmations
+ * @param start_id row/serial ID where to start the iteration (0 from
+ * the start, exclusive, i.e. serial_ids must start from 1)
+ * @param cb function to call with results
+ * @param cb_cls closure for @a cb
+ * @return query result status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_deposit_confirmations (void *cls,
+ struct TALER_AUDITORDB_Session *session,
+ const struct TALER_MasterPublicKeyP *master_public_key,
+ uint64_t start_id,
+ TALER_AUDITORDB_DepositConfirmationCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (master_public_key),
+ GNUNET_PQ_query_param_uint64 (&start_id),
+ GNUNET_PQ_query_param_end
+ };
+ struct DepositConfirmationContext dcc = {
+ .master_pub = master_public_key,
+ .cb = cb,
+ .cb_cls = cb_cls
+ };
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+ "auditor_deposit_confirmation_select",
+ params,
+ &deposit_confirmation_cb,
+ &dcc);
+ if (qs > 0)
+ return dcc.qs;
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+ return qs;
+}
+
+
+
+/**
* Insert information about a denomination key and in particular
* the properties (value, fees, expiration times) the coins signed
* with this key have.
@@ -3264,6 +3422,7 @@ libtaler_plugin_auditordb_postgres_init (void *cls)
plugin->list_exchanges = &postgres_list_exchanges;
plugin->insert_exchange_signkey = &postgres_insert_exchange_signkey;
plugin->insert_deposit_confirmation = &postgres_insert_deposit_confirmation;
+ plugin->get_deposit_confirmations = &postgres_get_deposit_confirmations;
plugin->select_denomination_info = &postgres_select_denomination_info;
plugin->insert_denomination_info = &postgres_insert_denomination_info;
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 52344d645..3e91218ce 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -142,7 +142,8 @@ deposit_transaction (void *cls,
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
session,
- deposit);
+ deposit,
+ GNUNET_YES /* check refund deadline */);
if (qs < 0)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 3cd467c01..2ce865ae3 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2730,6 +2730,7 @@ postgres_get_reserve_history (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
+ * @param check_extras wether to check extra fields match or not
* @return 1 if we know this operation,
* 0 if this exact deposit is unknown to us,
* otherwise transaction error status
@@ -2737,7 +2738,8 @@ postgres_get_reserve_history (void *cls,
static enum GNUNET_DB_QueryStatus
postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_EXCHANGEDB_Deposit *deposit)
+ const struct TALER_EXCHANGEDB_Deposit *deposit,
+ int check_extras)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
@@ -2755,8 +2757,6 @@ postgres_have_deposit (void *cls,
&deposit2.refund_deadline),
TALER_PQ_result_spec_absolute_time ("wire_deadline",
&deposit2.wire_deadline),
- GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
- &deposit2.h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&deposit2.h_wire),
GNUNET_PQ_result_spec_end
@@ -2774,18 +2774,16 @@ postgres_have_deposit (void *cls,
return qs;
/* Now we check that the other information in @a deposit
also matches, and if not report inconsistencies. */
- if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
- &deposit2.amount_with_fee)) ||
- (deposit->timestamp.abs_value_us !=
- deposit2.timestamp.abs_value_us) ||
+ if ( ( (check_extras) &&
+ ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
+ &deposit2.amount_with_fee)) ||
+ (deposit->timestamp.abs_value_us !=
+ deposit2.timestamp.abs_value_us) ) ) ||
(deposit->refund_deadline.abs_value_us !=
- deposit2.refund_deadline.abs_value_us) ||
- (0 != memcmp (&deposit->h_contract_terms,
- &deposit2.h_contract_terms,
- sizeof (struct GNUNET_HashCode))) ||
+ deposit2.refund_deadline.abs_value_us) ||
(0 != memcmp (&deposit->h_wire,
- &deposit2.h_wire,
- sizeof (struct GNUNET_HashCode))) )
+ &deposit2.h_wire,
+ sizeof (struct GNUNET_HashCode)) ) )
{
/* Inconsistencies detected! Does not match! (We might want to
expand the API with a 'get_deposit' function to return the
diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h
index 76dcc3507..92494ab32 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -346,6 +346,20 @@ struct TALER_AUDITORDB_DepositConfirmation
/**
+ * Function called with deposit confirmations stored in
+ * the auditor's database.
+ *
+ * @param cls closure
+ * @param serial_id location of the @a dc in the database
+ * @param dc the deposit confirmation itself
+ */
+typedef void
+(*TALER_AUDITORDB_DepositConfirmationCallback)(void *cls,
+ uint64_t serial_id,
+ const struct TALER_AUDITORDB_DepositConfirmation *dc);
+
+
+/**
* Handle for one session with the database.
*/
struct TALER_AUDITORDB_Session;
@@ -526,6 +540,27 @@ struct TALER_AUDITORDB_Plugin
/**
+ * Get information about a deposit confirmations from the database.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to the database
+ * @param master_pub for which exchange do we want to get deposit confirmations
+ * @param start_id row/serial ID where to start the iteration (0 from
+ * the start, exclusive, i.e. serial_ids must start from 1)
+ * @param cb function to call with results
+ * @param cb_cls closure for @a cb
+ * @return query result status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*get_deposit_confirmations) (void *cls,
+ struct TALER_AUDITORDB_Session *session,
+ const struct TALER_MasterPublicKeyP *master_public_key,
+ uint64_t start_id,
+ TALER_AUDITORDB_DepositConfirmationCallback cb,
+ void *cb_cls);
+
+
+ /**
* Insert information about a denomination key and in particular
* the properties (value, fees, expiration times) the coins signed
* with this key have.
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index c8417a3a7..89df42190 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1431,6 +1431,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param deposit deposit to search for
+ * @param check_extras wether to check extra fields or not
* @return 1 if we know this operation,
* 0 if this exact deposit is unknown to us,
* otherwise transaction error status
@@ -1438,7 +1439,8 @@ struct TALER_EXCHANGEDB_Plugin
enum GNUNET_DB_QueryStatus
(*have_deposit) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
- const struct TALER_EXCHANGEDB_Deposit *deposit);
+ const struct TALER_EXCHANGEDB_Deposit *deposit,
+ int check_extras);
/**