diff options
Diffstat (limited to 'src/auditor')
-rw-r--r-- | src/auditor/taler-auditor-httpd.c | 7 | ||||
-rw-r--r-- | src/auditor/taler-auditor-httpd_deposit-confirmation.c | 113 | ||||
-rw-r--r-- | src/auditor/taler-auditor-httpd_deposit-confirmation.h | 12 | ||||
-rw-r--r-- | src/auditor/taler-auditor.c | 2 | ||||
-rw-r--r-- | src/auditor/taler-wire-auditor.c | 18 | ||||
-rwxr-xr-x | src/auditor/test-auditor.sh | 43 |
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 + +} # ************************************************** |