add check for #coins depoisted exceeding #coins issued (not just by amount), fixes #5446

This commit is contained in:
Christian Grothoff 2018-11-04 16:20:09 +01:00
parent 33441042a9
commit 332341cb7b
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016, 2017 Taler Systems SA Copyright (C) 2016, 2017, 2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the 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 terms of the GNU Affero Public License as published by the Free Software
@ -329,8 +329,8 @@ report (json_t *array,
* @param risk maximum risk that might have just become real (coins created by this @a dki) * @param risk maximum risk that might have just become real (coins created by this @a dki)
*/ */
static void static void
report_emergency (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki, report_emergency_by_amount (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
const struct TALER_Amount *risk) const struct TALER_Amount *risk)
{ {
report (report_emergencies, report (report_emergencies,
json_pack ("{s:o, s:o, s:s, s:s, s:o}", json_pack ("{s:o, s:o, s:s, s:s, s:o}",
@ -351,6 +351,49 @@ report_emergency (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki
} }
/**
* 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 dki 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_EXCHANGEDB_DenominationKeyInformationP *dki,
uint64_t num_issued,
uint64_t num_known,
const struct TALER_Amount *risk)
{
report (report_emergencies,
json_pack ("{s:o, s:I, s:I, s:o, s:s, s:s, s:o}",
"denompub_hash",
GNUNET_JSON_from_data_auto (&dki->properties.denom_hash),
"num_issued",
(json_int_t) num_issued,
"num_known",
(json_int_t) num_known,
"denom_risk",
TALER_JSON_from_amount (risk),
"start",
GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dki->properties.start)),
"deposit_end",
GNUNET_STRINGS_absolute_time_to_string (GNUNET_TIME_absolute_ntoh (dki->properties.expire_deposit)),
"value",
TALER_JSON_from_amount_nbo (&dki->properties.value)));
GNUNET_assert (GNUNET_OK ==
TALER_amount_add (&reported_emergency_sum,
&reported_emergency_sum,
risk));
}
/** /**
* Report a (serious) inconsistency in the exchange's database with * Report a (serious) inconsistency in the exchange's database with
* respect to calculations involving amounts. * respect to calculations involving amounts.
@ -2962,24 +3005,47 @@ sync_denomination (void *cls,
} }
else else
{ {
long long cnt;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Final balance for denomination `%s' is %s\n", "Final balance for denomination `%s' is %s (%llu)\n",
GNUNET_h2s (denom_hash), GNUNET_h2s (denom_hash),
TALER_amount2s (&ds->denom_balance)); TALER_amount2s (&ds->denom_balance),
if (ds->in_db) (unsigned long long) ds->num_issued);
qs = adb->update_denomination_balance (adb->cls, cnt = edb->count_known_coins (edb->cls,
asession, esession,
denom_hash, denom_hash);
&ds->denom_balance, if (0 > cnt)
&ds->denom_risk, {
ds->num_issued); /* Failed to obtain count? Bad database */
qs = (enum GNUNET_DB_QueryStatus) cnt;
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
cc->qs = qs;
}
else else
qs = adb->insert_denomination_balance (adb->cls, {
asession, if (ds->num_issued > (uint64_t) cnt)
denom_hash, {
&ds->denom_balance, report_emergency_by_count (dki,
&ds->denom_risk, cnt,
ds->num_issued); ds->num_issued,
&ds->denom_risk);
}
if (ds->in_db)
qs = adb->update_denomination_balance (adb->cls,
asession,
denom_hash,
&ds->denom_balance,
&ds->denom_risk,
ds->num_issued);
else
qs = adb->insert_denomination_balance (adb->cls,
asession,
denom_hash,
&ds->denom_balance,
&ds->denom_risk,
ds->num_issued);
}
} }
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{ {
@ -3272,7 +3338,6 @@ refresh_session_cb (void *cls,
amount_with_fee)); amount_with_fee));
return GNUNET_OK; return GNUNET_OK;
} }
// FIXME: free reveal_ctx.num_newcoins later!
{ {
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *new_dkis[reveal_ctx.num_newcoins]; const struct TALER_EXCHANGEDB_DenominationKeyInformationP *new_dkis[reveal_ctx.num_newcoins];
@ -3436,8 +3501,9 @@ refresh_session_cb (void *cls,
&dso->denom_balance, &dso->denom_balance,
amount_with_fee)) amount_with_fee))
{ {
report_emergency (dki, report_emergency_by_amount (dki,
&dso->denom_risk); &dso->denom_risk);
/* FIXME: we can't exactly just stop here! */
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
dso->denom_balance = tmp; dso->denom_balance = tmp;
@ -3593,8 +3659,9 @@ deposit_cb (void *cls,
&ds->denom_balance, &ds->denom_balance,
amount_with_fee)) amount_with_fee))
{ {
report_emergency (dki, report_emergency_by_amount (dki,
&ds->denom_risk); &ds->denom_risk);
/* FIXME: we can't exactly just stop here like this! */
cc->qs = GNUNET_DB_STATUS_HARD_ERROR; cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }