diff options
Diffstat (limited to 'src/auditor/taler-helper-auditor-aggregation.c')
-rw-r--r-- | src/auditor/taler-helper-auditor-aggregation.c | 370 |
1 files changed, 230 insertions, 140 deletions
diff --git a/src/auditor/taler-helper-auditor-aggregation.c b/src/auditor/taler-helper-auditor-aggregation.c index 1c22a48b2..a0f2a190f 100644 --- a/src/auditor/taler-helper-auditor-aggregation.c +++ b/src/auditor/taler-helper-auditor-aggregation.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016-2022 Taler Systems SA + Copyright (C) 2016-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero Public License as published by the Free Software @@ -34,17 +34,26 @@ static int global_ret; /** - * Checkpointing our progress for aggregations. + * Run in test mode. Exit when idle instead of + * going to sleep and waiting for more work. + * + * FIXME: not yet implemented! */ -static struct TALER_AUDITORDB_ProgressPointAggregation ppa; +static int test_mode; /** * Checkpointing our progress for aggregations. */ -static struct TALER_AUDITORDB_ProgressPointAggregation ppa_start; +static TALER_ARL_DEF_PP (aggregation_last_wire_out_serial_id); + +/** + * Total aggregation fees (wire fees) earned. + */ +static TALER_ARL_DEF_AB (aggregation_total_wire_fee_revenue); + /** - * Array of reports about row inconsitencies. + * Array of reports about row inconsistencies. */ static json_t *report_row_inconsistencies; @@ -102,11 +111,6 @@ static struct TALER_Amount total_arithmetic_delta_plus; static struct TALER_Amount total_arithmetic_delta_minus; /** - * Total aggregation fees earned. - */ -static struct TALER_Amount total_aggregation_fee_income; - -/** * Array of reports about coin operations with bad signatures. */ static json_t *report_bad_sig_losses; @@ -398,9 +402,9 @@ check_transaction_history_for_deposit ( struct TALER_Amount expenditures; struct TALER_Amount refunds; struct TALER_Amount spent; + struct TALER_Amount *deposited = NULL; struct TALER_Amount merchant_loss; const struct TALER_Amount *deposit_fee; - bool refund_deposit_fee; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking transaction history of coin %s\n", @@ -422,24 +426,33 @@ check_transaction_history_for_deposit ( compute positive (deposit, melt) and negative (refund) values separately here, and then subtract the negative from the positive at the end (after the loops). */ - refund_deposit_fee = false; deposit_fee = NULL; for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head; NULL != tl; tl = tl->next) { - const struct TALER_Amount *amount_with_fee; const struct TALER_Amount *fee_claimed; switch (tl->type) { case TALER_EXCHANGEDB_TT_DEPOSIT: /* check wire and h_wire are consistent */ - amount_with_fee = &tl->details.deposit->amount_with_fee; /* according to exchange*/ + if (NULL != deposited) + { + TALER_ARL_report (report_row_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("table", + "deposits"), + GNUNET_JSON_pack_uint64 ("row", + tl->serial_id), + GNUNET_JSON_pack_string ("diagnostic", + "multiple deposits of the same coin into the same contract detected"))); + } + deposited = &tl->details.deposit->amount_with_fee; /* according to exchange*/ fee_claimed = &tl->details.deposit->deposit_fee; /* Fee according to exchange DB */ TALER_ARL_amount_add (&expenditures, &expenditures, - amount_with_fee); + deposited); /* Check if this deposit is within the remit of the aggregation we are investigating, if so, include it in the totals. */ if ( (0 == GNUNET_memcmp (merchant_pub, @@ -450,7 +463,7 @@ check_transaction_history_for_deposit ( struct TALER_Amount amount_without_fee; TALER_ARL_amount_subtract (&amount_without_fee, - amount_with_fee, + deposited, fee_claimed); TALER_ARL_amount_add (merchant_gain, merchant_gain, @@ -474,97 +487,155 @@ check_transaction_history_for_deposit ( } break; case TALER_EXCHANGEDB_TT_MELT: - amount_with_fee = &tl->details.melt->amount_with_fee; - fee_claimed = &tl->details.melt->melt_fee; - TALER_ARL_amount_add (&expenditures, - &expenditures, - amount_with_fee); - /* Check that the fees given in the transaction list and in dki match */ - if (0 != - TALER_amount_cmp (&issue->fees.refresh, - fee_claimed)) { - /* Disagreement in fee structure between exchange and auditor */ - report_amount_arithmetic_inconsistency ("melt fee", - 0, - fee_claimed, - &issue->fees.refresh, - 1); + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.melt->amount_with_fee; + fee_claimed = &tl->details.melt->melt_fee; + TALER_ARL_amount_add (&expenditures, + &expenditures, + amount_with_fee); + /* Check that the fees given in the transaction list and in dki match */ + if (0 != + TALER_amount_cmp (&issue->fees.refresh, + fee_claimed)) + { + /* Disagreement in fee structure between exchange and auditor */ + report_amount_arithmetic_inconsistency ("melt fee", + 0, + fee_claimed, + &issue->fees.refresh, + 1); + } + break; } - break; case TALER_EXCHANGEDB_TT_REFUND: - amount_with_fee = &tl->details.refund->refund_amount; - fee_claimed = &tl->details.refund->refund_fee; - TALER_ARL_amount_add (&refunds, - &refunds, - amount_with_fee); - TALER_ARL_amount_add (&expenditures, - &expenditures, - fee_claimed); - /* Check if this refund is within the remit of the aggregation - we are investigating, if so, include it in the totals. */ - if ( (0 == GNUNET_memcmp (merchant_pub, - &tl->details.refund->merchant_pub)) && - (0 == GNUNET_memcmp (h_contract_terms, - &tl->details.refund->h_contract_terms)) ) { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Detected applicable refund of %s\n", - TALER_amount2s (amount_with_fee)); - TALER_ARL_amount_add (&merchant_loss, - &merchant_loss, + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.refund->refund_amount; + fee_claimed = &tl->details.refund->refund_fee; + TALER_ARL_amount_add (&refunds, + &refunds, amount_with_fee); - /* If there is a refund, we give back the deposit fee */ - /* FIXME: wrong: only if this is a FULL - refund we refund the deposit fee! */ - refund_deposit_fee = true; + TALER_ARL_amount_add (&expenditures, + &expenditures, + fee_claimed); + /* Check if this refund is within the remit of the aggregation + we are investigating, if so, include it in the totals. */ + if ( (0 == GNUNET_memcmp (merchant_pub, + &tl->details.refund->merchant_pub)) && + (0 == GNUNET_memcmp (h_contract_terms, + &tl->details.refund->h_contract_terms)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Detected applicable refund of %s\n", + TALER_amount2s (amount_with_fee)); + TALER_ARL_amount_add (&merchant_loss, + &merchant_loss, + amount_with_fee); + } + /* Check that the fees given in the transaction list and in dki match */ + if (0 != + TALER_amount_cmp (&issue->fees.refund, + fee_claimed)) + { + /* Disagreement in fee structure between exchange and auditor! */ + report_amount_arithmetic_inconsistency ("refund fee", + 0, + fee_claimed, + &issue->fees.refund, + 1); + } + break; } - /* Check that the fees given in the transaction list and in dki match */ - if (0 != - TALER_amount_cmp (&issue->fees.refund, - fee_claimed)) + case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP: { - /* Disagreement in fee structure between exchange and auditor! */ - report_amount_arithmetic_inconsistency ("refund fee", - 0, - fee_claimed, - &issue->fees.refund, - 1); + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.old_coin_recoup->value; + /* We count recoups of refreshed coins like refunds for the dirty old + coin, as they equivalently _increase_ the remaining value on the + _old_ coin */ + TALER_ARL_amount_add (&refunds, + &refunds, + amount_with_fee); + break; } - break; - case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP: - amount_with_fee = &tl->details.old_coin_recoup->value; - /* We count recoups of refreshed coins like refunds for the dirty old - coin, as they equivalently _increase_ the remaining value on the - _old_ coin */ - TALER_ARL_amount_add (&refunds, - &refunds, - amount_with_fee); - break; case TALER_EXCHANGEDB_TT_RECOUP: - /* We count recoups of the coin as expenditures, as it - equivalently decreases the remaining value of the recouped coin. */ - amount_with_fee = &tl->details.recoup->value; - TALER_ARL_amount_add (&expenditures, - &expenditures, - amount_with_fee); - break; + { + const struct TALER_Amount *amount_with_fee; + + /* We count recoups of the coin as expenditures, as it + equivalently decreases the remaining value of the recouped coin. */ + amount_with_fee = &tl->details.recoup->value; + TALER_ARL_amount_add (&expenditures, + &expenditures, + amount_with_fee); + break; + } case TALER_EXCHANGEDB_TT_RECOUP_REFRESH: - /* We count recoups of the coin as expenditures, as it - equivalently decreases the remaining value of the recouped coin. */ - amount_with_fee = &tl->details.recoup_refresh->value; - TALER_ARL_amount_add (&expenditures, - &expenditures, - amount_with_fee); - break; + { + const struct TALER_Amount *amount_with_fee; + + /* We count recoups of the coin as expenditures, as it + equivalently decreases the remaining value of the recouped coin. */ + amount_with_fee = &tl->details.recoup_refresh->value; + TALER_ARL_amount_add (&expenditures, + &expenditures, + amount_with_fee); + break; + } case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT: - amount_with_fee = &tl->details.purse_deposit->amount; - if (! tl->details.purse_deposit->refunded) + { + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.purse_deposit->amount; + if (! tl->details.purse_deposit->refunded) + TALER_ARL_amount_add (&expenditures, + &expenditures, + amount_with_fee); + break; + } + + case TALER_EXCHANGEDB_TT_PURSE_REFUND: + { + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.purse_refund->refund_amount; + fee_claimed = &tl->details.purse_refund->refund_fee; + TALER_ARL_amount_add (&refunds, + &refunds, + amount_with_fee); + TALER_ARL_amount_add (&expenditures, + &expenditures, + fee_claimed); + /* Check that the fees given in the transaction list and in dki match */ + if (0 != + TALER_amount_cmp (&issue->fees.refund, + fee_claimed)) + { + /* Disagreement in fee structure between exchange and auditor! */ + report_amount_arithmetic_inconsistency ("refund fee", + 0, + fee_claimed, + &issue->fees.refund, + 1); + } + break; + } + + case TALER_EXCHANGEDB_TT_RESERVE_OPEN: + { + const struct TALER_Amount *amount_with_fee; + + amount_with_fee = &tl->details.reserve_open->coin_contribution; TALER_ARL_amount_add (&expenditures, &expenditures, amount_with_fee); - break; - } + break; + } + } /* switch (tl->type) */ } /* for 'tl' */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -574,14 +645,15 @@ check_transaction_history_for_deposit ( "Aggregation loss due to refunds is %s\n", TALER_amount2s (&merchant_loss)); *deposit_gain = *merchant_gain; - if ( (refund_deposit_fee) && - (NULL != deposit_fee) ) + if ( (NULL != deposited) && + (NULL != deposit_fee) && + (0 == TALER_amount_cmp (&refunds, + deposited)) ) { - /* We had a /deposit operation AND a /refund operation, - and should thus not charge the merchant the /deposit fee */ - /* FIXME: this is wrong, the merchant never pays either - fee, the deposit fee is simply not charged to the coin - IF there is a full refund. */ + /* We had a /deposit operation AND /refund operations adding up to the + total deposited value including deposit fee. Thus, we should not + subtract the /deposit fee from the merchant gain (as it was also + refunded). */ TALER_ARL_amount_add (merchant_gain, merchant_gain, deposit_fee); @@ -699,6 +771,7 @@ wire_transfer_information_cb ( struct TALER_CoinPublicInfo coin; enum GNUNET_DB_QueryStatus qs; struct TALER_PaytoHashP hpt; + uint64_t etag_out; TALER_payto_hash (account_pay_uri, &hpt); @@ -711,10 +784,23 @@ wire_transfer_information_cb ( "h-payto does not match payto URI"); } /* Obtain coin's transaction history */ - qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, - coin_pub, - GNUNET_YES, - &tl); + /* TODO: could use 'start' mechanism to only fetch transactions + we did not yet process, instead of going over them + again and again.*/ + + { + struct TALER_Amount balance; + struct TALER_DenominationHashP h_denom_pub; + + qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls, + coin_pub, + 0, + 0, + &etag_out, + &balance, + &h_denom_pub, + &tl); + } if ( (qs < 0) || (NULL == tl) ) { @@ -1013,8 +1099,9 @@ check_wire_out_cb (void *cls, char *method; /* should be monotonically increasing */ - GNUNET_assert (rowid >= ppa.last_wire_out_serial_id); - ppa.last_wire_out_serial_id = rowid + 1; + GNUNET_assert (rowid >= + TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id)); + TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id) = rowid + 1; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Checking wire transfer %s over %s performed on %s\n", @@ -1101,8 +1188,8 @@ check_wire_out_cb (void *cls, &wcc.total_deposits, &final_amount); /* Sum up aggregation fees (we simply include the rounding gains) */ - TALER_ARL_amount_add (&total_aggregation_fee_income, - &total_aggregation_fee_income, + TALER_ARL_amount_add (&TALER_ARL_USE_AB (aggregation_total_wire_fee_revenue), + &TALER_ARL_USE_AB (aggregation_total_wire_fee_revenue), &exchange_gain); /* Check that calculated amount matches actual amount */ @@ -1174,9 +1261,10 @@ analyze_aggregations (void *cls) (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Analyzing aggregations\n"); - qsp = TALER_ARL_adb->get_auditor_progress_aggregation (TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &ppa); + qsp = TALER_ARL_adb->get_auditor_progress ( + TALER_ARL_adb->cls, + TALER_ARL_GET_PP (aggregation_last_wire_out_serial_id), + NULL); if (0 > qsp) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp); @@ -1189,18 +1277,19 @@ analyze_aggregations (void *cls) } else { - ppa_start = ppa; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Resuming aggregation audit at %llu\n", - (unsigned long long) ppa.last_wire_out_serial_id); + (unsigned long long) TALER_ARL_USE_PP ( + aggregation_last_wire_out_serial_id)); } memset (&ac, 0, sizeof (ac)); - qsx = TALER_ARL_adb->get_wire_fee_summary (TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &total_aggregation_fee_income); + qsx = TALER_ARL_adb->get_balance ( + TALER_ARL_adb->cls, + TALER_ARL_GET_AB (aggregation_total_wire_fee_revenue), + NULL); if (0 > qsx) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); @@ -1209,7 +1298,7 @@ analyze_aggregations (void *cls) ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; qs = TALER_ARL_edb->select_wire_out_above_serial_id ( TALER_ARL_edb->cls, - ppa.last_wire_out_serial_id, + TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id), &check_wire_out_cb, &ac); if (0 > qs) @@ -1235,30 +1324,30 @@ analyze_aggregations (void *cls) return ac.qs; } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx) - ac.qs = TALER_ARL_adb->insert_wire_fee_summary ( + ac.qs = TALER_ARL_adb->insert_balance ( TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &total_aggregation_fee_income); + TALER_ARL_SET_AB (aggregation_total_wire_fee_revenue), + NULL); else - ac.qs = TALER_ARL_adb->update_wire_fee_summary ( + ac.qs = TALER_ARL_adb->update_balance ( TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &total_aggregation_fee_income); + TALER_ARL_SET_AB (aggregation_total_wire_fee_revenue), + NULL); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs); return ac.qs; } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp) - qs = TALER_ARL_adb->update_auditor_progress_aggregation ( + qs = TALER_ARL_adb->update_auditor_progress ( TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &ppa); + TALER_ARL_SET_PP (aggregation_last_wire_out_serial_id), + NULL); else - qs = TALER_ARL_adb->insert_auditor_progress_aggregation ( + qs = TALER_ARL_adb->insert_auditor_progress ( TALER_ARL_adb->cls, - &TALER_ARL_master_pub, - &ppa); + TALER_ARL_SET_PP (aggregation_last_wire_out_serial_id), + NULL); if (0 >= qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1268,7 +1357,8 @@ analyze_aggregations (void *cls) } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Concluded aggregation audit step at %llu\n", - (unsigned long long) ppa.last_wire_out_serial_id); + (unsigned long long) TALER_ARL_USE_PP ( + aggregation_last_wire_out_serial_id)); return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -1303,7 +1393,8 @@ run (void *cls, "Starting audit\n"); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_aggregation_fee_income)); + &TALER_ARL_USE_AB ( + aggregation_total_wire_fee_revenue))); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, &total_wire_out_delta_plus)); @@ -1397,14 +1488,14 @@ run (void *cls, "total_arithmetic_delta_minus", &total_arithmetic_delta_minus), TALER_JSON_pack_amount ( - "total_aggregation_fee_income", - &total_aggregation_fee_income), + "aggregation_total_wire_fee_revenue", + &TALER_ARL_USE_AB (aggregation_total_wire_fee_revenue)), GNUNET_JSON_pack_uint64 ( "start_ppa_wire_out_serial_id", - ppa_start.last_wire_out_serial_id), + 0 /* defunct */), GNUNET_JSON_pack_uint64 ( "end_ppa_wire_out_serial_id", - ppa.last_wire_out_serial_id), + TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id)), /* block #4 */ TALER_JSON_pack_time_abs_human ( "auditor_start_time", @@ -1434,11 +1525,10 @@ main (int argc, "internal", "perform checks only applicable for exchange-internal audits", &internal_checks), - GNUNET_GETOPT_option_base32_auto ('m', - "exchange-key", - "KEY", - "public key of the exchange (Crockford base32 encoded)", - &TALER_ARL_master_pub), + GNUNET_GETOPT_option_flag ('t', + "test", + "run in test mode and exit when idle", + &test_mode), GNUNET_GETOPT_option_timetravel ('T', "timetravel"), GNUNET_GETOPT_OPTION_END |