2017-09-25 23:26:48 +02:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2019-08-23 21:14:50 +02:00
|
|
|
Copyright (C) 2017-2019 Taler Systems SA
|
2017-09-25 23:26:48 +02:00
|
|
|
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @file auditor/taler-wire-auditor.c
|
|
|
|
* @brief audits that wire transfers match those from an exchange database.
|
|
|
|
* @author Christian Grothoff
|
|
|
|
*
|
|
|
|
* - First, this auditor verifies that 'reserves_in' actually matches
|
|
|
|
* the incoming wire transfers from the bank.
|
|
|
|
* - Second, we check that the outgoing wire transfers match those
|
2017-10-12 20:46:42 +02:00
|
|
|
* given in the 'wire_out' table
|
2017-11-19 22:36:58 +01:00
|
|
|
* - Finally, we check that all wire transfers that should have been made,
|
|
|
|
* were actually made
|
2017-09-25 23:26:48 +02:00
|
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include <gnunet/gnunet_util_lib.h>
|
|
|
|
#include "taler_auditordb_plugin.h"
|
|
|
|
#include "taler_exchangedb_plugin.h"
|
|
|
|
#include "taler_json_lib.h"
|
|
|
|
#include "taler_wire_lib.h"
|
|
|
|
#include "taler_signatures.h"
|
|
|
|
|
2017-11-19 22:36:58 +01:00
|
|
|
/**
|
|
|
|
* How much time do we allow the aggregator to lag behind? If
|
|
|
|
* wire transfers should have been made more than #GRACE_PERIOD
|
|
|
|
* before, we issue warnings.
|
|
|
|
*/
|
|
|
|
#define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
|
2017-09-25 23:26:48 +02:00
|
|
|
|
2019-09-05 10:36:14 +02:00
|
|
|
/**
|
|
|
|
* How much do we allow the bank and the exchange to disagree about
|
|
|
|
* timestamps? Should be sufficiently large to avoid bogus reports from deltas
|
|
|
|
* created by imperfect clock synchronization and network delay.
|
|
|
|
*/
|
|
|
|
#define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
|
|
|
|
15)
|
|
|
|
|
2018-04-02 14:24:45 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Information we keep for each supported account.
|
|
|
|
*/
|
|
|
|
struct WireAccount
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Accounts are kept in a DLL.
|
|
|
|
*/
|
|
|
|
struct WireAccount *next;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Plugins are kept in a DLL.
|
|
|
|
*/
|
|
|
|
struct WireAccount *prev;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle to the plugin.
|
|
|
|
*/
|
|
|
|
struct TALER_WIRE_Plugin *wire_plugin;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Name of the section that configures this account.
|
|
|
|
*/
|
|
|
|
char *section_name;
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/**
|
|
|
|
* Active wire request for the transaction history.
|
|
|
|
*/
|
|
|
|
struct TALER_WIRE_HistoryHandle *hh;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Progress point for this account.
|
|
|
|
*/
|
|
|
|
struct TALER_AUDITORDB_WireAccountProgressPoint pp;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Where we are in the inbound (CREDIT) transaction history.
|
|
|
|
*/
|
|
|
|
void *in_wire_off;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Where we are in the inbound (DEBIT) transaction history.
|
|
|
|
*/
|
|
|
|
void *out_wire_off;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of bytes in #in_wire_off and #out_wire_off.
|
|
|
|
*/
|
|
|
|
size_t wire_off_size;
|
2019-08-25 16:18:24 +02:00
|
|
|
|
2018-04-02 14:24:45 +02:00
|
|
|
/**
|
|
|
|
* We should check for inbound transactions to this account.
|
|
|
|
*/
|
|
|
|
int watch_credit;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We should check for outbound transactions from this account.
|
|
|
|
*/
|
|
|
|
int watch_debit;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
/**
|
|
|
|
* Return value from main().
|
|
|
|
*/
|
|
|
|
static int global_ret;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Command-line option "-r": restart audit from scratch
|
|
|
|
*/
|
|
|
|
static int restart;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle to access the exchange's database.
|
|
|
|
*/
|
|
|
|
static struct TALER_EXCHANGEDB_Plugin *edb;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Which currency are we doing the audit for?
|
|
|
|
*/
|
|
|
|
static char *currency;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Our configuration.
|
|
|
|
*/
|
|
|
|
static const struct GNUNET_CONFIGURATION_Handle *cfg;
|
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
/**
|
|
|
|
* Map with information about incoming wire transfers.
|
|
|
|
* Maps hashes of the wire offsets to `struct ReserveInInfo`s.
|
|
|
|
*/
|
|
|
|
static struct GNUNET_CONTAINER_MultiHashMap *in_map;
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
/**
|
|
|
|
* Map with information about outgoing wire transfers.
|
2017-11-12 15:46:52 +01:00
|
|
|
* Maps hashes of the wire subjects (in binary encoding)
|
|
|
|
* to `struct ReserveOutInfo`s.
|
2017-10-12 20:46:42 +02:00
|
|
|
*/
|
|
|
|
static struct GNUNET_CONTAINER_MultiHashMap *out_map;
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
/**
|
|
|
|
* Our session with the #edb.
|
|
|
|
*/
|
|
|
|
static struct TALER_EXCHANGEDB_Session *esession;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle to access the auditor's database.
|
|
|
|
*/
|
|
|
|
static struct TALER_AUDITORDB_Plugin *adb;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Our session with the #adb.
|
|
|
|
*/
|
|
|
|
static struct TALER_AUDITORDB_Session *asession;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Master public key of the exchange to audit.
|
|
|
|
*/
|
|
|
|
static struct TALER_MasterPublicKeyP master_pub;
|
|
|
|
|
2018-04-02 14:24:45 +02:00
|
|
|
/**
|
|
|
|
* Head of list of wire accounts we still need to look at.
|
|
|
|
*/
|
|
|
|
static struct WireAccount *wa_head;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Tail of list of wire accounts we still need to look at.
|
|
|
|
*/
|
|
|
|
static struct WireAccount *wa_tail;
|
|
|
|
|
2017-09-30 21:28:17 +02:00
|
|
|
/**
|
|
|
|
* Query status for the incremental processing status in the auditordb.
|
|
|
|
*/
|
|
|
|
static enum GNUNET_DB_QueryStatus qsx;
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
/**
|
2017-10-12 20:46:42 +02:00
|
|
|
* Last reserve_in / wire_out serial IDs seen.
|
2017-09-25 23:26:48 +02:00
|
|
|
*/
|
2017-09-30 20:29:19 +02:00
|
|
|
static struct TALER_AUDITORDB_WireProgressPoint pp;
|
2017-09-25 23:26:48 +02:00
|
|
|
|
2017-10-16 12:11:49 +02:00
|
|
|
/**
|
2017-11-07 14:38:45 +01:00
|
|
|
* Array of reports about row inconsitencies in wire_out table.
|
|
|
|
*/
|
|
|
|
static json_t *report_wire_out_inconsistencies;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of reports about row inconsitencies in reserves_in table.
|
|
|
|
*/
|
|
|
|
static json_t *report_reserve_in_inconsistencies;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of reports about wrong bank account being recorded for
|
|
|
|
* incoming wire transfers.
|
|
|
|
*/
|
|
|
|
static json_t *report_missattribution_in_inconsistencies;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of reports about row inconcistencies.
|
2017-10-16 12:11:49 +02:00
|
|
|
*/
|
|
|
|
static json_t *report_row_inconsistencies;
|
|
|
|
|
2017-11-20 14:20:09 +01:00
|
|
|
/**
|
|
|
|
* Array of reports about inconcistencies in the database about
|
|
|
|
* the incoming wire transfers (exchange is not exactly to blame).
|
|
|
|
*/
|
|
|
|
static json_t *report_wire_format_inconsistencies;
|
|
|
|
|
2017-10-16 12:11:49 +02:00
|
|
|
/**
|
|
|
|
* Array of reports about minor row inconcistencies.
|
|
|
|
*/
|
|
|
|
static json_t *report_row_minor_inconsistencies;
|
|
|
|
|
2017-11-19 22:36:58 +01:00
|
|
|
/**
|
|
|
|
* Array of reports about lagging transactions.
|
|
|
|
*/
|
|
|
|
static json_t *report_lags;
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/**
|
|
|
|
* Amount that is considered "tiny"
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount tiny_amount;
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
|
|
|
* Total amount that was transferred too much from the exchange.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_bad_amount_out_plus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Total amount that was transferred too little from the exchange.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_bad_amount_out_minus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Total amount that was transferred too much to the exchange.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_bad_amount_in_plus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Total amount that was transferred too little to the exchange.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_bad_amount_in_minus;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Total amount where the exchange has the wrong sender account
|
|
|
|
* for incoming funds and may thus wire funds to the wrong
|
|
|
|
* destination when closing the reserve.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_missattribution_in;
|
|
|
|
|
2017-11-19 22:36:58 +01:00
|
|
|
/**
|
|
|
|
* Total amount which the exchange did not transfer in time.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_amount_lag;
|
|
|
|
|
2017-11-20 14:20:09 +01:00
|
|
|
/**
|
|
|
|
* Total amount affected by wire format trouble.s
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount total_wire_format_amount;
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
|
|
|
* Amount of zero in our currency.
|
|
|
|
*/
|
|
|
|
static struct TALER_Amount zero;
|
|
|
|
|
2017-09-30 21:28:17 +02:00
|
|
|
|
|
|
|
/* ***************************** Shutdown **************************** */
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
2017-10-12 16:26:52 +02:00
|
|
|
* Entry in map with wire information we expect to obtain from the
|
2017-10-12 20:46:42 +02:00
|
|
|
* bank later.
|
2017-10-12 16:26:52 +02:00
|
|
|
*/
|
|
|
|
struct ReserveInInfo
|
|
|
|
{
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
2017-10-12 16:26:52 +02:00
|
|
|
* Hash of expected row offset.
|
|
|
|
*/
|
|
|
|
struct GNUNET_HashCode row_off_hash;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Number of bytes in @e row_off.
|
|
|
|
*/
|
|
|
|
size_t row_off_size;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expected details about the wire transfer.
|
|
|
|
*/
|
|
|
|
struct TALER_WIRE_TransferDetails details;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* RowID in reserves_in table.
|
|
|
|
*/
|
|
|
|
uint64_t rowid;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
2017-10-12 20:46:42 +02:00
|
|
|
* Entry in map with wire information we expect to obtain from the
|
|
|
|
* #edb later.
|
|
|
|
*/
|
|
|
|
struct ReserveOutInfo
|
|
|
|
{
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
/**
|
2017-10-12 20:46:42 +02:00
|
|
|
* Hash of the wire transfer subject.
|
|
|
|
*/
|
|
|
|
struct GNUNET_HashCode subject_hash;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Expected details about the wire transfer.
|
|
|
|
*/
|
|
|
|
struct TALER_WIRE_TransferDetails details;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-09-04 04:06:16 +02:00
|
|
|
/**
|
|
|
|
* Convert absolute time to human-readable JSON string.
|
|
|
|
*
|
|
|
|
* @param at time to convert
|
|
|
|
* @return human-readable string representing the time
|
|
|
|
*/
|
|
|
|
static json_t *
|
|
|
|
json_from_time_abs (struct GNUNET_TIME_Absolute at)
|
|
|
|
{
|
|
|
|
return json_string
|
|
|
|
(GNUNET_STRINGS_absolute_time_to_string (at));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
/**
|
|
|
|
* Free entry in #in_map.
|
|
|
|
*
|
|
|
|
* @param cls NULL
|
|
|
|
* @param key unused key
|
|
|
|
* @param value the `struct ReserveInInfo` to free
|
|
|
|
* @return #GNUNET_OK
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
free_rii (void *cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
const struct GNUNET_HashCode *key,
|
|
|
|
void *value)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
|
|
|
struct ReserveInInfo *rii = value;
|
|
|
|
|
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_remove (in_map,
|
|
|
|
key,
|
|
|
|
rii));
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_free (rii->details.account_url);
|
|
|
|
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
|
2017-10-12 16:26:52 +02:00
|
|
|
GNUNET_free (rii);
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
/**
|
|
|
|
* Free entry in #out_map.
|
|
|
|
*
|
|
|
|
* @param cls NULL
|
|
|
|
* @param key unused key
|
|
|
|
* @param value the `struct ReserveOutInfo` to free
|
|
|
|
* @return #GNUNET_OK
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
free_roi (void *cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
const struct GNUNET_HashCode *key,
|
|
|
|
void *value)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
|
|
|
struct ReserveOutInfo *roi = value;
|
|
|
|
|
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_remove (out_map,
|
|
|
|
key,
|
|
|
|
roi));
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_free (roi->details.account_url);
|
|
|
|
GNUNET_free_non_null (roi->details.wtid_s); /* field not used (yet) */
|
2017-10-12 20:46:42 +02:00
|
|
|
GNUNET_free (roi);
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-30 21:28:17 +02:00
|
|
|
/**
|
|
|
|
* Task run on shutdown.
|
2017-10-06 21:36:40 +02:00
|
|
|
*
|
|
|
|
* @param cls NULL
|
2017-09-30 21:28:17 +02:00
|
|
|
*/
|
|
|
|
static void
|
2017-10-06 21:36:40 +02:00
|
|
|
do_shutdown (void *cls)
|
2017-09-30 21:28:17 +02:00
|
|
|
{
|
2018-04-02 14:24:45 +02:00
|
|
|
struct WireAccount *wa;
|
|
|
|
|
2017-10-16 12:11:49 +02:00
|
|
|
if (NULL != report_row_inconsistencies)
|
|
|
|
{
|
|
|
|
json_t *report;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2017-10-16 12:11:49 +02:00
|
|
|
GNUNET_assert (NULL != report_row_minor_inconsistencies);
|
2017-11-07 14:38:45 +01:00
|
|
|
report = json_pack ("{s:o, s:o, s:o, s:o, s:o,"
|
2017-11-19 22:36:58 +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 }",
|
2017-11-07 14:38:45 +01:00
|
|
|
/* blocks of 5 */
|
2019-09-05 10:03:56 +02:00
|
|
|
/* Tested in test-auditor.sh #11, #15 */
|
2019-08-23 21:14:50 +02:00
|
|
|
"wire_out_amount_inconsistencies",
|
2017-11-07 14:38:45 +01:00
|
|
|
report_wire_out_inconsistencies,
|
|
|
|
"total_wire_out_delta_plus",
|
|
|
|
TALER_JSON_from_amount (&total_bad_amount_out_plus),
|
2019-09-30 22:11:24 +02:00
|
|
|
/* Tested in test-auditor.sh #11, #15, #19 */
|
2017-11-07 14:38:45 +01:00
|
|
|
"total_wire_out_delta_minus",
|
|
|
|
TALER_JSON_from_amount (&total_bad_amount_out_minus),
|
2019-09-04 04:06:16 +02:00
|
|
|
/* Tested in test-auditor.sh #2 */
|
2019-08-23 21:14:50 +02:00
|
|
|
"reserve_in_amount_inconsistencies",
|
2017-11-07 14:38:45 +01:00
|
|
|
report_reserve_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
/* Tested in test-auditor.sh #2 */
|
2017-11-08 19:07:21 +01:00
|
|
|
"total_wire_in_delta_plus",
|
2017-11-07 14:38:45 +01:00
|
|
|
TALER_JSON_from_amount (&total_bad_amount_in_plus),
|
|
|
|
/* block */
|
2019-09-04 04:06:16 +02:00
|
|
|
/* Tested in test-auditor.sh #3 */
|
2017-11-07 14:38:45 +01:00
|
|
|
"total_wire_in_delta_minus",
|
|
|
|
TALER_JSON_from_amount (&total_bad_amount_in_minus),
|
2019-09-04 04:06:16 +02:00
|
|
|
/* Tested in test-auditor.sh #9 */
|
2017-11-07 14:38:45 +01:00
|
|
|
"missattribution_in_inconsistencies",
|
|
|
|
report_missattribution_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
/* Tested in test-auditor.sh #9 */
|
2017-11-07 14:38:45 +01:00
|
|
|
"total_missattribution_in",
|
|
|
|
TALER_JSON_from_amount (&total_missattribution_in),
|
2019-08-23 21:14:50 +02:00
|
|
|
"row_inconsistencies",
|
2017-11-07 14:38:45 +01:00
|
|
|
report_row_inconsistencies,
|
2019-09-05 10:03:56 +02:00
|
|
|
/* Tested in test-auditor.sh #10/#17 */
|
2019-08-23 21:14:50 +02:00
|
|
|
"row_minor_inconsistencies",
|
2017-11-19 22:36:58 +01:00
|
|
|
report_row_minor_inconsistencies,
|
|
|
|
/* block */
|
2019-09-30 22:11:24 +02:00
|
|
|
/* Tested in test-auditor.sh #19 */
|
2017-11-20 14:20:09 +01:00
|
|
|
"total_wire_format_amount",
|
|
|
|
TALER_JSON_from_amount (&total_wire_format_amount),
|
2019-09-30 22:11:24 +02:00
|
|
|
/* Tested in test-auditor.sh #19 */
|
2017-11-20 14:20:09 +01:00
|
|
|
"wire_format_inconsistencies",
|
|
|
|
report_wire_format_inconsistencies,
|
2017-11-19 22:36:58 +01:00
|
|
|
"total_amount_lag",
|
2019-09-04 05:16:04 +02:00
|
|
|
TALER_JSON_from_amount (&total_amount_lag),
|
2017-11-19 22:36:58 +01:00
|
|
|
"lag_details",
|
|
|
|
report_lags);
|
2017-11-08 18:44:12 +01:00
|
|
|
GNUNET_break (NULL != report);
|
2017-10-16 12:11:49 +02:00
|
|
|
json_dumpf (report,
|
2019-08-23 21:14:50 +02:00
|
|
|
stdout,
|
|
|
|
JSON_INDENT (2));
|
2017-10-16 12:11:49 +02:00
|
|
|
json_decref (report);
|
2017-11-08 18:44:12 +01:00
|
|
|
report_wire_out_inconsistencies = NULL;
|
|
|
|
report_reserve_in_inconsistencies = NULL;
|
2017-10-16 12:11:49 +02:00
|
|
|
report_row_inconsistencies = NULL;
|
|
|
|
report_row_minor_inconsistencies = NULL;
|
2017-11-08 18:44:12 +01:00
|
|
|
report_missattribution_in_inconsistencies = NULL;
|
2017-11-19 22:36:58 +01:00
|
|
|
report_lags = NULL;
|
2017-11-20 14:20:09 +01:00
|
|
|
report_wire_format_inconsistencies = NULL;
|
2017-10-16 12:11:49 +02:00
|
|
|
}
|
2017-10-12 16:26:52 +02:00
|
|
|
if (NULL != in_map)
|
|
|
|
{
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (in_map,
|
2019-08-23 21:14:50 +02:00
|
|
|
&free_rii,
|
|
|
|
NULL);
|
2017-10-12 16:26:52 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (in_map);
|
|
|
|
in_map = NULL;
|
|
|
|
}
|
2017-10-12 20:46:42 +02:00
|
|
|
if (NULL != out_map)
|
|
|
|
{
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (out_map,
|
2019-08-23 21:14:50 +02:00
|
|
|
&free_roi,
|
|
|
|
NULL);
|
2017-10-12 20:46:42 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (out_map);
|
|
|
|
out_map = NULL;
|
|
|
|
}
|
2018-04-02 14:24:45 +02:00
|
|
|
while (NULL != (wa = wa_head))
|
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
if (NULL != wa->hh)
|
|
|
|
{
|
|
|
|
struct TALER_WIRE_Plugin *wp = wa->wire_plugin;
|
|
|
|
|
|
|
|
wp->get_history_cancel (wp->cls,
|
|
|
|
wa->hh);
|
|
|
|
wa->hh = NULL;
|
|
|
|
}
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_CONTAINER_DLL_remove (wa_head,
|
|
|
|
wa_tail,
|
|
|
|
wa);
|
|
|
|
TALER_WIRE_plugin_unload (wa->wire_plugin);
|
|
|
|
GNUNET_free (wa->section_name);
|
2019-08-24 22:49:35 +02:00
|
|
|
GNUNET_free_non_null (wa->in_wire_off);
|
|
|
|
GNUNET_free_non_null (wa->out_wire_off);
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_free (wa);
|
|
|
|
}
|
2017-09-30 21:28:17 +02:00
|
|
|
if (NULL != adb)
|
|
|
|
{
|
|
|
|
TALER_AUDITORDB_plugin_unload (adb);
|
|
|
|
adb = NULL;
|
|
|
|
}
|
|
|
|
if (NULL != edb)
|
|
|
|
{
|
|
|
|
TALER_EXCHANGEDB_plugin_unload (edb);
|
|
|
|
edb = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
|
|
|
|
/* ***************************** Report logic **************************** */
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
|
2017-10-16 12:11:49 +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-07 14:38:45 +01:00
|
|
|
*/
|
2017-10-16 12:11:49 +02:00
|
|
|
static void
|
|
|
|
report (json_t *array,
|
2019-08-23 21:14:50 +02:00
|
|
|
json_t *object)
|
2017-10-16 12:11:49 +02:00
|
|
|
{
|
|
|
|
GNUNET_assert (NULL != object);
|
|
|
|
GNUNET_assert (0 ==
|
2019-08-23 21:14:50 +02:00
|
|
|
json_array_append_new (array,
|
|
|
|
object));
|
2017-10-16 12:11:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
/* *************************** General transaction logic ****************** */
|
|
|
|
|
|
|
|
/**
|
2017-09-30 21:28:17 +02:00
|
|
|
* Commit the transaction, checkpointing our progress in the auditor
|
|
|
|
* DB.
|
2017-09-25 23:26:48 +02:00
|
|
|
*
|
2017-09-30 21:28:17 +02:00
|
|
|
* @param qs transaction status so far
|
2017-09-25 23:26:48 +02:00
|
|
|
* @return transaction status code
|
|
|
|
*/
|
|
|
|
static enum GNUNET_DB_QueryStatus
|
2017-09-30 21:28:17 +02:00
|
|
|
commit (enum GNUNET_DB_QueryStatus qs)
|
2017-09-25 23:26:48 +02:00
|
|
|
{
|
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-23 21:14:50 +02:00
|
|
|
"Serialization issue, not recording progress\n");
|
2017-09-25 23:26:48 +02:00
|
|
|
else
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
2019-08-23 21:14:50 +02:00
|
|
|
"Hard error, not recording progress\n");
|
2017-09-30 21:28:17 +02:00
|
|
|
adb->rollback (adb->cls,
|
|
|
|
asession);
|
|
|
|
edb->rollback (edb->cls,
|
|
|
|
esession);
|
2017-09-25 23:26:48 +02:00
|
|
|
return qs;
|
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
for (struct WireAccount *wa = wa_head;
|
|
|
|
NULL != wa;
|
|
|
|
wa = wa->next)
|
|
|
|
{
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
|
|
|
|
qs = adb->update_wire_auditor_account_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
wa->section_name,
|
|
|
|
&wa->pp,
|
|
|
|
wa->in_wire_off,
|
|
|
|
wa->out_wire_off,
|
|
|
|
wa->wire_off_size);
|
|
|
|
else
|
|
|
|
qs = adb->insert_wire_auditor_account_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
wa->section_name,
|
|
|
|
&wa->pp,
|
|
|
|
wa->in_wire_off,
|
|
|
|
wa->out_wire_off,
|
|
|
|
wa->wire_off_size);
|
|
|
|
if (0 >= qs)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Failed to update auditor DB, not recording progress\n");
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
|
|
|
}
|
2019-08-25 16:18:24 +02:00
|
|
|
}
|
2017-09-25 23:26:48 +02:00
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
|
2017-09-30 20:29:19 +02:00
|
|
|
qs = adb->update_wire_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2019-08-24 22:49:35 +02:00
|
|
|
&pp);
|
2017-09-25 23:26:48 +02:00
|
|
|
else
|
2017-09-30 20:29:19 +02:00
|
|
|
qs = adb->insert_wire_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2019-08-24 22:49:35 +02:00
|
|
|
&pp);
|
2017-09-25 23:26:48 +02:00
|
|
|
if (0 >= qs)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-23 21:14:50 +02:00
|
|
|
"Failed to update auditor DB, not recording progress\n");
|
2017-09-25 23:26:48 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Concluded audit step at %s\n",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (pp.last_timestamp));
|
2017-09-25 23:26:48 +02:00
|
|
|
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
|
|
|
{
|
|
|
|
qs = edb->commit (edb->cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
esession);
|
2017-09-25 23:26:48 +02:00
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Exchange DB commit failed, rolling back transaction\n");
|
|
|
|
adb->rollback (adb->cls,
|
|
|
|
asession);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
qs = adb->commit (adb->cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
asession);
|
2017-09-25 23:26:48 +02:00
|
|
|
if (0 > qs)
|
|
|
|
{
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
2017-09-25 23:26:48 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"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);
|
|
|
|
}
|
|
|
|
return qs;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/* ***************************** Analyze required transfers ************************ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called on deposits that are past their due date
|
|
|
|
* and have not yet seen a wire transfer.
|
|
|
|
*
|
|
|
|
* @param cls closure
|
|
|
|
* @param rowid deposit table row of the coin's deposit
|
|
|
|
* @param coin_pub public key of the coin
|
|
|
|
* @param amount value of the deposit, including fee
|
|
|
|
* @param wire where should the funds be wired
|
|
|
|
* @param deadline what was the requested wire transfer deadline
|
|
|
|
* @param tiny did the exchange defer this transfer because it is too small?
|
|
|
|
* @param done did the exchange claim that it made a transfer?
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
wire_missing_cb (void *cls,
|
|
|
|
uint64_t rowid,
|
|
|
|
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|
|
|
const struct TALER_Amount *amount,
|
|
|
|
const json_t *wire,
|
|
|
|
struct GNUNET_TIME_Absolute deadline,
|
|
|
|
/* bool? */ int tiny,
|
|
|
|
/* bool? */ int done)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_amount_lag,
|
|
|
|
&total_amount_lag,
|
|
|
|
amount));
|
|
|
|
if ( (GNUNET_YES == tiny) &&
|
|
|
|
(0 > TALER_amount_cmp (amount,
|
|
|
|
&tiny_amount)) )
|
|
|
|
return; /* acceptable, amount was tiny */
|
|
|
|
report (report_lags,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:s, s:o, s:O}",
|
2019-08-24 22:49:35 +02:00
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"amount", TALER_JSON_from_amount (amount),
|
2019-09-04 04:06:16 +02:00
|
|
|
"deadline", json_from_time_abs (deadline),
|
2019-08-24 22:49:35 +02:00
|
|
|
"claimed_done", (done) ? "yes" : "no",
|
|
|
|
"coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
|
|
|
|
"account", wire));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks that all wire transfers that should have happened
|
|
|
|
* (based on deposits) have indeed happened.
|
2019-08-25 16:18:24 +02:00
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* FIXME: this check _might_ rather belong with the
|
|
|
|
* taler-auditor logic.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
check_for_required_transfers ()
|
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute next_timestamp;
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2019-08-25 16:18:24 +02:00
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
next_timestamp = GNUNET_TIME_absolute_get ();
|
2019-08-24 22:53:32 +02:00
|
|
|
(void) GNUNET_TIME_round_abs (&next_timestamp);
|
2019-08-24 22:49:35 +02:00
|
|
|
/* Subtract #GRACE_PERIOD, so we can be a bit behind in processing
|
|
|
|
without immediately raising undue concern */
|
|
|
|
next_timestamp = GNUNET_TIME_absolute_subtract (next_timestamp,
|
|
|
|
GRACE_PERIOD);
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Analyzing exchange's unfinished deposits\n");
|
|
|
|
qs = edb->select_deposits_missing_wire (edb->cls,
|
|
|
|
esession,
|
|
|
|
pp.last_timestamp,
|
|
|
|
next_timestamp,
|
|
|
|
&wire_missing_cb,
|
|
|
|
&next_timestamp);
|
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
pp.last_timestamp = next_timestamp;
|
|
|
|
/* conclude with success */
|
|
|
|
commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
|
2019-08-24 23:06:18 +02:00
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
2019-08-24 22:49:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
/* ***************************** Analyze reserves_out ************************ */
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/**
|
|
|
|
* Clean up after processing wire out data.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
conclude_wire_out ()
|
|
|
|
{
|
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (out_map);
|
|
|
|
out_map = NULL;
|
|
|
|
check_for_required_transfers ();
|
|
|
|
}
|
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
|
|
|
|
/**
|
2017-10-12 20:46:42 +02:00
|
|
|
* Function called with details about outgoing wire transfers
|
|
|
|
* as claimed by the exchange DB.
|
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls a `struct WireAccount`
|
2017-10-12 20:46:42 +02:00
|
|
|
* @param rowid unique serial ID for the refresh session in our DB
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param date timestamp of the transfer (roughly)
|
2017-10-12 20:46:42 +02:00
|
|
|
* @param wtid wire transfer subject
|
|
|
|
* @param wire wire transfer details of the receiver
|
|
|
|
* @param amount amount that was wired
|
|
|
|
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
2017-10-12 16:26:52 +02:00
|
|
|
*/
|
2017-10-12 20:46:42 +02:00
|
|
|
static int
|
|
|
|
wire_out_cb (void *cls,
|
2017-11-07 14:38:45 +01:00
|
|
|
uint64_t rowid,
|
|
|
|
struct GNUNET_TIME_Absolute date,
|
|
|
|
const struct TALER_WireTransferIdentifierRawP *wtid,
|
|
|
|
const json_t *wire,
|
|
|
|
const struct TALER_Amount *amount)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 20:46:42 +02:00
|
|
|
struct GNUNET_HashCode key;
|
|
|
|
struct ReserveOutInfo *roi;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Exchange wire OUT at %s of %s with WTID %s\n",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (date),
|
|
|
|
TALER_amount2s (amount),
|
|
|
|
TALER_B2S (wtid));
|
2017-10-12 20:46:42 +02:00
|
|
|
GNUNET_CRYPTO_hash (wtid,
|
2019-08-23 21:14:50 +02:00
|
|
|
sizeof (struct TALER_WireTransferIdentifierRawP),
|
|
|
|
&key);
|
2019-09-05 03:52:26 +02:00
|
|
|
roi = GNUNET_CONTAINER_multihashmap_get (out_map,
|
2019-08-23 21:14:50 +02:00
|
|
|
&key);
|
2017-10-12 20:46:42 +02:00
|
|
|
if (NULL == roi)
|
|
|
|
{
|
2017-11-07 14:38:45 +01:00
|
|
|
/* Wire transfer was not made (yet) at all (but would have been
|
|
|
|
justified), so the entire amount is missing / still to be done.
|
|
|
|
This is moderately harmless, it might just be that the aggreator
|
|
|
|
has not yet fully caught up with the transfers it should do. */
|
|
|
|
report (report_wire_out_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"amount_wired", TALER_JSON_from_amount (&zero),
|
|
|
|
"amount_justified", TALER_JSON_from_amount (amount),
|
|
|
|
"wtid", GNUNET_JSON_from_data_auto (wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (date),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "wire transfer not made (yet?)"));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_minus,
|
|
|
|
&total_bad_amount_out_minus,
|
|
|
|
amount));
|
2017-10-12 20:46:42 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2017-11-07 14:38:45 +01:00
|
|
|
{
|
2018-04-02 14:24:45 +02:00
|
|
|
char *payto_url;
|
|
|
|
|
|
|
|
payto_url = TALER_JSON_wire_to_payto (wire);
|
|
|
|
if (0 != strcasecmp (payto_url,
|
|
|
|
roi->details.account_url))
|
|
|
|
{
|
|
|
|
/* Destination bank account is wrong in actual wire transfer, so
|
|
|
|
we should count the wire transfer as entirely spurious, and
|
|
|
|
additionally consider the justified wire transfer as missing. */
|
|
|
|
report (report_wire_out_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s}",
|
2018-04-02 14:24:45 +02:00
|
|
|
"row", (json_int_t) rowid,
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_wired", TALER_JSON_from_amount (
|
|
|
|
&roi->details.amount),
|
2018-04-02 14:24:45 +02:00
|
|
|
"amount_justified", TALER_JSON_from_amount (&zero),
|
|
|
|
"wtid", GNUNET_JSON_from_data_auto (wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (date),
|
2018-04-02 14:24:45 +02:00
|
|
|
"diagnostic", "recevier account missmatch"));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_plus,
|
|
|
|
&total_bad_amount_out_plus,
|
|
|
|
&roi->details.amount));
|
|
|
|
report (report_wire_out_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s}",
|
2018-04-02 14:24:45 +02:00
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"amount_wired", TALER_JSON_from_amount (&zero),
|
|
|
|
"amount_justified", TALER_JSON_from_amount (amount),
|
|
|
|
"wtid", GNUNET_JSON_from_data_auto (wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (date),
|
2018-04-02 14:24:45 +02:00
|
|
|
"diagnostic", "receiver account missmatch"));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_minus,
|
|
|
|
&total_bad_amount_out_minus,
|
|
|
|
amount));
|
|
|
|
GNUNET_free (payto_url);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
GNUNET_free (payto_url);
|
2017-11-07 14:38:45 +01:00
|
|
|
}
|
2017-10-12 20:46:42 +02:00
|
|
|
if (0 != TALER_amount_cmp (&roi->details.amount,
|
2019-08-23 21:14:50 +02:00
|
|
|
amount))
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_wire_out_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"amount_justified", TALER_JSON_from_amount (amount),
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_wired", TALER_JSON_from_amount (
|
|
|
|
&roi->details.amount),
|
2017-11-07 14:38:45 +01:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (date),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "wire amount does not match"));
|
|
|
|
if (0 < TALER_amount_cmp (amount,
|
|
|
|
&roi->details.amount))
|
|
|
|
{
|
|
|
|
/* amount > roi->details.amount: wire transfer was smaller than it should have been */
|
|
|
|
struct TALER_Amount delta;
|
|
|
|
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_subtract (&delta,
|
|
|
|
amount,
|
|
|
|
&roi->details.amount));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_minus,
|
|
|
|
&total_bad_amount_out_minus,
|
|
|
|
&delta));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* roi->details.amount < amount: wire transfer was larger than it should have been */
|
|
|
|
struct TALER_Amount delta;
|
|
|
|
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_subtract (&delta,
|
|
|
|
&roi->details.amount,
|
|
|
|
amount));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_plus,
|
|
|
|
&total_bad_amount_out_plus,
|
|
|
|
&delta));
|
|
|
|
}
|
|
|
|
goto cleanup;
|
2017-10-12 20:46:42 +02:00
|
|
|
}
|
2019-09-05 10:36:14 +02:00
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2019-09-05 10:36:14 +02:00
|
|
|
struct GNUNET_TIME_Relative delta;
|
|
|
|
|
|
|
|
if (roi->details.execution_date.abs_value_us >
|
|
|
|
date.abs_value_us)
|
|
|
|
delta = GNUNET_TIME_absolute_get_difference (date,
|
|
|
|
roi->details.execution_date);
|
|
|
|
else
|
|
|
|
delta = GNUNET_TIME_absolute_get_difference (roi->details.execution_date,
|
|
|
|
date);
|
|
|
|
if (delta.rel_value_us > TIME_TOLERANCE.rel_value_us)
|
|
|
|
{
|
|
|
|
char *details;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&details,
|
|
|
|
"execution date missmatch (%s)",
|
|
|
|
GNUNET_STRINGS_relative_time_to_string (delta,
|
|
|
|
GNUNET_YES));
|
|
|
|
report (report_row_minor_inconsistencies,
|
|
|
|
json_pack ("{s:s, s:I, s:s}",
|
|
|
|
"table", "wire_out",
|
|
|
|
"row", (json_int_t) rowid,
|
|
|
|
"diagnostic", details));
|
|
|
|
GNUNET_free (details);
|
|
|
|
}
|
2017-10-12 20:46:42 +02:00
|
|
|
}
|
2019-09-05 10:36:14 +02:00
|
|
|
|
2019-09-30 22:11:24 +02:00
|
|
|
cleanup:
|
2017-10-12 20:46:42 +02:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2019-08-23 21:14:50 +02:00
|
|
|
free_roi (NULL,
|
|
|
|
&key,
|
|
|
|
roi));
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->pp.last_wire_out_serial_id = rowid + 1;
|
2017-10-12 20:46:42 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2017-10-12 16:26:52 +02:00
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
|
|
|
|
/**
|
2017-11-07 14:38:45 +01:00
|
|
|
* Complain that we failed to match an entry from #out_map. This
|
|
|
|
* means a wire transfer was made without proper justification.
|
2017-10-12 20:46:42 +02:00
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls a `struct WireAccount`
|
2017-10-12 20:46:42 +02:00
|
|
|
* @param key unused key
|
2017-11-07 14:38:45 +01:00
|
|
|
* @param value the `struct ReserveOutInfo` to report
|
2017-10-12 20:46:42 +02:00
|
|
|
* @return #GNUNET_OK
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
complain_out_not_found (void *cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
const struct GNUNET_HashCode *key,
|
|
|
|
void *value)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 20:46:42 +02:00
|
|
|
struct ReserveOutInfo *roi = value;
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
(void) wa; // FIXME: log which account is affected...
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_wire_out_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"row", (json_int_t) 0,
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_wired", TALER_JSON_from_amount (
|
|
|
|
&roi->details.amount),
|
2017-11-07 14:38:45 +01:00
|
|
|
"amount_justified", TALER_JSON_from_amount (&zero),
|
2017-11-12 15:46:52 +01:00
|
|
|
"wtid", (NULL == roi->details.wtid_s)
|
|
|
|
? GNUNET_JSON_from_data_auto (&roi->details.wtid)
|
|
|
|
: json_string (roi->details.wtid_s),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (
|
2019-08-25 16:18:24 +02:00
|
|
|
roi->details.execution_date),
|
|
|
|
"diagnostic",
|
|
|
|
"justification for wire transfer not found"));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_out_plus,
|
|
|
|
&total_bad_amount_out_plus,
|
|
|
|
&roi->details.amount));
|
2017-10-12 20:46:42 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-19 22:36:58 +01:00
|
|
|
/**
|
2019-08-24 22:49:35 +02:00
|
|
|
* Main function for processing 'reserves_out' data. We start by going over
|
|
|
|
* the DEBIT transactions this time, and then verify that all of them are
|
|
|
|
* justified by 'reserves_out'.
|
2018-04-02 14:24:45 +02:00
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls `struct WireAccount` with a wire account list to process
|
2018-04-02 14:24:45 +02:00
|
|
|
*/
|
|
|
|
static void
|
2019-08-24 22:49:35 +02:00
|
|
|
process_debits (void *cls);
|
2018-04-02 14:24:45 +02:00
|
|
|
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
/**
|
|
|
|
* Go over the "wire_out" table of the exchange and
|
|
|
|
* verify that all wire outs are in that table.
|
2019-08-24 22:49:35 +02:00
|
|
|
*
|
|
|
|
* @param wa wire account we are processing
|
2017-10-12 20:46:42 +02:00
|
|
|
*/
|
|
|
|
static void
|
2019-08-24 22:49:35 +02:00
|
|
|
check_exchange_wire_out (struct WireAccount *wa)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Analyzing exchange's wire OUT table for account `%s'\n",
|
|
|
|
wa->section_name);
|
2018-04-02 21:12:18 +02:00
|
|
|
qs = edb->select_wire_out_above_serial_id_by_account (edb->cls,
|
|
|
|
esession,
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->section_name,
|
2019-08-25 16:18:24 +02:00
|
|
|
wa->pp.
|
|
|
|
last_wire_out_serial_id,
|
2018-04-02 21:12:18 +02:00
|
|
|
&wire_out_cb,
|
2019-08-24 22:49:35 +02:00
|
|
|
wa);
|
2017-10-12 20:46:42 +02:00
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (out_map,
|
2019-08-24 22:49:35 +02:00
|
|
|
&complain_out_not_found,
|
|
|
|
wa);
|
2017-11-07 14:38:45 +01:00
|
|
|
/* clean up */
|
2017-10-12 20:46:42 +02:00
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (out_map,
|
2019-08-24 22:49:35 +02:00
|
|
|
&free_roi,
|
|
|
|
NULL);
|
|
|
|
process_debits (wa->next);
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
/**
|
|
|
|
* This function is called for all transactions that
|
|
|
|
* are credited to the exchange's account (incoming
|
|
|
|
* transactions).
|
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls `struct WireAccount` with current wire account to process
|
2017-12-06 19:24:00 +01:00
|
|
|
* @param ec error code in case something went wrong
|
2017-10-12 20:46:42 +02:00
|
|
|
* @param dir direction of the transfer
|
|
|
|
* @param row_off identification of the position at which we are querying
|
|
|
|
* @param row_off_size number of bytes in @a row_off
|
|
|
|
* @param details details about the wire transfer
|
|
|
|
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
history_debit_cb (void *cls,
|
2017-12-06 19:24:00 +01:00
|
|
|
enum TALER_ErrorCode ec,
|
2019-08-23 21:14:50 +02:00
|
|
|
enum TALER_BANK_Direction dir,
|
|
|
|
const void *row_off,
|
|
|
|
size_t row_off_size,
|
|
|
|
const struct TALER_WIRE_TransferDetails *details)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 20:46:42 +02:00
|
|
|
struct ReserveOutInfo *roi;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
if (TALER_BANK_DIRECTION_NONE == dir)
|
|
|
|
{
|
2017-12-06 19:24:00 +01:00
|
|
|
if (TALER_EC_NONE != ec)
|
|
|
|
{
|
|
|
|
/* FIXME: log properly to audit report! */
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Error fetching history: %u!\n",
|
|
|
|
(unsigned int) ec);
|
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->hh = NULL;
|
|
|
|
check_exchange_wire_out (wa);
|
2017-10-12 20:46:42 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
|
|
|
|
TALER_amount2s (&details->amount),
|
|
|
|
TALER_B2S (&details->wtid));
|
2017-11-12 15:46:52 +01:00
|
|
|
if (NULL != details->wtid_s)
|
|
|
|
{
|
|
|
|
char *diagnostic;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&diagnostic,
|
2019-09-30 22:11:24 +02:00
|
|
|
"malformed subject `%s'",
|
2017-11-12 15:46:52 +01:00
|
|
|
details->wtid_s);
|
2017-11-20 14:20:09 +01:00
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_wire_format_amount,
|
|
|
|
&total_wire_format_amount,
|
|
|
|
&details->amount));
|
|
|
|
report (report_wire_format_inconsistencies,
|
|
|
|
json_pack ("{s:o, s:o, s:s}",
|
2017-11-12 15:46:52 +01:00
|
|
|
"amount", TALER_JSON_from_amount (&details->amount),
|
2019-09-30 22:11:24 +02:00
|
|
|
"wire_offset", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2017-11-12 15:46:52 +01:00
|
|
|
"diagnostic", diagnostic));
|
|
|
|
GNUNET_free (diagnostic);
|
2017-11-20 21:55:24 +01:00
|
|
|
return GNUNET_OK;
|
2017-11-12 15:46:52 +01:00
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
|
|
|
|
/* Update offset */
|
|
|
|
if (NULL == wa->out_wire_off)
|
|
|
|
{
|
|
|
|
wa->wire_off_size = row_off_size;
|
|
|
|
wa->out_wire_off = GNUNET_malloc (row_off_size);
|
|
|
|
}
|
|
|
|
if (wa->wire_off_size != row_off_size)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
commit (GNUNET_DB_STATUS_HARD_ERROR);
|
|
|
|
wa->hh = NULL;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
memcpy (wa->out_wire_off,
|
|
|
|
row_off,
|
|
|
|
row_off_size);
|
|
|
|
|
2017-10-12 20:46:42 +02:00
|
|
|
roi = GNUNET_new (struct ReserveOutInfo);
|
2017-11-12 15:46:52 +01:00
|
|
|
GNUNET_CRYPTO_hash (&details->wtid,
|
2019-08-24 22:49:35 +02:00
|
|
|
sizeof (details->wtid),
|
|
|
|
&roi->subject_hash);
|
2017-10-12 20:46:42 +02:00
|
|
|
roi->details.amount = details->amount;
|
|
|
|
roi->details.execution_date = details->execution_date;
|
2017-11-12 15:46:52 +01:00
|
|
|
roi->details.wtid = details->wtid;
|
2018-04-02 14:24:45 +02:00
|
|
|
roi->details.account_url = GNUNET_strdup (details->account_url);
|
2017-10-12 20:46:42 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CONTAINER_multihashmap_put (out_map,
|
2019-08-24 22:49:35 +02:00
|
|
|
&roi->subject_hash,
|
|
|
|
roi,
|
|
|
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2017-11-12 15:46:52 +01:00
|
|
|
char *diagnostic;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&diagnostic,
|
2019-09-30 22:11:24 +02:00
|
|
|
"duplicate subject hash `%s'",
|
2017-11-12 15:46:52 +01:00
|
|
|
TALER_B2S (&roi->subject_hash));
|
2017-11-20 14:20:09 +01:00
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_wire_format_amount,
|
|
|
|
&total_wire_format_amount,
|
|
|
|
&details->amount));
|
|
|
|
report (report_wire_format_inconsistencies,
|
|
|
|
json_pack ("{s:o, s:o, s:s}",
|
2017-11-12 15:46:52 +01:00
|
|
|
"amount", TALER_JSON_from_amount (&details->amount),
|
2019-09-30 22:11:24 +02:00
|
|
|
"wire_offset", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2017-11-12 15:46:52 +01:00
|
|
|
"diagnostic", diagnostic));
|
|
|
|
GNUNET_free (diagnostic);
|
2017-11-20 21:55:24 +01:00
|
|
|
return GNUNET_OK;
|
2017-10-12 20:46:42 +02:00
|
|
|
}
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2019-08-24 22:49:35 +02:00
|
|
|
* Main function for processing 'reserves_out' data. We start by going over
|
|
|
|
* the DEBIT transactions this time, and then verify that all of them are
|
|
|
|
* justified by 'reserves_out'.
|
|
|
|
*
|
|
|
|
* @param cls `struct WireAccount` with a wire account list to process
|
2017-10-12 20:46:42 +02:00
|
|
|
*/
|
|
|
|
static void
|
2019-08-24 22:49:35 +02:00
|
|
|
process_debits (void *cls)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
|
|
|
struct TALER_WIRE_Plugin *wp;
|
|
|
|
|
2019-08-25 16:18:24 +02:00
|
|
|
/* skip accounts where DEBIT is not enabled */
|
2019-08-24 23:14:15 +02:00
|
|
|
while ( (NULL != wa) &&
|
2019-08-25 15:40:01 +02:00
|
|
|
(GNUNET_NO == wa->watch_debit) )
|
2019-08-24 23:14:15 +02:00
|
|
|
wa = wa->next;
|
2019-08-24 22:49:35 +02:00
|
|
|
if (NULL == wa)
|
|
|
|
{
|
|
|
|
/* end of iteration, now check wire_out to see
|
|
|
|
if it matches #out_map */
|
|
|
|
conclude_wire_out ();
|
|
|
|
return;
|
|
|
|
}
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Checking bank DEBIT records of account `%s'\n",
|
|
|
|
wa->section_name);
|
|
|
|
GNUNET_assert (NULL == wa->hh);
|
|
|
|
wp = wa->wire_plugin;
|
|
|
|
wa->hh = wp->get_history (wp->cls,
|
|
|
|
wa->section_name,
|
|
|
|
TALER_BANK_DIRECTION_DEBIT,
|
|
|
|
wa->out_wire_off,
|
|
|
|
wa->wire_off_size,
|
|
|
|
INT64_MAX,
|
|
|
|
&history_debit_cb,
|
|
|
|
wa);
|
|
|
|
if (NULL == wa->hh)
|
2017-10-12 20:46:42 +02:00
|
|
|
{
|
|
|
|
fprintf (stderr,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Failed to obtain bank transaction history for `%s'\n",
|
|
|
|
wa->section_name);
|
2017-10-12 20:46:42 +02:00
|
|
|
commit (GNUNET_DB_STATUS_HARD_ERROR);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/**
|
|
|
|
* Begin analyzing wire_out.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
begin_debit_audit ()
|
|
|
|
{
|
|
|
|
out_map = GNUNET_CONTAINER_multihashmap_create (1024,
|
|
|
|
GNUNET_YES);
|
|
|
|
process_debits (wa_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-30 21:28:17 +02:00
|
|
|
/* ***************************** Analyze reserves_in ************************ */
|
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
/**
|
|
|
|
* Conclude the credit history check by logging entries that
|
|
|
|
* were not found and freeing resources. Then move on to
|
|
|
|
* processing debits.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
conclude_credit_history ()
|
|
|
|
{
|
|
|
|
GNUNET_CONTAINER_multihashmap_destroy (in_map);
|
|
|
|
in_map = NULL;
|
|
|
|
/* credit done, now check debits */
|
|
|
|
begin_debit_audit ();
|
|
|
|
}
|
|
|
|
|
2017-09-30 21:28:17 +02:00
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
/**
|
|
|
|
* Function called with details about incoming wire transfers
|
|
|
|
* as claimed by the exchange DB.
|
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls a `struct WireAccount` we are processing
|
2019-08-26 03:16:36 +02:00
|
|
|
* @param rowid unique serial ID for the entry in our DB
|
2017-10-12 16:26:52 +02:00
|
|
|
* @param reserve_pub public key of the reserve (also the WTID)
|
|
|
|
* @param credit amount that was received
|
2018-04-02 14:24:45 +02:00
|
|
|
* @param sender_url payto://-URL of the sender's bank account
|
2017-10-12 16:26:52 +02:00
|
|
|
* @param wire_reference unique identifier for the wire transfer (plugin-specific format)
|
|
|
|
* @param wire_reference_size number of bytes in @a wire_reference
|
|
|
|
* @param execution_date when did we receive the funds
|
|
|
|
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
reserve_in_cb (void *cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
uint64_t rowid,
|
|
|
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
|
|
|
const struct TALER_Amount *credit,
|
|
|
|
const char *sender_url,
|
|
|
|
const void *wire_reference,
|
|
|
|
size_t wire_reference_size,
|
|
|
|
struct GNUNET_TIME_Absolute execution_date)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 16:26:52 +02:00
|
|
|
struct ReserveInInfo *rii;
|
|
|
|
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-26 03:16:36 +02:00
|
|
|
"Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
|
|
|
|
(unsigned long long) rowid,
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_STRINGS_absolute_time_to_string (execution_date),
|
|
|
|
TALER_amount2s (credit),
|
|
|
|
TALER_B2S (reserve_pub));
|
2017-10-12 16:26:52 +02:00
|
|
|
rii = GNUNET_new (struct ReserveInInfo);
|
|
|
|
GNUNET_CRYPTO_hash (wire_reference,
|
2019-08-25 16:18:24 +02:00
|
|
|
wire_reference_size,
|
|
|
|
&rii->row_off_hash);
|
2017-10-12 16:26:52 +02:00
|
|
|
rii->row_off_size = wire_reference_size;
|
|
|
|
rii->details.amount = *credit;
|
|
|
|
rii->details.execution_date = execution_date;
|
2017-11-12 15:46:52 +01:00
|
|
|
/* reserve public key should be the WTID */
|
|
|
|
GNUNET_assert (sizeof (rii->details.wtid) ==
|
|
|
|
sizeof (*reserve_pub));
|
|
|
|
memcpy (&rii->details.wtid,
|
|
|
|
reserve_pub,
|
|
|
|
sizeof (*reserve_pub));
|
2018-04-02 14:24:45 +02:00
|
|
|
rii->details.account_url = GNUNET_strdup (sender_url);
|
2017-10-12 16:26:52 +02:00
|
|
|
rii->rowid = rowid;
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CONTAINER_multihashmap_put (in_map,
|
2019-08-23 21:14:50 +02:00
|
|
|
&rii->row_off_hash,
|
|
|
|
rii,
|
|
|
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_row_inconsistencies,
|
|
|
|
json_pack ("{s:s, s:I, s:o, s:s}",
|
|
|
|
"table", "reserves_in",
|
|
|
|
"row", (json_int_t) rowid,
|
2019-08-25 16:18:24 +02:00
|
|
|
"wire_offset_hash", GNUNET_JSON_from_data_auto (
|
|
|
|
&rii->row_off_hash),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "duplicate wire offset"));
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_free (rii->details.account_url);
|
|
|
|
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
|
2017-11-08 18:44:12 +01:00
|
|
|
GNUNET_free (rii);
|
|
|
|
return GNUNET_OK;
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->pp.last_reserve_in_serial_id = rowid + 1;
|
2017-10-12 16:26:52 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Complain that we failed to match an entry from #in_map.
|
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls a `struct WireAccount`
|
2017-10-12 16:26:52 +02:00
|
|
|
* @param key unused key
|
|
|
|
* @param value the `struct ReserveInInfo` to free
|
|
|
|
* @return #GNUNET_OK
|
|
|
|
*/
|
|
|
|
static int
|
2017-10-12 20:46:42 +02:00
|
|
|
complain_in_not_found (void *cls,
|
2019-08-23 21:14:50 +02:00
|
|
|
const struct GNUNET_HashCode *key,
|
|
|
|
void *value)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 16:26:52 +02:00
|
|
|
struct ReserveInInfo *rii = value;
|
|
|
|
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_reserve_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:s, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"row", (json_int_t) rii->rowid,
|
2019-09-04 04:06:16 +02:00
|
|
|
"amount_exchange_expected", TALER_JSON_from_amount (
|
2019-08-25 16:18:24 +02:00
|
|
|
&rii->details.amount),
|
2017-11-07 14:38:45 +01:00
|
|
|
"amount_wired", TALER_JSON_from_amount (&zero),
|
2017-11-12 15:46:52 +01:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (
|
2019-08-25 16:18:24 +02:00
|
|
|
rii->details.execution_date),
|
2019-08-26 03:16:36 +02:00
|
|
|
"account", wa->section_name,
|
2019-08-25 16:18:24 +02:00
|
|
|
"diagnostic",
|
|
|
|
"incoming wire transfer claimed by exchange not found"));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_in_minus,
|
|
|
|
&total_bad_amount_in_minus,
|
|
|
|
&rii->details.amount));
|
2017-10-12 16:26:52 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-20 21:55:24 +01:00
|
|
|
/**
|
2019-08-24 22:49:35 +02:00
|
|
|
* Start processing the next wire account.
|
|
|
|
* Shuts down if we are done.
|
|
|
|
*
|
|
|
|
* @param cls `struct WireAccount` with a wire account list to process
|
2017-11-20 21:55:24 +01:00
|
|
|
*/
|
|
|
|
static void
|
2019-08-24 22:49:35 +02:00
|
|
|
process_credits (void *cls);
|
2017-11-20 21:55:24 +01:00
|
|
|
|
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
/**
|
2017-10-12 20:46:42 +02:00
|
|
|
* This function is called for all transactions that
|
|
|
|
* are credited to the exchange's account (incoming
|
|
|
|
* transactions).
|
2017-09-30 21:28:17 +02:00
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls `struct WireAccount` we are processing
|
2017-12-06 19:24:00 +01:00
|
|
|
* @param ec error code in case something went wrong
|
2017-09-30 21:28:17 +02:00
|
|
|
* @param dir direction of the transfer
|
|
|
|
* @param row_off identification of the position at which we are querying
|
|
|
|
* @param row_off_size number of bytes in @a row_off
|
|
|
|
* @param details details about the wire transfer
|
|
|
|
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
|
2017-09-25 23:26:48 +02:00
|
|
|
*/
|
2017-09-30 21:28:17 +02:00
|
|
|
static int
|
|
|
|
history_credit_cb (void *cls,
|
2017-12-06 19:24:00 +01:00
|
|
|
enum TALER_ErrorCode ec,
|
2017-09-30 21:28:17 +02:00
|
|
|
enum TALER_BANK_Direction dir,
|
|
|
|
const void *row_off,
|
|
|
|
size_t row_off_size,
|
|
|
|
const struct TALER_WIRE_TransferDetails *details)
|
2017-09-25 23:26:48 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
2017-10-12 16:26:52 +02:00
|
|
|
struct ReserveInInfo *rii;
|
|
|
|
struct GNUNET_HashCode key;
|
2017-11-07 14:38:45 +01:00
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
if (TALER_BANK_DIRECTION_NONE == dir)
|
2017-09-25 23:26:48 +02:00
|
|
|
{
|
2017-12-06 19:24:00 +01:00
|
|
|
if (TALER_EC_NONE != ec)
|
|
|
|
{
|
|
|
|
/* FIXME: log properly to audit report! */
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Error fetching history: %u!\n",
|
|
|
|
(unsigned int) ec);
|
|
|
|
}
|
2017-09-30 21:28:17 +02:00
|
|
|
/* end of operation */
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->hh = NULL;
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Reconciling CREDIT processing of account `%s'\n",
|
|
|
|
wa->section_name);
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (in_map,
|
|
|
|
&complain_in_not_found,
|
|
|
|
wa);
|
|
|
|
/* clean up before 2nd phase */
|
|
|
|
GNUNET_CONTAINER_multihashmap_iterate (in_map,
|
|
|
|
&free_rii,
|
|
|
|
NULL);
|
|
|
|
process_credits (wa->next);
|
2017-11-20 21:55:24 +01:00
|
|
|
return GNUNET_OK;
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
2019-08-23 21:14:50 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Analyzing bank CREDIT at %s of %s with WTID %s\n",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
|
|
|
|
TALER_amount2s (&details->amount),
|
|
|
|
TALER_B2S (&details->wtid));
|
2017-10-12 16:26:52 +02:00
|
|
|
GNUNET_CRYPTO_hash (row_off,
|
2019-08-23 21:14:50 +02:00
|
|
|
row_off_size,
|
|
|
|
&key);
|
2017-10-12 16:26:52 +02:00
|
|
|
rii = GNUNET_CONTAINER_multihashmap_get (in_map,
|
2019-08-23 21:14:50 +02:00
|
|
|
&key);
|
2017-10-12 16:26:52 +02:00
|
|
|
if (NULL == rii)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-23 21:14:50 +02:00
|
|
|
"Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n",
|
2019-08-25 16:18:24 +02:00
|
|
|
GNUNET_STRINGS_absolute_time_to_string (
|
|
|
|
details->execution_date));
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->hh = NULL;
|
|
|
|
process_credits (wa->next);
|
2017-11-20 21:55:24 +01:00
|
|
|
return GNUNET_SYSERR; /* not an error, just end of processing */
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Update offset */
|
2019-08-24 22:49:35 +02:00
|
|
|
if (NULL == wa->in_wire_off)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->wire_off_size = row_off_size;
|
|
|
|
wa->in_wire_off = GNUNET_malloc (row_off_size);
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
if (wa->wire_off_size != row_off_size)
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
commit (GNUNET_DB_STATUS_HARD_ERROR);
|
2017-09-30 21:28:17 +02:00
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return GNUNET_SYSERR;
|
2017-09-30 20:47:52 +02:00
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
memcpy (wa->in_wire_off,
|
2019-08-23 21:14:50 +02:00
|
|
|
row_off,
|
|
|
|
row_off_size);
|
2017-10-12 16:26:52 +02:00
|
|
|
|
2019-08-24 22:49:35 +02:00
|
|
|
|
2017-10-12 16:26:52 +02:00
|
|
|
/* compare records with expected data */
|
|
|
|
if (row_off_size != rii->row_off_size)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_row_inconsistencies,
|
2019-08-26 03:16:36 +02:00
|
|
|
json_pack ("{s:s, s:I, s:o, s:o, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"table", "reserves_in",
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2017-11-07 14:38:45 +01:00
|
|
|
"wire_offset_hash", GNUNET_JSON_from_data_auto (&key),
|
|
|
|
"diagnostic", "wire reference size missmatch"));
|
2017-10-12 16:26:52 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2019-04-08 15:25:43 +02:00
|
|
|
if (0 != GNUNET_memcmp (&details->wtid,
|
|
|
|
&rii->details.wtid))
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_reserve_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}",
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_exchange_expected", TALER_JSON_from_amount (
|
|
|
|
&rii->details.amount),
|
2017-11-07 14:38:45 +01:00
|
|
|
"amount_wired", TALER_JSON_from_amount (&zero),
|
2017-11-12 15:46:52 +01:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (
|
2019-08-25 16:18:24 +02:00
|
|
|
rii->details.execution_date),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "wire subject does not match"));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_in_minus,
|
|
|
|
&total_bad_amount_in_minus,
|
|
|
|
&rii->details.amount));
|
|
|
|
report (report_reserve_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}",
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_exchange_expected", TALER_JSON_from_amount (
|
|
|
|
&zero),
|
|
|
|
"amount_wired", TALER_JSON_from_amount (
|
|
|
|
&details->amount),
|
2017-11-12 15:46:52 +01:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (&details->wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (
|
2019-08-25 16:18:24 +02:00
|
|
|
details->execution_date),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "wire subject does not match"));
|
|
|
|
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_in_plus,
|
|
|
|
&total_bad_amount_in_plus,
|
|
|
|
&details->amount));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (0 != TALER_amount_cmp (&rii->details.amount,
|
2019-08-23 21:14:50 +02:00
|
|
|
&details->amount))
|
2017-11-07 14:38:45 +01:00
|
|
|
{
|
|
|
|
report (report_reserve_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}",
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2019-08-25 16:18:24 +02:00
|
|
|
"amount_exchange_expected", TALER_JSON_from_amount (
|
|
|
|
&rii->details.amount),
|
|
|
|
"amount_wired", TALER_JSON_from_amount (
|
|
|
|
&details->amount),
|
2017-11-12 15:46:52 +01:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (&details->wtid),
|
2019-09-04 04:06:16 +02:00
|
|
|
"timestamp", json_from_time_abs (
|
2019-08-25 16:18:24 +02:00
|
|
|
details->execution_date),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "wire amount does not match"));
|
|
|
|
if (0 < TALER_amount_cmp (&details->amount,
|
|
|
|
&rii->details.amount))
|
|
|
|
{
|
|
|
|
/* details->amount > rii->details.amount: wire transfer was larger than it should have been */
|
|
|
|
struct TALER_Amount delta;
|
|
|
|
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_subtract (&delta,
|
|
|
|
&details->amount,
|
|
|
|
&rii->details.amount));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_in_plus,
|
|
|
|
&total_bad_amount_in_plus,
|
|
|
|
&delta));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
|
|
|
|
struct TALER_Amount delta;
|
|
|
|
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_subtract (&delta,
|
|
|
|
&rii->details.amount,
|
|
|
|
&details->amount));
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_bad_amount_in_minus,
|
|
|
|
&total_bad_amount_in_minus,
|
|
|
|
&delta));
|
|
|
|
}
|
|
|
|
goto cleanup;
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
2018-04-02 14:24:45 +02:00
|
|
|
if (0 != strcasecmp (details->account_url,
|
|
|
|
rii->details.account_url))
|
2017-10-12 16:26:52 +02:00
|
|
|
{
|
2017-11-07 14:38:45 +01:00
|
|
|
report (report_missattribution_in_inconsistencies,
|
2019-09-04 04:06:16 +02:00
|
|
|
json_pack ("{s:o, s:I, s:o, s:o}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"amount", TALER_JSON_from_amount (&rii->details.amount),
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2019-08-25 16:18:24 +02:00
|
|
|
"wtid", GNUNET_JSON_from_data_auto (
|
|
|
|
&rii->details.wtid)));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_break (GNUNET_OK ==
|
|
|
|
TALER_amount_add (&total_missattribution_in,
|
|
|
|
&total_missattribution_in,
|
|
|
|
&rii->details.amount));
|
|
|
|
}
|
|
|
|
if (details->execution_date.abs_value_us !=
|
|
|
|
rii->details.execution_date.abs_value_us)
|
|
|
|
{
|
|
|
|
report (report_row_minor_inconsistencies,
|
2019-08-26 03:16:36 +02:00
|
|
|
json_pack ("{s:s, s:I, s:o, s:s}",
|
2017-11-07 14:38:45 +01:00
|
|
|
"table", "reserves_in",
|
2019-08-26 03:16:36 +02:00
|
|
|
"row", (json_int_t) rii->rowid,
|
|
|
|
"raw_bank_row", GNUNET_JSON_from_data (row_off,
|
|
|
|
row_off_size),
|
2017-11-07 14:38:45 +01:00
|
|
|
"diagnostic", "execution date missmatch"));
|
2017-10-12 16:26:52 +02:00
|
|
|
}
|
2019-09-30 22:11:24 +02:00
|
|
|
cleanup:
|
2017-10-12 16:26:52 +02:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2019-08-23 21:14:50 +02:00
|
|
|
free_rii (NULL,
|
|
|
|
&key,
|
|
|
|
rii));
|
2017-09-30 21:28:17 +02:00
|
|
|
return GNUNET_OK;
|
2017-09-25 23:26:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-11-19 22:36:58 +01:00
|
|
|
/* ***************************** Setup logic ************************ */
|
2017-09-30 21:28:17 +02:00
|
|
|
|
|
|
|
|
2018-04-02 14:24:45 +02:00
|
|
|
/**
|
|
|
|
* Start processing the next wire account.
|
|
|
|
* Shuts down if we are done.
|
|
|
|
*
|
2019-08-24 22:49:35 +02:00
|
|
|
* @param cls `struct WireAccount` with a wire account list to process
|
2018-04-02 14:24:45 +02:00
|
|
|
*/
|
|
|
|
static void
|
2019-08-24 22:49:35 +02:00
|
|
|
process_credits (void *cls)
|
2018-04-02 14:24:45 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
struct WireAccount *wa = cls;
|
|
|
|
struct TALER_WIRE_Plugin *wp;
|
2018-04-02 14:24:45 +02:00
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
2019-08-24 23:14:15 +02:00
|
|
|
|
|
|
|
/* skip accounts where CREDIT is not enabled */
|
|
|
|
while ( (NULL != wa) &&
|
2019-08-25 15:40:01 +02:00
|
|
|
(GNUNET_NO == wa->watch_credit) )
|
2019-08-24 23:14:15 +02:00
|
|
|
wa = wa->next;
|
2019-08-24 22:49:35 +02:00
|
|
|
if (NULL == wa)
|
2018-04-02 14:24:45 +02:00
|
|
|
{
|
2019-08-24 22:49:35 +02:00
|
|
|
/* done with all accounts, conclude check */
|
|
|
|
conclude_credit_history ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Analyzing exchange's wire IN table for account `%s'\n",
|
|
|
|
wa->section_name);
|
|
|
|
qs = edb->select_reserves_in_above_serial_id_by_account (edb->cls,
|
|
|
|
esession,
|
|
|
|
wa->section_name,
|
2019-08-25 16:18:24 +02:00
|
|
|
wa->pp.
|
|
|
|
last_reserve_in_serial_id,
|
2019-08-24 22:49:35 +02:00
|
|
|
&reserve_in_cb,
|
|
|
|
wa);
|
|
|
|
if (0 > qs)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
|
|
global_ret = 1;
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
|
2018-04-02 14:24:45 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Starting bank CREDIT history of account `%s'\n",
|
2018-04-02 14:24:45 +02:00
|
|
|
wa->section_name);
|
|
|
|
wp = wa->wire_plugin;
|
2019-08-24 22:49:35 +02:00
|
|
|
wa->hh = wp->get_history (wp->cls,
|
|
|
|
wa->section_name,
|
|
|
|
TALER_BANK_DIRECTION_CREDIT,
|
|
|
|
wa->in_wire_off,
|
|
|
|
wa->wire_off_size,
|
|
|
|
INT64_MAX,
|
|
|
|
&history_credit_cb,
|
|
|
|
wa);
|
|
|
|
if (NULL == wa->hh)
|
|
|
|
{
|
|
|
|
fprintf (stderr,
|
|
|
|
"Failed to obtain bank transaction history\n");
|
|
|
|
commit (GNUNET_DB_STATUS_HARD_ERROR);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Begin audit of CREDITs to the exchange.
|
2019-08-25 16:18:24 +02:00
|
|
|
*/
|
2019-08-24 22:49:35 +02:00
|
|
|
static void
|
|
|
|
begin_credit_audit ()
|
|
|
|
{
|
|
|
|
in_map = GNUNET_CONTAINER_multihashmap_create (1024,
|
|
|
|
GNUNET_YES);
|
|
|
|
/* now go over all bank accounts and check delta with in_map */
|
|
|
|
process_credits (wa_head);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start the database transactions and begin the audit.
|
2019-08-25 16:18:24 +02:00
|
|
|
*/
|
2019-08-24 22:49:35 +02:00
|
|
|
static void
|
|
|
|
begin_transaction ()
|
|
|
|
{
|
|
|
|
enum GNUNET_DB_QueryStatus qsx;
|
|
|
|
int ret;
|
2018-04-02 14:24:45 +02:00
|
|
|
|
|
|
|
ret = adb->start (adb->cls,
|
|
|
|
asession);
|
|
|
|
if (GNUNET_OK != ret)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
edb->preflight (edb->cls,
|
|
|
|
esession);
|
|
|
|
ret = edb->start (edb->cls,
|
|
|
|
esession,
|
|
|
|
"wire auditor");
|
|
|
|
if (GNUNET_OK != ret)
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
for (struct WireAccount *wa = wa_head;
|
|
|
|
NULL != wa;
|
|
|
|
wa = wa->next)
|
|
|
|
{
|
|
|
|
qsx = adb->get_wire_auditor_account_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
|
|
|
wa->section_name,
|
|
|
|
&wa->pp,
|
|
|
|
&wa->in_wire_off,
|
|
|
|
&wa->out_wire_off,
|
|
|
|
&wa->wire_off_size);
|
|
|
|
if (0 > qsx)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2018-04-02 14:24:45 +02:00
|
|
|
qsx = adb->get_wire_auditor_progress (adb->cls,
|
|
|
|
asession,
|
|
|
|
&master_pub,
|
2019-08-24 22:49:35 +02:00
|
|
|
&pp);
|
2018-04-02 14:24:45 +02:00
|
|
|
if (0 > qsx)
|
|
|
|
{
|
|
|
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
|
2019-08-25 16:18:24 +02:00
|
|
|
_ (
|
|
|
|
"First analysis using this auditor, starting audit from scratch\n"));
|
2018-04-02 14:24:45 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2019-08-24 22:49:35 +02:00
|
|
|
"Resuming audit at %s\n",
|
|
|
|
GNUNET_STRINGS_absolute_time_to_string (pp.last_timestamp));
|
2018-04-02 14:24:45 +02:00
|
|
|
}
|
2019-08-24 22:49:35 +02:00
|
|
|
begin_credit_audit ();
|
2018-04-02 14:24:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called with information about a wire account. Adds the
|
|
|
|
* account to our list for processing (if it is enabled and we can
|
|
|
|
* load the plugin).
|
|
|
|
*
|
|
|
|
* @param cls closure, NULL
|
|
|
|
* @param ai account information
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
process_account_cb (void *cls,
|
|
|
|
const struct TALER_EXCHANGEDB_AccountInfo *ai)
|
|
|
|
{
|
|
|
|
struct WireAccount *wa;
|
|
|
|
struct TALER_WIRE_Plugin *wp;
|
|
|
|
|
2019-08-24 23:14:15 +02:00
|
|
|
if ( (GNUNET_NO == ai->debit_enabled) &&
|
|
|
|
(GNUNET_NO == ai->credit_enabled) )
|
|
|
|
return; /* not an active exchange account */
|
2018-04-02 14:24:45 +02:00
|
|
|
wp = TALER_WIRE_plugin_load (cfg,
|
|
|
|
ai->plugin_name);
|
|
|
|
if (NULL == wp)
|
|
|
|
{
|
|
|
|
fprintf (stderr,
|
|
|
|
"Failed to load wire plugin `%s'\n",
|
|
|
|
ai->plugin_name);
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
2019-08-24 23:06:18 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Found exchange account `%s'\n",
|
|
|
|
ai->section_name);
|
2018-04-02 14:24:45 +02:00
|
|
|
wa = GNUNET_new (struct WireAccount);
|
|
|
|
wa->wire_plugin = wp;
|
|
|
|
wa->section_name = GNUNET_strdup (ai->section_name);
|
|
|
|
wa->watch_debit = ai->debit_enabled;
|
|
|
|
wa->watch_credit = ai->credit_enabled;
|
|
|
|
GNUNET_CONTAINER_DLL_insert (wa_head,
|
|
|
|
wa_tail,
|
|
|
|
wa);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-09-25 23:26:48 +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!)
|
|
|
|
* @param c configuration
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
run (void *cls,
|
|
|
|
char *const *args,
|
|
|
|
const char *cfgfile,
|
|
|
|
const struct GNUNET_CONFIGURATION_Handle *c)
|
|
|
|
{
|
2017-11-12 14:11:05 +01:00
|
|
|
static const struct TALER_MasterPublicKeyP zeromp;
|
2019-08-24 22:49:35 +02:00
|
|
|
char *tinys;
|
2017-09-30 21:28:17 +02:00
|
|
|
|
2017-09-25 23:26:48 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
|
|
"Launching auditor\n");
|
|
|
|
cfg = c;
|
2019-08-24 22:49:35 +02:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
|
|
|
"auditor",
|
|
|
|
"TINY_AMOUNT",
|
|
|
|
&tinys))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"auditor",
|
|
|
|
"TINY_AMOUNT");
|
|
|
|
global_ret = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_string_to_amount (tinys,
|
|
|
|
&tiny_amount))
|
|
|
|
{
|
|
|
|
GNUNET_free (tinys);
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"auditor",
|
|
|
|
"TINY_AMOUNT",
|
|
|
|
"invalid amount");
|
|
|
|
global_ret = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
GNUNET_free (tinys);
|
2019-04-08 15:25:43 +02:00
|
|
|
if (0 == GNUNET_memcmp (&zeromp,
|
|
|
|
&master_pub))
|
2017-11-12 14:11:05 +01:00
|
|
|
{
|
|
|
|
/* -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,
|
2019-08-25 16:18:24 +02:00
|
|
|
strlen (
|
|
|
|
master_public_key_str),
|
2017-11-12 14:11:05 +01:00
|
|
|
&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-09-25 23:26:48 +02: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;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
if (restart)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
|
|
"Full audit restart requested, dropping old audit data.\n");
|
|
|
|
GNUNET_break (GNUNET_OK ==
|
2019-02-14 14:23:16 +01:00
|
|
|
adb->drop_tables (adb->cls,
|
|
|
|
GNUNET_NO));
|
2017-09-25 23:26:48 +02:00
|
|
|
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));
|
|
|
|
}
|
2017-09-30 21:28:17 +02:00
|
|
|
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
|
|
|
|
NULL);
|
|
|
|
esession = edb->get_session (edb->cls);
|
|
|
|
if (NULL == esession)
|
|
|
|
{
|
|
|
|
fprintf (stderr,
|
|
|
|
"Failed to initialize exchange session.\n");
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
asession = adb->get_session (adb->cls);
|
|
|
|
if (NULL == asession)
|
|
|
|
{
|
|
|
|
fprintf (stderr,
|
|
|
|
"Failed to initialize auditor session.\n");
|
|
|
|
global_ret = 1;
|
|
|
|
GNUNET_SCHEDULER_shutdown ();
|
|
|
|
return;
|
|
|
|
}
|
2017-10-16 12:11:49 +02:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_wire_out_inconsistencies = json_array ()));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_reserve_in_inconsistencies = json_array ()));
|
2017-10-16 12:11:49 +02:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_row_minor_inconsistencies = json_array ()));
|
2017-11-20 14:20:09 +01:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_wire_format_inconsistencies = json_array ()));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_row_inconsistencies = json_array ()));
|
2017-11-08 18:44:12 +01:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_missattribution_in_inconsistencies = json_array ()));
|
2017-11-19 22:36:58 +01:00
|
|
|
GNUNET_assert (NULL !=
|
2019-08-25 16:18:24 +02:00
|
|
|
(report_lags = json_array ()));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_bad_amount_out_plus));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_bad_amount_out_minus));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_bad_amount_in_plus));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_bad_amount_in_minus));
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_missattribution_in));
|
2017-11-19 22:36:58 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_amount_lag));
|
2017-11-20 14:20:09 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&total_wire_format_amount));
|
2017-11-07 14:38:45 +01:00
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (currency,
|
|
|
|
&zero));
|
2018-04-02 14:24:45 +02:00
|
|
|
TALER_EXCHANGEDB_find_accounts (cfg,
|
|
|
|
&process_account_cb,
|
|
|
|
NULL);
|
2019-08-24 22:49:35 +02:00
|
|
|
begin_transaction ();
|
2017-09-25 23:26:48 +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-09-25 23:26:48 +02:00
|
|
|
GNUNET_GETOPT_option_flag ('r',
|
|
|
|
"restart",
|
|
|
|
"restart audit from the beginning (required on first run)",
|
|
|
|
&restart),
|
|
|
|
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-wire-auditor",
|
|
|
|
"MESSAGE",
|
|
|
|
NULL));
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
GNUNET_PROGRAM_run (argc,
|
2019-08-24 22:49:35 +02:00
|
|
|
argv,
|
2017-09-25 23:26:48 +02:00
|
|
|
"taler-wire-auditor",
|
2019-08-24 22:49:35 +02:00
|
|
|
"Audit exchange database for consistency with the bank's wire transfers",
|
|
|
|
options,
|
|
|
|
&run,
|
|
|
|
NULL))
|
2017-09-25 23:26:48 +02:00
|
|
|
return 1;
|
|
|
|
return global_ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* end of taler-wire-auditor.c */
|