aboutsummaryrefslogtreecommitdiff
path: root/src/auditor/taler-auditor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/auditor/taler-auditor.c')
-rw-r--r--src/auditor/taler-auditor.c5765
1 files changed, 0 insertions, 5765 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
deleted file mode 100644
index dedf828f1..000000000
--- a/src/auditor/taler-auditor.c
+++ /dev/null
@@ -1,5765 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2016-2020 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero Public License for more details.
-
- You should have received a copy of the GNU Affero Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file auditor/taler-auditor.c
- * @brief audits an exchange database.
- * @author Christian Grothoff
- *
- * README-FIRST:
- *
- * This code is being split up into
- * taler-auditor-{aggregation,coins,deposits,reserves}. It is still here as a
- * reference, but this file should be obsolete once the split has been
- * completed. DO NOT EDIT THIS FILE ANYMORE! -CG
- *
- * NOTE:
- * - This auditor does not verify that 'reserves_in' actually matches
- * the wire transfers from the bank. This needs to be checked separately!
- * - Similarly, we do not check that the outgoing wire transfers match those
- * given in the 'wire_out' table. This needs to be checked separately!
- *
- * TODO:
- * - reorganize: different passes are combined in one tool and one
- * file here, we should split this up!
- * - likely should do an iteration over known_coins instead of checking
- * those signatures again and again
- * - might want to bite the bullet and do asynchronous signature
- * verification to improve parallelism / speed -- we'll need to scale
- * this eventually anyway!
- *
- * UNDECIDED:
- * - do we care about checking the 'done' flag in deposit_cb?
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_auditordb_plugin.h"
-#include "taler_exchangedb_lib.h"
-#include "taler_json_lib.h"
-#include "taler_bank_service.h"
-#include "taler_signatures.h"
-
-
-/**
- * How many coin histories do we keep in RAM at any given point in
- * time? Used bound memory consumption of the auditor. Larger values
- * reduce database accesses.
- *
- * Set to a VERY low value here for testing. Practical values may be
- * in the millions.
- */
-#define MAX_COIN_SUMMARIES 4
-
-/**
- * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
- */
-#define DEPOSIT_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
-
-/**
- * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
- */
-#define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-/**
- * Command-line option "-r": restart audit from scratch
- */
-static int restart;
-
-/**
- * Handle to access the exchange's database.
- */
-static struct TALER_EXCHANGEDB_Plugin *edb;
-
-/**
- * Which currency are we doing the audit for?
- */
-static char *currency;
-
-/**
- * How many fractional digits does the currency use?
- */
-static struct TALER_Amount currency_round_unit;
-
-/**
- * Our configuration.
- */
-static const struct GNUNET_CONFIGURATION_Handle *cfg;
-
-/**
- * Our session with the #edb.
- */
-static struct TALER_EXCHANGEDB_Session *esession;
-
-/**
- * Handle to access the auditor's database.
- */
-static struct TALER_AUDITORDB_Plugin *adb;
-
-/**
- * Our session with the #adb.
- */
-static struct TALER_AUDITORDB_Session *asession;
-
-/**
- * After how long should idle reserves be closed?
- */
-static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
-
-/**
- * Master public key of the exchange to audit.
- */
-static struct TALER_MasterPublicKeyP master_pub;
-
-/**
- * Checkpointing our progress for reserves.
- */
-static struct TALER_AUDITORDB_ProgressPointReserve ppr;
-
-/**
- * Checkpointing our progress for aggregations.
- */
-static struct TALER_AUDITORDB_ProgressPointAggregation ppa;
-
-/**
- * Checkpointing our progress for coins.
- */
-static struct TALER_AUDITORDB_ProgressPointCoin ppc;
-
-/**
- * Checkpointing our progress for reserves.
- */
-static struct TALER_AUDITORDB_ProgressPointReserve ppr_start;
-
-/**
- * Checkpointing our progress for aggregations.
- */
-static struct TALER_AUDITORDB_ProgressPointAggregation ppa_start;
-
-/**
- * Checkpointing our progress for coins.
- */
-static struct TALER_AUDITORDB_ProgressPointCoin ppc_start;
-
-/**
- * Array of reports about denomination keys with an
- * emergency (more value deposited than withdrawn)
- */
-static json_t *report_emergencies;
-
-/**
- * Array of reports about denomination keys with an
- * emergency (more coins deposited than withdrawn)
- */
-static json_t *report_emergencies_by_count;
-
-/**
- * Array of reports about row inconsitencies.
- */
-static json_t *report_row_inconsistencies;
-
-/**
- * Array of reports about the denomination key not being
- * valid at the time of withdrawal.
- */
-static json_t *denomination_key_validity_withdraw_inconsistencies;
-
-/**
- * Array of reports about reserve balance insufficient inconsitencies.
- */
-static json_t *report_reserve_balance_insufficient_inconsistencies;
-
-/**
- * Total amount reserves were charged beyond their balance.
- */
-static struct TALER_Amount total_balance_insufficient_loss;
-
-/**
- * Array of reports about reserve balance summary wrong in database.
- */
-static json_t *report_reserve_balance_summary_wrong_inconsistencies;
-
-/**
- * Total delta between expected and stored reserve balance summaries,
- * for positive deltas.
- */
-static struct TALER_Amount total_balance_summary_delta_plus;
-
-/**
- * Total delta between expected and stored reserve balance summaries,
- * for negative deltas.
- */
-static struct TALER_Amount total_balance_summary_delta_minus;
-
-/**
- * Array of reports about reserve's not being closed inconsitencies.
- */
-static json_t *report_reserve_not_closed_inconsistencies;
-
-/**
- * Total amount affected by reserves not having been closed on time.
- */
-static struct TALER_Amount total_balance_reserve_not_closed;
-
-/**
- * Array of reports about irregular wire out entries.
- */
-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.
- */
-static struct TALER_Amount total_wire_out_delta_plus;
-
-/**
- * Total delta between calculated and stored wire out transfers
- * for negative deltas.
- */
-static struct TALER_Amount total_wire_out_delta_minus;
-
-/**
- * Array of reports about inconsistencies about coins.
- */
-static json_t *report_coin_inconsistencies;
-
-/**
- * Profits the exchange made by bad amount calculations on coins.
- */
-static struct TALER_Amount total_coin_delta_plus;
-
-/**
- * Losses the exchange made by bad amount calculations on coins.
- */
-static struct TALER_Amount total_coin_delta_minus;
-
-/**
- * Report about aggregate wire transfer fee profits.
- */
-static json_t *report_aggregation_fee_balances;
-
-/**
- * Report about amount calculation differences (causing profit
- * or loss at the exchange).
- */
-static json_t *report_amount_arithmetic_inconsistencies;
-
-/**
- * Array of reports about wire fees being ambiguous in terms of validity periods.
- */
-static json_t *report_fee_time_inconsistencies;
-
-/**
- * Profits the exchange made by bad amount calculations.
- */
-static struct TALER_Amount total_arithmetic_delta_plus;
-
-/**
- * Losses the exchange made by bad amount calculations.
- */
-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_by_count().
- */
-static struct TALER_Amount reported_emergency_risk_by_count;
-
-/**
- * Total amount reported in all calls to #report_emergency_by_amount().
- */
-static struct TALER_Amount reported_emergency_risk_by_amount;
-
-/**
- * Total amount in losses reported in all calls to #report_emergency_by_amount().
- */
-static struct TALER_Amount reported_emergency_loss;
-
-/**
- * Total amount in losses reported in all calls to #report_emergency_by_count().
- */
-static struct TALER_Amount reported_emergency_loss_by_count;
-
-/**
- * Expected balance in the escrow account.
- */
-static struct TALER_Amount total_escrow_balance;
-
-/**
- * Active risk exposure.
- */
-static struct TALER_Amount total_risk;
-
-/**
- * Actualized risk (= loss) from recoups.
- */
-static struct TALER_Amount total_recoup_loss;
-
-/**
- * Recoups we made on denominations that were not revoked (!?).
- */
-static struct TALER_Amount total_irregular_recoups;
-
-/**
- * Total withdraw fees earned.
- */
-static struct TALER_Amount total_withdraw_fee_income;
-
-/**
- * Total deposit fees earned.
- */
-static struct TALER_Amount total_deposit_fee_income;
-
-/**
- * Total melt fees earned.
- */
-static struct TALER_Amount total_melt_fee_income;
-
-/**
- * Total refund fees earned.
- */
-static struct TALER_Amount total_refund_fee_income;
-
-/**
- * Total aggregation fees earned.
- */
-static struct TALER_Amount total_aggregation_fee_income;
-
-/**
- * Array of reports about coin operations with bad signatures.
- */
-static json_t *report_bad_sig_losses;
-
-/**
- * Total amount lost by operations for which signatures were invalid.
- */
-static struct TALER_Amount total_bad_sig_loss;
-
-/**
- * Array of refresh transactions where the /refresh/reveal has not yet
- * happened (and may of course never happen).
- */
-static json_t *report_refreshs_hanging;
-
-/**
- * Total amount lost by operations for which signatures were invalid.
- */
-static struct TALER_Amount total_refresh_hanging;
-
-/**
- * At what time did the auditor process start?
- */
-static struct GNUNET_TIME_Absolute start_time;
-
-
-/* ********************************* helpers *************************** */
-
-/**
- * Convert absolute time to human-readable JSON string.
- *
- * @param at time to convert
- * @return human-readable string representing the time
- */
-static json_t *
-json_from_time_abs_nbo (struct GNUNET_TIME_AbsoluteNBO at)
-{
- return json_string
- (GNUNET_STRINGS_absolute_time_to_string
- (GNUNET_TIME_absolute_ntoh (at)));
-}
-
-
-/**
- * Convert absolute time to human-readable JSON string.
- *
- * @param at time to convert
- * @return human-readable string representing the time
- */
-static json_t *
-json_from_time_abs (struct GNUNET_TIME_Absolute at)
-{
- return json_string
- (GNUNET_STRINGS_absolute_time_to_string (at));
-}
-
-
-/* ***************************** Report logic **************************** */
-
-
-/**
- * Add @a object to the report @a array. Fail hard if this fails.
- *
- * @param array report array to append @a object to
- * @param object object to append, should be check that it is not NULL
- */
-static void
-report (json_t *array,
- json_t *object)
-{
- GNUNET_assert (NULL != object);
- GNUNET_assert (0 ==
- json_array_append_new (array,
- object));
-}
-
-
-/**
- * Called in case we detect an emergency situation where the exchange
- * is paying out a larger amount on a denomination than we issued in
- * that denomination. This means that the exchange's private keys
- * might have gotten compromised, and that we need to trigger an
- * emergency request to all wallets to deposit pending coins for the
- * denomination (and as an exchange suffer a huge financial loss).
- *
- * @param issue denomination key where the loss was detected
- * @param risk maximum risk that might have just become real (coins created by this @a issue)
- * @param loss actual losses already (actualized before denomination was revoked)
- */
-static void
-report_emergency_by_amount (const struct TALER_DenominationKeyValidityPS *issue,
- const struct TALER_Amount *risk,
- const struct TALER_Amount *loss)
-{
- report (report_emergencies,
- json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
- "denompub_hash",
- GNUNET_JSON_from_data_auto (&issue->denom_hash),
- "denom_risk",
- TALER_JSON_from_amount (risk),
- "denom_loss",
- TALER_JSON_from_amount (loss),
- "start",
- json_from_time_abs_nbo (issue->start),
- "deposit_end",
- json_from_time_abs_nbo (issue->expire_deposit),
- "value",
- TALER_JSON_from_amount_nbo (&issue->value)));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&reported_emergency_risk_by_amount,
- &reported_emergency_risk_by_amount,
- risk));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&reported_emergency_loss,
- &reported_emergency_loss,
- loss));
-}
-
-
-/**
- * Called in case we detect an emergency situation where the exchange
- * is paying out a larger NUMBER of coins of a denomination than we
- * issued in that denomination. This means that the exchange's
- * private keys might have gotten compromised, and that we need to
- * trigger an emergency request to all wallets to deposit pending
- * coins for the denomination (and as an exchange suffer a huge
- * financial loss).
- *
- * @param issue denomination key where the loss was detected
- * @param num_issued number of coins that were issued
- * @param num_known number of coins that have been deposited
- * @param risk amount that is at risk
- */
-static void
-report_emergency_by_count (const struct TALER_DenominationKeyValidityPS *issue,
- uint64_t num_issued,
- uint64_t num_known,
- const struct TALER_Amount *risk)
-{
- struct TALER_Amount denom_value;
-
- report (report_emergencies_by_count,
- json_pack ("{s:o, s:I, s:I, s:o, s:o, s:o, s:o}",
- "denompub_hash",
- GNUNET_JSON_from_data_auto (&issue->denom_hash),
- "num_issued",
- (json_int_t) num_issued,
- "num_known",
- (json_int_t) num_known,
- "denom_risk",
- TALER_JSON_from_amount (risk),
- "start",
- json_from_time_abs_nbo (issue->start),
- "deposit_end",
- json_from_time_abs_nbo (issue->expire_deposit),
- "value",
- TALER_JSON_from_amount_nbo (&issue->value)));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&reported_emergency_risk_by_count,
- &reported_emergency_risk_by_count,
- risk));
- TALER_amount_ntoh (&denom_value,
- &issue->value);
- for (uint64_t i = num_issued; i<num_known; i++)
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&reported_emergency_loss_by_count,
- &reported_emergency_loss_by_count,
- &denom_value));
-
-}
-
-
-/**
- * Report a (serious) inconsistency in the exchange's database with
- * respect to calculations involving amounts.
- *
- * @param operation what operation had the inconsistency
- * @param rowid affected row, UINT64_MAX if row is missing
- * @param exchange amount calculated by exchange
- * @param auditor amount calculated by auditor
- * @param profitable 1 if @a exchange being larger than @a auditor is
- * profitable for the exchange for this operation,
- * -1 if @a exchange being smaller than @a auditor is
- * profitable for the exchange, and 0 if it is unclear
- */
-static void
-report_amount_arithmetic_inconsistency (const char *operation,
- uint64_t rowid,
- const struct TALER_Amount *exchange,
- const struct TALER_Amount *auditor,
- int profitable)
-{
- struct TALER_Amount delta;
- struct TALER_Amount *target;
-
- if (0 < TALER_amount_cmp (exchange,
- auditor))
- {
- /* exchange > auditor */
- GNUNET_break (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- exchange,
- auditor));
- }
- else
- {
- /* auditor < exchange */
- profitable = -profitable;
- GNUNET_break (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- auditor,
- exchange));
- }
- report (report_amount_arithmetic_inconsistencies,
- json_pack ("{s:s, s:I, s:o, s:o, s:I}",
- "operation", operation,
- "rowid", (json_int_t) rowid,
- "exchange", TALER_JSON_from_amount (exchange),
- "auditor", TALER_JSON_from_amount (auditor),
- "profitable", (json_int_t) profitable));
- if (0 != profitable)
- {
- target = (1 == profitable)
- ? &total_arithmetic_delta_plus
- : &total_arithmetic_delta_minus;
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (target,
- target,
- &delta));
- }
-}
-
-
-/**
- * Report a (serious) inconsistency in the exchange's database with
- * respect to calculations involving amounts of a coin.
- *
- * @param operation what operation had the inconsistency
- * @param coin_pub affected coin
- * @param exchange amount calculated by exchange
- * @param auditor amount calculated by auditor
- * @param profitable 1 if @a exchange being larger than @a auditor is
- * profitable for the exchange for this operation,
- * -1 if @a exchange being smaller than @a auditor is
- * profitable for the exchange, and 0 if it is unclear
- */
-static void
-report_coin_arithmetic_inconsistency (const char *operation,
- const struct
- TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_Amount *exchange,
- const struct TALER_Amount *auditor,
- int profitable)
-{
- struct TALER_Amount delta;
- struct TALER_Amount *target;
-
- if (0 < TALER_amount_cmp (exchange,
- auditor))
- {
- /* exchange > auditor */
- GNUNET_break (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- exchange,
- auditor));
- }
- else
- {
- /* auditor < exchange */
- profitable = -profitable;
- GNUNET_break (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- auditor,
- exchange));
- }
- report (report_coin_inconsistencies,
- json_pack ("{s:s, s:o, s:o, s:o, s:I}",
- "operation", operation,
- "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
- "exchange", TALER_JSON_from_amount (exchange),
- "auditor", TALER_JSON_from_amount (auditor),
- "profitable", (json_int_t) profitable));
- if (0 != profitable)
- {
- target = (1 == profitable)
- ? &total_coin_delta_plus
- : &total_coin_delta_minus;
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (target,
- target,
- &delta));
- }
-}
-
-
-/**
- * Report a (serious) inconsistency in the exchange's database.
- *
- * @param table affected table
- * @param rowid affected row, UINT64_MAX if row is missing
- * @param diagnostic message explaining the problem
- */
-static void
-report_row_inconsistency (const char *table,
- uint64_t rowid,
- const char *diagnostic)
-{
- report (report_row_inconsistencies,
- json_pack ("{s:s, s:I, s:s}",
- "table", table,
- "row", (json_int_t) rowid,
- "diagnostic", diagnostic));
-}
-
-
-/* ************************* Transaction-global state ************************ */
-
-/**
- * Results about denominations, cached per-transaction, maps denomination pub hashes
- * to `struct TALER_DenominationKeyValidityPS`.
- */
-static struct GNUNET_CONTAINER_MultiHashMap *denominations;
-
-
-/**
- * Function called with the results of select_denomination_info()
- *
- * @param cls closure, NULL
- * @param issue issuing information with value, fees and other info about the denomination.
- * @return #GNUNET_OK (to continue)
- */
-static int
-add_denomination (void *cls,
- const struct TALER_DenominationKeyValidityPS *issue)
-{
- struct TALER_DenominationKeyValidityPS *i;
-
- (void) cls;
- if (NULL !=
- GNUNET_CONTAINER_multihashmap_get (denominations,
- &issue->denom_hash))
- return GNUNET_OK; /* value already known */
- {
- struct TALER_Amount value;
-
- TALER_amount_ntoh (&value,
- &issue->value);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Tracking denomination `%s' (%s)\n",
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (&value));
- TALER_amount_ntoh (&value,
- &issue->fee_withdraw);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Withdraw fee is %s\n",
- TALER_amount2s (&value));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Start time is %s\n",
- GNUNET_STRINGS_absolute_time_to_string
- (GNUNET_TIME_absolute_ntoh (issue->start)));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Expire deposit time is %s\n",
- GNUNET_STRINGS_absolute_time_to_string
- (GNUNET_TIME_absolute_ntoh (issue->expire_deposit)));
- }
- i = GNUNET_new (struct TALER_DenominationKeyValidityPS);
- *i = *issue;
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (denominations,
- &issue->denom_hash,
- i,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- return GNUNET_OK;
-}
-
-
-/**
- * Obtain information about a @a denom_pub.
- *
- * @param dh hash of the denomination public key to look up
- * @param[out] issue set to detailed information about @a denom_pub, NULL if not found, must
- * NOT be freed by caller
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-get_denomination_info_by_hash (const struct GNUNET_HashCode *dh,
- const struct
- TALER_DenominationKeyValidityPS **issue)
-{
- const struct TALER_DenominationKeyValidityPS *i;
- enum GNUNET_DB_QueryStatus qs;
-
- if (NULL == denominations)
- {
- denominations = GNUNET_CONTAINER_multihashmap_create (256,
- GNUNET_NO);
- qs = adb->select_denomination_info (adb->cls,
- asession,
- &master_pub,
- &add_denomination,
- NULL);
- if (0 > qs)
- {
- *issue = NULL;
- return qs;
- }
- }
- i = GNUNET_CONTAINER_multihashmap_get (denominations,
- dh);
- if (NULL != i)
- {
- /* cache hit */
- *issue = i;
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- }
- /* maybe database changed since we last iterated, give it one more shot */
- qs = adb->select_denomination_info (adb->cls,
- asession,
- &master_pub,
- &add_denomination,
- NULL);
- if (qs <= 0)
- {
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Denomination %s not found\n",
- TALER_B2S (dh));
- return qs;
- }
- i = GNUNET_CONTAINER_multihashmap_get (denominations,
- dh);
- if (NULL != i)
- {
- /* cache hit */
- *issue = i;
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- }
- /* We found more keys, but not the denomination we are looking for :-( */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Denomination %s not found\n",
- TALER_B2S (dh));
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
-}
-
-
-/**
- * Obtain information about a @a denom_pub.
- *
- * @param denom_pub key to look up
- * @param[out] issue set to detailed information about @a denom_pub, NULL if not found, must
- * NOT be freed by caller
- * @param[out] dh set to the hash of @a denom_pub, may be NULL
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-get_denomination_info (const struct TALER_DenominationPublicKey *denom_pub,
- const struct
- TALER_DenominationKeyValidityPS **issue,
- struct GNUNET_HashCode *dh)
-{
- struct GNUNET_HashCode hc;
-
- if (NULL == dh)
- dh = &hc;
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
- dh);
- return get_denomination_info_by_hash (dh,
- issue);
-}
-
-
-/**
- * Free denomination key information.
- *
- * @param cls NULL
- * @param key unused
- * @param value the `struct TALER_EXCHANGEDB_DenominationKeyInformationP *` to free
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-free_dk_info (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- struct TALER_DenominationKeyValidityPS *issue = value;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Done with denomination `%s'\n",
- GNUNET_h2s (key));
- GNUNET_free (issue);
- return GNUNET_OK;
-}
-
-
-/**
- * Purge transaction global state cache, the transaction is
- * done and we do not want to have the state cross over to
- * the next transaction.
- */
-static void
-clear_transaction_state_cache ()
-{
- if (NULL == denominations)
- return;
- GNUNET_CONTAINER_multihashmap_iterate (denominations,
- &free_dk_info,
- NULL);
- GNUNET_CONTAINER_multihashmap_destroy (denominations);
- denominations = NULL;
-}
-
-
-/* ***************************** Analyze reserves ************************ */
-/* This logic checks the reserves_in, reserves_out and reserves-tables */
-
-/**
- * Summary data we keep per reserve.
- */
-struct ReserveSummary
-{
- /**
- * Public key of the reserve.
- * Always set when the struct is first initialized.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- /**
- * Sum of all incoming transfers during this transaction.
- * Updated only in #handle_reserve_in().
- */
- struct TALER_Amount total_in;
-
- /**
- * Sum of all outgoing transfers during this transaction (includes fees).
- * Updated only in #handle_reserve_out().
- */
- struct TALER_Amount total_out;
-
- /**
- * Sum of withdraw fees encountered during this transaction.
- */
- struct TALER_Amount total_fee;
-
- /**
- * Previous balance of the reserve as remembered by the auditor.
- * (updated based on @e total_in and @e total_out at the end).
- */
- struct TALER_Amount a_balance;
-
- /**
- * Previous withdraw fee balance of the reserve, as remembered by the auditor.
- * (updated based on @e total_fee at the end).
- */
- struct TALER_Amount a_withdraw_fee_balance;
-
- /**
- * Previous reserve expiration data, as remembered by the auditor.
- * (updated on-the-fly in #handle_reserve_in()).
- */
- struct GNUNET_TIME_Absolute a_expiration_date;
-
- /**
- * Which account did originally put money into the reserve?
- */
- char *sender_account;
-
- /**
- * Did we have a previous reserve info? Used to decide between
- * UPDATE and INSERT later. Initialized in
- * #load_auditor_reserve_summary() together with the a-* values
- * (if available).
- */
- int had_ri;
-
-};
-
-
-/**
- * Load the auditor's remembered state about the reserve into @a rs.
- * The "total_in" and "total_out" amounts of @a rs must already be
- * initialized (so we can determine the currency).
- *
- * @param[in,out] rs reserve summary to (fully) initialize
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-load_auditor_reserve_summary (struct ReserveSummary *rs)
-{
- enum GNUNET_DB_QueryStatus qs;
- uint64_t rowid;
-
- qs = adb->get_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &rowid,
- &rs->a_balance,
- &rs->a_withdraw_fee_balance,
- &rs->a_expiration_date,
- &rs->sender_account);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- rs->had_ri = GNUNET_NO;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (rs->total_in.currency,
- &rs->a_balance));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (rs->total_in.currency,
- &rs->a_withdraw_fee_balance));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Creating fresh reserve `%s' with starting balance %s\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&rs->a_balance));
- return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
- }
- rs->had_ri = GNUNET_YES;
- if ( (GNUNET_YES !=
- TALER_amount_cmp_currency (&rs->a_balance,
- &rs->a_withdraw_fee_balance)) ||
- (GNUNET_YES !=
- TALER_amount_cmp_currency (&rs->total_in,
- &rs->a_balance)) )
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Auditor remembers reserve `%s' has balance %s\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&rs->a_balance));
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/**
- * Closure to the various callbacks we make while checking a reserve.
- */
-struct ReserveContext
-{
- /**
- * Map from hash of reserve's public key to a `struct ReserveSummary`.
- */
- struct GNUNET_CONTAINER_MultiHashMap *reserves;
-
- /**
- * Map from hash of denomination's public key to a
- * static string "revoked" for keys that have been revoked,
- * or "master signature invalid" in case the revocation is
- * there but bogus.
- */
- struct GNUNET_CONTAINER_MultiHashMap *revoked;
-
- /**
- * Transaction status code, set to error codes if applicable.
- */
- enum GNUNET_DB_QueryStatus qs;
-
-};
-
-
-/**
- * Function called with details about incoming wire transfers.
- *
- * @param cls our `struct ReserveContext`
- * @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 reference identifying the wire transfer
- * @param execution_date when did we receive the funds
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-handle_reserve_in (void *cls,
- uint64_t rowid,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_Amount *credit,
- const char *sender_account_details,
- uint64_t wire_reference,
- struct GNUNET_TIME_Absolute execution_date)
-{
- struct ReserveContext *rc = cls;
- struct GNUNET_HashCode key;
- struct ReserveSummary *rs;
- struct GNUNET_TIME_Absolute expiry;
- enum GNUNET_DB_QueryStatus qs;
-
- (void) wire_reference;
- /* should be monotonically increasing */
- GNUNET_assert (rowid >= ppr.last_reserve_in_serial_id);
- ppr.last_reserve_in_serial_id = rowid + 1;
-
- GNUNET_CRYPTO_hash (reserve_pub,
- sizeof (*reserve_pub),
- &key);
- rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
- &key);
- if (NULL == rs)
- {
- rs = GNUNET_new (struct ReserveSummary);
- rs->sender_account = GNUNET_strdup (sender_account_details);
- rs->reserve_pub = *reserve_pub;
- rs->total_in = *credit;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (credit->currency,
- &rs->total_out));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (credit->currency,
- &rs->total_fee));
- if (0 > (qs = load_auditor_reserve_summary (rs)))
- {
- GNUNET_break (0);
- GNUNET_free (rs);
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (rc->reserves,
- &key,
- rs,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- else
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_in,
- &rs->total_in,
- credit));
- if (NULL == rs->sender_account)
- rs->sender_account = GNUNET_strdup (sender_account_details);
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Additional incoming wire transfer for reserve `%s' of %s\n",
- TALER_B2S (reserve_pub),
- TALER_amount2s (credit));
- expiry = GNUNET_TIME_absolute_add (execution_date,
- idle_reserve_expiration_time);
- rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
- expiry);
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about withdraw operations. Verifies
- * the signature and updates the reserve's balance.
- *
- * @param cls our `struct ReserveContext`
- * @param rowid unique serial ID for the refresh session in our DB
- * @param h_blind_ev blinded hash of the coin's public key
- * @param denom_pub public denomination key of the deposited coin
- * @param reserve_pub public key of the reserve
- * @param reserve_sig signature over the withdraw operation
- * @param execution_date when did the wallet withdraw the coin
- * @param amount_with_fee amount that was withdrawn
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-handle_reserve_out (void *cls,
- uint64_t rowid,
- const struct GNUNET_HashCode *h_blind_ev,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_ReserveSignatureP *reserve_sig,
- struct GNUNET_TIME_Absolute execution_date,
- const struct TALER_Amount *amount_with_fee)
-{
- struct ReserveContext *rc = cls;
- struct TALER_WithdrawRequestPS wsrd;
- struct GNUNET_HashCode key;
- struct ReserveSummary *rs;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct TALER_Amount withdraw_fee;
- struct GNUNET_TIME_Absolute valid_start;
- struct GNUNET_TIME_Absolute expire_withdraw;
- enum GNUNET_DB_QueryStatus qs;
-
- /* should be monotonically increasing */
- GNUNET_assert (rowid >= ppr.last_reserve_out_serial_id);
- ppr.last_reserve_out_serial_id = rowid + 1;
-
- /* lookup denomination pub data (make sure denom_pub is valid, establish fees) */
- qs = get_denomination_info (denom_pub,
- &issue,
- &wsrd.h_denomination_pub);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Hard database error trying to get denomination %s (%s) from database!\n",
- TALER_B2S (denom_pub),
- TALER_amount2s (amount_with_fee));
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("withdraw",
- rowid,
- "denomination key not found");
- return GNUNET_OK;
- }
-
- /* check that execution date is within withdraw range for denom_pub */
- valid_start = GNUNET_TIME_absolute_ntoh (issue->start);
- expire_withdraw = GNUNET_TIME_absolute_ntoh (issue->expire_withdraw);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
- (unsigned long long) valid_start.abs_value_us,
- (unsigned long long) expire_withdraw.abs_value_us,
- (unsigned long long) execution_date.abs_value_us);
- if ( (valid_start.abs_value_us > execution_date.abs_value_us) ||
- (expire_withdraw.abs_value_us < execution_date.abs_value_us) )
- {
- report (denomination_key_validity_withdraw_inconsistencies,
- json_pack ("{s:I, s:o, s:o, s:o}",
- "row", (json_int_t) rowid,
- "execution_date",
- json_from_time_abs (execution_date),
- "reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub),
- "denompub_h", GNUNET_JSON_from_data_auto (
- &wsrd.h_denomination_pub)));
- }
-
- /* check reserve_sig */
- wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
- wsrd.purpose.size = htonl (sizeof (wsrd));
- wsrd.reserve_pub = *reserve_pub;
- TALER_amount_hton (&wsrd.amount_with_fee,
- amount_with_fee);
- wsrd.withdraw_fee = issue->fee_withdraw;
- wsrd.h_coin_envelope = *h_blind_ev;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
- &wsrd.purpose,
- &reserve_sig->eddsa_signature,
- &reserve_pub->eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "withdraw",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount_with_fee),
- "key_pub", GNUNET_JSON_from_data_auto (reserve_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee));
- return GNUNET_OK;
- }
-
- GNUNET_CRYPTO_hash (reserve_pub,
- sizeof (*reserve_pub),
- &key);
- rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
- &key);
- if (NULL == rs)
- {
- rs = GNUNET_new (struct ReserveSummary);
- rs->reserve_pub = *reserve_pub;
- rs->total_out = *amount_with_fee;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount_with_fee->currency,
- &rs->total_in));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount_with_fee->currency,
- &rs->total_fee));
- qs = load_auditor_reserve_summary (rs);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_free (rs);
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (rc->reserves,
- &key,
- rs,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- else
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_out,
- &rs->total_out,
- amount_with_fee));
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Reserve `%s' reduced by %s from withdraw\n",
- TALER_B2S (reserve_pub),
- TALER_amount2s (amount_with_fee));
- TALER_amount_ntoh (&withdraw_fee,
- &issue->fee_withdraw);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Increasing withdraw profits by fee %s\n",
- TALER_amount2s (&withdraw_fee));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_fee,
- &rs->total_fee,
- &withdraw_fee));
-
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about withdraw operations. Verifies
- * the signature and updates the reserve's balance.
- *
- * @param cls our `struct ReserveContext`
- * @param rowid unique serial ID for the refresh session in our DB
- * @param timestamp when did we receive the recoup request
- * @param amount how much should be added back to the reserve
- * @param reserve_pub public key of the reserve
- * @param coin public information about the coin, denomination signature is
- * already verified in #check_recoup()
- * @param denom_pub public key of the denomionation of @a coin
- * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
- * @param coin_blind blinding factor used to blind the coin
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-handle_recoup_by_reserve (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute timestamp,
- const struct TALER_Amount *amount,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct
- TALER_DenominationBlindingKeyP *coin_blind)
-{
- struct ReserveContext *rc = cls;
- struct GNUNET_HashCode key;
- struct ReserveSummary *rs;
- struct GNUNET_TIME_Absolute expiry;
- struct TALER_RecoupRequestPS pr;
- struct TALER_MasterSignatureP msig;
- uint64_t rev_rowid;
- enum GNUNET_DB_QueryStatus qs;
- const char *rev;
-
- (void) denom_pub;
- /* should be monotonically increasing */
- GNUNET_assert (rowid >= ppr.last_reserve_recoup_serial_id);
- ppr.last_reserve_recoup_serial_id = rowid + 1;
- /* We know that denom_pub matches denom_pub_hash because this
- is how the SQL statement joined the tables. */
- pr.h_denom_pub = coin->denom_pub_hash;
- pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP);
- pr.purpose.size = htonl (sizeof (pr));
- pr.coin_pub = coin->coin_pub;
- pr.coin_blind = *coin_blind;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
- &pr.purpose,
- &coin_sig->eddsa_signature,
- &coin->coin_pub.eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "recoup",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount),
- "key_pub", GNUNET_JSON_from_data_auto (
- &coin->coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount));
- }
-
- /* check that the coin was eligible for recoup!*/
- rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
- &pr.h_denom_pub);
- if (NULL == rev)
- {
- qs = edb->get_denomination_revocation (edb->cls,
- esession,
- &pr.h_denom_pub,
- &msig,
- &rev_rowid);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("recoup",
- rowid,
- "denomination key not in revocation set");
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_irregular_recoups,
- &total_irregular_recoups,
- amount));
- }
- else
- {
- /* verify msig */
- struct TALER_MasterDenominationKeyRevocationPS kr;
-
- kr.purpose.purpose = htonl (
- TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED);
- kr.purpose.size = htonl (sizeof (kr));
- kr.h_denom_pub = pr.h_denom_pub;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (
- TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED,
- &kr.purpose,
- &msig.eddsa_signature,
- &master_pub.eddsa_pub))
- {
- rev = "master signature invalid";
- }
- else
- {
- rev = "revoked";
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (rc->revoked,
- &pr.h_denom_pub,
- (void *) rev,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- }
- else
- {
- rev_rowid = 0; /* reported elsewhere */
- }
- if ( (NULL != rev) &&
- (0 == strcmp (rev, "master signature invalid")) )
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "recoup-master",
- "row", (json_int_t) rev_rowid,
- "loss", TALER_JSON_from_amount (amount),
- "key_pub", GNUNET_JSON_from_data_auto (&master_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount));
- }
-
- GNUNET_CRYPTO_hash (reserve_pub,
- sizeof (*reserve_pub),
- &key);
- rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
- &key);
- if (NULL == rs)
- {
- rs = GNUNET_new (struct ReserveSummary);
- rs->reserve_pub = *reserve_pub;
- rs->total_in = *amount;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount->currency,
- &rs->total_out));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount->currency,
- &rs->total_fee));
- qs = load_auditor_reserve_summary (rs);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_free (rs);
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (rc->reserves,
- &key,
- rs,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- else
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_in,
- &rs->total_in,
- amount));
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Additional /recoup value to for reserve `%s' of %s\n",
- TALER_B2S (reserve_pub),
- TALER_amount2s (amount));
- expiry = GNUNET_TIME_absolute_add (timestamp,
- idle_reserve_expiration_time);
- rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
- expiry);
- return GNUNET_OK;
-}
-
-
-/**
- * Obtain the closing fee for a transfer at @a time for target
- * @a receiver_account.
- *
- * @param receiver_account payto:// URI of the target account
- * @param atime when was the transfer made
- * @param[out] fee set to the closing fee
- * @return #GNUNET_OK on success
- */
-static int
-get_closing_fee (const char *receiver_account,
- struct GNUNET_TIME_Absolute atime,
- struct TALER_Amount *fee)
-{
- struct TALER_MasterSignatureP master_sig;
- struct GNUNET_TIME_Absolute start_date;
- struct GNUNET_TIME_Absolute end_date;
- struct TALER_Amount wire_fee;
- char *method;
-
- method = TALER_payto_get_method (receiver_account);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Method is `%s'\n",
- method);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- edb->get_wire_fee (edb->cls,
- esession,
- method,
- atime,
- &start_date,
- &end_date,
- &wire_fee,
- fee,
- &master_sig))
- {
- report_row_inconsistency ("closing-fee",
- atime.abs_value_us,
- "closing fee unavailable at given time");
- GNUNET_free (method);
- return GNUNET_SYSERR;
- }
- GNUNET_free (method);
- return GNUNET_OK;
-}
-
-
-/**
- * Function called about reserve closing operations
- * the aggregator triggered.
- *
- * @param cls closure
- * @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
- * @param closing_fee how much did we charge for closing the reserve
- * @param reserve_pub public key of the reserve
- * @param receiver_account where did we send the funds
- * @param transfer_details details about the wire transfer
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-handle_reserve_closed (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute 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 *transfer_details)
-{
- struct ReserveContext *rc = cls;
- struct GNUNET_HashCode key;
- struct ReserveSummary *rs;
- enum GNUNET_DB_QueryStatus qs;
-
- (void) transfer_details;
- /* should be monotonically increasing */
- GNUNET_assert (rowid >= ppr.last_reserve_close_serial_id);
- ppr.last_reserve_close_serial_id = rowid + 1;
-
- GNUNET_CRYPTO_hash (reserve_pub,
- sizeof (*reserve_pub),
- &key);
- rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
- &key);
- if (NULL == rs)
- {
- rs = GNUNET_new (struct ReserveSummary);
- rs->reserve_pub = *reserve_pub;
- rs->total_out = *amount_with_fee;
- rs->total_fee = *closing_fee;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount_with_fee->currency,
- &rs->total_in));
- qs = load_auditor_reserve_summary (rs);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_free (rs);
- rc->qs = qs;
- return GNUNET_SYSERR;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (rc->reserves,
- &key,
- rs,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- else
- {
- struct TALER_Amount expected_fee;
-
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_out,
- &rs->total_out,
- amount_with_fee));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&rs->total_fee,
- &rs->total_fee,
- closing_fee));
- /* verify closing_fee is correct! */
- if (GNUNET_OK !=
- get_closing_fee (receiver_account,
- execution_date,
- &expected_fee))
- {
- GNUNET_break (0);
- }
- else if (0 != TALER_amount_cmp (&expected_fee,
- closing_fee))
- {
- report_amount_arithmetic_inconsistency ("closing aggregation fee",
- rowid,
- closing_fee,
- &expected_fee,
- 1);
- }
- }
- if (NULL == rs->sender_account)
- {
- GNUNET_break (GNUNET_NO == rs->had_ri);
- report_row_inconsistency ("reserves_close",
- rowid,
- "target account not verified, auditor does not know reserve");
- }
- else if (0 != strcmp (rs->sender_account,
- receiver_account))
- {
- report_row_inconsistency ("reserves_close",
- rowid,
- "target account does not match origin account");
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Additional closing operation for reserve `%s' of %s\n",
- TALER_B2S (reserve_pub),
- TALER_amount2s (amount_with_fee));
- return GNUNET_OK;
-}
-
-
-/**
- * Check that the reserve summary matches what the exchange database
- * thinks about the reserve, and update our own state of the reserve.
- *
- * Remove all reserves that we are happy with from the DB.
- *
- * @param cls our `struct ReserveContext`
- * @param key hash of the reserve public key
- * @param value a `struct ReserveSummary`
- * @return #GNUNET_OK to process more entries
- */
-static int
-verify_reserve_balance (void *cls,
- const struct GNUNET_HashCode *key,
- void *value)
-{
- struct ReserveContext *rc = cls;
- struct ReserveSummary *rs = value;
- struct TALER_EXCHANGEDB_Reserve reserve;
- struct TALER_Amount balance;
- struct TALER_Amount nbalance;
- struct TALER_Amount cfee;
- enum GNUNET_DB_QueryStatus qs;
- int ret;
-
- ret = GNUNET_OK;
- reserve.pub = rs->reserve_pub;
- qs = edb->reserves_get (edb->cls,
- esession,
- &reserve);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- char *diag;
-
- GNUNET_asprintf (&diag,
- "Failed to find summary for reserve `%s'\n",
- TALER_B2S (&rs->reserve_pub));
- report_row_inconsistency ("reserve-summary",
- UINT64_MAX,
- diag);
- GNUNET_free (diag);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- GNUNET_break (0);
- qs = GNUNET_DB_STATUS_HARD_ERROR;
- }
- rc->qs = qs;
- return GNUNET_OK;
- }
-
- if (GNUNET_OK !=
- TALER_amount_add (&balance,
- &rs->total_in,
- &rs->a_balance))
- {
- GNUNET_break (0);
- goto cleanup;
- }
-
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&nbalance,
- &balance,
- &rs->total_out))
- {
- struct TALER_Amount loss;
-
- GNUNET_break (GNUNET_SYSERR !=
- TALER_amount_subtract (&loss,
- &rs->total_out,
- &balance));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_balance_insufficient_loss,
- &total_balance_insufficient_loss,
- &loss));
- report (report_reserve_balance_insufficient_inconsistencies,
- json_pack ("{s:o, s:o}",
- "reserve_pub",
- GNUNET_JSON_from_data_auto (&rs->reserve_pub),
- "loss",
- TALER_JSON_from_amount (&loss)));
- goto cleanup;
- }
- if (0 != TALER_amount_cmp (&nbalance,
- &reserve.balance))
- {
- struct TALER_Amount delta;
-
- if (0 < TALER_amount_cmp (&nbalance,
- &reserve.balance))
- {
- /* balance > reserve.balance */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- &nbalance,
- &reserve.balance));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_balance_summary_delta_plus,
- &total_balance_summary_delta_plus,
- &delta));
- }
- else
- {
- /* balance < reserve.balance */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- &reserve.balance,
- &nbalance));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_balance_summary_delta_minus,
- &total_balance_summary_delta_minus,
- &delta));
- }
- report (report_reserve_balance_summary_wrong_inconsistencies,
- json_pack ("{s:o, s:o, s:o}",
- "reserve_pub",
- GNUNET_JSON_from_data_auto (&rs->reserve_pub),
- "exchange",
- TALER_JSON_from_amount (&reserve.balance),
- "auditor",
- TALER_JSON_from_amount (&nbalance)));
- goto cleanup;
- }
-
- /* Check that reserve is being closed if it is past its expiration date */
-
- if (CLOSING_GRACE_PERIOD.rel_value_us <
- GNUNET_TIME_absolute_get_duration (rs->a_expiration_date).rel_value_us)
- {
- if ( (NULL != rs->sender_account) &&
- (GNUNET_OK ==
- get_closing_fee (rs->sender_account,
- rs->a_expiration_date,
- &cfee)) )
- {
- if (1 == TALER_amount_cmp (&nbalance,
- &cfee))
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_balance_reserve_not_closed,
- &total_balance_reserve_not_closed,
- &nbalance));
- report (report_reserve_not_closed_inconsistencies,
- json_pack ("{s:o, s:o, s:o}",
- "reserve_pub",
- GNUNET_JSON_from_data_auto (&rs->reserve_pub),
- "balance",
- TALER_JSON_from_amount (&nbalance),
- "expiration_time",
- json_from_time_abs (rs->a_expiration_date)));
- }
- }
- else
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_balance_reserve_not_closed,
- &total_balance_reserve_not_closed,
- &nbalance));
- report (report_reserve_not_closed_inconsistencies,
- json_pack ("{s:o, s:o, s:o, s:s}",
- "reserve_pub",
- GNUNET_JSON_from_data_auto (&rs->reserve_pub),
- "balance",
- TALER_JSON_from_amount (&nbalance),
- "expiration_time",
- json_from_time_abs (rs->a_expiration_date),
- "diagnostic",
- "could not determine closing fee"));
- }
- }
-
- /* Add withdraw fees we encountered to totals */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Reserve reserve `%s' made %s in withdraw fees\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&rs->total_fee));
- if (GNUNET_YES !=
- TALER_amount_add (&rs->a_withdraw_fee_balance,
- &rs->a_withdraw_fee_balance,
- &rs->total_fee))
- {
- GNUNET_break (0);
- ret = GNUNET_SYSERR;
- goto cleanup;
- }
- if ( (GNUNET_YES !=
- TALER_amount_add (&total_escrow_balance,
- &total_escrow_balance,
- &rs->total_in)) ||
- (GNUNET_SYSERR ==
- TALER_amount_subtract (&total_escrow_balance,
- &total_escrow_balance,
- &rs->total_out)) ||
- (GNUNET_YES !=
- TALER_amount_add (&total_withdraw_fee_income,
- &total_withdraw_fee_income,
- &rs->total_fee)) )
- {
- GNUNET_break (0);
- ret = GNUNET_SYSERR;
- goto cleanup;
- }
-
- if ( (0ULL == balance.value) &&
- (0U == balance.fraction) )
- {
- /* balance is zero, drop reserve details (and then do not update/insert) */
- if (rs->had_ri)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Final balance of reserve `%s' is %s, dropping it\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&nbalance));
- qs = adb->del_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub);
- if (0 >= qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- ret = GNUNET_SYSERR;
- rc->qs = qs;
- goto cleanup;
- }
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Final balance of reserve `%s' is %s, no need to remember it\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&nbalance));
- }
- ret = GNUNET_OK;
- goto cleanup;
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Remembering final balance of reserve `%s' as %s\n",
- TALER_B2S (&rs->reserve_pub),
- TALER_amount2s (&nbalance));
-
- if (rs->had_ri)
- qs = adb->update_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &nbalance,
- &rs->a_withdraw_fee_balance,
- rs->a_expiration_date);
- else
- qs = adb->insert_reserve_info (adb->cls,
- asession,
- &rs->reserve_pub,
- &master_pub,
- &nbalance,
- &rs->a_withdraw_fee_balance,
- rs->a_expiration_date,
- rs->sender_account);
- if (0 >= qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- ret = GNUNET_SYSERR;
- rc->qs = qs;
- }
-cleanup:
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
- key,
- rs));
- GNUNET_free_non_null (rs->sender_account);
- GNUNET_free (rs);
- return ret;
-}
-
-
-/**
- * Analyze reserves for being well-formed.
- *
- * @param cls NULL
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-analyze_reserves (void *cls)
-{
- struct ReserveContext rc;
- enum GNUNET_DB_QueryStatus qsx;
- enum GNUNET_DB_QueryStatus qs;
- enum GNUNET_DB_QueryStatus qsp;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Analyzing reserves\n");
- qsp = adb->get_auditor_progress_reserve (adb->cls,
- asession,
- &master_pub,
- &ppr);
- 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
- {
- ppr_start = ppr;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Resuming reserve audit at %llu/%llu/%llu/%llu\n"),
- (unsigned long long) ppr.last_reserve_in_serial_id,
- (unsigned long long) ppr.last_reserve_out_serial_id,
- (unsigned long long) ppr.last_reserve_recoup_serial_id,
- (unsigned long long) ppr.last_reserve_close_serial_id);
- }
- rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- qsx = adb->get_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_withdraw_fee_income);
- if (qsx < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
- return qsx;
- }
- rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
- GNUNET_NO);
- rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
- GNUNET_NO);
-
- qs = edb->select_reserves_in_above_serial_id (edb->cls,
- esession,
- ppr.last_reserve_in_serial_id,
- &handle_reserve_in,
- &rc);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- qs = edb->select_withdrawals_above_serial_id (edb->cls,
- esession,
- ppr.last_reserve_out_serial_id,
- &handle_reserve_out,
- &rc);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- qs = edb->select_recoup_above_serial_id (edb->cls,
- esession,
- ppr.last_reserve_recoup_serial_id,
- &handle_recoup_by_reserve,
- &rc);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- qs = edb->select_reserve_closed_above_serial_id (edb->cls,
- esession,
- ppr.
- last_reserve_close_serial_id,
- &handle_reserve_closed,
- &rc);
- if (qs < 0)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
-
- GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
- &verify_reserve_balance,
- &rc);
- GNUNET_break (0 ==
- GNUNET_CONTAINER_multihashmap_size (rc.reserves));
- GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
- GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
-
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs)
- return qs;
-
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
- {
- qs = adb->insert_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_withdraw_fee_income);
- }
- else
- {
- qs = adb->update_reserve_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_withdraw_fee_income);
- }
- if (0 >= qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
- qs = adb->update_auditor_progress_reserve (adb->cls,
- asession,
- &master_pub,
- &ppr);
- else
- qs = adb->insert_auditor_progress_reserve (adb->cls,
- asession,
- &master_pub,
- &ppr);
- 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;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Concluded reserve audit step at %llu/%llu/%llu/%llu\n"),
- (unsigned long long) ppr.last_reserve_in_serial_id,
- (unsigned long long) ppr.last_reserve_out_serial_id,
- (unsigned long long) ppr.last_reserve_recoup_serial_id,
- (unsigned long long) ppr.last_reserve_close_serial_id);
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/* *********************** Analyze aggregations ******************** */
-/* This logic checks that the aggregator did the right thing
- paying each merchant what they were due (and on time). */
-
-
-/**
- * Information about wire fees charged by the exchange.
- */
-struct WireFeeInfo
-{
-
- /**
- * Kept in a DLL.
- */
- struct WireFeeInfo *next;
-
- /**
- * Kept in a DLL.
- */
- struct WireFeeInfo *prev;
-
- /**
- * When does the fee go into effect (inclusive).
- */
- struct GNUNET_TIME_Absolute start_date;
-
- /**
- * When does the fee stop being in effect (exclusive).
- */
- struct GNUNET_TIME_Absolute end_date;
-
- /**
- * How high is the wire fee.
- */
- struct TALER_Amount wire_fee;
-
- /**
- * How high is the closing fee.
- */
- struct TALER_Amount closing_fee;
-
-};
-
-
-/**
- * Closure for callbacks during #analyze_merchants().
- */
-struct AggregationContext
-{
-
- /**
- * DLL of wire fees charged by the exchange.
- */
- struct WireFeeInfo *fee_head;
-
- /**
- * DLL of wire fees charged by the exchange.
- */
- struct WireFeeInfo *fee_tail;
-
- /**
- * Final result status.
- */
- enum GNUNET_DB_QueryStatus qs;
-};
-
-
-/**
- * Closure for #wire_transfer_information_cb.
- */
-struct WireCheckContext
-{
-
- /**
- * Corresponding merchant context.
- */
- struct AggregationContext *ac;
-
- /**
- * Total deposits claimed by all transactions that were aggregated
- * under the given @e wtid.
- */
- struct TALER_Amount total_deposits;
-
- /**
- * Hash of the wire transfer details of the receiver.
- */
- struct GNUNET_HashCode h_wire;
-
- /**
- * Execution time of the wire transfer.
- */
- struct GNUNET_TIME_Absolute date;
-
- /**
- * Database transaction status.
- */
- enum GNUNET_DB_QueryStatus qs;
-
-};
-
-
-/**
- * Check coin's transaction history for plausibility. Does NOT check
- * the signatures (those are checked independently), but does calculate
- * the amounts for the aggregation table and checks that the total
- * claimed coin value is within the value of the coin's denomination.
- *
- * @param coin_pub public key of the coin (for reporting)
- * @param h_contract_terms hash of the proposal for which we calculate the amount
- * @param merchant_pub public key of the merchant (who is allowed to issue refunds)
- * @param issue denomination information about the coin
- * @param tl_head head of transaction history to verify
- * @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
- * @param[out] deposit_gain amount the coin contributes excluding refunds
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-check_transaction_history_for_deposit (const struct
- TALER_CoinSpendPublicKeyP *coin_pub,
- const struct
- GNUNET_HashCode *h_contract_terms,
- const struct
- TALER_MerchantPublicKeyP *merchant_pub,
- const struct
- TALER_DenominationKeyValidityPS *issue,
- const struct
- TALER_EXCHANGEDB_TransactionList *tl_head,
- struct TALER_Amount *merchant_gain,
- struct TALER_Amount *deposit_gain)
-{
- struct TALER_Amount expenditures;
- struct TALER_Amount refunds;
- struct TALER_Amount spent;
- struct TALER_Amount value;
- struct TALER_Amount merchant_loss;
- struct TALER_Amount final_gain;
- const struct TALER_Amount *deposit_fee;
- int refund_deposit_fee;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Checking transaction history of coin %s\n",
- TALER_B2S (coin_pub));
-
- GNUNET_assert (NULL != tl_head);
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &expenditures));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &refunds));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- merchant_gain));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &merchant_loss));
- /* Go over transaction history to compute totals; note that we do not
- know the order, so instead of subtracting we compute positive
- (deposit, melt) and negative (refund) values separately here,
- and then subtract the negative from the positive after the loop. */
- refund_deposit_fee = GNUNET_NO;
- deposit_fee = NULL;
- for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;
- NULL != tl;
- tl = tl->next)
- {
- const struct TALER_Amount *amount_with_fee;
- const struct TALER_Amount *fee;
- const struct TALER_AmountNBO *fee_dki;
- struct TALER_Amount tmp;
-
- switch (tl->type)
- {
- case TALER_EXCHANGEDB_TT_DEPOSIT:
- /* check wire and h_wire are consistent */
- {
- struct GNUNET_HashCode hw;
-
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (
- tl->details.deposit->receiver_wire_account,
- &hw))
- {
- report_row_inconsistency ("deposits",
- tl->serial_id,
- "wire value malformed");
- }
- else if (0 !=
- GNUNET_memcmp (&hw,
- &tl->details.deposit->h_wire))
- {
- report_row_inconsistency ("deposits",
- tl->serial_id,
- "h(wire) does not match wire");
- }
- }
- amount_with_fee = &tl->details.deposit->amount_with_fee;
- fee = &tl->details.deposit->deposit_fee;
- fee_dki = &issue->fee_deposit;
- if (GNUNET_OK !=
- TALER_amount_add (&expenditures,
- &expenditures,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- /* Check if this deposit is within the remit of the aggregation
- we are investigating, if so, include it in the totals. */
- if ( (0 == GNUNET_memcmp (merchant_pub,
- &tl->details.deposit->merchant_pub)) &&
- (0 == GNUNET_memcmp (h_contract_terms,
- &tl->details.deposit->h_contract_terms)) )
- {
- struct TALER_Amount amount_without_fee;
-
- if (GNUNET_OK !=
- TALER_amount_subtract (&amount_without_fee,
- amount_with_fee,
- fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (merchant_gain,
- merchant_gain,
- &amount_without_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Detected applicable deposit of %s\n",
- TALER_amount2s (&amount_without_fee));
- deposit_fee = fee;
- }
- /* Check that the fees given in the transaction list and in dki match */
- TALER_amount_ntoh (&tmp,
- fee_dki);
- if (0 !=
- TALER_amount_cmp (&tmp,
- fee))
- {
- /* Disagreement in fee structure within DB, should be impossible! */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- case TALER_EXCHANGEDB_TT_MELT:
- amount_with_fee = &tl->details.melt->amount_with_fee;
- fee = &tl->details.melt->melt_fee;
- fee_dki = &issue->fee_refresh;
- if (GNUNET_OK !=
- TALER_amount_add (&expenditures,
- &expenditures,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- /* Check that the fees given in the transaction list and in dki match */
- TALER_amount_ntoh (&tmp,
- fee_dki);
- if (0 !=
- TALER_amount_cmp (&tmp,
- fee))
- {
- /* Disagreement in fee structure within DB, should be impossible! */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- case TALER_EXCHANGEDB_TT_REFUND:
- amount_with_fee = &tl->details.refund->refund_amount;
- fee = &tl->details.refund->refund_fee;
- fee_dki = &issue->fee_refund;
- if (GNUNET_OK !=
- TALER_amount_add (&refunds,
- &refunds,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&expenditures,
- &expenditures,
- fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- /* Check if this refund is within the remit of the aggregation
- we are investigating, if so, include it in the totals. */
- if ( (0 == GNUNET_memcmp (merchant_pub,
- &tl->details.refund->merchant_pub)) &&
- (0 == GNUNET_memcmp (h_contract_terms,
- &tl->details.refund->h_contract_terms)) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Detected applicable refund of %s\n",
- TALER_amount2s (amount_with_fee));
- if (GNUNET_OK !=
- TALER_amount_add (&merchant_loss,
- &merchant_loss,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- refund_deposit_fee = GNUNET_YES;
- }
- /* Check that the fees given in the transaction list and in dki match */
- TALER_amount_ntoh (&tmp,
- fee_dki);
- if (0 !=
- TALER_amount_cmp (&tmp,
- fee))
- {
- /* Disagreement in fee structure within DB, should be impossible! */
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
- amount_with_fee = &tl->details.old_coin_recoup->value;
- if (GNUNET_OK !=
- TALER_amount_add (&refunds,
- &refunds,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- case TALER_EXCHANGEDB_TT_RECOUP:
- amount_with_fee = &tl->details.recoup->value;
- if (GNUNET_OK !=
- TALER_amount_add (&expenditures,
- &expenditures,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
- amount_with_fee = &tl->details.recoup_refresh->value;
- if (GNUNET_OK !=
- TALER_amount_add (&expenditures,
- &expenditures,
- amount_with_fee))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- break;
- }
- } /* for 'tl' */
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Deposits without fees are %s\n",
- TALER_amount2s (merchant_gain));
-
- /* Calculate total balance change, i.e. expenditures (recoup, deposit, refresh)
- minus refunds (refunds, recoup-to-old) */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Subtracting refunds of %s from coin value loss\n",
- TALER_amount2s (&refunds));
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&spent,
- &expenditures,
- &refunds))
- {
- /* refunds above expenditures? Bad! */
- report_coin_arithmetic_inconsistency ("refund (balance)",
- coin_pub,
- &expenditures,
- &refunds,
- 1);
- return GNUNET_SYSERR;
- }
-
- /* Now check that 'spent' is less or equal than the total coin value */
- TALER_amount_ntoh (&value,
- &issue->value);
- if (1 == TALER_amount_cmp (&spent,
- &value))
- {
- /* spent > value */
- report_coin_arithmetic_inconsistency ("spend",
- coin_pub,
- &spent,
- &value,
- -1);
- return GNUNET_SYSERR;
- }
-
- /* Finally, update @a merchant_gain by subtracting what he "lost"
- from refunds */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Merchant 'loss' due to refunds is %s\n",
- TALER_amount2s (&merchant_loss));
- *deposit_gain = *merchant_gain;
- if ( (GNUNET_YES == refund_deposit_fee) &&
- (NULL != deposit_fee) )
- {
- /* We had a /deposit operation AND a /refund operation,
- and should thus not charge the merchant the /deposit fee */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (merchant_gain,
- merchant_gain,
- deposit_fee));
- }
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&final_gain,
- merchant_gain,
- &merchant_loss))
- {
- /* refunds above deposits? Bad! */
- report_coin_arithmetic_inconsistency ("refund (merchant)",
- coin_pub,
- merchant_gain,
- &merchant_loss,
- 1);
- return GNUNET_SYSERR;
- }
- *merchant_gain = final_gain;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Final merchant gain after refunds is %s\n",
- TALER_amount2s (deposit_gain));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Coin %s contributes %s to contract %s\n",
- TALER_B2S (coin_pub),
- TALER_amount2s (merchant_gain),
- GNUNET_h2s (h_contract_terms));
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with the results of the lookup of the
- * transaction data associated with a wire transfer identifier.
- *
- * @param cls a `struct WireCheckContext`
- * @param rowid which row in the table is the information from (for diagnostics)
- * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
- * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
- * @param account_details where did we transfer the funds?
- * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
- * @param h_contract_terms which proposal was this payment about
- * @param denom_pub denomination of @a coin_pub
- * @param coin_pub which public key was this payment about
- * @param coin_value amount contributed by this coin in total (with fee),
- * but excluding refunds by this coin
- * @param deposit_fee applicable deposit fee for this coin, actual
- * fees charged may differ if coin was refunded
- */
-static void
-wire_transfer_information_cb (void *cls,
- uint64_t rowid,
- const struct
- TALER_MerchantPublicKeyP *merchant_pub,
- const struct GNUNET_HashCode *h_wire,
- const json_t *account_details,
- struct GNUNET_TIME_Absolute exec_time,
- const struct GNUNET_HashCode *h_contract_terms,
- const struct
- TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_Amount *coin_value,
- const struct TALER_Amount *deposit_fee)
-{
- struct WireCheckContext *wcc = cls;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct TALER_Amount computed_value;
- struct TALER_Amount coin_value_without_fee;
- struct TALER_Amount total_deposit_without_refunds;
- struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_CoinPublicInfo coin;
- enum GNUNET_DB_QueryStatus qs;
- struct GNUNET_HashCode hw;
-
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (account_details,
- &hw))
- {
- report_row_inconsistency ("aggregation",
- rowid,
- "failed to compute hash of given wire data");
- }
- else if (0 !=
- GNUNET_memcmp (&hw,
- h_wire))
- {
- report_row_inconsistency ("aggregation",
- rowid,
- "database contains wrong hash code for wire details");
- }
-
- /* Obtain coin's transaction history */
- qs = edb->get_coin_transactions (edb->cls,
- esession,
- coin_pub,
- GNUNET_YES,
- &tl);
- if ( (qs < 0) ||
- (NULL == tl) )
- {
- wcc->qs = qs;
- report_row_inconsistency ("aggregation",
- rowid,
- "no transaction history for coin claimed in aggregation");
- return;
- }
- qs = edb->get_known_coin (edb->cls,
- esession,
- coin_pub,
- &coin);
- if (qs < 0)
- {
- GNUNET_break (0); /* this should be a foreign key violation at this point! */
- wcc->qs = qs;
- report_row_inconsistency ("aggregation",
- rowid,
- "could not get coin details for coin claimed in aggregation");
- return;
- }
-
- qs = get_denomination_info_by_hash (&coin.denom_pub_hash,
- &issue);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
- edb->free_coin_transaction_list (edb->cls,
- tl);
- if (0 == qs)
- report_row_inconsistency ("aggregation",
- rowid,
- "could not find denomination key for coin claimed in aggregation");
- else
- wcc->qs = qs;
- return;
- }
- if (GNUNET_OK !=
- TALER_test_coin_valid (&coin,
- denom_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "wire",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (coin_value),
- "key_pub", GNUNET_JSON_from_data_auto (
- &issue->denom_hash)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- coin_value));
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
- edb->free_coin_transaction_list (edb->cls,
- tl);
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("deposit",
- rowid,
- "coin denomination signature invalid");
- return;
- }
- GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
- coin.denom_sig.rsa_signature = NULL; /* just to be sure */
- GNUNET_assert (NULL != issue); /* mostly to help static analysis */
- /* Check transaction history to see if it supports aggregate
- valuation */
- if (GNUNET_OK !=
- check_transaction_history_for_deposit (coin_pub,
- h_contract_terms,
- merchant_pub,
- issue,
- tl,
- &computed_value,
- &total_deposit_without_refunds))
- {
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("coin history",
- rowid,
- "failed to verify coin history (for deposit)");
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Coin contributes %s to aggregate (deposits after fees and refunds)\n",
- TALER_amount2s (&computed_value));
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&coin_value_without_fee,
- coin_value,
- deposit_fee))
- {
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
- rowid,
- coin_value,
- deposit_fee,
- -1);
- return;
- }
- if (0 !=
- TALER_amount_cmp (&total_deposit_without_refunds,
- &coin_value_without_fee))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Expected coin contribution of %s to aggregate\n",
- TALER_amount2s (&coin_value_without_fee));
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_amount_arithmetic_inconsistency ("aggregation (contribution)",
- rowid,
- &coin_value_without_fee,
- &total_deposit_without_refunds,
- -1);
- }
- edb->free_coin_transaction_list (edb->cls,
- tl);
-
- /* Check other details of wire transfer match */
- if (0 != GNUNET_memcmp (h_wire,
- &wcc->h_wire))
- {
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("aggregation",
- rowid,
- "target of outgoing wire transfer do not match hash of wire from deposit");
- }
- if (exec_time.abs_value_us != wcc->date.abs_value_us)
- {
- /* This should be impossible from database constraints */
- GNUNET_break (0);
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- report_row_inconsistency ("aggregation",
- rowid,
- "date given in aggregate does not match wire transfer date");
- }
-
- /* Add coin's contribution to total aggregate value */
- {
- struct TALER_Amount res;
-
- if (GNUNET_OK !=
- TALER_amount_add (&res,
- &wcc->total_deposits,
- &computed_value))
- {
- GNUNET_break (0);
- wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return;
- }
- wcc->total_deposits = res;
- }
-}
-
-
-/**
- * Lookup the wire fee that the exchange charges at @a timestamp.
- *
- * @param ac context for caching the result
- * @param method method of the wire plugin
- * @param timestamp time for which we need the fee
- * @return NULL on error (fee unknown)
- */
-static const struct TALER_Amount *
-get_wire_fee (struct AggregationContext *ac,
- const char *method,
- struct GNUNET_TIME_Absolute timestamp)
-{
- struct WireFeeInfo *wfi;
- struct WireFeeInfo *pos;
- struct TALER_MasterSignatureP master_sig;
-
- /* Check if fee is already loaded in cache */
- for (pos = ac->fee_head; NULL != pos; pos = pos->next)
- {
- if ( (pos->start_date.abs_value_us <= timestamp.abs_value_us) &&
- (pos->end_date.abs_value_us > timestamp.abs_value_us) )
- return &pos->wire_fee;
- if (pos->start_date.abs_value_us > timestamp.abs_value_us)
- break;
- }
-
- /* Lookup fee in exchange database */
- wfi = GNUNET_new (struct WireFeeInfo);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- edb->get_wire_fee (edb->cls,
- esession,
- method,
- timestamp,
- &wfi->start_date,
- &wfi->end_date,
- &wfi->wire_fee,
- &wfi->closing_fee,
- &master_sig))
- {
- GNUNET_break (0);
- GNUNET_free (wfi);
- return NULL;
- }
-
- /* Check signature. (This is not terribly meaningful as the exchange can
- easily make this one up, but it means that we have proof that the master
- key was used for inconsistent wire fees if a merchant complains.) */
- {
- struct TALER_MasterWireFeePS wf;
-
- wf.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
- wf.purpose.size = htonl (sizeof (wf));
- GNUNET_CRYPTO_hash (method,
- strlen (method) + 1,
- &wf.h_wire_method);
- wf.start_date = GNUNET_TIME_absolute_hton (wfi->start_date);
- wf.end_date = GNUNET_TIME_absolute_hton (wfi->end_date);
- TALER_amount_hton (&wf.wire_fee,
- &wfi->wire_fee);
- TALER_amount_hton (&wf.closing_fee,
- &wfi->closing_fee);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
- &wf.purpose,
- &master_sig.eddsa_signature,
- &master_pub.eddsa_pub))
- {
- report_row_inconsistency ("wire-fee",
- timestamp.abs_value_us,
- "wire fee signature invalid at given time");
- }
- }
-
- /* Established fee, keep in sorted list */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Wire fee is %s starting at %s\n",
- TALER_amount2s (&wfi->wire_fee),
- GNUNET_STRINGS_absolute_time_to_string (wfi->start_date));
- if ( (NULL == pos) ||
- (NULL == pos->prev) )
- GNUNET_CONTAINER_DLL_insert (ac->fee_head,
- ac->fee_tail,
- wfi);
- else
- GNUNET_CONTAINER_DLL_insert_after (ac->fee_head,
- ac->fee_tail,
- pos->prev,
- wfi);
- /* Check non-overlaping fee invariant */
- if ( (NULL != wfi->prev) &&
- (wfi->prev->end_date.abs_value_us > wfi->start_date.abs_value_us) )
- {
- report (report_fee_time_inconsistencies,
- json_pack ("{s:s, s:s, s:o}",
- "type", method,
- "diagnostic", "start date before previous end date",
- "time", json_from_time_abs (wfi->start_date)));
- }
- if ( (NULL != wfi->next) &&
- (wfi->next->start_date.abs_value_us >= wfi->end_date.abs_value_us) )
- {
- report (report_fee_time_inconsistencies,
- json_pack ("{s:s, s:s, s:o}",
- "type", method,
- "diagnostic", "end date date after next start date",
- "time", json_from_time_abs (wfi->end_date)));
- }
- return &wfi->wire_fee;
-}
-
-
-/**
- * Check that a wire transfer made by the exchange is valid
- * (has matching deposits).
- *
- * @param cls a `struct AggregationContext`
- * @param rowid identifier of the respective row in the database
- * @param date timestamp of the wire transfer (roughly)
- * @param wtid wire transfer subject
- * @param wire wire transfer details of the receiver
- * @param amount amount that was wired
- * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
- */
-static int
-check_wire_out_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute date,
- const struct TALER_WireTransferIdentifierRawP *wtid,
- const json_t *wire,
- const struct TALER_Amount *amount)
-{
- struct AggregationContext *ac = cls;
- struct WireCheckContext wcc;
- struct TALER_Amount final_amount;
- struct TALER_Amount exchange_gain;
- enum GNUNET_DB_QueryStatus qs;
- char *method;
-
- /* should be monotonically increasing */
- GNUNET_assert (rowid >= ppa.last_wire_out_serial_id);
- ppa.last_wire_out_serial_id = rowid + 1;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Checking wire transfer %s over %s performed on %s\n",
- TALER_B2S (wtid),
- TALER_amount2s (amount),
- GNUNET_STRINGS_absolute_time_to_string (date));
- if (NULL == (method = TALER_JSON_wire_to_method (wire)))
- {
- report_row_inconsistency ("wire_out",
- rowid,
- "specified wire address lacks method");
- return GNUNET_OK;
- }
-
- wcc.ac = ac;
- wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- wcc.date = date;
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount->currency,
- &wcc.total_deposits));
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (wire,
- &wcc.h_wire))
- {
- GNUNET_break (0);
- GNUNET_free (method);
- return GNUNET_SYSERR;
- }
- qs = edb->lookup_wire_transfer (edb->cls,
- esession,
- wtid,
- &wire_transfer_information_cb,
- &wcc);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- ac->qs = qs;
- GNUNET_free (method);
- return GNUNET_SYSERR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
- {
- /* Note: detailed information was already logged
- in #wire_transfer_information_cb, so here we
- only log for debugging */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Inconsitency for wire_out %llu (WTID %s) detected\n",
- (unsigned long long) rowid,
- TALER_B2S (wtid));
- }
-
-
- /* Subtract aggregation fee from total (if possible) */
- {
- const struct TALER_Amount *wire_fee;
-
- wire_fee = get_wire_fee (ac,
- method,
- date);
- if (NULL == wire_fee)
- {
- report_row_inconsistency ("wire-fee",
- date.abs_value_us,
- "wire fee unavailable for given time");
- /* If fee is unknown, we just assume the fee is zero */
- final_amount = wcc.total_deposits;
- }
- else if (GNUNET_SYSERR ==
- TALER_amount_subtract (&final_amount,
- &wcc.total_deposits,
- wire_fee))
- {
- report_amount_arithmetic_inconsistency ("wire out (fee structure)",
- rowid,
- &wcc.total_deposits,
- wire_fee,
- -1);
- /* If fee arithmetic fails, we just assume the fee is zero */
- final_amount = wcc.total_deposits;
- }
- }
- GNUNET_free (method);
-
- /* Round down to amount supported by wire method */
- GNUNET_break (GNUNET_SYSERR !=
- TALER_amount_round_down (&final_amount,
- &currency_round_unit));
-
- /* Calculate the exchange's gain as the fees plus rounding differences! */
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&exchange_gain,
- &wcc.total_deposits,
- &final_amount))
- {
- GNUNET_break (0);
- ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
-
- /* Sum up aggregation fees (we simply include the rounding gains) */
- if (GNUNET_OK !=
- TALER_amount_add (&total_aggregation_fee_income,
- &total_aggregation_fee_income,
- &exchange_gain))
- {
- GNUNET_break (0);
- ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
-
- /* Check that calculated amount matches actual amount */
- if (0 != TALER_amount_cmp (amount,
- &final_amount))
- {
- struct TALER_Amount delta;
-
- if (0 < TALER_amount_cmp (amount,
- &final_amount))
- {
- /* amount > final_amount */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- amount,
- &final_amount));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_wire_out_delta_plus,
- &total_wire_out_delta_plus,
- &delta));
- }
- else
- {
- /* amount < final_amount */
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_subtract (&delta,
- &final_amount,
- amount));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&total_wire_out_delta_minus,
- &total_wire_out_delta_minus,
- &delta));
- }
-
- report (report_wire_out_inconsistencies,
- json_pack ("{s:O, s:I, s:o, s:o}",
- "destination_account", wire,
- "rowid", (json_int_t) rowid,
- "expected",
- TALER_JSON_from_amount (&final_amount),
- "claimed",
- TALER_JSON_from_amount (amount)));
- return GNUNET_OK;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Wire transfer %s is OK\n",
- TALER_B2S (wtid));
- return GNUNET_OK;
-}
-
-
-/**
- * Analyze the exchange aggregator's payment processing.
- *
- * @param cls closure
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-analyze_aggregations (void *cls)
-{
- struct AggregationContext ac;
- struct WireFeeInfo *wfi;
- enum GNUNET_DB_QueryStatus qsx;
- enum GNUNET_DB_QueryStatus qs;
- enum GNUNET_DB_QueryStatus qsp;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Analyzing aggregations\n");
- qsp = adb->get_auditor_progress_aggregation (adb->cls,
- asession,
- &master_pub,
- &ppa);
- 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
- {
- ppa_start = ppa;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Resuming aggregation audit at %llu\n"),
- (unsigned long long) ppa.last_wire_out_serial_id);
- }
-
- memset (&ac,
- 0,
- sizeof (ac));
- qsx = adb->get_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &total_aggregation_fee_income);
- if (0 > qsx)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
- return qsx;
- }
- ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- qs = edb->select_wire_out_above_serial_id (edb->cls,
- esession,
- ppa.last_wire_out_serial_id,
- &check_wire_out_cb,
- &ac);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- ac.qs = qs;
- }
- while (NULL != (wfi = ac.fee_head))
- {
- GNUNET_CONTAINER_DLL_remove (ac.fee_head,
- ac.fee_tail,
- wfi);
- GNUNET_free (wfi);
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- /* there were no wire out entries to be looked at, we are done */
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
- return ac.qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
- ac.qs = adb->insert_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &total_aggregation_fee_income);
- else
- ac.qs = adb->update_wire_fee_summary (adb->cls,
- asession,
- &master_pub,
- &total_aggregation_fee_income);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
- return ac.qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
- qs = adb->update_auditor_progress_aggregation (adb->cls,
- asession,
- &master_pub,
- &ppa);
- else
- qs = adb->insert_auditor_progress_aggregation (adb->cls,
- asession,
- &master_pub,
- &ppa);
- 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;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Concluded aggregation audit step at %llu\n"),
- (unsigned long long) ppa.last_wire_out_serial_id);
-
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/* ************************* Analyze coins ******************** */
-/* This logic checks that the exchange did the right thing for each
- coin, checking deposits, refunds, refresh* and known_coins
- tables */
-
-
-/**
- * Summary data we keep per denomination.
- */
-struct DenominationSummary
-{
- /**
- * Total value of outstanding (not deposited) coins issued with this
- * denomination key.
- */
- struct TALER_Amount denom_balance;
-
- /**
- * Total losses made (once coins deposited exceed
- * coins withdrawn and thus the @e denom_balance is
- * effectively negative).
- */
- struct TALER_Amount denom_loss;
-
- /**
- * Total value of coins issued with this denomination key.
- */
- struct TALER_Amount denom_risk;
-
- /**
- * Total value of coins subjected to recoup with this denomination key.
- */
- struct TALER_Amount denom_recoup;
-
- /**
- * How many coins (not their amount!) of this denomination
- * did the exchange issue overall?
- */
- uint64_t num_issued;
-
- /**
- * Denomination key information for this denomination.
- */
- const struct TALER_DenominationKeyValidityPS *issue;
-
- /**
- * #GNUNET_YES if this record already existed in the DB.
- * Used to decide between insert/update in
- * #sync_denomination().
- */
- int in_db;
-
- /**
- * Should we report an emergency for this denomination?
- */
- int report_emergency;
-
- /**
- * #GNUNET_YES if this denomination was revoked.
- */
- int was_revoked;
-};
-
-
-/**
- * Closure for callbacks during #analyze_coins().
- */
-struct CoinContext
-{
-
- /**
- * Map for tracking information about denominations.
- */
- struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
-
- /**
- * Current write/replace offset in the circular @e summaries buffer.
- */
- unsigned int summaries_off;
-
- /**
- * Transaction status code.
- */
- enum GNUNET_DB_QueryStatus qs;
-
-};
-
-
-/**
- * Initialize information about denomination from the database.
- *
- * @param denom_hash hash of the public key of the denomination
- * @param[out] ds summary to initialize
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-init_denomination (const struct GNUNET_HashCode *denom_hash,
- struct DenominationSummary *ds)
-{
- enum GNUNET_DB_QueryStatus qs;
- struct TALER_MasterSignatureP msig;
- uint64_t rowid;
-
- qs = adb->get_denomination_balance (adb->cls,
- asession,
- denom_hash,
- &ds->denom_balance,
- &ds->denom_loss,
- &ds->denom_risk,
- &ds->denom_recoup,
- &ds->num_issued);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- ds->in_db = GNUNET_YES;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Starting balance for denomination `%s' is %s\n",
- GNUNET_h2s (denom_hash),
- TALER_amount2s (&ds->denom_balance));
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- }
- qs = edb->get_denomination_revocation (edb->cls,
- esession,
- denom_hash,
- &msig,
- &rowid);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 < qs)
- {
- /* check revocation signature */
- struct TALER_MasterDenominationKeyRevocationPS rm;
-
- rm.purpose.purpose = htonl (
- TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED);
- rm.purpose.size = htonl (sizeof (rm));
- rm.h_denom_pub = *denom_hash;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (
- TALER_SIGNATURE_MASTER_DENOMINATION_KEY_REVOKED,
- &rm.purpose,
- &msig.eddsa_signature,
- &master_pub.eddsa_pub))
- {
- report_row_inconsistency ("denomination revocation table",
- rowid,
- "revocation signature invalid");
- }
- else
- {
- ds->was_revoked = GNUNET_YES;
- }
- }
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &ds->denom_balance));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &ds->denom_loss));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &ds->denom_risk));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &ds->denom_recoup));
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Starting balance for denomination `%s' is %s\n",
- GNUNET_h2s (denom_hash),
- TALER_amount2s (&ds->denom_balance));
- return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/**
- * Obtain the denomination summary for the given @a dh
- *
- * @param cc our execution context
- * @param issue denomination key information for @a dh
- * @param dh the denomination hash to use for the lookup
- * @return NULL on error
- */
-static struct DenominationSummary *
-get_denomination_summary (struct CoinContext *cc,
- const struct TALER_DenominationKeyValidityPS *issue,
- const struct GNUNET_HashCode *dh)
-{
- struct DenominationSummary *ds;
-
- ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
- dh);
- if (NULL != ds)
- return ds;
- ds = GNUNET_new (struct DenominationSummary);
- ds->issue = issue;
- if (0 > (cc->qs = init_denomination (dh,
- ds)))
- {
- GNUNET_break (0);
- GNUNET_free (ds);
- return NULL;
- }
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
- dh,
- ds,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- return ds;
-}
-
-
-/**
- * Write information about the current knowledge about a denomination key
- * back to the database and update our global reporting data about the
- * denomination. Also remove and free the memory of @a value.
- *
- * @param cls the `struct CoinContext`
- * @param denom_hash the hash of the denomination key
- * @param value a `struct DenominationSummary`
- * @return #GNUNET_OK (continue to iterate)
- */
-static int
-sync_denomination (void *cls,
- const struct GNUNET_HashCode *denom_hash,
- void *value)
-{
- struct CoinContext *cc = cls;
- struct DenominationSummary *ds = value;
- const struct TALER_DenominationKeyValidityPS *issue = ds->issue;
- struct GNUNET_TIME_Absolute now;
- struct GNUNET_TIME_Absolute expire_deposit;
- struct GNUNET_TIME_Absolute expire_deposit_grace;
- enum GNUNET_DB_QueryStatus qs;
-
- now = GNUNET_TIME_absolute_get ();
- expire_deposit = GNUNET_TIME_absolute_ntoh (issue->expire_deposit);
- /* add day grace period to deal with clocks not being perfectly synchronized */
- expire_deposit_grace = GNUNET_TIME_absolute_add (expire_deposit,
- DEPOSIT_GRACE_PERIOD);
- if (now.abs_value_us > expire_deposit_grace.abs_value_us)
- {
- /* Denominationkey has expired, book remaining balance of
- outstanding coins as revenue; and reduce cc->risk exposure. */
- if (ds->in_db)
- qs = adb->del_denomination_balance (adb->cls,
- asession,
- denom_hash);
- else
- qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
- ( (0 != ds->denom_risk.value) ||
- (0 != ds->denom_risk.fraction) ) )
- {
- /* The denomination expired and carried a balance; we can now
- book the remaining balance as profit, and reduce our risk
- exposure by the accumulated risk of the denomination. */
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&total_risk,
- &total_risk,
- &ds->denom_risk))
- {
- /* Holy smokes, our risk assessment was inconsistent!
- This is really, really bad. */
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- }
- }
- if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
- ( (0 != ds->denom_balance.value) ||
- (0 != ds->denom_balance.fraction) ) )
- {
- /* book denom_balance coin expiration profits! */
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Denomination `%s' expired, booking %s in expiration profits\n",
- GNUNET_h2s (denom_hash),
- TALER_amount2s (&ds->denom_balance));
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
- (qs = adb->insert_historic_denom_revenue (adb->cls,
- asession,
- &master_pub,
- denom_hash,
- expire_deposit,
- &ds->denom_balance,
- &ds->denom_recoup)))
- {
- /* Failed to store profits? Bad database */
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- }
- }
- }
- else
- {
- long long cnt;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Final balance for denomination `%s' is %s (%llu)\n",
- GNUNET_h2s (denom_hash),
- TALER_amount2s (&ds->denom_balance),
- (unsigned long long) ds->num_issued);
- cnt = edb->count_known_coins (edb->cls,
- esession,
- denom_hash);
- if (0 > cnt)
- {
- /* Failed to obtain count? Bad database */
- qs = (enum GNUNET_DB_QueryStatus) cnt;
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- }
- else
- {
- if (ds->num_issued < (uint64_t) cnt)
- {
- report_emergency_by_count (issue,
- ds->num_issued,
- cnt,
- &ds->denom_risk);
- }
- if (GNUNET_YES == ds->report_emergency)
- {
- report_emergency_by_amount (issue,
- &ds->denom_risk,
- &ds->denom_loss);
-
- }
- if (ds->in_db)
- qs = adb->update_denomination_balance (adb->cls,
- asession,
- denom_hash,
- &ds->denom_balance,
- &ds->denom_loss,
- &ds->denom_risk,
- &ds->denom_recoup,
- ds->num_issued);
- else
- qs = adb->insert_denomination_balance (adb->cls,
- asession,
- denom_hash,
- &ds->denom_balance,
- &ds->denom_loss,
- &ds->denom_risk,
- &ds->denom_recoup,
- ds->num_issued);
- }
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- }
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
- denom_hash,
- ds));
- GNUNET_free (ds);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != cc->qs)
- return GNUNET_SYSERR;
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about all withdraw operations.
- * Updates the denomination balance and the overall balance as
- * we now have additional coins that have been issued.
- *
- * Note that the signature was already checked in
- * #handle_reserve_out(), so we do not check it again here.
- *
- * @param cls our `struct CoinContext`
- * @param rowid unique serial ID for the refresh session in our DB
- * @param h_blind_ev blinded hash of the coin's public key
- * @param denom_pub public denomination key of the deposited coin
- * @param reserve_pub public key of the reserve
- * @param reserve_sig signature over the withdraw operation (verified elsewhere)
- * @param execution_date when did the wallet withdraw the coin
- * @param amount_with_fee amount that was withdrawn
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-withdraw_cb (void *cls,
- uint64_t rowid,
- const struct GNUNET_HashCode *h_blind_ev,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_ReserveSignatureP *reserve_sig,
- struct GNUNET_TIME_Absolute execution_date,
- const struct TALER_Amount *amount_with_fee)
-{
- struct CoinContext *cc = cls;
- struct DenominationSummary *ds;
- struct GNUNET_HashCode dh;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct TALER_Amount value;
- enum GNUNET_DB_QueryStatus qs;
-
- (void) h_blind_ev;
- (void) reserve_pub;
- (void) reserve_sig;
- (void) execution_date;
- (void) amount_with_fee;
- GNUNET_assert (rowid >= ppc.last_withdraw_serial_id); /* should be monotonically increasing */
- ppc.last_withdraw_serial_id = rowid + 1;
-
- qs = get_denomination_info (denom_pub,
- &issue,
- &dh);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("withdraw",
- rowid,
- "denomination key not found");
- return GNUNET_OK;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- /* This really ought to be a transient DB error. */
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
- ds = get_denomination_summary (cc,
- issue,
- &dh);
- if (NULL == ds)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- TALER_amount_ntoh (&value,
- &issue->value);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Issued coin in denomination `%s' of total value %s\n",
- GNUNET_h2s (&dh),
- TALER_amount2s (&value));
- ds->num_issued++;
- if (GNUNET_OK !=
- TALER_amount_add (&ds->denom_balance,
- &ds->denom_balance,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "New balance of denomination `%s' is %s\n",
- GNUNET_h2s (&dh),
- TALER_amount2s (&ds->denom_balance));
- if (GNUNET_OK !=
- TALER_amount_add (&total_escrow_balance,
- &total_escrow_balance,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&total_risk,
- &total_risk,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&ds->denom_risk,
- &ds->denom_risk,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Closure for #reveal_data_cb().
- */
-struct RevealContext
-{
-
- /**
- * Denomination public keys of the new coins.
- */
- struct TALER_DenominationPublicKey *new_dps;
-
- /**
- * Size of the @a new_dp and @a new_dps arrays.
- */
- unsigned int num_freshcoins;
-};
-
-
-/**
- * Function called with information about a refresh order.
- *
- * @param cls closure
- * @param num_freshcoins size of the @a rrcs array
- * @param rrcs array of @a num_freshcoins information about coins to be created
- * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
- * @param tprivs array of @e num_tprivs transfer private keys
- * @param tp transfer public key information
- */
-static void
-reveal_data_cb (void *cls,
- uint32_t num_freshcoins,
- const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
- unsigned int num_tprivs,
- const struct TALER_TransferPrivateKeyP *tprivs,
- const struct TALER_TransferPublicKeyP *tp)
-{
- struct RevealContext *rctx = cls;
-
- (void) num_tprivs;
- (void) tprivs;
- (void) tp;
- rctx->num_freshcoins = num_freshcoins;
- rctx->new_dps = GNUNET_new_array (num_freshcoins,
- struct TALER_DenominationPublicKey);
- for (unsigned int i = 0; i<num_freshcoins; i++)
- rctx->new_dps[i].rsa_public_key
- = GNUNET_CRYPTO_rsa_public_key_dup (rrcs[i].denom_pub.rsa_public_key);
-}
-
-
-/**
- * Check that the @a coin_pub is a known coin with a proper
- * signature for denominatinon @a denom_pub. If not, report
- * a loss of @a loss_potential.
- *
- * @param coin_pub public key of a coin
- * @param denom_pub expected denomination of the coin
- * @param loss_potential how big could the loss be if the coin is
- * not properly signed
- * @return database transaction status, on success
- * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
- */
-static enum GNUNET_DB_QueryStatus
-check_known_coin (const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_Amount *loss_potential)
-{
- struct TALER_CoinPublicInfo ci;
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Checking denomination signature on %s\n",
- TALER_B2S (coin_pub));
- qs = edb->get_known_coin (edb->cls,
- esession,
- coin_pub,
- &ci);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (GNUNET_YES !=
- TALER_test_coin_valid (&ci,
- denom_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "known-coin",
- "row", (json_int_t) -1,
- "loss", TALER_JSON_from_amount (loss_potential),
- "key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- loss_potential));
-
- }
- GNUNET_CRYPTO_rsa_signature_free (ci.denom_sig.rsa_signature);
- return qs;
-}
-
-
-/**
- * Function called with details about coins that were melted, with the
- * goal of auditing the refresh's execution. Verifies the signature
- * and updates our information about coins outstanding (the old coin's
- * denomination has less, the fresh coins increased outstanding
- * balances).
- *
- * @param cls closure
- * @param rowid unique serial ID for the refresh session in our DB
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param coin_sig signature from the coin
- * @param amount_with_fee amount that was deposited including fee
- * @param noreveal_index which index was picked by the exchange in cut-and-choose
- * @param rc what is the refresh commitment
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-refresh_session_cb (void *cls,
- uint64_t rowid,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_Amount *amount_with_fee,
- uint32_t noreveal_index,
- const struct TALER_RefreshCommitmentP *rc)
-{
- struct CoinContext *cc = cls;
- struct TALER_RefreshMeltCoinAffirmationPS rmc;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct DenominationSummary *dso;
- struct TALER_Amount amount_without_fee;
- struct TALER_Amount tmp;
- enum GNUNET_DB_QueryStatus qs;
-
- (void) noreveal_index;
- GNUNET_assert (rowid >= ppc.last_melt_serial_id); /* should be monotonically increasing */
- ppc.last_melt_serial_id = rowid + 1;
-
- qs = get_denomination_info (denom_pub,
- &issue,
- NULL);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("melt",
- rowid,
- "denomination key not found");
- return GNUNET_OK;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
- qs = check_known_coin (coin_pub,
- denom_pub,
- amount_with_fee);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
-
- /* verify melt signature */
- rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
- rmc.purpose.size = htonl (sizeof (rmc));
- rmc.rc = *rc;
- TALER_amount_hton (&rmc.amount_with_fee,
- amount_with_fee);
- rmc.melt_fee = issue->fee_refresh;
- rmc.coin_pub = *coin_pub;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
- &rmc.purpose,
- &coin_sig->eddsa_signature,
- &coin_pub->eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "melt",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount_with_fee),
- "key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee));
- return GNUNET_OK;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Melting coin %s in denomination `%s' of value %s\n",
- TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (amount_with_fee));
-
- {
- struct RevealContext reveal_ctx;
- struct TALER_Amount refresh_cost;
- int err;
-
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (amount_with_fee->currency,
- &refresh_cost));
- memset (&reveal_ctx,
- 0,
- sizeof (reveal_ctx));
- qs = edb->get_refresh_reveal (edb->cls,
- esession,
- rc,
- &reveal_data_cb,
- &reveal_ctx);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
- (0 == reveal_ctx.num_freshcoins) )
- {
- /* This can happen if /refresh/reveal was not yet called or only
- with invalid data, even if the exchange is correctly
- operating. We still report it. */
- report (report_refreshs_hanging,
- json_pack ("{s:I, s:o, s:o}",
- "row", (json_int_t) rowid,
- "amount", TALER_JSON_from_amount (amount_with_fee),
- "coin_pub", GNUNET_JSON_from_data_auto (coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_refresh_hanging,
- &total_refresh_hanging,
- amount_with_fee));
- return GNUNET_OK;
- }
-
- {
- const struct TALER_DenominationKeyValidityPS *new_issues[reveal_ctx.
- num_freshcoins];
-
- /* Update outstanding amounts for all new coin's denominations, and check
- that the resulting amounts are consistent with the value being refreshed. */
- err = GNUNET_OK;
- for (unsigned int i = 0; i<reveal_ctx.num_freshcoins; i++)
- {
- /* lookup new coin denomination key */
- qs = get_denomination_info (&reveal_ctx.new_dps[i],
- &new_issues[i],
- NULL);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("refresh_reveal",
- rowid,
- "denomination key not found");
- err = GNUNET_NO; /* terminate, but return "OK" */
- }
- else if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- err = GNUNET_SYSERR; /* terminate, return GNUNET_SYSERR */
- }
- GNUNET_CRYPTO_rsa_public_key_free (
- reveal_ctx.new_dps[i].rsa_public_key);
- reveal_ctx.new_dps[i].rsa_public_key = NULL;
- }
- GNUNET_free (reveal_ctx.new_dps);
- reveal_ctx.new_dps = NULL;
-
- if (GNUNET_OK != err)
- return (GNUNET_SYSERR == err) ? GNUNET_SYSERR : GNUNET_OK;
-
- /* calculate total refresh cost */
- for (unsigned int i = 0; i<reveal_ctx.num_freshcoins; i++)
- {
- /* update cost of refresh */
- struct TALER_Amount fee;
- struct TALER_Amount value;
-
- TALER_amount_ntoh (&fee,
- &new_issues[i]->fee_withdraw);
- TALER_amount_ntoh (&value,
- &new_issues[i]->value);
- if ( (GNUNET_OK !=
- TALER_amount_add (&refresh_cost,
- &refresh_cost,
- &fee)) ||
- (GNUNET_OK !=
- TALER_amount_add (&refresh_cost,
- &refresh_cost,
- &value)) )
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- }
-
- /* compute contribution of old coin */
- {
- struct TALER_Amount melt_fee;
-
- TALER_amount_ntoh (&melt_fee,
- &issue->fee_refresh);
- if (GNUNET_OK !=
- TALER_amount_subtract (&amount_without_fee,
- amount_with_fee,
- &melt_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- }
-
- /* check old coin covers complete expenses */
- if (1 == TALER_amount_cmp (&refresh_cost,
- &amount_without_fee))
- {
- /* refresh_cost > amount_without_fee */
- report_amount_arithmetic_inconsistency ("melt (fee)",
- rowid,
- &amount_without_fee,
- &refresh_cost,
- -1);
- return GNUNET_OK;
- }
-
- /* update outstanding denomination amounts */
- for (unsigned int i = 0; i<reveal_ctx.num_freshcoins; i++)
- {
- struct DenominationSummary *dsi;
- struct TALER_Amount value;
-
- dsi = get_denomination_summary (cc,
- new_issues[i],
- &new_issues[i]->denom_hash);
- if (NULL == dsi)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- TALER_amount_ntoh (&value,
- &new_issues[i]->value);
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Created fresh coin in denomination `%s' of value %s\n",
- GNUNET_h2s (&new_issues[i]->denom_hash),
- TALER_amount2s (&value));
- dsi->num_issued++;
- if (GNUNET_OK !=
- TALER_amount_add (&dsi->denom_balance,
- &dsi->denom_balance,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&dsi->denom_risk,
- &dsi->denom_risk,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "New balance of denomination `%s' is %s\n",
- GNUNET_h2s (&new_issues[i]->denom_hash),
- TALER_amount2s (&dsi->denom_balance));
- if (GNUNET_OK !=
- TALER_amount_add (&total_escrow_balance,
- &total_escrow_balance,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&total_risk,
- &total_risk,
- &value))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- }
- }
- }
-
- /* update old coin's denomination balance */
- dso = get_denomination_summary (cc,
- issue,
- &issue->denom_hash);
- if (NULL == dso)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&tmp,
- &dso->denom_balance,
- amount_with_fee))
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&dso->denom_loss,
- &dso->denom_loss,
- amount_with_fee));
- dso->report_emergency = GNUNET_YES;
- }
- else
- {
- dso->denom_balance = tmp;
- }
- if (-1 == TALER_amount_cmp (&total_escrow_balance,
- amount_with_fee))
- {
- /* This can theoretically happen if for example the exchange
- never issued any coins (i.e. escrow balance is zero), but
- accepted a forged coin (i.e. emergency situation after
- private key compromise). In that case, we cannot even
- subtract the profit we make from the fee from the escrow
- balance. Tested as part of test-auditor.sh, case #18 */report_amount_arithmetic_inconsistency (
- "subtracting refresh fee from escrow balance",
- rowid,
- &total_escrow_balance,
- amount_with_fee,
- 0);
- }
- else
- {
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_subtract (&total_escrow_balance,
- &total_escrow_balance,
- amount_with_fee));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "New balance of denomination `%s' after melt is %s\n",
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (&dso->denom_balance));
-
- /* update global melt fees */
- {
- struct TALER_Amount rfee;
-
- TALER_amount_ntoh (&rfee,
- &issue->fee_refresh);
- if (GNUNET_OK !=
- TALER_amount_add (&total_melt_fee_income,
- &total_melt_fee_income,
- &rfee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- }
-
- /* We're good! */
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about deposits that have been made,
- * with the goal of auditing the deposit's execution.
- *
- * @param cls closure
- * @param rowid unique serial ID for the deposit in our DB
- * @param timestamp when did the deposit happen
- * @param merchant_pub public key of the merchant
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param coin_sig signature from the coin
- * @param amount_with_fee amount that was deposited including fee
- * @param h_contract_terms hash of the proposal data known to merchant and customer
- * @param refund_deadline by which the merchant adviced that he might want
- * to get a refund
- * @param wire_deadline by which the merchant adviced that he would like the
- * wire transfer to be executed
- * @param receiver_wire_account wire details for the merchant, NULL from iterate_matching_deposits()
- * @param done flag set if the deposit was already executed (or not)
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-deposit_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute timestamp,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_Amount *amount_with_fee,
- const struct GNUNET_HashCode *h_contract_terms,
- struct GNUNET_TIME_Absolute refund_deadline,
- struct GNUNET_TIME_Absolute wire_deadline,
- const json_t *receiver_wire_account,
- int done)
-{
- struct CoinContext *cc = cls;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct DenominationSummary *ds;
- struct TALER_DepositRequestPS dr;
- struct TALER_Amount tmp;
- enum GNUNET_DB_QueryStatus qs;
-
- (void) wire_deadline;
- (void) done;
- GNUNET_assert (rowid >= ppc.last_deposit_serial_id); /* should be monotonically increasing */
- ppc.last_deposit_serial_id = rowid + 1;
-
- qs = get_denomination_info (denom_pub,
- &issue,
- NULL);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("deposits",
- rowid,
- "denomination key not found");
- return GNUNET_OK;
- }
-
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
- qs = check_known_coin (coin_pub,
- denom_pub,
- amount_with_fee);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
-
- /* Verify deposit signature */
- dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
- dr.purpose.size = htonl (sizeof (dr));
- dr.h_contract_terms = *h_contract_terms;
- if (GNUNET_OK !=
- TALER_JSON_merchant_wire_signature_hash (receiver_wire_account,
- &dr.h_wire))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "deposit",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount_with_fee),
- "key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee));
- return GNUNET_OK;
- }
- dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
- dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
- TALER_amount_hton (&dr.amount_with_fee,
- amount_with_fee);
- dr.deposit_fee = issue->fee_deposit;
- dr.merchant = *merchant_pub;
- dr.coin_pub = *coin_pub;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
- &dr.purpose,
- &coin_sig->eddsa_signature,
- &coin_pub->eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "deposit",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount_with_fee),
- "key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee));
- return GNUNET_OK;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Deposited coin %s in denomination `%s' of value %s\n",
- TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (amount_with_fee));
-
- /* update old coin's denomination balance */
- ds = get_denomination_summary (cc,
- issue,
- &issue->denom_hash);
- if (NULL == ds)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_SYSERR ==
- TALER_amount_subtract (&tmp,
- &ds->denom_balance,
- amount_with_fee))
- {
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_add (&ds->denom_loss,
- &ds->denom_loss,
- amount_with_fee));
- ds->report_emergency = GNUNET_YES;
- }
- else
- {
- ds->denom_balance = tmp;
- }
-
- if (-1 == TALER_amount_cmp (&total_escrow_balance,
- amount_with_fee))
- {
- /* This can theoretically happen if for example the exchange
- never issued any coins (i.e. escrow balance is zero), but
- accepted a forged coin (i.e. emergency situation after
- private key compromise). In that case, we cannot even
- subtract the profit we make from the fee from the escrow
- balance. Tested as part of test-auditor.sh, case #18 */report_amount_arithmetic_inconsistency (
- "subtracting deposit fee from escrow balance",
- rowid,
- &total_escrow_balance,
- amount_with_fee,
- 0);
- }
- else
- {
- GNUNET_assert (GNUNET_SYSERR !=
- TALER_amount_subtract (&total_escrow_balance,
- &total_escrow_balance,
- amount_with_fee));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "New balance of denomination `%s' after deposit is %s\n",
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (&ds->denom_balance));
-
- /* update global up melt fees */
- {
- struct TALER_Amount dfee;
-
- TALER_amount_ntoh (&dfee,
- &issue->fee_deposit);
- if (GNUNET_OK !=
- TALER_amount_add (&total_deposit_fee_income,
- &total_deposit_fee_income,
- &dfee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- }
-
- return GNUNET_OK;
-}
-
-
-/**
- * Function called with details about coins that were refunding,
- * with the goal of auditing the refund's execution. Adds the
- * refunded amount back to the outstanding balance of the respective
- * denomination.
- *
- * @param cls closure
- * @param rowid unique serial ID for the refund in our DB
- * @param denom_pub denomination public key of @a coin_pub
- * @param coin_pub public key of the coin
- * @param merchant_pub public key of the merchant
- * @param merchant_sig signature of the merchant
- * @param h_contract_terms hash of the proposal data known to merchant and customer
- * @param rtransaction_id refund transaction ID chosen by the merchant
- * @param amount_with_fee amount that was deposited including fee
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-refund_cb (void *cls,
- uint64_t rowid,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const struct TALER_MerchantSignatureP *merchant_sig,
- const struct GNUNET_HashCode *h_contract_terms,
- uint64_t rtransaction_id,
- const struct TALER_Amount *amount_with_fee)
-{
- struct CoinContext *cc = cls;
- const struct TALER_DenominationKeyValidityPS *issue;
- struct DenominationSummary *ds;
- struct TALER_RefundRequestPS rr;
- struct TALER_Amount amount_without_fee;
- struct TALER_Amount refund_fee;
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_assert (rowid >= ppc.last_refund_serial_id); /* should be monotonically increasing */
- ppc.last_refund_serial_id = rowid + 1;
-
- qs = get_denomination_info (denom_pub,
- &issue,
- NULL);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("refunds",
- rowid,
- "denomination key not found");
- return GNUNET_SYSERR;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return GNUNET_SYSERR;
- }
-
- /* verify refund signature */
- rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
- rr.purpose.size = htonl (sizeof (rr));
- rr.h_contract_terms = *h_contract_terms;
- rr.coin_pub = *coin_pub;
- rr.merchant = *merchant_pub;
- rr.rtransaction_id = GNUNET_htonll (rtransaction_id);
- TALER_amount_hton (&rr.refund_amount,
- amount_with_fee);
- rr.refund_fee = issue->fee_refund;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
- &rr.purpose,
- &merchant_sig->eddsa_sig,
- &merchant_pub->eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "refund",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount_with_fee),
- "key_pub", GNUNET_JSON_from_data_auto (merchant_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount_with_fee));
- return GNUNET_OK;
- }
-
- TALER_amount_ntoh (&refund_fee,
- &issue->fee_refund);
- if (GNUNET_OK !=
- TALER_amount_subtract (&amount_without_fee,
- amount_with_fee,
- &refund_fee))
- {
- report_amount_arithmetic_inconsistency ("refund (fee)",
- rowid,
- &amount_without_fee,
- &refund_fee,
- -1);
- return GNUNET_OK;
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Refunding coin %s in denomination `%s' value %s\n",
- TALER_B2S (coin_pub),
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (amount_with_fee));
-
- /* update coin's denomination balance */
- ds = get_denomination_summary (cc,
- issue,
- &issue->denom_hash);
- if (NULL == ds)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&ds->denom_balance,
- &ds->denom_balance,
- &amount_without_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&ds->denom_risk,
- &ds->denom_risk,
- &amount_without_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&total_escrow_balance,
- &total_escrow_balance,
- &amount_without_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- TALER_amount_add (&total_risk,
- &total_risk,
- &amount_without_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "New balance of denomination `%s' after refund is %s\n",
- GNUNET_h2s (&issue->denom_hash),
- TALER_amount2s (&ds->denom_balance));
-
- /* update total refund fee balance */
- if (GNUNET_OK !=
- TALER_amount_add (&total_refund_fee_income,
- &total_refund_fee_income,
- &refund_fee))
- {
- GNUNET_break (0);
- cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
- return GNUNET_SYSERR;
- }
-
- return GNUNET_OK;
-}
-
-
-/**
- * Check that the recoup operation was properly initiated by a coin
- * and update the denomination's losses accordingly.
- *
- * @param cc the context with details about the coin
- * @param rowid row identifier used to uniquely identify the recoup operation
- * @param amount how much should be added back to the reserve
- * @param coin public information about the coin
- * @param denom_pub public key of the denomionation of @a coin
- * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
- * @param coin_blind blinding factor used to blind the coin
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-check_recoup (struct CoinContext *cc,
- uint64_t rowid,
- const struct TALER_Amount *amount,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
-{
- struct TALER_RecoupRequestPS pr;
- struct DenominationSummary *ds;
- enum GNUNET_DB_QueryStatus qs;
- const struct TALER_DenominationKeyValidityPS *issue;
-
- if (GNUNET_OK !=
- TALER_test_coin_valid (coin,
- denom_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "recoup",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount),
- "key_pub", GNUNET_JSON_from_data_auto (
- &pr.h_denom_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount));
- }
- qs = get_denomination_info (denom_pub,
- &issue,
- &pr.h_denom_pub);
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- report_row_inconsistency ("recoup",
- rowid,
- "denomination key not found");
- return GNUNET_OK;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- /* The key not existing should be prevented by foreign key constraints,
- so must be a transient DB error. */
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- cc->qs = qs;
- return GNUNET_SYSERR;
- }
- pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP);
- pr.purpose.size = htonl (sizeof (pr));
- pr.coin_pub = coin->coin_pub;
- pr.coin_blind = *coin_blind;
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
- &pr.purpose,
- &coin_sig->eddsa_signature,
- &coin->coin_pub.eddsa_pub))
- {
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "recoup",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount),
- "coin_pub", GNUNET_JSON_from_data_auto (
- &coin->coin_pub)));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_bad_sig_loss,
- &total_bad_sig_loss,
- amount));
- return GNUNET_OK;
- }
- ds = get_denomination_summary (cc,
- issue,
- &issue->denom_hash);
- if (GNUNET_NO == ds->was_revoked)
- {
- /* Woopsie, we allowed recoup on non-revoked denomination!? */
- report (report_bad_sig_losses,
- json_pack ("{s:s, s:I, s:o, s:o}",
- "operation", "recoup (denomination not revoked)",
- "row", (json_int_t) rowid,
- "loss", TALER_JSON_from_amount (amount),
- "coin_pub", GNUNET_JSON_from_data_auto (
- &coin->coin_pub)));
- }
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&ds->denom_recoup,
- &ds->denom_recoup,
- amount));
- GNUNET_break (GNUNET_OK ==
- TALER_amount_add (&total_recoup_loss,
- &total_recoup_loss,
- amount));
- return GNUNET_OK;
-}
-
-
-/**
- * Function called about recoups the exchange has to perform.
- *
- * @param cls a `struct CoinContext *`
- * @param rowid row identifier used to uniquely identify the recoup operation
- * @param timestamp when did we receive the recoup request
- * @param amount how much should be added back to the reserve
- * @param reserve_pub public key of the reserve
- * @param coin public information about the coin
- * @param denom_pub denomination public key of @a coin
- * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
- * @param coin_blind blinding factor used to blind the coin
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-recoup_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute timestamp,
- const struct TALER_Amount *amount,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
-{
- struct CoinContext *cc = cls;
-
- (void) timestamp;
- (void) reserve_pub;
- return check_recoup (cc,
- rowid,
- amount,
- coin,
- denom_pub,
- coin_sig,
- coin_blind);
-}
-
-
-/**
- * Function called about recoups on refreshed coins the exchange has to
- * perform.
- *
- * @param cls a `struct CoinContext *`
- * @param rowid row identifier used to uniquely identify the recoup operation
- * @param timestamp when did we receive the recoup request
- * @param amount how much should be added back to the reserve
- * @param old_coin_pub original coin that was refreshed to create @a coin
- * @param coin public information about the coin
- * @param denom_pub denomination public key of @a coin
- * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
- * @param coin_blind blinding factor used to blind the coin
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
- */
-static int
-recoup_refresh_cb (void *cls,
- uint64_t rowid,
- struct GNUNET_TIME_Absolute timestamp,
- const struct TALER_Amount *amount,
- const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
- const struct TALER_CoinPublicInfo *coin,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendSignatureP *coin_sig,
- const struct TALER_DenominationBlindingKeyP *coin_blind)
-{
- struct CoinContext *cc = cls;
-
- (void) timestamp;
- (void) old_coin_pub;
- return check_recoup (cc,
- rowid,
- amount,
- coin,
- denom_pub,
- coin_sig,
- coin_blind);
-}
-
-
-/**
- * Analyze the exchange's processing of coins.
- *
- * @param cls closure
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-analyze_coins (void *cls)
-{
- struct CoinContext cc;
- enum GNUNET_DB_QueryStatus qs;
- enum GNUNET_DB_QueryStatus qsx;
- enum GNUNET_DB_QueryStatus qsp;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Analyzing coins\n");
- qsp = adb->get_auditor_progress_coin (adb->cls,
- asession,
- &master_pub,
- &ppc);
- 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 from scratch\n");
- }
- else
- {
- ppc_start = ppc;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Resuming coin audit at %llu/%llu/%llu/%llu/%llu\n",
- (unsigned long long) ppc.last_deposit_serial_id,
- (unsigned long long) ppc.last_melt_serial_id,
- (unsigned long long) ppc.last_refund_serial_id,
- (unsigned long long) ppc.last_withdraw_serial_id,
- (unsigned long long) ppc.last_recoup_refresh_serial_id);
- }
-
- /* setup 'cc' */
- cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
- GNUNET_NO);
- qsx = adb->get_balance_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_deposit_fee_income,
- &total_melt_fee_income,
- &total_refund_fee_income,
- &total_risk,
- &total_recoup_loss,
- &total_irregular_recoups);
- if (0 > qsx)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
- return qsx;
- }
-
- /* process withdrawals */
- if (0 >
- (qs = edb->select_withdrawals_above_serial_id (edb->cls,
- esession,
- ppc.
- last_withdraw_serial_id,
- &withdraw_cb,
- &cc)) )
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
-
- /* process refunds */
- if (0 >
- (qs = edb->select_refunds_above_serial_id (edb->cls,
- esession,
- ppc.last_refund_serial_id,
- &refund_cb,
- &cc)))
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
-
- /* process refreshs */
- if (0 >
- (qs = edb->select_refreshes_above_serial_id (edb->cls,
- esession,
- ppc.last_melt_serial_id,
- &refresh_session_cb,
- &cc)))
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
-
- /* process deposits */
- if (0 >
- (qs = edb->select_deposits_above_serial_id (edb->cls,
- esession,
- ppc.last_deposit_serial_id,
- &deposit_cb,
- &cc)))
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
-
- /* process recoups */
- if (0 >
- (qs = edb->select_recoup_above_serial_id (edb->cls,
- esession,
- ppc.last_recoup_serial_id,
- &recoup_cb,
- &cc)))
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
- if (0 >
- (qs = edb->select_recoup_refresh_above_serial_id (edb->cls,
- esession,
- ppc.
- last_recoup_refresh_serial_id,
- &recoup_refresh_cb,
- &cc)))
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
- if (0 > cc.qs)
- return cc.qs;
-
- /* sync 'cc' back to disk */
- cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
- &sync_denomination,
- &cc);
- GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
- if (0 > cc.qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
- return cc.qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
- qs = adb->update_balance_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_deposit_fee_income,
- &total_melt_fee_income,
- &total_refund_fee_income,
- &total_risk,
- &total_recoup_loss,
- &total_irregular_recoups);
- else
- qs = adb->insert_balance_summary (adb->cls,
- asession,
- &master_pub,
- &total_escrow_balance,
- &total_deposit_fee_income,
- &total_melt_fee_income,
- &total_refund_fee_income,
- &total_risk,
- &total_recoup_loss,
- &total_irregular_recoups);
- if (0 >= qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- return qs;
- }
-
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
- qs = adb->update_auditor_progress_coin (adb->cls,
- asession,
- &master_pub,
- &ppc);
- else
- qs = adb->insert_auditor_progress_coin (adb->cls,
- asession,
- &master_pub,
- &ppc);
- 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;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Concluded coin audit step at %llu/%llu/%llu/%llu/%llu\n"),
- (unsigned long long) ppc.last_deposit_serial_id,
- (unsigned long long) ppc.last_melt_serial_id,
- (unsigned long long) ppc.last_refund_serial_id,
- (unsigned long long) ppc.last_withdraw_serial_id,
- (unsigned long long) ppc.last_recoup_refresh_serial_id);
- return qs;
-}
-
-
-/* *************************** 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)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Found deposit %s in exchange database\n",
- GNUNET_h2s (&dc->h_contract_terms));
- 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:o, s:o, s:I, s:o}",
- "timestamp",
- json_from_time_abs (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;
-
- (void) cls;
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "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;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Analyzed %d deposit confirmations (above serial ID %llu)\n",
- (int) qsx,
- (unsigned long long) ppdc.last_deposit_confirmation_serial_id);
- 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 ****************** */
-
-/**
- * Type of an analysis function. Each analysis function runs in
- * its own transaction scope and must thus be internally consistent.
- *
- * @param cls closure
- * @return transaction status code
- */
-typedef enum GNUNET_DB_QueryStatus
-(*Analysis)(void *cls);
-
-
-/**
- * Perform the given @a analysis within a transaction scope.
- * Commit on success.
- *
- * @param analysis analysis to run
- * @param analysis_cls closure for @a analysis
- * @return #GNUNET_OK if @a analysis succeessfully committed,
- * #GNUNET_NO if we had an error on commit (retry may help)
- * #GNUNET_SYSERR on hard errors
- */
-static int
-transact (Analysis analysis,
- void *analysis_cls)
-{
- int ret;
- enum GNUNET_DB_QueryStatus qs;
-
- ret = adb->start (adb->cls,
- asession);
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- edb->preflight (edb->cls,
- esession);
- ret = edb->start (edb->cls,
- esession,
- "auditor");
- if (GNUNET_OK != ret)
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- qs = analysis (analysis_cls);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- qs = edb->commit (edb->cls,
- esession);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Exchange DB commit failed, rolling back transaction\n");
- adb->rollback (adb->cls,
- asession);
- }
- else
- {
- qs = adb->commit (adb->cls,
- asession);
- if (0 > qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Auditor DB commit failed!\n");
- }
- }
- }
- else
- {
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Processing failed (or no changes), rolling back transaction\n");
- adb->rollback (adb->cls,
- asession);
- edb->rollback (edb->cls,
- esession);
- }
- clear_transaction_state_cache ();
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- return GNUNET_OK;
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- return GNUNET_OK;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- return GNUNET_NO;
- case GNUNET_DB_STATUS_HARD_ERROR:
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
-}
-
-
-/**
- * Initialize DB sessions and run the analysis.
- */
-static void
-setup_sessions_and_run ()
-{
- esession = edb->get_session (edb->cls);
- if (NULL == esession)
- {
- fprintf (stderr,
- "Failed to initialize exchange session.\n");
- global_ret = 1;
- return;
- }
- asession = adb->get_session (adb->cls);
- if (NULL == asession)
- {
- fprintf (stderr,
- "Failed to initialize auditor session.\n");
- global_ret = 1;
- return;
- }
-
- GNUNET_break (GNUNET_SYSERR !=
- transact (&analyze_reserves,
- NULL));
- GNUNET_break (GNUNET_SYSERR !=
- transact (&analyze_aggregations,
- NULL));
- GNUNET_break (GNUNET_SYSERR !=
- transact (&analyze_coins,
- NULL));
- GNUNET_break (GNUNET_SYSERR !=
- transact (&analyze_deposit_confirmations,
- NULL));
-}
-
-
-/**
- * Test if the given @a mpub matches the #master_pub.
- * If so, set "found" to GNUNET_YES.
- *
- * @param cls a `int *` pointing to "found"
- * @param mpub exchange master public key to compare
- * @param exchange_url URL of the exchange (ignored)
- */
-static void
-test_master_present (void *cls,
- const struct TALER_MasterPublicKeyP *mpub,
- const char *exchange_url)
-{
- int *found = cls;
-
- (void) exchange_url;
- if (0 == GNUNET_memcmp (mpub,
- &master_pub))
- *found = GNUNET_YES;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param c configuration
- */
-static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *c)
-{
- static const struct TALER_MasterPublicKeyP zeromp;
- struct TALER_Amount income_fee_total;
- json_t *report;
- struct TALER_AUDITORDB_Session *as;
- int found;
-
- (void) cls;
- (void) args;
- (void) cfgfile;
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Launching auditor\n");
- cfg = c;
- start_time = GNUNET_TIME_absolute_get ();
- if (0 == GNUNET_memcmp (&zeromp,
- &master_pub))
- {
- /* -m option not given, try configuration */
- char *master_public_key_str;
-
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "exchange",
- "MASTER_PUBLIC_KEY",
- &master_public_key_str))
- {
- fprintf (stderr,
- "Pass option -m or set it in the configuration!\n");
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchange",
- "MASTER_PUBLIC_KEY");
- global_ret = 1;
- return;
- }
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_public_key_from_string (master_public_key_str,
- strlen (
- master_public_key_str),
- &master_pub.eddsa_pub))
- {
- fprintf (stderr,
- "Invalid master public key given in configuration file.");
- GNUNET_free (master_public_key_str);
- global_ret = 1;
- return;
- }
- GNUNET_free (master_public_key_str);
- } /* end of -m not given */
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Taler auditor running for exchange master public key %s\n",
- TALER_B2S (&master_pub));
-
- if (GNUNET_OK !=
- TALER_config_get_currency (cfg,
- &currency))
- {
- global_ret = 1;
- return;
- }
- {
- if (GNUNET_OK !=
- TALER_config_get_amount (cfg,
- "taler",
- "CURRENCY_ROUND_UNIT",
- &currency_round_unit))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid or missing amount in `TALER' under `CURRENCY_ROUND_UNIT'\n");
- global_ret = 1;
- return;
- }
- }
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_time (cfg,
- "exchangedb",
- "IDLE_RESERVE_EXPIRATION_TIME",
- &idle_reserve_expiration_time))
- {
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "exchangedb",
- "IDLE_RESERVE_EXPIRATION_TIME");
- global_ret = 1;
- return;
- }
- if (NULL ==
- (edb = TALER_EXCHANGEDB_plugin_load (cfg)))
- {
- fprintf (stderr,
- "Failed to initialize exchange database plugin.\n");
- global_ret = 1;
- return;
- }
- if (NULL ==
- (adb = TALER_AUDITORDB_plugin_load (cfg)))
- {
- fprintf (stderr,
- "Failed to initialize auditor database plugin.\n");
- global_ret = 1;
- TALER_EXCHANGEDB_plugin_unload (edb);
- return;
- }
- found = GNUNET_NO;
- as = adb->get_session (adb->cls);
- if (NULL == as)
- {
- fprintf (stderr,
- "Failed to start session with auditor database.\n");
- global_ret = 1;
- TALER_AUDITORDB_plugin_unload (adb);
- TALER_EXCHANGEDB_plugin_unload (edb);
- return;
- }
- (void) adb->list_exchanges (adb->cls,
- as,
- &test_master_present,
- &found);
- if (GNUNET_NO == found)
- {
- fprintf (stderr,
- "Exchange's master public key `%s' not known to auditor DB. Did you forget to run `taler-auditor-exchange`?\n",
- GNUNET_p2s (&master_pub.eddsa_pub));
- global_ret = 1;
- TALER_AUDITORDB_plugin_unload (adb);
- TALER_EXCHANGEDB_plugin_unload (edb);
- return;
- }
- if (restart)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Full audit restart requested, dropping old audit data.\n");
- GNUNET_break (GNUNET_OK ==
- adb->drop_tables (adb->cls,
- GNUNET_NO));
- TALER_AUDITORDB_plugin_unload (adb);
- if (NULL ==
- (adb = TALER_AUDITORDB_plugin_load (cfg)))
- {
- fprintf (stderr,
- "Failed to initialize auditor database plugin after drop.\n");
- global_ret = 1;
- TALER_EXCHANGEDB_plugin_unload (edb);
- return;
- }
- GNUNET_break (GNUNET_OK ==
- adb->create_tables (adb->cls));
- }
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Starting audit\n");
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &reported_emergency_loss));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &reported_emergency_risk_by_amount));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &reported_emergency_risk_by_count));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &reported_emergency_loss_by_count));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_escrow_balance));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_risk));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_recoup_loss));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_irregular_recoups));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_withdraw_fee_income));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_deposit_fee_income));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_melt_fee_income));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_refund_fee_income));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_aggregation_fee_income));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_balance_insufficient_loss));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_balance_summary_delta_plus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_balance_summary_delta_minus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_wire_out_delta_plus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_wire_out_delta_minus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_arithmetic_delta_plus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_arithmetic_delta_minus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_coin_delta_plus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_coin_delta_minus));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_balance_reserve_not_closed));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_bad_sig_loss));
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (currency,
- &total_refresh_hanging));
- GNUNET_assert (NULL !=
- (report_emergencies = json_array ()));
- GNUNET_assert (NULL !=
- (report_emergencies_by_count = json_array ()));
- GNUNET_assert (NULL !=
- (report_row_inconsistencies = json_array ()));
- GNUNET_assert (NULL !=
- (denomination_key_validity_withdraw_inconsistencies =
- json_array ()));
- GNUNET_assert (NULL !=
- (report_reserve_balance_summary_wrong_inconsistencies =
- json_array ()));
- GNUNET_assert (NULL !=
- (report_reserve_balance_insufficient_inconsistencies =
- json_array ()));
- GNUNET_assert (NULL !=
- (report_reserve_not_closed_inconsistencies = json_array ()));
- 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 ()));
- GNUNET_assert (NULL !=
- (report_amount_arithmetic_inconsistencies = json_array ()));
- GNUNET_assert (NULL !=
- (report_bad_sig_losses = json_array ()));
- GNUNET_assert (NULL !=
- (report_refreshs_hanging = json_array ()));
- GNUNET_assert (NULL !=
- (report_fee_time_inconsistencies = json_array ()));
- setup_sessions_and_run ();
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Audit complete\n");
- TALER_AUDITORDB_plugin_unload (adb);
- TALER_EXCHANGEDB_plugin_unload (edb);
-
- GNUNET_assert (TALER_amount_add (&income_fee_total,
- &total_withdraw_fee_income,
- &total_deposit_fee_income));
- GNUNET_assert (TALER_amount_add (&income_fee_total,
- &income_fee_total,
- &total_melt_fee_income));
- GNUNET_assert (TALER_amount_add (&income_fee_total,
- &income_fee_total,
- &total_refund_fee_income));
- GNUNET_assert (TALER_amount_add (&income_fee_total,
- &income_fee_total,
- &total_aggregation_fee_income));
- report = json_pack ("{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: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, s:o, s:o, s:o, s:o,"
- " s:o, s:I, s:I, s:I, s:I,"
- " s:I, s:I, s:I, s:I, s:I,"
- " s:I, s:I, s:I, s:I, s:I,"
- " s:I, s:I, s:I, s:I, s:I,"
- " s:I, s:I, s:I, s:o, s:o,"
- " s:o }",
- /* blocks of 5 for easier counting/matching to format string */
- /* block */
- "reserve_balance_insufficient_inconsistencies",
- report_reserve_balance_insufficient_inconsistencies,
- /* Tested in test-auditor.sh #3 */
- "total_loss_balance_insufficient",
- TALER_JSON_from_amount (&total_balance_insufficient_loss),
- /* Tested in test-auditor.sh #3 */
- "reserve_balance_summary_wrong_inconsistencies",
- report_reserve_balance_summary_wrong_inconsistencies,
- "total_balance_summary_delta_plus",
- TALER_JSON_from_amount (
- &total_balance_summary_delta_plus),
- "total_balance_summary_delta_minus",
- TALER_JSON_from_amount (
- &total_balance_summary_delta_minus),
- /* block */
- "total_escrow_balance",
- TALER_JSON_from_amount (&total_escrow_balance),
- "total_active_risk",
- TALER_JSON_from_amount (&total_risk),
- "total_withdraw_fee_income",
- TALER_JSON_from_amount (&total_withdraw_fee_income),
- "total_deposit_fee_income",
- TALER_JSON_from_amount (&total_deposit_fee_income),
- "total_melt_fee_income",
- TALER_JSON_from_amount (&total_melt_fee_income),
- /* block */
- "total_refund_fee_income",
- TALER_JSON_from_amount (&total_refund_fee_income),
- "income_fee_total",
- TALER_JSON_from_amount (&income_fee_total),
- /* Tested in test-auditor.sh #18 */
- "emergencies",
- report_emergencies,
- /* Tested in test-auditor.sh #18 */
- "emergencies_risk_by_amount",
- TALER_JSON_from_amount (
- &reported_emergency_risk_by_amount),
- /* Tested in test-auditor.sh #21 */
- "reserve_not_closed_inconsistencies",
- report_reserve_not_closed_inconsistencies,
- /* block */
- /* Tested in test-auditor.sh #21 */
- "total_balance_reserve_not_closed",
- TALER_JSON_from_amount (
- &total_balance_reserve_not_closed),
- "wire_out_inconsistencies",
- report_wire_out_inconsistencies,
- "total_wire_out_delta_plus",
- TALER_JSON_from_amount (&total_wire_out_delta_plus),
- "total_wire_out_delta_minus",
- TALER_JSON_from_amount (&total_wire_out_delta_minus),
- /* Tested in test-auditor.sh #4/#5/#6/#7/#13 */
- "bad_sig_losses",
- report_bad_sig_losses,
- /* block */
- /* Tested in test-auditor.sh #4/#5/#6/#7/#13 */
- "total_bad_sig_loss",
- TALER_JSON_from_amount (&total_bad_sig_loss),
- /* Tested in test-auditor.sh #14/#15 */
- "row_inconsistencies",
- report_row_inconsistencies,
- /* Tested in test-auditor.sh #23 */
- "denomination_key_validity_withdraw_inconsistencies",
- denomination_key_validity_withdraw_inconsistencies,
- "coin_inconsistencies",
- report_coin_inconsistencies,
- "total_coin_delta_plus",
- TALER_JSON_from_amount (&total_coin_delta_plus),
- /* block */
- "total_coin_delta_minus",
- TALER_JSON_from_amount (&total_coin_delta_minus),
- "amount_arithmetic_inconsistencies",
- report_amount_arithmetic_inconsistencies,
- "total_arithmetic_delta_plus",
- TALER_JSON_from_amount (&total_arithmetic_delta_plus),
- "total_arithmetic_delta_minus",
- TALER_JSON_from_amount (&total_arithmetic_delta_minus),
- "total_aggregation_fee_income",
- TALER_JSON_from_amount (&total_aggregation_fee_income),
- /* block */
- "wire_fee_time_inconsistencies",
- report_fee_time_inconsistencies,
- /* Tested in test-auditor.sh #12 */
- "total_refresh_hanging",
- TALER_JSON_from_amount (&total_refresh_hanging),
- /* Tested in test-auditor.sh #12 */
- "refresh_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),
- "total_recoup_loss",
- TALER_JSON_from_amount (&total_recoup_loss),
- /* Tested in test-auditor.sh #18 */
- "emergencies_by_count",
- report_emergencies_by_count,
- /* Tested in test-auditor.sh #18 */
- "emergencies_risk_by_count",
- TALER_JSON_from_amount (
- &reported_emergency_risk_by_count),
- /* Tested in test-auditor.sh #18 */
- "emergencies_loss",
- TALER_JSON_from_amount (&reported_emergency_loss),
- /* block */
- /* Tested in test-auditor.sh #18 */
- "emergencies_loss_by_count",
- TALER_JSON_from_amount (
- &reported_emergency_loss_by_count),
- "start_ppr_reserve_in_serial_id",
- (json_int_t) ppr_start.last_reserve_in_serial_id,
- "start_ppr_reserve_out_serial_id",
- (json_int_t) ppr_start.last_reserve_out_serial_id,
- "start_ppr_reserve_recoup_serial_id",
- (json_int_t) ppr_start.last_reserve_recoup_serial_id,
- "start_ppr_reserve_close_serial_id",
- (json_int_t) ppr_start.last_reserve_close_serial_id,
- /* block */
- "end_ppr_reserve_in_serial_id",
- (json_int_t) ppr.last_reserve_in_serial_id,
- "end_ppr_reserve_out_serial_id",
- (json_int_t) ppr.last_reserve_out_serial_id,
- "end_ppr_reserve_recoup_serial_id",
- (json_int_t) ppr.last_reserve_recoup_serial_id,
- "end_ppr_reserve_close_serial_id",
- (json_int_t) ppr.last_reserve_close_serial_id,
- "start_ppa_wire_out_serial_id",
- (json_int_t) ppa_start.last_wire_out_serial_id,
- /* block */
- "end_ppa_wire_out_serial_id",
- (json_int_t) ppa.last_wire_out_serial_id,
- "start_ppc_withdraw_serial_id",
- (json_int_t) ppc_start.last_withdraw_serial_id,
- "start_ppc_deposit_serial_id",
- (json_int_t) ppc_start.last_deposit_serial_id,
- "start_ppc_melt_serial_id",
- (json_int_t) ppc_start.last_melt_serial_id,
- "start_ppc_refund_serial_id",
- (json_int_t) ppc_start.last_refund_serial_id,
- /* block */
- "start_ppc_recoup_serial_id",
- (json_int_t) ppc_start.last_recoup_serial_id,
- "start_ppc_recoup_refresh_serial_id",
- (json_int_t) ppc_start.last_recoup_refresh_serial_id,
- "end_ppc_withdraw_serial_id",
- (json_int_t) ppc.last_withdraw_serial_id,
- "end_ppc_deposit_serial_id",
- (json_int_t) ppc.last_deposit_serial_id,
- "end_ppc_melt_serial_id",
- (json_int_t) ppc.last_melt_serial_id,
- /* block */
- "end_ppc_refund_serial_id",
- (json_int_t) ppc.last_refund_serial_id,
- "end_ppc_recoup_serial_id",
- (json_int_t) ppc.last_recoup_serial_id,
- "end_ppc_recoup_refresh_serial_id",
- (json_int_t) ppc.last_recoup_refresh_serial_id,
- "auditor_start_time", json_string (
- GNUNET_STRINGS_absolute_time_to_string (start_time)),
- "auditor_end_time", json_string (
- GNUNET_STRINGS_absolute_time_to_string (
- GNUNET_TIME_absolute_get ())),
- /* block */
- "total_irregular_recoups",
- TALER_JSON_from_amount (&total_irregular_recoups)
- );
- GNUNET_break (NULL != report);
- json_dumpf (report,
- stdout,
- JSON_INDENT (2));
- json_decref (report);
-}
-
-
-/**
- * The main function of the database initialization tool.
- * Used to initialize the Taler Exchange's database.
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
- char *const *argv)
-{
- const struct GNUNET_GETOPT_CommandLineOption options[] = {
- GNUNET_GETOPT_option_base32_auto ('m',
- "exchange-key",
- "KEY",
- "public key of the exchange (Crockford base32 encoded)",
- &master_pub),
- GNUNET_GETOPT_option_flag ('r',
- "restart",
- "restart audit from the beginning (required on first run)",
- &restart),
- GNUNET_GETOPT_option_timetravel ('T',
- "timetravel"),
- GNUNET_GETOPT_OPTION_END
- };
-
- /* force linker to link against libtalerutil; if we do
- not do this, the linker may "optimize" libtalerutil
- away and skip #TALER_OS_init(), which we do need */
- (void) TALER_project_data_default ();
- GNUNET_assert (GNUNET_OK ==
- GNUNET_log_setup ("taler-auditor",
- "MESSAGE",
- NULL));
- if (GNUNET_OK !=
- GNUNET_PROGRAM_run (argc,
- argv,
- "taler-auditor",
- "Audit Taler exchange database",
- options,
- &run,
- NULL))
- return 1;
- return global_ret;
-}
-
-
-/* end of taler-auditor.c */