2016-10-06 15:17:10 +02:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2017-11-27 23:42:17 +01:00
|
|
|
Copyright (C) 2016, 2017 Taler Systems SA
|
2016-10-06 15:17:10 +02:00
|
|
|
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
2017-11-20 14:20:09 +01:00
|
|
|
terms of the GNU Affero Public License as published by the Free Software
|
2016-10-06 15:17:10 +02:00
|
|
|
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
|
2017-11-20 14:20:09 +01:00
|
|
|
A PARTICULAR PURPOSE. See the GNU Affero Public License for more details.
|
2016-10-06 15:17:10 +02:00
|
|
|
|
2017-11-20 14:20:09 +01:00
|
|
|
You should have received a copy of the GNU Affero Public License along with
|
2016-10-06 15:17:10 +02:00
|
|
|
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
|
2017-03-14 15:13:50 +01:00
|
|
|
*
|
|
|
|
* 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
|
2017-03-18 16:56:31 +01:00
|
|
|
* given in the 'wire_out' table. This needs to be checked separately!
|
2017-03-20 02:29:33 +01:00
|
|
|
*
|
|
|
|
* KNOWN BUGS:
|
|
|
|
* - error handling if denomination keys are used that are not known to the
|
|
|
|
* auditor is, eh, awful / non-existent. We just throw the DB's constraint
|
|
|
|
* violation back at the user. Great UX.
|
2016-10-06 15:17:10 +02:00
|
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include <gnunet/gnunet_util_lib.h>
|
2016-10-08 19:04:21 +02:00
|
|
|
#include "taler_auditordb_plugin.h"
|
2016-10-06 15:17:10 +02:00
|
|
|
#include "taler_exchangedb_plugin.h"
|
2017-03-17 17:17:07 +01:00
|
|
|
#include "taler_json_lib.h"
|
2017-03-18 16:56:31 +01:00
|
|
|
#include "taler_wire_lib.h"
|
2017-03-14 15:13:50 +01:00
|
|
|
#include "taler_signatures.h"
|
2016-10-06 15:17:10 +02:00
|
|
|
|
|
|
|
|
2017-03-15 19:28:05 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
|
2017-03-18 23:03:00 +01:00
|
|
|
/**
|
|
|
|
* Use a 1 day grace period to deal with clocks not being perfectly synchronized.
|
|
|
|
*/
|
|
|
|
#define DEPOSIT_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
|
|
|
|
|
2017-04-19 21:41:53 +02:00
|
|
|
/**
|
|
|
|
* Use a 1 day grace period to deal with clocks not being perfectly synchronized.
|
|
|
|
*/
|
|
|
|
#define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
|
|
|
|
|
2016-10-06 15:17:10 +02:00
|
|
|
/**
|
|
|
|
* Return value from main().
|
|
|
|
*/
|
|
|
|
static int global_ret;
|
|
|
|
|
2017-03-19 13:53:50 +01:00
|
|
|
/**
|
|
|
|
* Command-line option "-r": restart audit from scratch
|
|
|
|
*/
|
|
|
|
static int restart;
|
|
|
|
|
2016-10-06 15:17:10 +02:00
|
|
|
/**
|
|
|
|
* Handle to access the exchange's database.
|
|
|
|
*/
|
|
|
|
static struct TALER_EXCHANGEDB_Plugin *edb;
|
|
|
|
|
2017-03-15 14:19:57 +01:00
|
|
|
/**
|
|
|
|
* Which currency are we doing the audit for?
|
|
|
|
*/
|
|
|
|
static char *currency;
|
|
|
|
|
2017-03-18 16:56:31 +01:00
|
|
|
/**
|
|
|
|
* Our configuration.
|
|
|
|
*/
|
|
|
|
static const struct GNUNET_CONFIGURATION_Handle *cfg;
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
/**
|
|
|
|
* Our session with the #edb.
|
|
|
|
*/
|
|
|
|
static struct TALER_EXCHANGEDB_Session *esession;
|
|
|
|
|
2016-10-06 15:17:10 +02:00
|
|
|
/**
|
|
|
|
* Handle to access the auditor's database.
|
|
|
|
*/
|
|
|
|
static struct TALER_AUDITORDB_Plugin *adb;
|
|
|
|
|
2017-03-14 12:22:03 +01:00
|
|
|
/**
|
|
|
|
* Our session with the #adb.
|
|
|
|
*/
|
|
|
|
static struct TALER_AUDITORDB_Session *asession;
|
|
|
|
|
2017-04-20 21:38:02 +02:00
|
|
|
/**
|
|
|
|
* After how long should idle reserves be closed?
|
|
|
|
*/
|
|
|
|
static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
|
|
|
|
|
2017-03-14 12:22:03 +01:00
|
|
|
/**
|
|
|
|
* Master public key of the exchange to audit.
|
|
|
|
*/
|
|
|
|
static struct TALER_MasterPublicKeyP master_pub;
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
/**
|
|
|
|
* Last reserve_in serial ID seen.
|
|
|
|
*/
|
2017-03-15 13:12:45 +01:00
|
|
|
static struct TALER_AUDITORDB_ProgressPoint pp;
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2017-10-16 11:49:32 +02:00
|
|
|
/**
|
|
|
|
* Array of reports about denomination keys with an
|
|
|
|
* emergency (more deposited than withdrawn)
|
|
|
|
*/
|
|
|
|
static json_t *report_emergencies;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of reports about row inconsitencies.
|
|
|
|
*/
|
|
|
|
static json_t *report_row_inconsistencies;
|
|
|
|
|
|
|
|
/**
|
2017-11-06 14:54:52 +01:00
|
|
|
* Array of reports about the denomination key not being
|
|
|
|
* valid at the time of withdrawal.
|
2017-10-16 11:49:32 +02:00
|
|
|
*/
|
2017-11-06 14:54:52 +01:00
|
|
|
static json_t *denomination_key_validity_withdraw_inconsistencies;
|
2017-10-16 11:49:32 +02:00
|
|
|
|
|
|
|
/**
|
2017-11-06 14:54:52 +01:00
|
|
|
* Array of reports about reserve balance insufficient inconsitencies.
|
2017-10-16 11:49:32 +02:00
|
|
|
*/
|
2017-11-06 14:54:52 +01:00
|
|
|
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;
|
2017-10-16 11:49:32 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of reports about irregular wire out entries.
|
|
|
|
*/
|
|
|
|
static json_t *report_wire_out_inconsistencies;
|
|
|
|
|
2017-11-06 14:54:52 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-10-16 11:49:32 +02:00
|
|
|
/**
|
|
|
|
* Array of reports about inconsistencies about coins.
|
|
|
|
*/
|
|
|
|
static json_t *report_coin_inconsistencies;
|
|
|
|
|
2017-11-06 19:11:43 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-10-16 11:49:32 +02:00
|
|
|
/**
|
2017-11-06 00:03:08 +01:00
|
|
|
* Report about aggregate wire transfer fee profits.
|
2017-10-16 11:49:32 +02:00
|
|
|
*/
|
2017-11-06 00:03:08 +01:00
|
|
|
static json_t *report_aggregation_fee_balances;
|
2017-10-16 11:49:32 +02:00
|
|
|
|
2017-11-06 19:11:43 +01:00
|
|
|
/**
|
|
|
|
* Report about amount calculation differences (causing profit
|
|
|
|
* or loss at the exchange).
|
|
|
|
*/
|
|
|
|
static json_t *report_amount_arithmetic_inconsistencies;
|
|
|
|
|
2017-11-20 14:20:09 +01:00
|
|
|
/**
|
|
|
|
* Array of reports about wire fees being ambiguous in terms of validity periods.
|
|
|
|
*/
|
|
|
|
static json_t *report_fee_time_inconsistencies;
|
|
|
|
|
2017-11-06 19:11:43 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-10-16 11:49:32 +02:00
|
|
|
/**
|
2017-11-06 00:03:08 +01:00
|
|
|
* Total amount reported in all calls to #report_emergency().
|
2017-10-16 11:49:32 +02:00
|
|
|
*/
|
2017-11-06 00:03:08 +01:00
|
|
|
static struct TALER_Amount reported_emergency_sum;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expected balance in the escrow account.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_escrow_balance;
|
2017-10-16 11:49:32 +02:00
|
|
|
|
|
|
|
/**
|
2017-11-06 00:03:08 +01:00
|
|
|
* Active risk exposure.
|
2017-10-16 11:49:32 +02:00
|
|
|
*/
|
2017-11-06 00:03:08 +01:00
|
|
|
static struct TALER_Amount total_risk;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-11-06 19:11:43 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
2017-10-16 11:49:32 +02:00
|
|
|
|
2017-11-27 23:42:17 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
|
|
|
|
/* ***************************** Report logic **************************** */
|
|
|
|
|
2017-03-17 18:27:53 +01:00
|
|
|
|
2017-10-16 11:49:32 +02:00
|
|
|
/**
|
|
|
|
* 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
|
2017-11-06 00:03:08 +01:00
|
|
|
*/
|
2017-10-16 11:49:32 +02:00
|
|
|
static void
|
|
|
|
report (json_t *array,
|
|
|
|
json_t *object)
|
|
|
|
{
|
|
|
|
GNUNET_assert (NULL != object);
|
|
|
|
GNUNET_assert (0 ==
|
|
|
|
json_array_append_new (array,
|
|
|
|
object));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-17 18:27:53 +01:00
|
|
|
/**
|
|
|
|
* 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 dki denomination key where the loss was detected
|
2017-11-06 00:03:08 +01:00
|
|
|
* @param risk maximum risk that might have just become real (coins created by this @a dki)
|
2017-03-17 18:27:53 +01:00
|
|
|
*/
|
|
|
|
static void
|
2017-11-06 00:03:08 +01:00
|
|
|
report_emergency (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
|
|
|
|
const struct TALER_Amount *risk)
|
2017-03-17 18:27:53 +01:00
|
|
|
{
|
2017-10-16 11:49:32 +02:00
|
|
|
report (report_emergencies,
|
2017-11-06 00:03:08 +01:00
|
|
|
json_pack ("{s:o, s:o, s:s, s:s, s:o}",
|
2017-10-16 11:49:32 +02:00
|
|
|
"denompub_hash",
|
2017-11-06 00:03:08 +01:00
|
|
|
GNUNET_JSON_from_data_auto (&dki->properties.denom_hash),
|
|
|
|
"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));
|
2017-03-17 18:27:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-06 19:11:43 +01:00
|
|
|
/**
|
|
|
|
* 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 proftable 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 = 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 proftable 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 = profitable
|
|
|
|
? &total_coin_delta_plus
|
|
|
|
: &total_coin_delta_minus;
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (target,
|
|
|
|
target,
|
|
|
|
&delta));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
{
|
2017-10-16 11:49:32 +02:00
|
|
|
report (report_row_inconsistencies,
|
|
|
|
json_pack ("{s:s, s:I, s:s}",
|
|
|
|
"table", table,
|
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"diagnostic", diagnostic));
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ************************* Transaction-global state ************************ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Results about denominations, cached per-transaction.
|
|
|
|
*/
|
|
|
|
static struct GNUNET_CONTAINER_MultiHashMap *denominations;
|
2017-03-14 12:22:03 +01:00
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
/**
|
|
|
|
* Obtain information about a @a denom_pub.
|
|
|
|
*
|
|
|
|
* @param denom_pub key to look up
|
|
|
|
* @param[out] dki set to detailed information about @a denom_pub, NULL if not found, must
|
|
|
|
* NOT be freed by caller
|
2017-03-15 19:28:05 +01:00
|
|
|
* @param[out] dh set to the hash of @a denom_pub, may be NULL
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-14 15:13:50 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-14 15:13:50 +01:00
|
|
|
get_denomination_info (const struct TALER_DenominationPublicKey *denom_pub,
|
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP **dki,
|
|
|
|
struct GNUNET_HashCode *dh)
|
|
|
|
{
|
|
|
|
struct GNUNET_HashCode hc;
|
|
|
|
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dkip;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
|
|
|
|
if (NULL == dh)
|
|
|
|
dh = &hc;
|
|
|
|
GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
|
|
|
|
dh);
|
2017-03-19 13:53:50 +01:00
|
|
|
if (NULL == denominations)
|
|
|
|
denominations = GNUNET_CONTAINER_multihashmap_create (256,
|
|
|
|
GNUNET_NO);
|
2017-03-14 15:13:50 +01:00
|
|
|
dkip = GNUNET_CONTAINER_multihashmap_get (denominations,
|
|
|
|
dh);
|
|
|
|
if (NULL != dkip)
|
2017-03-18 23:03:00 +01:00
|
|
|
{
|
|
|
|
/* cache hit */
|
|
|
|
*dki = dkip;
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-18 23:03:00 +01:00
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
dkip = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKeyInformationP);
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->get_denomination_info (edb->cls,
|
|
|
|
esession,
|
|
|
|
denom_pub,
|
|
|
|
dkip);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
|
|
|
GNUNET_free (dkip);
|
|
|
|
*dki = NULL;
|
2017-06-25 01:46:19 +02:00
|
|
|
return qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
|
|
|
struct TALER_Amount value;
|
|
|
|
|
|
|
|
TALER_amount_ntoh (&value,
|
|
|
|
&dkip->properties.value);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Tracking denomination `%s' (%s)\n",
|
|
|
|
GNUNET_h2s (dh),
|
|
|
|
TALER_amount2s (&value));
|
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
*dki = dkip;
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
GNUNET_CONTAINER_multihashmap_put (denominations,
|
|
|
|
dh,
|
|
|
|
dkip,
|
|
|
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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_EXCHANGEDB_DenominationKeyInformationP *dki = value;
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Done with denomination `%s'\n",
|
|
|
|
GNUNET_h2s (key));
|
2017-03-14 15:13:50 +01:00
|
|
|
GNUNET_free (dki);
|
|
|
|
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 ************************ */
|
2017-03-14 18:00:17 +01:00
|
|
|
/* This logic checks the reserves_in, reserves_out and reserves-tables */
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
/**
|
|
|
|
* Summary data we keep per reserve.
|
|
|
|
*/
|
|
|
|
struct ReserveSummary
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Public key of the reserve.
|
2017-03-14 18:00:17 +01:00
|
|
|
* Always set when the struct is first initialized.
|
2016-11-17 14:31:44 +01:00
|
|
|
*/
|
|
|
|
struct TALER_ReservePublicKeyP reserve_pub;
|
|
|
|
|
|
|
|
/**
|
2017-03-14 18:00:17 +01:00
|
|
|
* Sum of all incoming transfers during this transaction.
|
|
|
|
* Updated only in #handle_reserve_in().
|
2016-11-17 14:31:44 +01:00
|
|
|
*/
|
|
|
|
struct TALER_Amount total_in;
|
|
|
|
|
|
|
|
/**
|
2017-03-14 18:00:17 +01:00
|
|
|
* Sum of all outgoing transfers during this transaction (includes fees).
|
|
|
|
* Updated only in #handle_reserve_out().
|
2016-11-17 14:31:44 +01:00
|
|
|
*/
|
|
|
|
struct TALER_Amount total_out;
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/**
|
|
|
|
* Sum of withdraw fees encountered during this transaction.
|
|
|
|
*/
|
|
|
|
struct TALER_Amount total_fee;
|
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
/**
|
|
|
|
* Previous balance of the reserve as remembered by the auditor.
|
2017-03-14 18:00:17 +01:00
|
|
|
* (updated based on @e total_in and @e total_out at the end).
|
2017-03-14 15:13:50 +01:00
|
|
|
*/
|
|
|
|
struct TALER_Amount a_balance;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Previous withdraw fee balance of the reserve, as remembered by the auditor.
|
2017-03-14 18:00:17 +01:00
|
|
|
* (updated based on @e total_fee at the end).
|
2017-03-14 15:13:50 +01:00
|
|
|
*/
|
|
|
|
struct TALER_Amount a_withdraw_fee_balance;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Previous reserve expiration data, as remembered by the auditor.
|
2017-03-14 18:00:17 +01:00
|
|
|
* (updated on-the-fly in #handle_reserve_in()).
|
2017-03-14 15:13:50 +01:00
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Absolute a_expiration_date;
|
|
|
|
|
|
|
|
/**
|
2017-03-14 18:00:17 +01:00
|
|
|
* 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).
|
2017-03-14 15:13:50 +01:00
|
|
|
*/
|
|
|
|
int had_ri;
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-14 15:13:50 +01:00
|
|
|
* Load the auditor's remembered state about the reserve into @a rs.
|
2017-03-14 18:00:17 +01:00
|
|
|
* The "total_in" and "total_out" amounts of @a rs must already be
|
|
|
|
* initialized (so we can determine the currency).
|
2017-03-14 15:13:50 +01:00
|
|
|
*
|
2017-04-20 07:49:56 +02:00
|
|
|
* @param[in,out] rs reserve summary to (fully) initialize
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2016-11-17 14:31:44 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-14 15:13:50 +01:00
|
|
|
load_auditor_reserve_summary (struct ReserveSummary *rs)
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-15 13:36:47 +01:00
|
|
|
uint64_t rowid;
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
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);
|
|
|
|
if (0 > qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
|
|
|
rs->had_ri = GNUNET_NO;
|
2017-03-14 18:00:17 +01:00
|
|
|
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));
|
2017-03-20 02:29:33 +01:00
|
|
|
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));
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
|
|
|
rs->had_ri = GNUNET_YES;
|
2017-03-14 18:00:17 +01:00
|
|
|
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);
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Auditor remembers reserve `%s' has balance %s\n",
|
|
|
|
TALER_B2S (&rs->reserve_pub),
|
|
|
|
TALER_amount2s (&rs->a_balance));
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
2016-11-17 14:31:44 +01:00
|
|
|
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-04-19 13:46:38 +02:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
/**
|
|
|
|
* Transaction status code, set to error codes if applicable.
|
|
|
|
*/
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-10-06 20:02:28 +02:00
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
/**
|
|
|
|
* Function called with details about incoming wire transfers.
|
|
|
|
*
|
2017-03-14 18:00:17 +01:00
|
|
|
* @param cls our `struct ReserveContext`
|
2016-11-17 14:31:44 +01:00
|
|
|
* @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
|
2017-05-05 13:41:32 +02:00
|
|
|
* @param wire_reference unique reference identifying the wire transfer (binary blob)
|
|
|
|
* @param wire_reference_size number of bytes in @a wire_reference
|
2016-11-17 14:31:44 +01:00
|
|
|
* @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 json_t *sender_account_details,
|
2017-05-05 13:41:32 +02:00
|
|
|
const void *wire_reference,
|
|
|
|
size_t wire_reference_size,
|
2016-11-17 14:31:44 +01:00
|
|
|
struct GNUNET_TIME_Absolute execution_date)
|
|
|
|
{
|
2017-03-14 18:00:17 +01:00
|
|
|
struct ReserveContext *rc = cls;
|
2016-11-17 14:31:44 +01:00
|
|
|
struct GNUNET_HashCode key;
|
|
|
|
struct ReserveSummary *rs;
|
2017-03-14 18:00:17 +01:00
|
|
|
struct GNUNET_TIME_Absolute expiry;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-04-19 21:41:53 +02:00
|
|
|
/* should be monotonically increasing */
|
2017-05-05 13:41:32 +02:00
|
|
|
GNUNET_assert (rowid >= pp.last_reserve_in_serial_id);
|
2017-03-15 13:12:45 +01:00
|
|
|
pp.last_reserve_in_serial_id = rowid + 1;
|
2017-04-19 21:41:53 +02:00
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
GNUNET_CRYPTO_hash (reserve_pub,
|
|
|
|
sizeof (*reserve_pub),
|
|
|
|
&key);
|
2017-03-14 18:00:17 +01:00
|
|
|
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
&key);
|
|
|
|
if (NULL == rs)
|
|
|
|
{
|
|
|
|
rs = GNUNET_new (struct ReserveSummary);
|
|
|
|
rs->reserve_pub = *reserve_pub;
|
|
|
|
rs->total_in = *credit;
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (credit->currency,
|
|
|
|
&rs->total_out));
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (credit->currency,
|
|
|
|
&rs->total_fee));
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > (qs = load_auditor_reserve_summary (rs)))
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (rs);
|
2017-06-25 01:46:19 +02:00
|
|
|
rc->qs = qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2016-11-17 14:31:44 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
&key,
|
|
|
|
rs,
|
|
|
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&rs->total_in,
|
|
|
|
&rs->total_in,
|
|
|
|
credit));
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Additional incoming wire transfer for reserve `%s' of %s\n",
|
|
|
|
TALER_B2S (reserve_pub),
|
|
|
|
TALER_amount2s (credit));
|
2017-03-14 18:00:17 +01:00
|
|
|
expiry = GNUNET_TIME_absolute_add (execution_date,
|
2017-04-20 21:38:02 +02:00
|
|
|
idle_reserve_expiration_time);
|
2017-03-14 18:00:17 +01:00
|
|
|
rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
|
|
|
|
expiry);
|
2016-11-17 14:31:44 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-17 17:17:07 +01:00
|
|
|
* Function called with details about withdraw operations. Verifies
|
|
|
|
* the signature and updates the reserve's balance.
|
2016-11-17 14:31:44 +01:00
|
|
|
*
|
2017-03-14 18:00:17 +01:00
|
|
|
* @param cls our `struct ReserveContext`
|
2016-11-17 14:31:44 +01:00
|
|
|
* @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 denom_sig signature over 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_DenominationSignature *denom_sig,
|
|
|
|
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)
|
|
|
|
{
|
2017-03-14 18:00:17 +01:00
|
|
|
struct ReserveContext *rc = cls;
|
2017-03-14 15:13:50 +01:00
|
|
|
struct TALER_WithdrawRequestPS wsrd;
|
2016-11-17 14:31:44 +01:00
|
|
|
struct GNUNET_HashCode key;
|
|
|
|
struct ReserveSummary *rs;
|
2017-03-14 15:13:50 +01:00
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
2017-03-14 18:00:17 +01:00
|
|
|
struct TALER_Amount withdraw_fee;
|
|
|
|
struct GNUNET_TIME_Absolute valid_start;
|
|
|
|
struct GNUNET_TIME_Absolute expire_withdraw;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
/* should be monotonically increasing */
|
2017-03-15 13:12:45 +01:00
|
|
|
GNUNET_assert (rowid >= pp.last_reserve_out_serial_id);
|
|
|
|
pp.last_reserve_out_serial_id = rowid + 1;
|
2017-03-14 15:13:50 +01:00
|
|
|
|
|
|
|
/* lookup denomination pub data (make sure denom_pub is valid, establish fees) */
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (denom_pub,
|
|
|
|
&dki,
|
|
|
|
&wsrd.h_denomination_pub);
|
|
|
|
if (0 > qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
rc->qs = qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
2017-03-15 19:28:05 +01:00
|
|
|
report_row_inconsistency ("withdraw",
|
2017-03-14 15:13:50 +01:00
|
|
|
rowid,
|
|
|
|
"denomination key not found (foreign key constraint violated)");
|
2017-06-25 01:46:19 +02:00
|
|
|
rc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-14 15:13:50 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/* check that execution date is within withdraw range for denom_pub */
|
|
|
|
valid_start = GNUNET_TIME_absolute_ntoh (dki->properties.start);
|
|
|
|
expire_withdraw = GNUNET_TIME_absolute_ntoh (dki->properties.expire_withdraw);
|
|
|
|
if ( (valid_start.abs_value_us > execution_date.abs_value_us) ||
|
|
|
|
(expire_withdraw.abs_value_us < execution_date.abs_value_us) )
|
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
report (denomination_key_validity_withdraw_inconsistencies,
|
|
|
|
json_pack ("{s:I, s:s, s:o, s:o}",
|
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"execution_date", GNUNET_STRINGS_absolute_time_to_string (execution_date),
|
|
|
|
"reserve_pub", GNUNET_JSON_from_data_auto (reserve_pub),
|
|
|
|
"denompub_h", GNUNET_JSON_from_data_auto (&wsrd.h_denomination_pub)));
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
|
|
|
|
/* check reserve_sig */
|
|
|
|
wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
|
|
|
|
wsrd.purpose.size = htonl (sizeof (wsrd));
|
2017-03-19 13:53:50 +01:00
|
|
|
wsrd.reserve_pub = *reserve_pub;
|
2017-03-14 15:13:50 +01:00
|
|
|
TALER_amount_hton (&wsrd.amount_with_fee,
|
|
|
|
amount_with_fee);
|
|
|
|
wsrd.withdraw_fee = dki->properties.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))
|
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
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));
|
2017-03-14 15:13:50 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
GNUNET_CRYPTO_hash (reserve_pub,
|
|
|
|
sizeof (*reserve_pub),
|
|
|
|
&key);
|
2017-03-14 18:00:17 +01:00
|
|
|
rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
&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));
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (amount_with_fee->currency,
|
|
|
|
&rs->total_fee));
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = load_auditor_reserve_summary (rs);
|
|
|
|
if (0 > qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-03-14 15:13:50 +01:00
|
|
|
GNUNET_free (rs);
|
2017-06-25 01:46:19 +02:00
|
|
|
rc->qs = qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2016-11-17 14:31:44 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_CONTAINER_multihashmap_put (rc->reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
&key,
|
|
|
|
rs,
|
|
|
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&rs->total_out,
|
|
|
|
&rs->total_out,
|
|
|
|
amount_with_fee));
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Reserve `%s' reduced by %s from withdraw\n",
|
|
|
|
TALER_B2S (reserve_pub),
|
|
|
|
TALER_amount2s (amount_with_fee));
|
2017-03-14 18:00:17 +01:00
|
|
|
TALER_amount_ntoh (&withdraw_fee,
|
|
|
|
&dki->properties.fee_withdraw);
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&rs->total_fee,
|
|
|
|
&rs->total_fee,
|
|
|
|
&withdraw_fee));
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-04 15:38:58 +02:00
|
|
|
/**
|
|
|
|
* 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 payback request
|
|
|
|
* @param amount how much should be added back to the reserve
|
|
|
|
* @param reserve_pub public key of the reserve
|
2017-04-04 16:27:27 +02:00
|
|
|
* @param coin public information about the coin
|
2017-04-04 15:38:58 +02:00
|
|
|
* @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_PAYBACK
|
|
|
|
* @param coin_blind blinding factor used to blind the coin
|
|
|
|
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
handle_payback_by_reserve (void *cls,
|
|
|
|
uint64_t rowid,
|
|
|
|
struct GNUNET_TIME_Absolute timestamp,
|
|
|
|
const struct TALER_Amount *amount,
|
|
|
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
2017-04-04 16:27:27 +02:00
|
|
|
const struct TALER_CoinPublicInfo *coin,
|
2017-04-04 15:38:58 +02:00
|
|
|
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;
|
2017-04-04 17:05:38 +02:00
|
|
|
struct TALER_PaybackRequestPS pr;
|
2017-04-08 22:52:32 +02:00
|
|
|
struct TALER_MasterSignatureP msig;
|
2017-04-16 17:21:26 +02:00
|
|
|
uint64_t rev_rowid;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-04-19 13:46:38 +02:00
|
|
|
const char *rev;
|
2017-04-04 15:38:58 +02:00
|
|
|
|
|
|
|
/* should be monotonically increasing */
|
|
|
|
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
|
|
|
|
pp.last_reserve_payback_serial_id = rowid + 1;
|
2017-11-06 19:11:43 +01:00
|
|
|
GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
|
|
|
|
&pr.h_denom_pub);
|
2017-04-04 15:38:58 +02:00
|
|
|
|
2017-04-04 17:05:38 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_test_coin_valid (coin))
|
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
report (report_bad_sig_losses,
|
|
|
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
|
|
|
"operation", "payback-verify",
|
|
|
|
"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));
|
2017-04-04 17:05:38 +02:00
|
|
|
}
|
|
|
|
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK);
|
|
|
|
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_PAYBACK,
|
|
|
|
&pr.purpose,
|
|
|
|
&coin_sig->eddsa_signature,
|
|
|
|
&coin->coin_pub.eddsa_pub))
|
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
report (report_bad_sig_losses,
|
|
|
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
|
|
|
"operation", "payback",
|
|
|
|
"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));
|
2017-04-04 17:05:38 +02:00
|
|
|
}
|
2017-04-08 22:52:32 +02:00
|
|
|
|
|
|
|
/* check that the coin was eligible for payback!*/
|
2017-04-19 13:46:38 +02:00
|
|
|
rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
|
|
|
|
&pr.h_denom_pub);
|
|
|
|
if (NULL == rev)
|
2017-04-08 22:52:32 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->get_denomination_revocation (edb->cls,
|
|
|
|
esession,
|
|
|
|
&pr.h_denom_pub,
|
|
|
|
&msig,
|
|
|
|
&rev_rowid);
|
|
|
|
if (0 > qs)
|
2017-04-19 13:46:38 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
rc->qs = qs;
|
2017-04-19 13:46:38 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
2017-04-19 13:46:38 +02:00
|
|
|
{
|
|
|
|
report_row_inconsistency ("payback",
|
|
|
|
rowid,
|
2017-05-05 13:41:32 +02:00
|
|
|
"denomination key not in revocation set");
|
2017-04-19 13:46:38 +02:00
|
|
|
}
|
|
|
|
else
|
2017-04-08 22:52:32 +02:00
|
|
|
{
|
2017-04-19 13:46:38 +02:00
|
|
|
/* verify msig */
|
|
|
|
struct TALER_MasterDenominationKeyRevocation kr;
|
2017-05-05 13:41:32 +02:00
|
|
|
|
2017-04-19 13:46:38 +02:00
|
|
|
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));
|
2017-04-08 22:52:32 +02:00
|
|
|
}
|
|
|
|
}
|
2017-11-06 19:11:43 +01:00
|
|
|
if (0 == strcmp (rev, "master signature invalid"))
|
|
|
|
{
|
|
|
|
report (report_bad_sig_losses,
|
|
|
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
|
|
|
"operation", "payback-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));
|
|
|
|
}
|
2017-04-08 22:52:32 +02:00
|
|
|
|
2017-04-04 15:38:58 +02:00
|
|
|
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));
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = load_auditor_reserve_summary (rs);
|
|
|
|
if (0 > qs)
|
2017-04-04 15:38:58 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-04-04 15:38:58 +02:00
|
|
|
GNUNET_free (rs);
|
2017-06-25 01:46:19 +02:00
|
|
|
rc->qs = qs;
|
2017-04-04 15:38:58 +02:00
|
|
|
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 /payback value to for reserve `%s' of %s\n",
|
|
|
|
TALER_B2S (reserve_pub),
|
|
|
|
TALER_amount2s (amount));
|
|
|
|
expiry = GNUNET_TIME_absolute_add (timestamp,
|
2017-04-20 21:38:02 +02:00
|
|
|
idle_reserve_expiration_time);
|
2017-04-04 15:38:58 +02:00
|
|
|
rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
|
|
|
|
expiry);
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-04-19 21:41:53 +02:00
|
|
|
/**
|
|
|
|
* 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 json_t *receiver_account,
|
2017-04-20 21:38:02 +02:00
|
|
|
const struct TALER_WireTransferIdentifierRawP *transfer_details)
|
2017-04-19 21:41:53 +02:00
|
|
|
{
|
|
|
|
struct ReserveContext *rc = cls;
|
|
|
|
struct GNUNET_HashCode key;
|
|
|
|
struct ReserveSummary *rs;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-05-05 13:41:32 +02:00
|
|
|
|
2017-04-19 21:41:53 +02:00
|
|
|
/* should be monotonically increasing */
|
|
|
|
GNUNET_assert (rowid >= pp.last_reserve_close_serial_id);
|
|
|
|
pp.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));
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = load_auditor_reserve_summary (rs);
|
|
|
|
if (0 > qs)
|
2017-04-19 21:41:53 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-04-19 21:41:53 +02:00
|
|
|
GNUNET_free (rs);
|
2017-06-25 01:46:19 +02:00
|
|
|
rc->qs = qs;
|
2017-04-19 21:41:53 +02:00
|
|
|
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_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&rs->total_fee,
|
|
|
|
&rs->total_fee,
|
|
|
|
closing_fee));
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
2017-03-14 18:00:17 +01:00
|
|
|
* @param cls our `struct ReserveContext`
|
2016-11-17 14:31:44 +01:00
|
|
|
* @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)
|
|
|
|
{
|
2017-03-14 18:00:17 +01:00
|
|
|
struct ReserveContext *rc = cls;
|
2016-11-17 14:31:44 +01:00
|
|
|
struct ReserveSummary *rs = value;
|
|
|
|
struct TALER_EXCHANGEDB_Reserve reserve;
|
|
|
|
struct TALER_Amount balance;
|
2017-06-11 02:12:56 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
int ret;
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
ret = GNUNET_OK;
|
2016-11-17 15:17:27 +01:00
|
|
|
reserve.pub = rs->reserve_pub;
|
2017-06-11 02:12:56 +02:00
|
|
|
qs = edb->reserve_get (edb->cls,
|
|
|
|
esession,
|
|
|
|
&reserve);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2016-11-17 14:31:44 +01:00
|
|
|
{
|
2017-03-14 15:13:50 +01:00
|
|
|
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);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
qs = GNUNET_DB_STATUS_HARD_ERROR;
|
|
|
|
}
|
|
|
|
rc->qs = qs;
|
2016-11-17 14:31:44 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&balance,
|
|
|
|
&rs->total_in,
|
|
|
|
&rs->a_balance))
|
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
GNUNET_break (0);
|
2017-03-14 18:00:17 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-11-17 14:31:44 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (&balance,
|
2017-03-14 18:00:17 +01:00
|
|
|
&balance,
|
2016-11-17 14:31:44 +01:00
|
|
|
&rs->total_out))
|
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
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, s:o, s:s}",
|
|
|
|
"reserve_pub",
|
|
|
|
GNUNET_JSON_from_data_auto (&rs->reserve_pub),
|
|
|
|
"loss",
|
|
|
|
TALER_JSON_from_amount (&loss)));
|
2017-03-14 15:13:50 +01:00
|
|
|
goto cleanup;
|
2016-11-17 14:31:44 +01:00
|
|
|
}
|
|
|
|
if (0 != TALER_amount_cmp (&balance,
|
|
|
|
&reserve.balance))
|
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
struct TALER_Amount delta;
|
|
|
|
|
|
|
|
if (0 < TALER_amount_cmp (&balance,
|
|
|
|
&reserve.balance))
|
|
|
|
{
|
|
|
|
/* balance > reserve.balance */
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_subtract (&delta,
|
|
|
|
&balance,
|
|
|
|
&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,
|
|
|
|
&balance));
|
|
|
|
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 (&balance)));
|
2017-03-14 15:13:50 +01:00
|
|
|
goto cleanup;
|
2016-11-17 14:31:44 +01:00
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2017-04-19 21:41:53 +02:00
|
|
|
/* 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) &&
|
|
|
|
( (0 != balance.value) ||
|
|
|
|
(0 != balance.fraction) ) )
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-04-19 21:41:53 +02:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2017-11-06 14:54:52 +01:00
|
|
|
TALER_amount_add (&total_balance_reserve_not_closed,
|
|
|
|
&total_balance_reserve_not_closed,
|
|
|
|
&balance));
|
|
|
|
report (report_reserve_not_closed_inconsistencies,
|
|
|
|
json_pack ("{s:o, s:o, s:s}",
|
|
|
|
"reserve_pub",
|
|
|
|
GNUNET_JSON_from_data_auto (&rs->reserve_pub),
|
|
|
|
"balance",
|
|
|
|
TALER_JSON_from_amount (&balance),
|
|
|
|
"expiration_time",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (rs->a_expiration_date)));
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
|
|
|
|
2017-03-20 03:06:23 +01:00
|
|
|
/* 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 !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 03:06:23 +01:00
|
|
|
&rs->total_in)) ||
|
|
|
|
(GNUNET_SYSERR ==
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_subtract (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 03:06:23 +01:00
|
|
|
&rs->total_out)) ||
|
|
|
|
(GNUNET_YES !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_withdraw_fee_income,
|
|
|
|
&total_withdraw_fee_income,
|
2017-03-20 03:06:23 +01:00
|
|
|
&rs->total_fee)) )
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
ret = GNUNET_SYSERR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
if ( (0ULL == balance.value) &&
|
|
|
|
(0U == balance.fraction) )
|
|
|
|
{
|
|
|
|
/* TODO: balance is zero, drop reserve details (and then do not update/insert) */
|
|
|
|
if (rs->had_ri)
|
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Final balance of reserve `%s' is %s, dropping it\n",
|
|
|
|
TALER_B2S (&rs->reserve_pub),
|
|
|
|
TALER_amount2s (&balance));
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->del_reserve_info (adb->cls,
|
|
|
|
asession,
|
|
|
|
&rs->reserve_pub,
|
|
|
|
&master_pub);
|
|
|
|
if (0 >= qs)
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
ret = GNUNET_SYSERR;
|
|
|
|
rc->qs = qs;
|
2017-03-14 18:00:17 +01:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
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 (&balance));
|
|
|
|
}
|
2017-03-14 18:00:17 +01:00
|
|
|
ret = GNUNET_OK;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2016-11-17 15:19:14 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
2017-03-20 02:29:33 +01:00
|
|
|
"Remembering final balance of reserve `%s' as %s\n",
|
|
|
|
TALER_B2S (&rs->reserve_pub),
|
|
|
|
TALER_amount2s (&balance));
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
if (rs->had_ri)
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->update_reserve_info (adb->cls,
|
|
|
|
asession,
|
|
|
|
&rs->reserve_pub,
|
|
|
|
&master_pub,
|
|
|
|
&balance,
|
|
|
|
&rs->a_withdraw_fee_balance,
|
|
|
|
rs->a_expiration_date);
|
2017-03-14 15:13:50 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->insert_reserve_info (adb->cls,
|
|
|
|
asession,
|
|
|
|
&rs->reserve_pub,
|
|
|
|
&master_pub,
|
|
|
|
&balance,
|
|
|
|
&rs->a_withdraw_fee_balance,
|
|
|
|
rs->a_expiration_date);
|
|
|
|
if (0 >= qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
ret = GNUNET_SYSERR;
|
|
|
|
rc->qs = qs;
|
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
cleanup:
|
2016-11-17 14:31:44 +01:00
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
key,
|
|
|
|
rs));
|
|
|
|
GNUNET_free (rs);
|
2017-03-14 15:13:50 +01:00
|
|
|
return ret;
|
2016-11-17 14:31:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Analyze reserves for being well-formed.
|
|
|
|
*
|
2017-03-14 12:22:03 +01:00
|
|
|
* @param cls NULL
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2016-11-17 14:31:44 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-14 12:22:03 +01:00
|
|
|
analyze_reserves (void *cls)
|
2016-11-17 14:31:44 +01:00
|
|
|
{
|
2017-03-14 18:00:17 +01:00
|
|
|
struct ReserveContext rc;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qsx;
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Analyzing reserves\n");
|
2017-06-25 01:46:19 +02:00
|
|
|
rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
|
|
|
qsx = adb->get_reserve_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_withdraw_fee_income);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (qsx < 0)
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
return qsx;
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
|
|
|
rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
|
|
|
|
GNUNET_NO);
|
2017-04-19 13:46:38 +02:00
|
|
|
rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
|
|
|
|
GNUNET_NO);
|
2016-11-17 15:02:22 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->select_reserves_in_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_reserve_in_serial_id,
|
|
|
|
&handle_reserve_in,
|
|
|
|
&rc);
|
|
|
|
if (qs < 0)
|
2017-03-18 22:06:27 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 22:06:27 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->select_reserves_out_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_reserve_out_serial_id,
|
|
|
|
&handle_reserve_out,
|
|
|
|
&rc);
|
|
|
|
if (qs < 0)
|
2017-03-18 22:06:27 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 22:06:27 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->select_payback_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_reserve_payback_serial_id,
|
|
|
|
&handle_payback_by_reserve,
|
|
|
|
&rc);
|
|
|
|
if (qs < 0)
|
2017-04-04 15:38:58 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-04-04 15:38:58 +02:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->select_reserve_closed_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_reserve_close_serial_id,
|
|
|
|
&handle_reserve_closed,
|
|
|
|
&rc);
|
|
|
|
if (qs < 0)
|
2017-04-19 21:41:53 +02:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-04-19 21:41:53 +02:00
|
|
|
}
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
|
2016-11-17 14:31:44 +01:00
|
|
|
&verify_reserve_balance,
|
2017-03-14 18:00:17 +01:00
|
|
|
&rc);
|
2017-03-14 15:13:50 +01:00
|
|
|
GNUNET_break (0 ==
|
2017-03-14 18:00:17 +01:00
|
|
|
GNUNET_CONTAINER_multihashmap_size (rc.reserves));
|
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
|
2017-04-19 13:46:38 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs)
|
|
|
|
return qs;
|
2016-11-17 14:31:44 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->insert_reserve_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_withdraw_fee_income);
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->update_reserve_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_withdraw_fee_income);
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 >= qs)
|
2017-03-18 22:06:27 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 22:06:27 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* *********************** Analyze aggregations ******************** */
|
|
|
|
/* This logic checks that the aggregator did the right thing
|
|
|
|
paying each merchant what they were due (and on time). */
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Information we keep per loaded wire plugin.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WirePlugin
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Kept in a DLL.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WirePlugin *next;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-18 22:20:48 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Kept in a DLL.
|
2017-03-18 22:20:48 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WirePlugin *prev;
|
2017-03-18 22:20:48 +01:00
|
|
|
|
2017-03-18 23:03:00 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Name of the wire method.
|
2017-03-18 23:03:00 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
char *type;
|
2017-03-18 23:03:00 +01:00
|
|
|
|
2017-03-15 14:19:57 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Handle to the wire plugin.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_WIRE_Plugin *plugin;
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-20 03:58:51 +01:00
|
|
|
/**
|
|
|
|
* 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 fee.
|
|
|
|
*/
|
|
|
|
struct TALER_Amount wire_fee;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Closure for callbacks during #analyze_merchants().
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct AggregationContext
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* DLL of wire plugins encountered.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WirePlugin *wire_head;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-15 14:19:57 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* DLL of wire plugins encountered.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WirePlugin *wire_tail;
|
|
|
|
|
2017-03-20 03:58:51 +01:00
|
|
|
/**
|
|
|
|
* DLL of wire fees charged by the exchange.
|
|
|
|
*/
|
|
|
|
struct WireFeeInfo *fee_head;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DLL of wire fees charged by the exchange.
|
|
|
|
*/
|
|
|
|
struct WireFeeInfo *fee_tail;
|
|
|
|
|
2017-03-20 03:32:16 +01:00
|
|
|
/**
|
|
|
|
* How much did we make in aggregation fees.
|
|
|
|
*/
|
2017-11-06 00:03:08 +01:00
|
|
|
struct TALER_Amount total_aggregation_feesX;
|
2017-03-20 03:32:16 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Final result status.
|
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find the relevant wire plugin.
|
|
|
|
*
|
|
|
|
* @param ac context to search
|
|
|
|
* @param type type of the wire plugin to load
|
|
|
|
* @return NULL on error
|
|
|
|
*/
|
|
|
|
static struct TALER_WIRE_Plugin *
|
|
|
|
get_wire_plugin (struct AggregationContext *ac,
|
|
|
|
const char *type)
|
|
|
|
{
|
|
|
|
struct WirePlugin *wp;
|
|
|
|
struct TALER_WIRE_Plugin *plugin;
|
|
|
|
|
|
|
|
for (wp = ac->wire_head; NULL != wp; wp = wp->next)
|
|
|
|
if (0 == strcmp (type,
|
|
|
|
wp->type))
|
|
|
|
return wp->plugin;
|
|
|
|
plugin = TALER_WIRE_plugin_load (cfg,
|
|
|
|
type);
|
|
|
|
if (NULL == plugin)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Failed to locate wire plugin for `%s'\n",
|
|
|
|
type);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
wp = GNUNET_new (struct WirePlugin);
|
|
|
|
wp->type = GNUNET_strdup (type);
|
|
|
|
wp->plugin = plugin;
|
|
|
|
GNUNET_CONTAINER_DLL_insert (ac->wire_head,
|
|
|
|
ac->wire_tail,
|
|
|
|
wp);
|
|
|
|
return plugin;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closure for #wire_transfer_information_cb.
|
|
|
|
*/
|
|
|
|
struct WireCheckContext
|
|
|
|
{
|
2017-03-15 14:19:57 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Corresponding merchant context.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct AggregationContext *ac;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Total deposits claimed by all transactions that were aggregated
|
|
|
|
* under the given @e wtid.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount total_deposits;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Hash of the wire transfer details of the receiver.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct GNUNET_HashCode h_wire;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Execution time of the wire transfer.
|
2017-03-15 14:19:57 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct GNUNET_TIME_Absolute date;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-15 19:28:05 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Wire method used for the transfer.
|
2017-03-15 19:28:05 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
const char *method;
|
2017-03-15 19:28:05 +01:00
|
|
|
|
2017-03-17 18:46:11 +01:00
|
|
|
/**
|
2017-06-25 01:46:19 +02:00
|
|
|
* Database transaction status.
|
2017-03-17 18:46:11 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-17 18:46:11 +01:00
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-03-15 14:19:57 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* 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.
|
2017-03-15 14:19:57 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @param coin_pub public key of the coin (for reporting)
|
2017-05-29 01:15:41 +02:00
|
|
|
* @param h_contract_terms hash of the proposal for which we calculate the amount
|
2017-03-20 02:29:33 +01:00
|
|
|
* @param merchant_pub public key of the merchant (who is allowed to issue refunds)
|
|
|
|
* @param dki 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] merchant_fees fees the exchange charged the merchant for the transaction(s)
|
2017-03-15 14:19:57 +01:00
|
|
|
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
|
|
|
|
*/
|
|
|
|
static int
|
2017-03-20 02:29:33 +01:00
|
|
|
check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
2017-05-29 01:15:41 +02:00
|
|
|
const struct GNUNET_HashCode *h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
|
|
|
|
const struct TALER_EXCHANGEDB_TransactionList *tl_head,
|
|
|
|
struct TALER_Amount *merchant_gain,
|
|
|
|
struct TALER_Amount *merchant_fees)
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount expenditures;
|
|
|
|
struct TALER_Amount refunds;
|
|
|
|
struct TALER_Amount spent;
|
|
|
|
struct TALER_Amount value;
|
|
|
|
struct TALER_Amount merchant_loss;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Checking transaction history of coin %s\n",
|
|
|
|
TALER_B2S (coin_pub));
|
|
|
|
|
|
|
|
GNUNET_assert (NULL != tl_head);
|
2017-10-06 21:21:20 +02:00
|
|
|
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_fees));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&merchant_loss));
|
2017-03-20 02:29:33 +01:00
|
|
|
/* 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. */
|
|
|
|
for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != tl;tl = tl->next)
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
const struct TALER_Amount *amount_with_fee;
|
|
|
|
const struct TALER_Amount *fee;
|
|
|
|
const struct TALER_AmountNBO *fee_dki;
|
|
|
|
struct TALER_Amount tmp;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
switch (tl->type) {
|
|
|
|
case TALER_EXCHANGEDB_TT_DEPOSIT:
|
|
|
|
amount_with_fee = &tl->details.deposit->amount_with_fee;
|
|
|
|
fee = &tl->details.deposit->deposit_fee;
|
|
|
|
fee_dki = &dki->properties.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 == memcmp (merchant_pub,
|
|
|
|
&tl->details.deposit->merchant_pub,
|
|
|
|
sizeof (struct TALER_MerchantPublicKeyP))) &&
|
2017-05-29 01:15:41 +02:00
|
|
|
(0 == memcmp (h_contract_terms,
|
|
|
|
&tl->details.deposit->h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
sizeof (struct GNUNET_HashCode))) )
|
|
|
|
{
|
|
|
|
struct TALER_Amount amount_without_fee;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
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));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (merchant_fees,
|
|
|
|
merchant_fees,
|
|
|
|
fee))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
}
|
2017-04-16 02:16:41 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
break;
|
|
|
|
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
|
2017-11-27 23:42:17 +01:00
|
|
|
amount_with_fee = &tl->details.melt->session.amount_with_fee;
|
2017-03-20 02:29:33 +01:00
|
|
|
fee = &tl->details.melt->melt_fee;
|
|
|
|
fee_dki = &dki->properties.fee_refresh;
|
2017-03-17 17:17:07 +01:00
|
|
|
if (GNUNET_OK !=
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_add (&expenditures,
|
|
|
|
&expenditures,
|
|
|
|
amount_with_fee))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-04-16 02:16:41 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
break;
|
|
|
|
case TALER_EXCHANGEDB_TT_REFUND:
|
|
|
|
amount_with_fee = &tl->details.refund->refund_amount;
|
|
|
|
fee = &tl->details.refund->refund_fee;
|
|
|
|
fee_dki = &dki->properties.fee_refund;
|
2017-03-17 17:17:07 +01:00
|
|
|
if (GNUNET_OK !=
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_add (&refunds,
|
|
|
|
&refunds,
|
|
|
|
amount_with_fee))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_add (&expenditures,
|
|
|
|
&expenditures,
|
|
|
|
fee))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Check if this refund is within the remit of the aggregation
|
|
|
|
we are investigating, if so, include it in the totals. */
|
|
|
|
if ( (0 == memcmp (merchant_pub,
|
|
|
|
&tl->details.refund->merchant_pub,
|
|
|
|
sizeof (struct TALER_MerchantPublicKeyP))) &&
|
2017-05-29 01:15:41 +02:00
|
|
|
(0 == memcmp (h_contract_terms,
|
|
|
|
&tl->details.refund->h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
sizeof (struct GNUNET_HashCode))) )
|
|
|
|
{
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&merchant_loss,
|
|
|
|
&merchant_loss,
|
|
|
|
amount_with_fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Detected applicable refund of %s\n",
|
|
|
|
TALER_amount2s (amount_with_fee));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (merchant_fees,
|
|
|
|
merchant_fees,
|
|
|
|
fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
}
|
2017-04-16 02:16:41 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
break;
|
2017-04-04 16:27:27 +02:00
|
|
|
case TALER_EXCHANGEDB_TT_PAYBACK:
|
|
|
|
amount_with_fee = &tl->details.payback->value;
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&expenditures,
|
|
|
|
&expenditures,
|
|
|
|
amount_with_fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
break;
|
2017-03-17 17:17:07 +01:00
|
|
|
}
|
2017-03-15 19:28:05 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
} /* for 'tl' */
|
2017-03-15 19:28:05 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Calculate total balance change, i.e. expenditures minus refunds */
|
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (&spent,
|
|
|
|
&expenditures,
|
|
|
|
&refunds))
|
2017-03-15 19:28:05 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
/* refunds above expenditures? Bad! */
|
2017-11-06 19:11:43 +01:00
|
|
|
report_coin_arithmetic_inconsistency ("refund (balance)",
|
|
|
|
coin_pub,
|
|
|
|
&expenditures,
|
|
|
|
&refunds,
|
|
|
|
0);
|
2017-03-15 19:28:05 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Now check that 'spent' is less or equal than total coin value */
|
|
|
|
TALER_amount_ntoh (&value,
|
|
|
|
&dki->properties.value);
|
|
|
|
if (1 == TALER_amount_cmp (&spent,
|
|
|
|
&value))
|
2017-03-17 14:09:58 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
/* spent > value */
|
2017-11-06 19:11:43 +01:00
|
|
|
report_coin_arithmetic_inconsistency ("spend",
|
|
|
|
coin_pub,
|
|
|
|
&spent,
|
|
|
|
&value,
|
|
|
|
-1);
|
2017-03-17 14:09:58 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-15 19:28:05 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Finally, update @a merchant_gain by subtracting what he "lost" from refunds */
|
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (merchant_gain,
|
|
|
|
merchant_gain,
|
|
|
|
&merchant_loss))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
/* refunds above deposits? Bad! */
|
2017-11-06 19:11:43 +01:00
|
|
|
report_coin_arithmetic_inconsistency ("refund (merchant)",
|
|
|
|
coin_pub,
|
|
|
|
merchant_gain,
|
|
|
|
&merchant_loss,
|
|
|
|
0);
|
2017-03-17 17:17:07 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Coin %s contributes %s to contract %s\n",
|
|
|
|
TALER_B2S (coin_pub),
|
|
|
|
TALER_amount2s (merchant_gain),
|
2017-05-29 01:15:41 +02:00
|
|
|
GNUNET_h2s (h_contract_terms));
|
2017-03-15 14:19:57 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-15 19:28:05 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Function called with the results of the lookup of the
|
|
|
|
* transaction data associated with a wire transfer identifier.
|
2017-03-15 19:28:05 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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 wire_method which wire plugin was used for the transfer?
|
|
|
|
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
|
|
|
|
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
|
2017-05-29 01:15:41 +02:00
|
|
|
* @param h_contract_terms which proposal was this payment about
|
2017-03-20 02:29:33 +01:00
|
|
|
* @param coin_pub which public key was this payment about
|
|
|
|
* @param coin_value amount contributed by this coin in total (with fee)
|
|
|
|
* @param coin_fee applicable fee for this coin
|
2017-03-15 19:28:05 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
static void
|
|
|
|
wire_transfer_information_cb (void *cls,
|
|
|
|
uint64_t rowid,
|
|
|
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
|
|
|
const char *wire_method,
|
|
|
|
const struct GNUNET_HashCode *h_wire,
|
|
|
|
struct GNUNET_TIME_Absolute exec_time,
|
2017-05-29 01:15:41 +02:00
|
|
|
const struct GNUNET_HashCode *h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|
|
|
const struct TALER_Amount *coin_value,
|
|
|
|
const struct TALER_Amount *coin_fee)
|
2017-03-15 19:28:05 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct WireCheckContext *wcc = cls;
|
2017-03-15 19:28:05 +01:00
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount computed_value;
|
|
|
|
struct TALER_Amount computed_fees;
|
|
|
|
struct TALER_Amount coin_value_without_fee;
|
|
|
|
struct TALER_EXCHANGEDB_TransactionList *tl;
|
|
|
|
const struct TALER_CoinPublicInfo *coin;
|
2017-06-19 16:07:34 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/* Obtain coin's transaction history */
|
2017-06-19 16:07:34 +02:00
|
|
|
qs = edb->get_coin_transactions (edb->cls,
|
2017-03-20 02:29:33 +01:00
|
|
|
esession,
|
2017-06-19 16:07:34 +02:00
|
|
|
coin_pub,
|
|
|
|
&tl);
|
|
|
|
if ( (qs < 0) ||
|
|
|
|
(NULL == tl) )
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("aggregation",
|
|
|
|
rowid,
|
|
|
|
"no transaction history for coin claimed in aggregation");
|
|
|
|
return;
|
|
|
|
}
|
2017-03-15 19:28:05 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Obtain general denomination information about the coin */
|
|
|
|
coin = NULL;
|
|
|
|
switch (tl->type)
|
|
|
|
{
|
|
|
|
case TALER_EXCHANGEDB_TT_DEPOSIT:
|
|
|
|
coin = &tl->details.deposit->coin;
|
|
|
|
break;
|
|
|
|
case TALER_EXCHANGEDB_TT_REFRESH_MELT:
|
2017-11-27 23:42:17 +01:00
|
|
|
coin = &tl->details.melt->session.coin;
|
2017-03-20 02:29:33 +01:00
|
|
|
break;
|
|
|
|
case TALER_EXCHANGEDB_TT_REFUND:
|
|
|
|
coin = &tl->details.refund->coin;
|
|
|
|
break;
|
2017-04-04 16:27:27 +02:00
|
|
|
case TALER_EXCHANGEDB_TT_PAYBACK:
|
|
|
|
coin = &tl->details.payback->coin;
|
|
|
|
break;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
GNUNET_assert (NULL != coin); /* hard check that switch worked */
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (&coin->denom_pub,
|
|
|
|
&dki,
|
|
|
|
NULL);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-15 19:28:05 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-03-20 02:29:33 +01:00
|
|
|
edb->free_coin_transaction_list (edb->cls,
|
|
|
|
tl);
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("aggregation",
|
|
|
|
rowid,
|
|
|
|
"could not find denomination key for coin claimed in aggregation");
|
|
|
|
return;
|
2017-03-15 19:28:05 +01:00
|
|
|
}
|
|
|
|
|
2017-10-06 22:04:51 +02:00
|
|
|
GNUNET_assert (NULL != dki); /* mostly to help static analysis */
|
2017-06-25 01:46:19 +02:00
|
|
|
/* Check transaction history to see if it supports aggregate
|
|
|
|
valuation */
|
2017-03-20 02:29:33 +01:00
|
|
|
check_transaction_history (coin_pub,
|
2017-05-29 01:15:41 +02:00
|
|
|
h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
merchant_pub,
|
|
|
|
dki,
|
|
|
|
tl,
|
|
|
|
&computed_value,
|
|
|
|
&computed_fees);
|
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (&coin_value_without_fee,
|
|
|
|
coin_value,
|
|
|
|
coin_fee))
|
2017-03-17 17:17:07 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-11-06 19:11:43 +01:00
|
|
|
report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
|
|
|
|
rowid,
|
|
|
|
coin_value,
|
|
|
|
coin_fee,
|
|
|
|
-1);
|
2017-03-20 02:29:33 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (0 !=
|
|
|
|
TALER_amount_cmp (&computed_value,
|
|
|
|
&coin_value_without_fee))
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-11-06 19:11:43 +01:00
|
|
|
report_amount_arithmetic_inconsistency ("aggregation (contribution)",
|
|
|
|
rowid,
|
|
|
|
&coin_value_without_fee,
|
|
|
|
&computed_value,
|
|
|
|
-1);
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
if (0 !=
|
|
|
|
TALER_amount_cmp (&computed_fees,
|
|
|
|
coin_fee))
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-11-06 19:11:43 +01:00
|
|
|
report_amount_arithmetic_inconsistency ("aggregation (fee)",
|
|
|
|
rowid,
|
|
|
|
coin_fee,
|
|
|
|
&computed_fees,
|
|
|
|
1);
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
edb->free_coin_transaction_list (edb->cls,
|
|
|
|
tl);
|
|
|
|
|
|
|
|
/* Check other details of wire transfer match */
|
|
|
|
if (0 != strcmp (wire_method,
|
|
|
|
wcc->method))
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("aggregation",
|
|
|
|
rowid,
|
|
|
|
"wire method of aggregate do not match wire transfer");
|
2017-03-17 17:17:07 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
if (0 != memcmp (h_wire,
|
|
|
|
&wcc->h_wire,
|
|
|
|
sizeof (struct GNUNET_HashCode)))
|
2017-03-17 17:49:55 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("aggregation",
|
2017-03-17 17:49:55 +01:00
|
|
|
rowid,
|
2017-03-20 02:29:33 +01:00
|
|
|
"account details of aggregate do not match account details of wire transfer");
|
|
|
|
return;
|
2017-03-17 17:49:55 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
if (exec_time.abs_value_us != wcc->date.abs_value_us)
|
2017-03-17 17:49:55 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
/* This should be impossible from database constraints */
|
2017-03-17 17:49:55 +01:00
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("aggregation",
|
|
|
|
rowid,
|
|
|
|
"date given in aggregate does not match wire transfer date");
|
|
|
|
return;
|
2017-03-17 17:49:55 +01:00
|
|
|
}
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* Add coin's contribution to total aggregate value */
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&wcc->total_deposits,
|
|
|
|
&wcc->total_deposits,
|
2017-11-06 19:11:43 +01:00
|
|
|
&coin_value_without_fee))
|
2017-06-25 01:46:19 +02:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
2017-03-15 19:28:05 +01:00
|
|
|
}
|
2017-03-15 14:19:57 +01:00
|
|
|
|
|
|
|
|
2017-03-20 03:32:16 +01:00
|
|
|
/**
|
|
|
|
* Lookup the wire fee that the exchange charges at @a timestamp.
|
|
|
|
*
|
|
|
|
* @param ac context for caching the result
|
2017-03-20 03:58:51 +01:00
|
|
|
* @param type type of the wire plugin
|
2017-03-20 03:32:16 +01:00
|
|
|
* @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,
|
2017-03-20 03:58:51 +01:00
|
|
|
const char *type,
|
2017-03-20 03:32:16 +01:00
|
|
|
struct GNUNET_TIME_Absolute timestamp)
|
|
|
|
{
|
2017-03-20 03:58:51 +01:00
|
|
|
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);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
2017-03-20 03:58:51 +01:00
|
|
|
edb->get_wire_fee (edb->cls,
|
|
|
|
esession,
|
|
|
|
type,
|
|
|
|
timestamp,
|
|
|
|
&wfi->start_date,
|
|
|
|
&wfi->end_date,
|
|
|
|
&wfi->wire_fee,
|
|
|
|
&master_sig))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (wfi);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check signature (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 wp;
|
|
|
|
|
|
|
|
wp.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
|
|
|
|
wp.purpose.size = htonl (sizeof (wp));
|
|
|
|
GNUNET_CRYPTO_hash (type,
|
|
|
|
strlen (type) + 1,
|
|
|
|
&wp.h_wire_method);
|
|
|
|
wp.start_date = GNUNET_TIME_absolute_hton (wfi->start_date);
|
|
|
|
wp.end_date = GNUNET_TIME_absolute_hton (wfi->end_date);
|
|
|
|
TALER_amount_hton (&wp.wire_fee,
|
|
|
|
&wfi->wire_fee);
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
|
|
|
|
&wp.purpose,
|
|
|
|
&master_sig.eddsa_signature,
|
|
|
|
&master_pub.eddsa_pub))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
GNUNET_free (wfi);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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 */
|
2017-11-20 14:20:09 +01:00
|
|
|
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:s}",
|
|
|
|
"type", type,
|
|
|
|
"diagnostic", "start date before previous end date",
|
|
|
|
"time", GNUNET_STRINGS_absolute_time_to_string (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:s}",
|
|
|
|
"type", type,
|
|
|
|
"diagnostic", "end date date after next start date",
|
|
|
|
"time", GNUNET_STRINGS_absolute_time_to_string (wfi->end_date)));
|
|
|
|
}
|
2017-03-20 03:58:51 +01:00
|
|
|
return &wfi->wire_fee;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-14 18:00:17 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Check that a wire transfer made by the exchange is valid
|
|
|
|
* (has matching deposits).
|
2017-03-14 18:00:17 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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
|
2017-04-04 15:38:58 +02:00
|
|
|
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-04-04 15:38:58 +02:00
|
|
|
static int
|
2017-03-20 02:29:33 +01:00
|
|
|
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)
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct AggregationContext *ac = cls;
|
|
|
|
struct WireCheckContext wcc;
|
|
|
|
json_t *method;
|
|
|
|
struct TALER_WIRE_Plugin *plugin;
|
2017-03-20 03:32:16 +01:00
|
|
|
const struct TALER_Amount *wire_fee;
|
|
|
|
struct TALER_Amount final_amount;
|
|
|
|
struct TALER_Amount exchange_gain;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-15 14:19:57 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* should be monotonically increasing */
|
|
|
|
GNUNET_assert (rowid >= pp.last_wire_out_serial_id);
|
|
|
|
pp.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));
|
|
|
|
wcc.ac = ac;
|
|
|
|
method = json_object_get (wire,
|
|
|
|
"type");
|
|
|
|
if ( (NULL == method) ||
|
|
|
|
(! json_is_string (method)) )
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("wire_out",
|
|
|
|
rowid,
|
|
|
|
"specified wire address lacks type");
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_OK;
|
2017-03-15 14:19:57 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
wcc.method = json_string_value (method);
|
2017-06-25 01:46:19 +02:00
|
|
|
wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
wcc.date = date;
|
2017-10-06 22:08:35 +02:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (amount->currency,
|
|
|
|
&wcc.total_deposits));
|
2017-10-06 20:02:28 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_JSON_hash (wire,
|
|
|
|
&wcc.h_wire))
|
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
2017-10-06 20:02:28 +02:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
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;
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
/* FIXME: can we provide a more detailed error report? */
|
2017-03-20 02:29:33 +01:00
|
|
|
report_row_inconsistency ("wire_out",
|
|
|
|
rowid,
|
|
|
|
"audit of associated transactions failed");
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_OK;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Subtract aggregation fee from total */
|
|
|
|
wire_fee = get_wire_fee (ac,
|
2017-03-20 03:58:51 +01:00
|
|
|
wcc.method,
|
2017-03-20 03:32:16 +01:00
|
|
|
date);
|
|
|
|
if (NULL == wire_fee)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (&final_amount,
|
|
|
|
&wcc.total_deposits,
|
|
|
|
wire_fee))
|
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
report_amount_arithmetic_inconsistency ("wire out (fee structure)",
|
|
|
|
rowid,
|
|
|
|
&wcc.total_deposits,
|
|
|
|
wire_fee,
|
|
|
|
-1);
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_OK;
|
2017-03-15 14:19:57 +01:00
|
|
|
}
|
2017-03-20 03:32:16 +01:00
|
|
|
|
|
|
|
/* Round down to amount supported by wire method */
|
2017-03-20 02:29:33 +01:00
|
|
|
plugin = get_wire_plugin (ac,
|
|
|
|
wcc.method);
|
|
|
|
if (NULL == plugin)
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
2017-03-15 14:19:57 +01:00
|
|
|
}
|
2017-03-20 03:32:16 +01:00
|
|
|
|
2017-11-06 14:54:52 +01:00
|
|
|
GNUNET_break (GNUNET_SYSERR !=
|
|
|
|
plugin->amount_round (plugin->cls,
|
|
|
|
&final_amount));
|
2017-03-20 03:32:16 +01:00
|
|
|
|
|
|
|
/* Calculate the exchange's gain as the fees plus rounding differences! */
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_subtract (&exchange_gain,
|
|
|
|
&wcc.total_deposits,
|
|
|
|
&final_amount))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Sum up aggregation fees (we simply include the rounding gains) */
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_aggregation_fee_income,
|
|
|
|
&total_aggregation_fee_income,
|
2017-03-20 03:32:16 +01:00
|
|
|
&exchange_gain))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Check that calculated amount matches actual amount */
|
2017-03-20 02:29:33 +01:00
|
|
|
if (0 != TALER_amount_cmp (amount,
|
2017-03-20 03:58:51 +01:00
|
|
|
&final_amount))
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-11-06 14:54:52 +01:00
|
|
|
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)));
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_OK;
|
2017-03-15 14:19:57 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Wire transfer %s is OK\n",
|
|
|
|
TALER_B2S (wtid));
|
2017-04-04 15:38:58 +02:00
|
|
|
return GNUNET_OK;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Analyze the exchange aggregator's payment processing.
|
|
|
|
*
|
|
|
|
* @param cls closure
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-20 02:29:33 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-20 02:29:33 +01:00
|
|
|
analyze_aggregations (void *cls)
|
|
|
|
{
|
|
|
|
struct AggregationContext ac;
|
|
|
|
struct WirePlugin *wc;
|
2017-03-20 03:58:51 +01:00
|
|
|
struct WireFeeInfo *wfi;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qsx;
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Analyzing aggregations\n");
|
2017-06-25 01:46:19 +02:00
|
|
|
ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
ac.wire_head = NULL;
|
|
|
|
ac.wire_tail = NULL;
|
2017-03-20 03:58:51 +01:00
|
|
|
ac.fee_head = NULL;
|
|
|
|
ac.fee_tail = NULL;
|
2017-06-25 01:46:19 +02:00
|
|
|
qsx = adb->get_wire_fee_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_aggregation_fee_income);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > qsx)
|
2017-03-20 04:17:06 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
return qsx;
|
2017-03-20 04:17:06 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
|
|
|
qs = edb->select_wire_out_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_wire_out_serial_id,
|
|
|
|
&check_wire_out_cb,
|
|
|
|
&ac);
|
|
|
|
if (0 > qs)
|
2017-03-15 14:19:57 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
ac.qs = qs;
|
2017-03-15 14:19:57 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
while (NULL != (wc = ac.wire_head))
|
2017-03-17 18:46:11 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_CONTAINER_DLL_remove (ac.wire_head,
|
|
|
|
ac.wire_tail,
|
|
|
|
wc);
|
|
|
|
TALER_WIRE_plugin_unload (wc->plugin);
|
|
|
|
GNUNET_free (wc->type);
|
|
|
|
GNUNET_free (wc);
|
2017-03-17 18:46:11 +01:00
|
|
|
}
|
2017-03-20 03:58:51 +01:00
|
|
|
while (NULL != (wfi = ac.fee_head))
|
|
|
|
{
|
|
|
|
GNUNET_CONTAINER_DLL_remove (ac.fee_head,
|
|
|
|
ac.fee_tail,
|
|
|
|
wfi);
|
|
|
|
GNUNET_free (wfi);
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
|
2017-03-20 03:32:16 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
|
|
|
|
return ac.qs;
|
2017-03-20 03:32:16 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
|
|
|
|
ac.qs = adb->insert_wire_fee_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_aggregation_fee_income);
|
2017-03-20 04:17:06 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
ac.qs = adb->update_wire_fee_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_aggregation_fee_income);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
|
2017-03-20 04:17:06 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
|
|
|
|
return ac.qs;
|
2017-03-20 04:17:06 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-14 18:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* ************************* Analyze coins ******************** */
|
|
|
|
/* This logic checks that the exchange did the right thing for each
|
|
|
|
coin, checking deposits, refunds, refresh* and known_coins
|
|
|
|
tables */
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Summary data we keep per denomination.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct DenominationSummary
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Total value of outstanding (not deposited) coins issued with this
|
|
|
|
* denomination key.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount denom_balance;
|
2017-03-18 16:56:31 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Total value of coins issued with this denomination key.
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount denom_risk;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Denomination key information for this denomination.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* #GNUNET_YES if this record already existed in the DB.
|
|
|
|
* Used to decide between insert/update in
|
|
|
|
* #sync_denomination().
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
int in_db;
|
2017-03-18 16:56:31 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Closure for callbacks during #analyze_coins().
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct CoinContext
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Map for tracking information about denominations.
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/**
|
|
|
|
* Current write/replace offset in the circular @e summaries buffer.
|
|
|
|
*/
|
|
|
|
unsigned int summaries_off;
|
|
|
|
|
|
|
|
/**
|
2017-06-25 01:46:19 +02:00
|
|
|
* Transaction status code.
|
2017-03-20 02:29:33 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Initialize information about denomination from the database.
|
2017-03-18 16:56:31 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @param denom_hash hash of the public key of the denomination
|
|
|
|
* @param[out] ds summary to initialize
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-20 02:29:33 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-20 02:29:33 +01:00
|
|
|
init_denomination (const struct GNUNET_HashCode *denom_hash,
|
|
|
|
struct DenominationSummary *ds)
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->get_denomination_balance (adb->cls,
|
2017-03-20 02:29:33 +01:00
|
|
|
asession,
|
|
|
|
denom_hash,
|
|
|
|
&ds->denom_balance,
|
|
|
|
&ds->denom_risk);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
|
|
|
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));
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&ds->denom_balance));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&ds->denom_risk));
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Starting balance for denomination `%s' is %s\n",
|
|
|
|
GNUNET_h2s (denom_hash),
|
|
|
|
TALER_amount2s (&ds->denom_balance));
|
2017-06-25 01:46:19 +02:00
|
|
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Obtain the denomination summary for the given @a dh
|
|
|
|
*
|
|
|
|
* @param cc our execution context
|
|
|
|
* @param dki denomination key information for @a dh
|
|
|
|
* @param dh the denomination hash to use for the lookup
|
2017-03-18 16:56:31 +01:00
|
|
|
* @return NULL on error
|
2017-03-14 18:00:17 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
static struct DenominationSummary *
|
|
|
|
get_denomination_summary (struct CoinContext *cc,
|
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
|
|
|
|
const struct GNUNET_HashCode *dh)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct DenominationSummary *ds;
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
|
|
|
|
dh);
|
|
|
|
if (NULL != ds)
|
|
|
|
return ds;
|
|
|
|
ds = GNUNET_new (struct DenominationSummary);
|
|
|
|
ds->dki = dki;
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > (cc->qs = init_denomination (dh,
|
|
|
|
ds)))
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
|
|
|
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`
|
2017-04-20 07:49:56 +02:00
|
|
|
* @param denom_hash the hash of the denomination key
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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_EXCHANGEDB_DenominationKeyInformationP *dki = ds->dki;
|
|
|
|
struct GNUNET_TIME_Absolute now;
|
|
|
|
struct GNUNET_TIME_Absolute expire_deposit;
|
|
|
|
struct GNUNET_TIME_Absolute expire_deposit_grace;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
now = GNUNET_TIME_absolute_get ();
|
|
|
|
expire_deposit = GNUNET_TIME_absolute_ntoh (dki->properties.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)
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->del_denomination_balance (adb->cls,
|
|
|
|
asession,
|
|
|
|
denom_hash);
|
2017-03-20 02:29:33 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
|
|
|
if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
|
2017-03-20 02:29:33 +01:00
|
|
|
( (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 ==
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_subtract (&total_risk,
|
|
|
|
&total_risk,
|
2017-03-20 02:29:33 +01:00
|
|
|
&ds->denom_risk))
|
|
|
|
{
|
|
|
|
/* Holy smokes, our risk assessment was inconsistent!
|
|
|
|
This is really, really bad. */
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
|
2017-03-20 02:29:33 +01:00
|
|
|
( (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));
|
2017-06-25 01:46:19 +02:00
|
|
|
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)))
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
|
|
|
/* Failed to store profits? Bad database */
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Final balance for denomination `%s' is %s\n",
|
|
|
|
GNUNET_h2s (denom_hash),
|
|
|
|
TALER_amount2s (&ds->denom_balance));
|
|
|
|
if (ds->in_db)
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->update_denomination_balance (adb->cls,
|
|
|
|
asession,
|
|
|
|
denom_hash,
|
|
|
|
&ds->denom_balance,
|
|
|
|
&ds->denom_risk);
|
2017-03-20 02:29:33 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->insert_denomination_balance (adb->cls,
|
|
|
|
asession,
|
|
|
|
denom_hash,
|
|
|
|
&ds->denom_balance,
|
|
|
|
&ds->denom_risk);
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_assert (GNUNET_YES ==
|
|
|
|
GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
|
|
|
|
denom_hash,
|
|
|
|
ds));
|
|
|
|
GNUNET_free (ds);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != cc->qs)
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
return GNUNET_OK;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* 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 denom_sig signature over 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
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
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_DenominationSignature *denom_sig,
|
|
|
|
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)
|
2017-03-14 18:00:17 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct CoinContext *cc = cls;
|
|
|
|
struct DenominationSummary *ds;
|
|
|
|
struct GNUNET_HashCode dh;
|
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
|
|
|
struct TALER_Amount value;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-20 02:45:33 +01:00
|
|
|
GNUNET_assert (rowid >= pp.last_withdraw_serial_id); /* should be monotonically increasing */
|
|
|
|
pp.last_withdraw_serial_id = rowid + 1;
|
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (denom_pub,
|
|
|
|
&dki,
|
|
|
|
&dh);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
ds = get_denomination_summary (cc,
|
|
|
|
dki,
|
|
|
|
&dh);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (NULL == ds)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_ntoh (&value,
|
|
|
|
&dki->properties.value);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Issued coin in denomination `%s' of total value %s\n",
|
|
|
|
GNUNET_h2s (&dh),
|
|
|
|
TALER_amount2s (&value));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&ds->denom_balance,
|
|
|
|
&ds->denom_balance,
|
|
|
|
&value))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
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 !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 02:29:33 +01:00
|
|
|
&value))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 03:06:23 +01:00
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_risk,
|
|
|
|
&total_risk,
|
2017-03-20 03:06:23 +01:00
|
|
|
&value))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&ds->denom_risk,
|
|
|
|
&ds->denom_risk,
|
|
|
|
&value))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2017-03-14 18:00:17 +01:00
|
|
|
|
|
|
|
|
2017-11-27 23:42:17 +01:00
|
|
|
/**
|
|
|
|
* 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_dki arrays.
|
|
|
|
*/
|
|
|
|
unsigned int num_newcoins;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called with information about a refresh order.
|
|
|
|
*
|
|
|
|
* @param cls closure
|
|
|
|
* @param rowid unique serial ID for the row in our database
|
|
|
|
* @param num_newcoins size of the @a rrcs array
|
|
|
|
* @param rrcs array of @a num_newcoins 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_newcoins,
|
|
|
|
const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
|
|
|
|
unsigned int num_tprivs,
|
|
|
|
const struct TALER_TransferPrivateKeyP *tprivs,
|
|
|
|
const struct TALER_TransferPublicKeyP *tp)
|
|
|
|
{
|
|
|
|
struct RevealContext *rctx = cls;
|
|
|
|
|
|
|
|
rctx->num_newcoins = num_newcoins;
|
|
|
|
rctx->new_dps = GNUNET_new_array (num_newcoins,
|
|
|
|
struct TALER_DenominationPublicKey);
|
|
|
|
for (unsigned int i=0;i<num_newcoins;i++)
|
|
|
|
rctx->new_dps[i].rsa_public_key
|
|
|
|
= GNUNET_CRYPTO_rsa_public_key_dup (rrcs[i].denom_pub.rsa_public_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-18 16:56:31 +01:00
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* 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).
|
2017-03-18 16:56:31 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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 session_hash what is the session hash
|
|
|
|
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
|
|
|
static int
|
2017-03-20 02:29:33 +01:00
|
|
|
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,
|
2017-11-27 23:42:17 +01:00
|
|
|
uint32_t noreveal_index,
|
|
|
|
const struct TALER_RefreshCommitmentP *rc)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct CoinContext *cc = cls;
|
|
|
|
struct TALER_RefreshMeltCoinAffirmationPS rmc;
|
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
|
|
|
struct DenominationSummary *dso;
|
|
|
|
struct TALER_Amount amount_without_fee;
|
|
|
|
struct TALER_Amount tmp;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:45:33 +01:00
|
|
|
GNUNET_assert (rowid >= pp.last_melt_serial_id); /* should be monotonically increasing */
|
|
|
|
pp.last_melt_serial_id = rowid + 1;
|
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (denom_pub,
|
|
|
|
&dki,
|
|
|
|
NULL);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* verify melt signature */
|
|
|
|
rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
|
|
|
|
rmc.purpose.size = htonl (sizeof (rmc));
|
2017-11-27 23:42:17 +01:00
|
|
|
rmc.rc = *rc;
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_hton (&rmc.amount_with_fee,
|
|
|
|
amount_with_fee);
|
|
|
|
rmc.melt_fee = dki->properties.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))
|
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
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));
|
2017-03-20 02:29:33 +01:00
|
|
|
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 (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (amount_with_fee));
|
|
|
|
|
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
struct RevealContext reveal_ctx;
|
2017-03-20 02:29:33 +01:00
|
|
|
struct TALER_Amount refresh_cost;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (amount_with_fee->currency,
|
|
|
|
&refresh_cost));
|
2017-11-27 23:42:17 +01:00
|
|
|
memset (&reveal_ctx,
|
|
|
|
0,
|
|
|
|
sizeof (reveal_ctx));
|
|
|
|
qs = edb->get_refresh_reveal (edb->cls,
|
|
|
|
esession,
|
|
|
|
rc,
|
|
|
|
&reveal_data_cb,
|
|
|
|
&reveal_ctx);
|
|
|
|
if (0 > qs)
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
|
|
|
|
(0 == reveal_ctx.num_newcoins) )
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
/* 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;
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
// FIXME: free reveal_ctx.num_newcoins later!
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *new_dkis[reveal_ctx.num_newcoins];
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-11-27 23:42:17 +01:00
|
|
|
/* Update outstanding amounts for all new coin's denominations, and check
|
|
|
|
that the resulting amounts are consistent with the value being refreshed. */
|
|
|
|
err = GNUNET_NO;
|
|
|
|
for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
|
2017-03-19 06:50:08 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
/* lookup new coin denomination key */
|
|
|
|
qs = get_denomination_info (&reveal_ctx.new_dps[i],
|
|
|
|
&new_dkis[i],
|
|
|
|
NULL);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
|
|
|
err = GNUNET_YES;
|
|
|
|
}
|
|
|
|
GNUNET_CRYPTO_rsa_public_key_free (reveal_ctx.new_dps[i].rsa_public_key);
|
|
|
|
reveal_ctx.new_dps[i].rsa_public_key = NULL;
|
2017-03-19 06:50:08 +01:00
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
GNUNET_free (reveal_ctx.new_dps);
|
|
|
|
reveal_ctx.new_dps = NULL;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-11-27 23:42:17 +01:00
|
|
|
if (err)
|
|
|
|
return GNUNET_SYSERR;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-11-27 23:42:17 +01:00
|
|
|
/* calculate total refresh cost */
|
|
|
|
for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
|
2017-03-19 06:50:08 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
/* update cost of refresh */
|
|
|
|
struct TALER_Amount fee;
|
|
|
|
struct TALER_Amount value;
|
|
|
|
|
|
|
|
TALER_amount_ntoh (&fee,
|
|
|
|
&new_dkis[i]->properties.fee_withdraw);
|
|
|
|
TALER_amount_ntoh (&value,
|
|
|
|
&new_dkis[i]->properties.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;
|
|
|
|
}
|
2017-03-19 06:50:08 +01:00
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
|
|
|
|
/* compute contribution of old coin */
|
2017-03-20 03:06:23 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
struct TALER_Amount melt_fee;
|
|
|
|
|
|
|
|
TALER_amount_ntoh (&melt_fee,
|
|
|
|
&dki->properties.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;
|
|
|
|
}
|
2017-03-20 03:06:23 +01:00
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
|
|
|
|
/* check old coin covers complete expenses */
|
|
|
|
if (1 == TALER_amount_cmp (&refresh_cost,
|
|
|
|
&amount_without_fee))
|
2017-03-19 06:50:08 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
/* refresh_cost > amount_without_fee */
|
|
|
|
report_amount_arithmetic_inconsistency ("melt (fee)",
|
|
|
|
rowid,
|
|
|
|
&amount_without_fee,
|
|
|
|
&refresh_cost,
|
|
|
|
-1);
|
|
|
|
return GNUNET_OK;
|
2017-03-19 06:50:08 +01:00
|
|
|
}
|
2017-11-27 23:42:17 +01:00
|
|
|
|
|
|
|
/* update outstanding denomination amounts */
|
|
|
|
for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
|
2017-03-20 03:06:23 +01:00
|
|
|
{
|
2017-11-27 23:42:17 +01:00
|
|
|
struct DenominationSummary *dsi;
|
|
|
|
struct TALER_Amount value;
|
|
|
|
|
|
|
|
dsi = get_denomination_summary (cc,
|
|
|
|
new_dkis[i],
|
|
|
|
&new_dkis[i]->properties.denom_hash);
|
|
|
|
if (NULL == dsi)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
TALER_amount_ntoh (&value,
|
|
|
|
&new_dkis[i]->properties.value);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Created fresh coin in denomination `%s' of value %s\n",
|
|
|
|
GNUNET_h2s (&new_dkis[i]->properties.denom_hash),
|
|
|
|
TALER_amount2s (&value));
|
|
|
|
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_dkis[i]->properties.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;
|
|
|
|
}
|
2017-03-20 03:06:23 +01:00
|
|
|
}
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
2017-03-19 06:50:08 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* update old coin's denomination balance */
|
|
|
|
dso = get_denomination_summary (cc,
|
|
|
|
dki,
|
|
|
|
&dki->properties.denom_hash);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (NULL == dso)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
|
|
|
TALER_amount_subtract (&tmp,
|
|
|
|
&dso->denom_balance,
|
|
|
|
amount_with_fee))
|
|
|
|
{
|
2017-11-06 00:03:08 +01:00
|
|
|
report_emergency (dki,
|
|
|
|
&dso->denom_risk);
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
dso->denom_balance = tmp;
|
2017-03-20 03:06:23 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_subtract (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 03:06:23 +01:00
|
|
|
amount_with_fee))
|
|
|
|
{
|
|
|
|
/* This should not be possible, unless the AUDITOR
|
|
|
|
has a bug in tracking total balance. */
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"New balance of denomination `%s' after melt is %s\n",
|
|
|
|
GNUNET_h2s (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (&dso->denom_balance));
|
|
|
|
|
|
|
|
/* update global up melt fees */
|
|
|
|
{
|
|
|
|
struct TALER_Amount rfee;
|
|
|
|
|
|
|
|
TALER_amount_ntoh (&rfee,
|
|
|
|
&dki->properties.fee_refresh);
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_melt_fee_income,
|
|
|
|
&total_melt_fee_income,
|
2017-03-20 02:29:33 +01:00
|
|
|
&rfee))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-18 16:56:31 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
}
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* 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
|
2017-05-29 01:15:41 +02:00
|
|
|
* @param h_contract_terms hash of the proposal data known to merchant and customer
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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,
|
2017-05-29 01:15:41 +02:00
|
|
|
const struct GNUNET_HashCode *h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
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_EXCHANGEDB_DenominationKeyInformationP *dki;
|
|
|
|
struct DenominationSummary *ds;
|
|
|
|
struct TALER_DepositRequestPS dr;
|
|
|
|
struct TALER_Amount tmp;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
|
2017-03-20 02:45:33 +01:00
|
|
|
GNUNET_assert (rowid >= pp.last_deposit_serial_id); /* should be monotonically increasing */
|
|
|
|
pp.last_deposit_serial_id = rowid + 1;
|
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (denom_pub,
|
|
|
|
&dki,
|
|
|
|
NULL);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
cc->qs = qs;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Verify deposit signature */
|
|
|
|
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
|
|
|
|
dr.purpose.size = htonl (sizeof (dr));
|
2017-05-29 01:15:41 +02:00
|
|
|
dr.h_contract_terms = *h_contract_terms;
|
2017-03-20 02:29:33 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_JSON_hash (receiver_wire_account,
|
|
|
|
&dr.h_wire))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-18 16:56:31 +01:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-19 06:50:08 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
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 = dki->properties.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))
|
2017-03-19 06:50:08 +01:00
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
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));
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_OK;
|
2017-03-19 06:50:08 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Deposited coin %s in denomination `%s' of value %s\n",
|
|
|
|
TALER_B2S (coin_pub),
|
|
|
|
GNUNET_h2s (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (amount_with_fee));
|
2017-03-19 06:50:08 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* update old coin's denomination balance */
|
|
|
|
ds = get_denomination_summary (cc,
|
|
|
|
dki,
|
|
|
|
&dki->properties.denom_hash);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (NULL == ds)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-19 06:50:08 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_subtract (&tmp,
|
|
|
|
&ds->denom_balance,
|
|
|
|
amount_with_fee))
|
2017-03-19 06:50:08 +01:00
|
|
|
{
|
2017-11-06 00:03:08 +01:00
|
|
|
report_emergency (dki,
|
|
|
|
&ds->denom_risk);
|
2017-06-25 01:46:19 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-19 06:50:08 +01:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
ds->denom_balance = tmp;
|
2017-03-20 03:06:23 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_subtract (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 03:06:23 +01:00
|
|
|
amount_with_fee))
|
|
|
|
{
|
|
|
|
/* This should not be possible, unless the AUDITOR
|
|
|
|
has a bug in tracking total balance. */
|
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"New balance of denomination `%s' after deposit is %s\n",
|
|
|
|
GNUNET_h2s (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (&ds->denom_balance));
|
|
|
|
|
|
|
|
/* update global up melt fees */
|
|
|
|
{
|
|
|
|
struct TALER_Amount dfee;
|
|
|
|
|
|
|
|
TALER_amount_ntoh (&dfee,
|
|
|
|
&dki->properties.fee_deposit);
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_deposit_fee_income,
|
|
|
|
&total_deposit_fee_income,
|
2017-03-20 02:29:33 +01:00
|
|
|
&dfee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-18 16:56:31 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* 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.
|
2017-03-18 16:56:31 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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
|
2017-05-29 01:15:41 +02:00
|
|
|
* @param h_contract_terms hash of the proposal data known to merchant and customer
|
2017-03-20 02:29:33 +01:00
|
|
|
* @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
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
2017-03-20 02:29:33 +01:00
|
|
|
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,
|
2017-05-29 01:15:41 +02:00
|
|
|
const struct GNUNET_HashCode *h_contract_terms,
|
2017-03-20 02:29:33 +01:00
|
|
|
uint64_t rtransaction_id,
|
|
|
|
const struct TALER_Amount *amount_with_fee)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct CoinContext *cc = cls;
|
2017-03-18 16:56:31 +01:00
|
|
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
2017-03-20 02:29:33 +01:00
|
|
|
struct DenominationSummary *ds;
|
|
|
|
struct TALER_RefundRequestPS rr;
|
|
|
|
struct TALER_Amount amount_without_fee;
|
|
|
|
struct TALER_Amount refund_fee;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:45:33 +01:00
|
|
|
GNUNET_assert (rowid >= pp.last_refund_serial_id); /* should be monotonically increasing */
|
|
|
|
pp.last_refund_serial_id = rowid + 1;
|
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = get_denomination_info (denom_pub,
|
|
|
|
&dki,
|
|
|
|
NULL);
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* verify refund signature */
|
|
|
|
rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
|
|
|
|
rr.purpose.size = htonl (sizeof (rr));
|
2017-05-29 01:15:41 +02:00
|
|
|
rr.h_contract_terms = *h_contract_terms;
|
2017-03-20 02:29:33 +01:00
|
|
|
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 = dki->properties.fee_refund;
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
|
|
|
|
&rr.purpose,
|
|
|
|
&merchant_sig->eddsa_sig,
|
|
|
|
&merchant_pub->eddsa_pub))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
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));
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_OK;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
TALER_amount_ntoh (&refund_fee,
|
|
|
|
&dki->properties.fee_refund);
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_subtract (&amount_without_fee,
|
|
|
|
amount_with_fee,
|
|
|
|
&refund_fee))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-11-06 19:11:43 +01:00
|
|
|
report_amount_arithmetic_inconsistency ("refund (fee)",
|
|
|
|
rowid,
|
|
|
|
&amount_without_fee,
|
|
|
|
&refund_fee,
|
|
|
|
-1);
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_OK;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Refunding coin %s in denomination `%s' value %s\n",
|
|
|
|
TALER_B2S (coin_pub),
|
|
|
|
GNUNET_h2s (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (amount_with_fee));
|
|
|
|
|
|
|
|
/* update coin's denomination balance */
|
|
|
|
ds = get_denomination_summary (cc,
|
|
|
|
dki,
|
|
|
|
&dki->properties.denom_hash);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (NULL == ds)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&ds->denom_balance,
|
|
|
|
&ds->denom_balance,
|
|
|
|
&amount_without_fee))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-18 17:47:44 +01:00
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 03:06:23 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_add (&ds->denom_risk,
|
|
|
|
&ds->denom_risk,
|
|
|
|
&amount_without_fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_escrow_balance,
|
|
|
|
&total_escrow_balance,
|
2017-03-20 03:06:23 +01:00
|
|
|
&amount_without_fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_risk,
|
|
|
|
&total_risk,
|
2017-03-20 03:06:23 +01:00
|
|
|
&amount_without_fee))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 03:06:23 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"New balance of denomination `%s' after refund is %s\n",
|
|
|
|
GNUNET_h2s (&dki->properties.denom_hash),
|
|
|
|
TALER_amount2s (&ds->denom_balance));
|
|
|
|
|
|
|
|
/* update total refund fee balance */
|
|
|
|
if (GNUNET_OK !=
|
2017-11-06 00:03:08 +01:00
|
|
|
TALER_amount_add (&total_refund_fee_income,
|
|
|
|
&total_refund_fee_income,
|
2017-03-20 02:29:33 +01:00
|
|
|
&refund_fee))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_break (0);
|
2017-10-06 20:02:28 +02:00
|
|
|
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
return GNUNET_OK;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-20 02:29:33 +01:00
|
|
|
* Analyze the exchange's processing of coins.
|
2017-03-18 16:56:31 +01:00
|
|
|
*
|
2017-03-20 02:29:33 +01:00
|
|
|
* @param cls closure
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-18 16:56:31 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-20 02:29:33 +01:00
|
|
|
analyze_coins (void *cls)
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-03-20 02:29:33 +01:00
|
|
|
struct CoinContext cc;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
enum GNUNET_DB_QueryStatus qsx;
|
2017-03-18 16:56:31 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Analyzing coins\n");
|
|
|
|
/* setup 'cc' */
|
2017-06-25 01:46:19 +02:00
|
|
|
cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_NO);
|
|
|
|
qsx = adb->get_balance_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_deposit_fee_income,
|
|
|
|
&total_melt_fee_income,
|
|
|
|
&total_refund_fee_income,
|
|
|
|
&total_risk);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > qsx)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
return qsx;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/* process withdrawals */
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 >
|
|
|
|
(qs = edb->select_reserves_out_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_withdraw_serial_id,
|
|
|
|
&withdraw_cb,
|
|
|
|
&cc)) )
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/* process refunds */
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 >
|
|
|
|
(qs = edb->select_refunds_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_refund_serial_id,
|
|
|
|
&refund_cb,
|
|
|
|
&cc)))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/* process refreshs */
|
2017-10-06 20:02:28 +02:00
|
|
|
if (0 >
|
2017-06-25 01:46:19 +02:00
|
|
|
(qs = edb->select_refreshs_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_melt_serial_id,
|
|
|
|
&refresh_session_cb,
|
|
|
|
&cc)))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-14 18:00:17 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/* process deposits */
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 >
|
|
|
|
(qs = edb->select_deposits_above_serial_id (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_deposit_serial_id,
|
|
|
|
&deposit_cb,
|
|
|
|
&cc)))
|
2017-03-18 16:56:31 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
|
|
|
|
/* sync 'cc' back to disk */
|
2017-06-25 01:46:19 +02:00
|
|
|
cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
|
|
|
|
&sync_denomination,
|
|
|
|
&cc);
|
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
|
2017-06-25 01:46:19 +02:00
|
|
|
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,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_deposit_fee_income,
|
|
|
|
&total_melt_fee_income,
|
|
|
|
&total_refund_fee_income,
|
|
|
|
&total_risk);
|
2017-03-20 02:29:33 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->insert_balance_summary (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2017-11-06 00:03:08 +01:00
|
|
|
&total_escrow_balance,
|
|
|
|
&total_deposit_fee_income,
|
|
|
|
&total_melt_fee_income,
|
|
|
|
&total_refund_fee_income,
|
|
|
|
&total_risk);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 >= qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-18 16:56:31 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
return qs;
|
2016-11-17 14:31:44 +01:00
|
|
|
}
|
|
|
|
|
2016-10-06 15:17:10 +02:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
/* *************************** General transaction logic ****************** */
|
|
|
|
|
2017-03-14 12:22:03 +01:00
|
|
|
/**
|
2017-03-14 15:13:50 +01:00
|
|
|
* Type of an analysis function. Each analysis function runs in
|
|
|
|
* its own transaction scope and must thus be internally consistent.
|
2017-03-14 12:22:03 +01:00
|
|
|
*
|
|
|
|
* @param cls closure
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-14 12:22:03 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
typedef enum GNUNET_DB_QueryStatus
|
2017-03-14 12:22:03 +01:00
|
|
|
(*Analysis)(void *cls);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2017-03-14 15:13:50 +01:00
|
|
|
* Perform the given @a analysis incrementally, checkpointing our
|
|
|
|
* progress in the auditor DB.
|
2017-03-14 12:22:03 +01:00
|
|
|
*
|
|
|
|
* @param analysis analysis to run
|
|
|
|
* @param analysis_cls closure for @a analysis
|
2017-06-25 01:46:19 +02:00
|
|
|
* @return transaction status code
|
2017-03-14 12:22:03 +01:00
|
|
|
*/
|
2017-06-25 01:46:19 +02:00
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-03-14 15:13:50 +01:00
|
|
|
incremental_processing (Analysis analysis,
|
|
|
|
void *analysis_cls)
|
2017-03-14 12:22:03 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
enum GNUNET_DB_QueryStatus qsx;
|
2017-03-14 15:13:50 +01:00
|
|
|
|
2017-06-25 01:46:19 +02:00
|
|
|
qsx = adb->get_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
&pp);
|
|
|
|
if (0 > qsx)
|
2017-03-19 13:53:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
return qsx;
|
2017-03-19 13:53:50 +01:00
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
|
2017-03-19 13:53:50 +01:00
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
|
|
|
|
_("First analysis using this auditor, starting audit from scratch\n"));
|
|
|
|
}
|
2017-03-14 12:22:03 +01:00
|
|
|
else
|
2017-03-19 13:53:50 +01:00
|
|
|
{
|
2017-03-20 03:06:23 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2017-03-20 02:45:33 +01:00
|
|
|
_("Resuming audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu\n"),
|
2017-03-19 13:53:50 +01:00
|
|
|
(unsigned long long) pp.last_reserve_in_serial_id,
|
|
|
|
(unsigned long long) pp.last_reserve_out_serial_id,
|
2017-03-20 02:45:33 +01:00
|
|
|
(unsigned long long) pp.last_withdraw_serial_id,
|
2017-03-19 13:53:50 +01:00
|
|
|
(unsigned long long) pp.last_deposit_serial_id,
|
|
|
|
(unsigned long long) pp.last_melt_serial_id,
|
|
|
|
(unsigned long long) pp.last_refund_serial_id,
|
|
|
|
(unsigned long long) pp.last_wire_out_serial_id);
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = analysis (analysis_cls);
|
|
|
|
if (0 > qs)
|
2017-03-19 13:53:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Serialization issue, not recording progress\n");
|
|
|
|
else
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Hard database error, not recording progress\n");
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
|
|
|
|
qs = adb->update_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
&pp);
|
2017-03-20 02:29:33 +01:00
|
|
|
else
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->insert_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
&pp);
|
|
|
|
if (0 >= qs)
|
2017-03-19 13:53:50 +01:00
|
|
|
{
|
2017-06-25 12:59:46 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Failed to update auditor DB, not recording progress\n");
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
2017-03-19 13:53:50 +01:00
|
|
|
}
|
2017-03-20 03:06:23 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2017-03-20 02:45:33 +01:00
|
|
|
_("Concluded audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu\n\n"),
|
2017-03-15 13:12:45 +01:00
|
|
|
(unsigned long long) pp.last_reserve_in_serial_id,
|
|
|
|
(unsigned long long) pp.last_reserve_out_serial_id,
|
2017-03-20 02:45:33 +01:00
|
|
|
(unsigned long long) pp.last_withdraw_serial_id,
|
2017-03-15 13:12:45 +01:00
|
|
|
(unsigned long long) pp.last_deposit_serial_id,
|
|
|
|
(unsigned long long) pp.last_melt_serial_id,
|
|
|
|
(unsigned long long) pp.last_refund_serial_id,
|
2017-03-18 22:38:31 +01:00
|
|
|
(unsigned long long) pp.last_wire_out_serial_id);
|
2017-06-25 01:46:19 +02:00
|
|
|
return qs;
|
2017-03-14 15:13:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2017-06-25 01:46:19 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-03-14 12:22:03 +01:00
|
|
|
|
2017-03-14 15:13:50 +01:00
|
|
|
ret = adb->start (adb->cls,
|
|
|
|
asession);
|
|
|
|
if (GNUNET_OK != ret)
|
2017-03-17 18:46:11 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
ret = edb->start (edb->cls,
|
|
|
|
esession);
|
|
|
|
if (GNUNET_OK != ret)
|
2017-03-17 18:46:11 +01:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = incremental_processing (analysis,
|
2017-03-14 15:13:50 +01:00
|
|
|
analysis_cls);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
2017-03-17 18:46:11 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = edb->commit (edb->cls,
|
2017-03-17 18:46:11 +01:00
|
|
|
esession);
|
2017-06-25 01:46:19 +02:00
|
|
|
if (0 > qs)
|
2017-03-14 15:13:50 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
2017-03-17 18:46:11 +01:00
|
|
|
"Exchange DB commit failed, rolling back transaction\n");
|
2017-03-14 15:13:50 +01:00
|
|
|
adb->rollback (adb->cls,
|
|
|
|
asession);
|
|
|
|
}
|
2017-03-17 18:46:11 +01:00
|
|
|
else
|
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
qs = adb->commit (adb->cls,
|
|
|
|
asession);
|
|
|
|
if (0 > qs)
|
2017-03-17 18:46:11 +01:00
|
|
|
{
|
2017-06-25 01:46:19 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
2017-03-17 18:46:11 +01:00
|
|
|
"Auditor DB commit failed!\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Processing failed, rolling back transaction\n");
|
|
|
|
adb->rollback (adb->cls,
|
|
|
|
asession);
|
|
|
|
edb->rollback (edb->cls,
|
|
|
|
esession);
|
|
|
|
}
|
2017-03-14 15:13:50 +01:00
|
|
|
clear_transaction_state_cache ();
|
2017-06-25 01:46:19 +02:00
|
|
|
return qs;
|
2017-03-14 12:22:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
transact (&analyze_reserves,
|
|
|
|
NULL);
|
2017-03-19 01:09:00 +01:00
|
|
|
transact (&analyze_aggregations,
|
2017-03-14 18:00:17 +01:00
|
|
|
NULL);
|
2017-03-20 02:29:33 +01:00
|
|
|
transact (&analyze_coins,
|
|
|
|
NULL);
|
2017-03-14 12:22:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-10-06 15:17:10 +02:00
|
|
|
/**
|
|
|
|
* 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!)
|
2017-03-18 16:56:31 +01:00
|
|
|
* @param c configuration
|
2016-10-06 15:17:10 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
run (void *cls,
|
|
|
|
char *const *args,
|
|
|
|
const char *cfgfile,
|
2017-03-18 16:56:31 +01:00
|
|
|
const struct GNUNET_CONFIGURATION_Handle *c)
|
2016-10-06 15:17:10 +02:00
|
|
|
{
|
2017-11-12 14:11:05 +01:00
|
|
|
static const struct TALER_MasterPublicKeyP zeromp;
|
2017-11-06 00:03:08 +01:00
|
|
|
struct TALER_Amount income_fee_total;
|
2017-10-16 11:49:32 +02:00
|
|
|
json_t *report;
|
2017-11-06 00:03:08 +01:00
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Launching auditor\n");
|
2017-03-18 16:56:31 +01:00
|
|
|
cfg = c;
|
2017-11-12 14:11:05 +01:00
|
|
|
if (0 == memcmp (&zeromp,
|
|
|
|
&master_pub,
|
|
|
|
sizeof (struct TALER_MasterPublicKeyP)))
|
|
|
|
{
|
|
|
|
/* -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 */
|
|
|
|
|
2017-03-17 13:56:47 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
|
|
|
"taler",
|
|
|
|
"CURRENCY",
|
|
|
|
¤cy))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"taler",
|
|
|
|
"CURRENCY");
|
|
|
|
global_ret = 1;
|
|
|
|
return;
|
|
|
|
}
|
2017-04-20 21:38:02 +02:00
|
|
|
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;
|
|
|
|
}
|
2016-10-06 15:17:10 +02:00
|
|
|
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;
|
|
|
|
}
|
2017-03-20 02:29:33 +01:00
|
|
|
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));
|
|
|
|
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");
|
2017-11-06 00:03:08 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&reported_emergency_sum));
|
|
|
|
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_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));
|
2017-11-06 14:54:52 +01:00
|
|
|
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));
|
2017-11-06 19:11:43 +01:00
|
|
|
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));
|
2017-11-06 14:54:52 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_balance_reserve_not_closed));
|
2017-11-06 19:11:43 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_bad_sig_loss));
|
2017-11-27 23:42:17 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_refresh_hanging));
|
2017-10-16 11:49:32 +02:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_emergencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_row_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
2017-11-06 14:54:52 +01:00
|
|
|
(denomination_key_validity_withdraw_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_reserve_balance_summary_wrong_inconsistencies = json_array ()));
|
2017-10-16 11:49:32 +02:00
|
|
|
GNUNET_assert (NULL !=
|
2017-11-06 14:54:52 +01:00
|
|
|
(report_reserve_balance_insufficient_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_reserve_not_closed_inconsistencies = json_array ()));
|
2017-10-16 11:49:32 +02:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_wire_out_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_coin_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_aggregation_fee_balances = json_array ()));
|
2017-11-06 19:11:43 +01:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_amount_arithmetic_inconsistencies = json_array ()));
|
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_bad_sig_losses = json_array ()));
|
2017-11-27 23:42:17 +01:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_refreshs_hanging = json_array ()));
|
2017-11-20 14:20:09 +01:00
|
|
|
GNUNET_assert (NULL !=
|
|
|
|
(report_fee_time_inconsistencies = json_array ()));
|
2017-03-14 12:22:03 +01:00
|
|
|
setup_sessions_and_run ();
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Audit complete\n");
|
2016-10-06 15:17:10 +02:00
|
|
|
TALER_AUDITORDB_plugin_unload (adb);
|
|
|
|
TALER_EXCHANGEDB_plugin_unload (edb);
|
2017-11-06 00:03:08 +01:00
|
|
|
|
|
|
|
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,"
|
2017-11-06 14:54:52 +01:00
|
|
|
" 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,"
|
2017-11-06 19:11:43 +01:00
|
|
|
" s:o, s:o, s:o, s:o, s:o,"
|
2017-11-20 14:20:09 +01:00
|
|
|
" s:o, s:o, s:o, s:o, s:o,"
|
2017-11-27 23:42:17 +01:00
|
|
|
" s:o, s:o, s:o }",
|
2017-11-06 00:03:08 +01:00
|
|
|
/* blocks of 5 for easier counting/matching to format string */
|
|
|
|
/* block */
|
2017-11-06 14:54:52 +01:00
|
|
|
"reserve_balance_insufficient_inconsistencies",
|
|
|
|
report_reserve_balance_insufficient_inconsistencies,
|
|
|
|
"total_loss_balance_insufficient",
|
|
|
|
TALER_JSON_from_amount (&total_balance_insufficient_loss),
|
|
|
|
"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),
|
2017-11-08 19:07:21 +01:00
|
|
|
/* block */
|
2017-11-06 14:54:52 +01:00
|
|
|
"total_refund_fee_income",
|
|
|
|
TALER_JSON_from_amount (&total_refund_fee_income),
|
|
|
|
"income_fee_total",
|
|
|
|
TALER_JSON_from_amount (&income_fee_total),
|
|
|
|
"emergencies",
|
|
|
|
report_emergencies,
|
|
|
|
"emergencies_risk_total",
|
|
|
|
TALER_JSON_from_amount (&reported_emergency_sum),
|
|
|
|
"reserve_not_closed_inconsistencies",
|
|
|
|
report_reserve_not_closed_inconsistencies,
|
2017-11-08 19:07:21 +01:00
|
|
|
/* block */
|
2017-11-06 14:54:52 +01:00
|
|
|
"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),
|
2017-11-06 19:11:43 +01:00
|
|
|
"bad_sig_losses",
|
|
|
|
report_bad_sig_losses,
|
2017-11-08 19:07:21 +01:00
|
|
|
/* block */
|
2017-11-06 19:11:43 +01:00
|
|
|
"total_bad_sig_loss",
|
|
|
|
TALER_JSON_from_amount (&total_bad_sig_loss),
|
2017-11-06 14:54:52 +01:00
|
|
|
"row_inconsistencies",
|
|
|
|
report_row_inconsistencies,
|
|
|
|
"denomination_key_validity_withdraw_inconsistencies",
|
|
|
|
denomination_key_validity_withdraw_inconsistencies,
|
|
|
|
"coin_inconsistencies",
|
|
|
|
report_coin_inconsistencies,
|
2017-11-06 19:11:43 +01:00
|
|
|
"total_coin_delta_plus",
|
|
|
|
TALER_JSON_from_amount (&total_coin_delta_plus),
|
2017-11-08 19:07:21 +01:00
|
|
|
/* block */
|
2017-11-06 19:11:43 +01:00
|
|
|
"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),
|
2017-11-06 14:54:52 +01:00
|
|
|
"total_aggregation_fee_income",
|
2017-11-20 14:20:09 +01:00
|
|
|
TALER_JSON_from_amount (&total_aggregation_fee_income),
|
2017-11-08 19:07:21 +01:00
|
|
|
/* block */
|
2017-11-20 14:20:09 +01:00
|
|
|
"wire_fee_time_inconsistencies",
|
2017-11-27 23:42:17 +01:00
|
|
|
report_fee_time_inconsistencies,
|
|
|
|
"total_refresh_hanging",
|
|
|
|
TALER_JSON_from_amount (&total_refresh_hanging),
|
|
|
|
"refresh_hanging",
|
|
|
|
report_refreshs_hanging);
|
2017-11-08 19:07:21 +01:00
|
|
|
GNUNET_break (NULL != report);
|
2017-10-16 11:49:32 +02:00
|
|
|
json_dumpf (report,
|
|
|
|
stdout,
|
|
|
|
JSON_INDENT (2));
|
|
|
|
json_decref (report);
|
2016-10-06 15:17:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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[] = {
|
2017-11-12 14:11:05 +01:00
|
|
|
GNUNET_GETOPT_option_base32_auto ('m',
|
|
|
|
"exchange-key",
|
|
|
|
"KEY",
|
|
|
|
"public key of the exchange (Crockford base32 encoded)",
|
|
|
|
&master_pub),
|
2017-03-25 21:22:22 +01:00
|
|
|
GNUNET_GETOPT_option_flag ('r',
|
2017-10-16 11:49:32 +02:00
|
|
|
"restart",
|
|
|
|
"restart audit from the beginning (required on first run)",
|
|
|
|
&restart),
|
2016-10-06 15:17:10 +02:00
|
|
|
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",
|
2017-03-20 04:29:42 +01:00
|
|
|
"MESSAGE",
|
2016-10-06 15:17:10 +02:00
|
|
|
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 */
|