update auditor report format to capture nicely all diagnostics that may currently be generated by the auditor (closes #4962)
This commit is contained in:
parent
cc09fbbb29
commit
45c443f348
@ -108,31 +108,39 @@ compromise.
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Arithmetic problems}
|
||||||
|
|
||||||
|
This section lists cases where the arithmetic of the exchange
|
||||||
|
involving amounts disagrees with the arithmetic of the auditor.
|
||||||
|
Disagreements imply that either the exchange made a loss (sending out
|
||||||
|
too much money), or screwed a customer (and thus at least needs to fix
|
||||||
|
the financial damage done to the customer).
|
||||||
|
|
||||||
\begin{longtable}{p{1.5cm}|rl|rl|p{4cm}}
|
\begin{longtable}{p{5.5cm}|l|rl|rl}
|
||||||
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ \hline \hline
|
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
|
||||||
|
\hline \hline
|
||||||
\endfirsthead
|
\endfirsthead
|
||||||
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\ \hline \hline
|
{\bf Operation} & {\bf Table row} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline
|
||||||
\endhead
|
\endhead
|
||||||
\hline \hline
|
\hline \hline
|
||||||
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\
|
{\bf Operation} & {\bf Table row} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
|
||||||
\endfoot
|
\endfoot
|
||||||
\hline
|
\hline
|
||||||
% FIXME: replace these with the summary column adding up the amounts!
|
{\bf Total} & &
|
||||||
{\bf Reserve} & \multicolumn{2}{|c|}{ {\bf Expected}} & \multicolumn{2}{|c|}{ {\bf Observed}} & {\bf Diagnostic} \\
|
{{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} &
|
||||||
\caption{Reserve inconsistencies.}
|
{{ data.total_arithmetic_delta_plus.currency }} &
|
||||||
\label{table:reserve:inconsistencies}
|
{{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} &
|
||||||
|
{{ data.total_arithmetic_delta_minus.currency }} &
|
||||||
|
\caption{Arithmetic inconsistencies.}
|
||||||
|
\label{table:amount:arithmetic:inconsistencies}
|
||||||
\endlastfoot
|
\endlastfoot
|
||||||
{% for item in data.reserve_inconsistencies %}
|
{% for item in data.amount_arithmetic_inconsistencies %}
|
||||||
\multicolumn{6}{l}{ {\tt {{ item.reserve_pub }} } } \\
|
{{ item.operation }} &
|
||||||
\nopagebreak
|
{{ item.rowid }} &
|
||||||
&
|
{{ item.exchange.value }}.{{ item.exchange.fraction }} &
|
||||||
{{ item.expected.value }}.{{ item.expected.fraction }} &
|
{{ item.exchange.currency }} &
|
||||||
{{ item.expected.currency }} &
|
{{ item.auditor.value }}.{{ item.auditor.fraction }} &
|
||||||
{{ item.observed.value }}.{{ item.observed.fraction }} &
|
{{ item.auditor.currency }} \\ \hline
|
||||||
{{ item.observed.currency }} &
|
|
||||||
{{ item.diagnostic }} \\ \hline
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
\end{longtable}
|
\end{longtable}
|
||||||
|
|
||||||
@ -172,7 +180,7 @@ compromise resulting in proportional financial losses to the exchange.
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
\subsection{Claimed outgoing wire transfers}
|
\subsection{Claimed outgoing wire transfer inconsistencies}
|
||||||
|
|
||||||
This section is about the exchange's database containing a
|
This section is about the exchange's database containing a
|
||||||
justification to make an outgoing wire transfer for an aggregated
|
justification to make an outgoing wire transfer for an aggregated
|
||||||
@ -217,7 +225,88 @@ would be reported separately in Section~\ref{sec:wire_check_out}.
|
|||||||
|
|
||||||
\subsection{Coin history inconsistencies}
|
\subsection{Coin history inconsistencies}
|
||||||
|
|
||||||
TODO.
|
This section lists cases where the exchange made arithmetic errors found when
|
||||||
|
looking at the transaction history of a coin. The totals sum up the differences
|
||||||
|
in amounts that matter for profit/loss calculations of the exchange. When an
|
||||||
|
exchange merely shifted money from customers to merchants (or vice versa) without
|
||||||
|
any effects on its own balance, those entries are excluded from the total.
|
||||||
|
|
||||||
|
{% if data.coin_inconsistencies|length() == 0 %}
|
||||||
|
{\bf All coin histories were unproblematic.}
|
||||||
|
{% else %}
|
||||||
|
\begin{longtable}{l|p{5.5cm}|rl|rl}
|
||||||
|
{\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
|
||||||
|
\hline \hline
|
||||||
|
\endfirsthead
|
||||||
|
{\bf Operation} & {\bf Coin public key} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline
|
||||||
|
\endhead
|
||||||
|
\hline \hline
|
||||||
|
{\bf Operation} & {\bf Coin public key} & \ \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\
|
||||||
|
\endfoot
|
||||||
|
\hline
|
||||||
|
{\bf Total} & &
|
||||||
|
{{ data.total_coin_delta_plus.value }}.{{ data.total_coin_delta_plus.fraction }} &
|
||||||
|
{{ data.total_coin_delta_plus.currency }} &
|
||||||
|
- {{ data.total_coin_delta_minus.value }}.{{ data.total_coin_delta_minus.fraction }} &
|
||||||
|
{{ data.total_coin_delta_minus.currency }} &
|
||||||
|
\caption{Arithmetic inconsistencies of amount calculations involving a coin.}
|
||||||
|
\label{table:amount:arithmetic:coin:inconsistencies}
|
||||||
|
\endlastfoot
|
||||||
|
{% for item in data.coin_inconsistencies %}
|
||||||
|
{{ item.operation }} &
|
||||||
|
\multicolumn{5}{l}{ {\tt {{ item.coin_pub }} } } \\
|
||||||
|
\nopagebreak & &
|
||||||
|
{{ item.exchange.value }}.{{ item.exchange.fraction }} &
|
||||||
|
{{ item.exchange.currency }} &
|
||||||
|
{{ item.auditor.value }}.{{ item.auditor.fraction }} &
|
||||||
|
{{ item.auditor.currency }} \\ \hline
|
||||||
|
{% endfor %}
|
||||||
|
\end{longtable}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Operations with bad signatures}
|
||||||
|
|
||||||
|
This section lists operations that the exchange performed, but for
|
||||||
|
which the signatures provided are invalid. Hence the operations were
|
||||||
|
invalid and the amount involved should be considered lost.
|
||||||
|
|
||||||
|
The key given is always the key for which the signature verification
|
||||||
|
step failed. This is the reserve public key for ``withdraw''
|
||||||
|
operations, the coin public key for ``deposit'' and ``melt''
|
||||||
|
operations, the merchant's public key for ``melt'' operations,
|
||||||
|
the (hash of the) denomination public key for
|
||||||
|
``payback-verify'' and ``deposit-verify'' operations, and the master
|
||||||
|
public key for ``payback-master'' operations.
|
||||||
|
|
||||||
|
{% if data.reserve_wire_out_inconsistencies|length() == 0 %}
|
||||||
|
{\bf All signatures were valid.}
|
||||||
|
{% else %}
|
||||||
|
\begin{longtable}{p{1.5cm}|c|l|rl}
|
||||||
|
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\
|
||||||
|
\hline \hline
|
||||||
|
\endfirsthead
|
||||||
|
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\ \hline \hline
|
||||||
|
\endhead
|
||||||
|
\hline \hline
|
||||||
|
{\bf Public key} & {\bf Operation type} & Database row & \multicolumn{2}{|c|}{ {\bf Loss amount}} \\
|
||||||
|
\endfoot
|
||||||
|
\hline
|
||||||
|
{\bf Total losses} & & &
|
||||||
|
{{ data.total_bad_sig_loss.value}}.{{ data.total_bad_sig_loss.fraction}} & {{ data.total_bad_sig_loss.currency}} \\
|
||||||
|
\caption{Losses from operations performed on coins without proper signatures.}
|
||||||
|
\label{table:bad_signature_losses}
|
||||||
|
\endlastfoot
|
||||||
|
{% for item in data.bad_sig_losses %}
|
||||||
|
\multicolumn{5}{l}{ {\tt {{ item.key_pub }} } } \\
|
||||||
|
\nopagebreak
|
||||||
|
& {{ item.operation }} & {{ item.rowid }} &
|
||||||
|
{{ item.loss.value }}.{{ item.loss.fraction }} &
|
||||||
|
{{ item.loss.currency }} \\ \hline
|
||||||
|
{% endfor %}
|
||||||
|
\end{longtable}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
\subsection{Actual incoming wire transfers}
|
\subsection{Actual incoming wire transfers}
|
||||||
@ -269,6 +358,38 @@ translate into a financial loss (yet).
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
\subsection{Other issues}
|
||||||
|
|
||||||
|
This section describes issues found that do not have a clear financial
|
||||||
|
impact.
|
||||||
|
|
||||||
|
{% if data.row_inconsistencies|length() == 0 %}
|
||||||
|
{\bf No row inconsistencies found.}
|
||||||
|
{% else %}
|
||||||
|
\begin{longtable}{p{1.5cm}|l|p{5.5}}
|
||||||
|
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\
|
||||||
|
\hline \hline
|
||||||
|
\endfirsthead
|
||||||
|
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\
|
||||||
|
\hline \hline
|
||||||
|
\endhead
|
||||||
|
\hline \hline
|
||||||
|
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\
|
||||||
|
\endfoot
|
||||||
|
\hline
|
||||||
|
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\
|
||||||
|
\caption{Other issues found (by table and row).}
|
||||||
|
\label{table:misc}
|
||||||
|
\endlastfoot
|
||||||
|
{% for item in data.row_inconsistencies %}
|
||||||
|
{{ item.table }} &
|
||||||
|
{{ item.row }} &
|
||||||
|
{{ item.diagnostic }} \\ \hline
|
||||||
|
{% endfor %}
|
||||||
|
\end{longtable}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
\section{Delays and timing}
|
\section{Delays and timing}
|
||||||
|
|
||||||
This section describes issues that are likely caused simply by
|
This section describes issues that are likely caused simply by
|
||||||
|
@ -189,11 +189,37 @@ static struct TALER_Amount total_wire_out_delta_minus;
|
|||||||
*/
|
*/
|
||||||
static json_t *report_coin_inconsistencies;
|
static json_t *report_coin_inconsistencies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profits the exchange made by bad amount calculations on coins.
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount total_coin_delta_plus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Losses the exchange made by bad amount calculations on coins.
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount total_coin_delta_minus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report about aggregate wire transfer fee profits.
|
* Report about aggregate wire transfer fee profits.
|
||||||
*/
|
*/
|
||||||
static json_t *report_aggregation_fee_balances;
|
static json_t *report_aggregation_fee_balances;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report about amount calculation differences (causing profit
|
||||||
|
* or loss at the exchange).
|
||||||
|
*/
|
||||||
|
static json_t *report_amount_arithmetic_inconsistencies;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Profits the exchange made by bad amount calculations.
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount total_arithmetic_delta_plus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Losses the exchange made by bad amount calculations.
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount total_arithmetic_delta_minus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total amount reported in all calls to #report_emergency().
|
* Total amount reported in all calls to #report_emergency().
|
||||||
*/
|
*/
|
||||||
@ -234,6 +260,15 @@ static struct TALER_Amount total_refund_fee_income;
|
|||||||
*/
|
*/
|
||||||
static struct TALER_Amount total_aggregation_fee_income;
|
static struct TALER_Amount total_aggregation_fee_income;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of reports about coin operations with bad signatures.
|
||||||
|
*/
|
||||||
|
static json_t *report_bad_sig_losses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total amount lost by operations for which signatures were invalid.
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount total_bad_sig_loss;
|
||||||
|
|
||||||
|
|
||||||
/* ***************************** Report logic **************************** */
|
/* ***************************** Report logic **************************** */
|
||||||
@ -290,6 +325,128 @@ report_emergency (const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report a (serious) inconsistency in the exchange's database.
|
* Report a (serious) inconsistency in the exchange's database.
|
||||||
*
|
*
|
||||||
@ -310,33 +467,6 @@ report_row_inconsistency (const char *table,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Report a global inconsistency with respect to a coin's history.
|
|
||||||
*
|
|
||||||
* @param coin_pub the affected coin
|
|
||||||
* @param expected expected amount
|
|
||||||
* @param observed observed amount
|
|
||||||
* @param diagnostic message explaining what @a expected and @a observed refer to
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
report_coin_inconsistency (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|
||||||
const struct TALER_Amount *expected,
|
|
||||||
const struct TALER_Amount *observed,
|
|
||||||
const char *diagnostic)
|
|
||||||
{
|
|
||||||
report (report_coin_inconsistencies,
|
|
||||||
json_pack ("{s:o, s:o, s:o, s:s}",
|
|
||||||
"coin_pub",
|
|
||||||
GNUNET_JSON_from_data_auto (coin_pub),
|
|
||||||
"expected",
|
|
||||||
TALER_JSON_from_amount (expected),
|
|
||||||
"observed",
|
|
||||||
TALER_JSON_from_amount (observed),
|
|
||||||
"diagnostic",
|
|
||||||
diagnostic));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ************************* Transaction-global state ************************ */
|
/* ************************* Transaction-global state ************************ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -765,9 +895,16 @@ handle_reserve_out (void *cls,
|
|||||||
&reserve_sig->eddsa_signature,
|
&reserve_sig->eddsa_signature,
|
||||||
&reserve_pub->eddsa_pub))
|
&reserve_pub->eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("withdraw",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"invalid signature for withdrawal");
|
"operation", "withdraw",
|
||||||
|
"row", (json_int_t) rowid,
|
||||||
|
"loss", TALER_JSON_from_amount (amount_with_fee),
|
||||||
|
"key_pub", GNUNET_JSON_from_data_auto (reserve_pub)));
|
||||||
|
GNUNET_break (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&total_bad_sig_loss,
|
||||||
|
&total_bad_sig_loss,
|
||||||
|
amount_with_fee));
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -860,19 +997,26 @@ handle_payback_by_reserve (void *cls,
|
|||||||
/* should be monotonically increasing */
|
/* should be monotonically increasing */
|
||||||
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
|
GNUNET_assert (rowid >= pp.last_reserve_payback_serial_id);
|
||||||
pp.last_reserve_payback_serial_id = rowid + 1;
|
pp.last_reserve_payback_serial_id = rowid + 1;
|
||||||
|
GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
|
||||||
|
&pr.h_denom_pub);
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_test_coin_valid (coin))
|
TALER_test_coin_valid (coin))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("payback",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"coin denomination signature invalid");
|
"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));
|
||||||
}
|
}
|
||||||
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK);
|
pr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_PAYBACK);
|
||||||
pr.purpose.size = htonl (sizeof (pr));
|
pr.purpose.size = htonl (sizeof (pr));
|
||||||
pr.coin_pub = coin->coin_pub;
|
pr.coin_pub = coin->coin_pub;
|
||||||
GNUNET_CRYPTO_rsa_public_key_hash (coin->denom_pub.rsa_public_key,
|
|
||||||
&pr.h_denom_pub);
|
|
||||||
pr.coin_blind = *coin_blind;
|
pr.coin_blind = *coin_blind;
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK,
|
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_PAYBACK,
|
||||||
@ -880,9 +1024,16 @@ handle_payback_by_reserve (void *cls,
|
|||||||
&coin_sig->eddsa_signature,
|
&coin_sig->eddsa_signature,
|
||||||
&coin->coin_pub.eddsa_pub))
|
&coin->coin_pub.eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("payback",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"coin payback signature invalid");
|
"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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check that the coin was eligible for payback!*/
|
/* check that the coin was eligible for payback!*/
|
||||||
@ -921,9 +1072,6 @@ handle_payback_by_reserve (void *cls,
|
|||||||
&msig.eddsa_signature,
|
&msig.eddsa_signature,
|
||||||
&master_pub.eddsa_pub))
|
&master_pub.eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("denomination_revocations",
|
|
||||||
rev_rowid,
|
|
||||||
"master signature invalid");
|
|
||||||
rev = "master signature invalid";
|
rev = "master signature invalid";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -937,6 +1085,19 @@ handle_payback_by_reserve (void *cls,
|
|||||||
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
|
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
GNUNET_CRYPTO_hash (reserve_pub,
|
GNUNET_CRYPTO_hash (reserve_pub,
|
||||||
sizeof (*reserve_pub),
|
sizeof (*reserve_pub),
|
||||||
@ -1833,10 +1994,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|||||||
&refunds))
|
&refunds))
|
||||||
{
|
{
|
||||||
/* refunds above expenditures? Bad! */
|
/* refunds above expenditures? Bad! */
|
||||||
report_coin_inconsistency (coin_pub,
|
report_coin_arithmetic_inconsistency ("refund (balance)",
|
||||||
|
coin_pub,
|
||||||
&expenditures,
|
&expenditures,
|
||||||
&refunds,
|
&refunds,
|
||||||
"could not subtract refunded amount from expenditures");
|
0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1847,10 +2009,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|||||||
&value))
|
&value))
|
||||||
{
|
{
|
||||||
/* spent > value */
|
/* spent > value */
|
||||||
report_coin_inconsistency (coin_pub,
|
report_coin_arithmetic_inconsistency ("spend",
|
||||||
|
coin_pub,
|
||||||
&spent,
|
&spent,
|
||||||
&value,
|
&value,
|
||||||
"accepted deposits (minus refunds) exceeds denomination value");
|
-1);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1861,10 +2024,11 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
|||||||
&merchant_loss))
|
&merchant_loss))
|
||||||
{
|
{
|
||||||
/* refunds above deposits? Bad! */
|
/* refunds above deposits? Bad! */
|
||||||
report_coin_inconsistency (coin_pub,
|
report_coin_arithmetic_inconsistency ("refund (merchant)",
|
||||||
|
coin_pub,
|
||||||
merchant_gain,
|
merchant_gain,
|
||||||
&merchant_loss,
|
&merchant_loss,
|
||||||
"merchant was granted more refunds than he deposited");
|
0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
@ -1905,7 +2069,6 @@ wire_transfer_information_cb (void *cls,
|
|||||||
{
|
{
|
||||||
struct WireCheckContext *wcc = cls;
|
struct WireCheckContext *wcc = cls;
|
||||||
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
||||||
struct TALER_Amount contribution;
|
|
||||||
struct TALER_Amount computed_value;
|
struct TALER_Amount computed_value;
|
||||||
struct TALER_Amount computed_fees;
|
struct TALER_Amount computed_fees;
|
||||||
struct TALER_Amount coin_value_without_fee;
|
struct TALER_Amount coin_value_without_fee;
|
||||||
@ -1977,9 +2140,11 @@ wire_transfer_information_cb (void *cls,
|
|||||||
coin_fee))
|
coin_fee))
|
||||||
{
|
{
|
||||||
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
report_row_inconsistency ("aggregation",
|
report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
|
||||||
rowid,
|
rowid,
|
||||||
"inconsistent coin value and fee claimed in aggregation");
|
coin_value,
|
||||||
|
coin_fee,
|
||||||
|
-1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (0 !=
|
if (0 !=
|
||||||
@ -1987,18 +2152,22 @@ wire_transfer_information_cb (void *cls,
|
|||||||
&coin_value_without_fee))
|
&coin_value_without_fee))
|
||||||
{
|
{
|
||||||
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
report_row_inconsistency ("aggregation",
|
report_amount_arithmetic_inconsistency ("aggregation (contribution)",
|
||||||
rowid,
|
rowid,
|
||||||
"coin transaction history and aggregation disagree about coin's contribution");
|
&coin_value_without_fee,
|
||||||
|
&computed_value,
|
||||||
|
-1);
|
||||||
}
|
}
|
||||||
if (0 !=
|
if (0 !=
|
||||||
TALER_amount_cmp (&computed_fees,
|
TALER_amount_cmp (&computed_fees,
|
||||||
coin_fee))
|
coin_fee))
|
||||||
{
|
{
|
||||||
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
report_row_inconsistency ("aggregation",
|
report_amount_arithmetic_inconsistency ("aggregation (fee)",
|
||||||
rowid,
|
rowid,
|
||||||
"coin transaction history and aggregation disagree about applicable fees");
|
coin_fee,
|
||||||
|
&computed_fees,
|
||||||
|
1);
|
||||||
}
|
}
|
||||||
edb->free_coin_transaction_list (edb->cls,
|
edb->free_coin_transaction_list (edb->cls,
|
||||||
tl);
|
tl);
|
||||||
@ -2032,23 +2201,12 @@ wire_transfer_information_cb (void *cls,
|
|||||||
"date given in aggregate does not match wire transfer date");
|
"date given in aggregate does not match wire transfer date");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (GNUNET_SYSERR ==
|
|
||||||
TALER_amount_subtract (&contribution,
|
|
||||||
coin_value,
|
|
||||||
coin_fee))
|
|
||||||
{
|
|
||||||
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
report_row_inconsistency ("aggregation",
|
|
||||||
rowid,
|
|
||||||
"could not calculate contribution of coin");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add coin's contribution to total aggregate value */
|
/* Add coin's contribution to total aggregate value */
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_amount_add (&wcc->total_deposits,
|
TALER_amount_add (&wcc->total_deposits,
|
||||||
&wcc->total_deposits,
|
&wcc->total_deposits,
|
||||||
&contribution))
|
&coin_value_without_fee))
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
@ -2229,6 +2387,7 @@ check_wire_out_cb (void *cls,
|
|||||||
}
|
}
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
|
||||||
{
|
{
|
||||||
|
/* FIXME: can we provide a more detailed error report? */
|
||||||
report_row_inconsistency ("wire_out",
|
report_row_inconsistency ("wire_out",
|
||||||
rowid,
|
rowid,
|
||||||
"audit of associated transactions failed");
|
"audit of associated transactions failed");
|
||||||
@ -2250,9 +2409,11 @@ check_wire_out_cb (void *cls,
|
|||||||
&wcc.total_deposits,
|
&wcc.total_deposits,
|
||||||
wire_fee))
|
wire_fee))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("wire_out",
|
report_amount_arithmetic_inconsistency ("wire out (fee structure)",
|
||||||
rowid,
|
rowid,
|
||||||
"could not subtract wire fee from total amount");
|
&wcc.total_deposits,
|
||||||
|
wire_fee,
|
||||||
|
-1);
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2852,9 +3013,16 @@ refresh_session_cb (void *cls,
|
|||||||
&coin_sig->eddsa_signature,
|
&coin_sig->eddsa_signature,
|
||||||
&coin_pub->eddsa_pub))
|
&coin_pub->eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("melt",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"invalid signature for coin melt");
|
"operation", "melt",
|
||||||
|
"row", (json_int_t) rowid,
|
||||||
|
"loss", TALER_JSON_from_amount (amount_with_fee),
|
||||||
|
"key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
|
||||||
|
GNUNET_break (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&total_bad_sig_loss,
|
||||||
|
&total_bad_sig_loss,
|
||||||
|
amount_with_fee));
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
@ -2952,9 +3120,11 @@ refresh_session_cb (void *cls,
|
|||||||
&amount_without_fee))
|
&amount_without_fee))
|
||||||
{
|
{
|
||||||
/* refresh_cost > amount_without_fee */
|
/* refresh_cost > amount_without_fee */
|
||||||
report_row_inconsistency ("melt",
|
report_amount_arithmetic_inconsistency ("melt (fee)",
|
||||||
rowid,
|
rowid,
|
||||||
"refresh costs exceed value of melt");
|
&amount_without_fee,
|
||||||
|
&refresh_cost,
|
||||||
|
-1);
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3160,9 +3330,16 @@ deposit_cb (void *cls,
|
|||||||
&coin_sig->eddsa_signature,
|
&coin_sig->eddsa_signature,
|
||||||
&coin_pub->eddsa_pub))
|
&coin_pub->eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("deposit",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"invalid signature for coin deposit");
|
"operation", "deposit",
|
||||||
|
"row", (json_int_t) rowid,
|
||||||
|
"loss", TALER_JSON_from_amount (amount_with_fee),
|
||||||
|
"key_pub", GNUNET_JSON_from_data_auto (coin_pub)));
|
||||||
|
GNUNET_break (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&total_bad_sig_loss,
|
||||||
|
&total_bad_sig_loss,
|
||||||
|
amount_with_fee));
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
@ -3293,9 +3470,16 @@ refund_cb (void *cls,
|
|||||||
&merchant_sig->eddsa_sig,
|
&merchant_sig->eddsa_sig,
|
||||||
&merchant_pub->eddsa_pub))
|
&merchant_pub->eddsa_pub))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("refund",
|
report (report_bad_sig_losses,
|
||||||
rowid,
|
json_pack ("{s:s, s:I, s:o, s:o}",
|
||||||
"invalid signature for refund");
|
"operation", "refund",
|
||||||
|
"row", (json_int_t) rowid,
|
||||||
|
"loss", TALER_JSON_from_amount (amount_with_fee),
|
||||||
|
"key_pub", GNUNET_JSON_from_data_auto (merchant_pub)));
|
||||||
|
GNUNET_break (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&total_bad_sig_loss,
|
||||||
|
&total_bad_sig_loss,
|
||||||
|
amount_with_fee));
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3306,9 +3490,11 @@ refund_cb (void *cls,
|
|||||||
amount_with_fee,
|
amount_with_fee,
|
||||||
&refund_fee))
|
&refund_fee))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("refund",
|
report_amount_arithmetic_inconsistency ("refund (fee)",
|
||||||
rowid,
|
rowid,
|
||||||
"refunded amount smaller than refund fee");
|
&amount_without_fee,
|
||||||
|
&refund_fee,
|
||||||
|
-1);
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3823,9 +4009,24 @@ run (void *cls,
|
|||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
&total_wire_out_delta_minus));
|
&total_wire_out_delta_minus));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_arithmetic_delta_plus));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_arithmetic_delta_minus));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_coin_delta_plus));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_coin_delta_minus));
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
&total_balance_reserve_not_closed));
|
&total_balance_reserve_not_closed));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&total_bad_sig_loss));
|
||||||
GNUNET_assert (NULL !=
|
GNUNET_assert (NULL !=
|
||||||
(report_emergencies = json_array ()));
|
(report_emergencies = json_array ()));
|
||||||
GNUNET_assert (NULL !=
|
GNUNET_assert (NULL !=
|
||||||
@ -3844,6 +4045,10 @@ run (void *cls,
|
|||||||
(report_coin_inconsistencies = json_array ()));
|
(report_coin_inconsistencies = json_array ()));
|
||||||
GNUNET_assert (NULL !=
|
GNUNET_assert (NULL !=
|
||||||
(report_aggregation_fee_balances = json_array ()));
|
(report_aggregation_fee_balances = json_array ()));
|
||||||
|
GNUNET_assert (NULL !=
|
||||||
|
(report_amount_arithmetic_inconsistencies = json_array ()));
|
||||||
|
GNUNET_assert (NULL !=
|
||||||
|
(report_bad_sig_losses = json_array ()));
|
||||||
setup_sessions_and_run ();
|
setup_sessions_and_run ();
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
"Audit complete\n");
|
"Audit complete\n");
|
||||||
@ -3866,7 +4071,9 @@ run (void *cls,
|
|||||||
" s:o, s:o, s:o, s:o, s:o,"
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
" s:o, s:o, s:o, s:o, s:o,"
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
" s:o, s:o, s:o, s:o, s:o,"
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
" s:o, s:o, s:o }",
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
|
" s:o}",
|
||||||
/* blocks of 5 for easier counting/matching to format string */
|
/* blocks of 5 for easier counting/matching to format string */
|
||||||
/* block */
|
/* block */
|
||||||
"reserve_balance_insufficient_inconsistencies",
|
"reserve_balance_insufficient_inconsistencies",
|
||||||
@ -3911,12 +4118,28 @@ run (void *cls,
|
|||||||
"total_wire_out_delta_minus",
|
"total_wire_out_delta_minus",
|
||||||
TALER_JSON_from_amount (&total_wire_out_delta_minus),
|
TALER_JSON_from_amount (&total_wire_out_delta_minus),
|
||||||
/* block */
|
/* block */
|
||||||
|
"bad_sig_losses",
|
||||||
|
report_bad_sig_losses,
|
||||||
|
"total_bad_sig_loss",
|
||||||
|
TALER_JSON_from_amount (&total_bad_sig_loss),
|
||||||
"row_inconsistencies",
|
"row_inconsistencies",
|
||||||
report_row_inconsistencies,
|
report_row_inconsistencies,
|
||||||
"denomination_key_validity_withdraw_inconsistencies",
|
"denomination_key_validity_withdraw_inconsistencies",
|
||||||
denomination_key_validity_withdraw_inconsistencies,
|
denomination_key_validity_withdraw_inconsistencies,
|
||||||
"coin_inconsistencies",
|
"coin_inconsistencies",
|
||||||
report_coin_inconsistencies,
|
report_coin_inconsistencies,
|
||||||
|
/* block */
|
||||||
|
"total_coin_delta_plus",
|
||||||
|
TALER_JSON_from_amount (&total_coin_delta_plus),
|
||||||
|
"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),
|
||||||
|
/* block */
|
||||||
"total_aggregation_fee_income",
|
"total_aggregation_fee_income",
|
||||||
TALER_JSON_from_amount (&total_aggregation_fee_income)
|
TALER_JSON_from_amount (&total_aggregation_fee_income)
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user