summaryrefslogtreecommitdiff
path: root/src/auditor
diff options
context:
space:
mode:
Diffstat (limited to 'src/auditor')
-rw-r--r--src/auditor/taler-auditor-httpd.c7
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.c113
-rw-r--r--src/auditor/taler-auditor-httpd_deposit-confirmation.h12
-rw-r--r--src/auditor/taler-auditor.c2
-rw-r--r--src/auditor/taler-wire-auditor.c18
-rwxr-xr-xsrc/auditor/test-auditor.sh43
6 files changed, 160 insertions, 35 deletions
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
index 4d5537e51..043d5b148 100644
--- a/src/auditor/taler-auditor-httpd.c
+++ b/src/auditor/taler-auditor-httpd.c
@@ -594,7 +594,7 @@ main (int argc,
if (GNUNET_OK !=
auditor_serve_process_config ())
return 1;
-
+ TEAH_DEPOSIT_CONFIRMATION_init ();
/* check for systemd-style FD passing */
listen_pid = getenv ("LISTEN_PID");
listen_fds = getenv ("LISTEN_FDS");
@@ -635,7 +635,10 @@ main (int argc,
fh = TALER_MHD_open_unix_path (serve_unixpath,
unixpath_mode);
if (-1 == fh)
+ {
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return 1;
+ }
}
mhd
@@ -659,6 +662,7 @@ main (int argc,
{
fprintf (stderr,
"Failed to start HTTP server.\n");
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return 1;
}
@@ -732,6 +736,7 @@ main (int argc,
break;
}
TALER_AUDITORDB_plugin_unload (TAH_plugin);
+ TEAH_DEPOSIT_CONFIRMATION_done ();
return (GNUNET_SYSERR == ret) ? 1 : 0;
}
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index 7759b5538..ab233ebc3 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -33,6 +33,19 @@
/**
+ * Cache of already verified exchange signing keys. Maps the hash of the
+ * `struct TALER_ExchangeSigningKeyValidityPS` to the (static) string
+ * "verified". Access to this map is guarded by the #lock.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *cache;
+
+/**
+ * Lock for operations on #cache.
+ */
+static pthread_mutex_t lock;
+
+
+/**
* We have parsed the JSON information about the deposit, do some
* basic sanity checks (especially that the signature on the coin is
* valid, and that this type of coin exists) and then execute the
@@ -55,6 +68,8 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
struct TALER_AUDITORDB_Session *session;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Absolute now;
+ struct GNUNET_HashCode h;
+ int cached;
now = GNUNET_TIME_absolute_get ();
if ( (es->ep_start.abs_value_us > now.abs_value_us) ||
@@ -68,10 +83,6 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
"master_sig (expired)");
}
- /* TODO (#6052): consider having an in-memory cache of already
- verified exchange signing keys, this could save us
- a signature check AND a database transaction per
- operation. */
/* check exchange signing key signature */
skv.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
skv.purpose.size = htonl (sizeof (struct TALER_ExchangeSigningKeyValidityPS));
@@ -80,18 +91,15 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
skv.expire = GNUNET_TIME_absolute_hton (es->ep_expire);
skv.end = GNUNET_TIME_absolute_hton (es->ep_end);
skv.signkey_pub = es->exchange_pub;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
- &skv.purpose,
- &es->master_sig.eddsa_signature,
- &es->master_public_key.eddsa_pub))
- {
- TALER_LOG_WARNING ("Invalid signature on exchange signing key\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
- "master_sig");
- }
+
+ /* check our cache */
+ GNUNET_CRYPTO_hash (&skv,
+ sizeof (skv),
+ &h);
+ GNUNET_assert (0 == pthread_mutex_lock (&lock));
+ cached = GNUNET_CONTAINER_multihashmap_contains (cache,
+ &h);
+ GNUNET_assert (0 == pthread_mutex_unlock (&lock));
session = TAH_plugin->get_session (TAH_plugin->cls);
if (NULL == session)
@@ -102,18 +110,45 @@ verify_and_execute_deposit_confirmation (struct MHD_Connection *connection,
TALER_EC_DB_SETUP_FAILED,
"failed to establish session with database");
}
- /* execute transaction */
- qs = TAH_plugin->insert_exchange_signkey (TAH_plugin->cls,
- session,
- es);
- if (0 > qs)
+ if (! cached)
{
- GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
- TALER_LOG_WARNING ("Failed to store exchange signing key in database\n");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_AUDITOR_EXCHANGE_STORE_DB_ERROR,
- "failed to persist exchange signing key");
+ /* Not in cache, need to verify the signature, persist it, and possibly cache it */
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
+ &skv.purpose,
+ &es->master_sig.eddsa_signature,
+ &es->master_public_key.eddsa_pub))
+ {
+ TALER_LOG_WARNING ("Invalid signature on exchange signing key\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_DEPOSIT_CONFIRMATION_SIGNATURE_INVALID,
+ "master_sig");
+ }
+
+ /* execute transaction */
+ qs = TAH_plugin->insert_exchange_signkey (TAH_plugin->cls,
+ session,
+ es);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
+ TALER_LOG_WARNING ("Failed to store exchange signing key in database\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_AUDITOR_EXCHANGE_STORE_DB_ERROR,
+ "failed to persist exchange signing key");
+ }
+
+ /* Cache it, due to concurreny it might already be in the cache,
+ so we do not cache it twice but also don't insist on the 'put' to
+ succeed. */
+ GNUNET_assert (0 == pthread_mutex_lock (&lock));
+ (void) GNUNET_CONTAINER_multihashmap_put (cache,
+ &h,
+ "verified",
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ GNUNET_assert (0 == pthread_mutex_unlock (&lock));
}
/* check deposit confirmation signature */
@@ -237,4 +272,28 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
}
+/**
+ * Initialize subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_init (void)
+{
+ cache = GNUNET_CONTAINER_multihashmap_create (32,
+ GNUNET_NO);
+ GNUNET_assert (0 == pthread_mutex_init (&lock, NULL));
+}
+
+
+/**
+ * Shut down subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_done (void)
+{
+ GNUNET_CONTAINER_multihashmap_destroy (cache);
+ cache = NULL;
+ GNUNET_assert (0 == pthread_mutex_destroy (&lock));
+}
+
+
/* end of taler-auditor-httpd_deposit-confirmation.c */
diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.h b/src/auditor/taler-auditor-httpd_deposit-confirmation.h
index 842eb3562..531f3c93c 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.h
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.h
@@ -25,6 +25,18 @@
#include <microhttpd.h>
#include "taler-auditor-httpd.h"
+/**
+ * Initialize subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_init (void);
+
+/**
+ * Shut down subsystem.
+ */
+void
+TEAH_DEPOSIT_CONFIRMATION_done (void);
+
/**
* Handle a "/deposit-confirmation" request. Parses the JSON, and, if
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 4e32d2a4e..0ec5e2d75 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -5709,6 +5709,8 @@ main (int argc,
"restart",
"restart audit from the beginning (required on first run)",
&restart),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 94fd773c9..92664fb8f 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -704,13 +704,15 @@ check_pending_rc (void *cls,
TALER_amount_add (&total_closure_amount_lag,
&total_closure_amount_lag,
&rc->amount));
- report (report_closure_lags,
- json_pack ("{s:I, s:o, s:o, s:o, s:s}",
- "row", (json_int_t) rc->rowid,
- "amount", TALER_JSON_from_amount (&rc->amount),
- "deadline", json_from_time_abs (rc->execution_date),
- "wtid", GNUNET_JSON_from_data_auto (&rc->wtid),
- "account", rc->receiver_account));
+ if ( (0 != rc->amount.value) ||
+ (0 != rc->amount.fraction) )
+ report (report_closure_lags,
+ json_pack ("{s:I, s:o, s:o, s:o, s:s}",
+ "row", (json_int_t) rc->rowid,
+ "amount", TALER_JSON_from_amount (&rc->amount),
+ "deadline", json_from_time_abs (rc->execution_date),
+ "wtid", GNUNET_JSON_from_data_auto (&rc->wtid),
+ "account", rc->receiver_account));
pp.last_reserve_close_uuid
= GNUNET_MIN (pp.last_reserve_close_uuid,
rc->rowid);
@@ -2312,6 +2314,8 @@ main (int argc,
"restart",
"restart audit from the beginning (required on first run)",
&restart),
+ GNUNET_GETOPT_option_timetravel ('T',
+ "timetravel"),
GNUNET_GETOPT_OPTION_END
};
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index 00ed05230..5f7d0c8c3 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -1473,6 +1473,49 @@ echo "UPDATE deposits SET wire='$OLD_WIRE' WHERE deposit_serial_id=${SERIAL}" |
}
+# Test for duplicate wire transfer subject
+function test_27() {
+echo "===========27: duplicate WTID detection ================="
+
+# Check wire transfer lag reported (no aggregator!)
+# NOTE: This test is EXPECTED to fail for ~1h after
+# re-generating the test database as we do not
+# report lag of less than 1h (see GRACE_PERIOD in
+# taler-wire-auditor.c)
+if [ $DATABASE_AGE -gt 3600 ]
+then
+
+ pre_audit aggregator
+
+ # Obtain data to duplicate.
+ ID=`echo "SELECT id FROM app_banktransaction WHERE debit_account_id=2 LIMIT 1" | psql $DB -Aqt`
+ WTID=`echo "SELECT subject FROM app_banktransaction WHERE debit_account_id=2 LIMIT 1" | psql $DB -Aqt`
+ echo "INSERT INTO app_banktransaction (amount,subject,date,credit_account_id,debit_account_id,cancelled) VALUES ('TESTKUDOS:1','$WTID',NOW(),12,2,'f')" | psql -Aqt $DB
+
+ audit_only
+ post_audit
+
+ echo -n "Testing inconsistency detection... "
+
+ AMOUNT=`jq -r .wire_format_inconsistencies[0].amount < test-wire-audit.json`
+ if test "${AMOUNT}" != "TESTKUDOS:1"
+ then
+ exit_fail "Amount wrong, got ${AMOUNT}"
+ fi
+
+ AMOUNT=`jq -r .total_wire_format_amount < test-wire-audit.json`
+ if test "${AMOUNT}" != "TESTKUDOS:1"
+ then
+ exit_fail "Wrong total wire format amount, got $AMOUNT"
+ fi
+
+ # cannot easily undo aggregator, hence full reload
+ full_reload
+else
+ echo "Test skipped (database too new)"
+fi
+
+}
# **************************************************