diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-10-30 17:36:57 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-10-30 17:36:57 +0100 |
commit | 2d55647f2aab1feb37d5439049f6824d11cda56c (patch) | |
tree | aef525c04a030b318a97b9d8ae77573ca73a8237 /src/auditor | |
parent | 38a078d543f53ca4925ea364919306395e7e4597 (diff) |
add support for reserve open/close operations to auditor, begin to split off purse auditing logic
Diffstat (limited to 'src/auditor')
-rw-r--r-- | src/auditor/Makefile.am | 41 | ||||
-rw-r--r-- | src/auditor/taler-auditor-sync.c | 2 | ||||
-rw-r--r-- | src/auditor/taler-auditor.in | 1 | ||||
-rw-r--r-- | src/auditor/taler-helper-auditor-coins.c | 402 | ||||
-rw-r--r-- | src/auditor/taler-helper-auditor-purses.c | 1123 | ||||
-rw-r--r-- | src/auditor/taler-helper-auditor-render.py | 12 | ||||
-rw-r--r-- | src/auditor/taler-helper-auditor-reserves.c | 1258 | ||||
-rw-r--r-- | src/auditor/taler-helper-auditor-wire.c | 30 | ||||
-rwxr-xr-x | src/auditor/test-auditor.sh | 18 | ||||
-rwxr-xr-x | src/auditor/test-revocation.sh | 4 |
10 files changed, 1967 insertions, 924 deletions
diff --git a/src/auditor/Makefile.am b/src/auditor/Makefile.am index 6eaff39fc..34f6334e8 100644 --- a/src/auditor/Makefile.am +++ b/src/auditor/Makefile.am @@ -65,9 +65,9 @@ taler_auditor_dbinit_CPPFLAGS = \ -I$(top_srcdir)/src/pq/ \ $(POSTGRESQL_CPPFLAGS) -taler_helper_auditor_reserves_SOURCES = \ - taler-helper-auditor-reserves.c -taler_helper_auditor_reserves_LDADD = \ +taler_helper_auditor_coins_SOURCES = \ + taler-helper-auditor-coins.c +taler_helper_auditor_coins_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/json/libtalerjson.la \ @@ -80,9 +80,9 @@ taler_helper_auditor_reserves_LDADD = \ -lgnunetutil \ $(XLIB) -taler_helper_auditor_coins_SOURCES = \ - taler-helper-auditor-coins.c -taler_helper_auditor_coins_LDADD = \ +taler_helper_auditor_aggregation_SOURCES = \ + taler-helper-auditor-aggregation.c +taler_helper_auditor_aggregation_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/json/libtalerjson.la \ @@ -95,9 +95,9 @@ taler_helper_auditor_coins_LDADD = \ -lgnunetutil \ $(XLIB) -taler_helper_auditor_aggregation_SOURCES = \ - taler-helper-auditor-aggregation.c -taler_helper_auditor_aggregation_LDADD = \ +taler_helper_auditor_deposits_SOURCES = \ + taler-helper-auditor-deposits.c +taler_helper_auditor_deposits_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/json/libtalerjson.la \ @@ -110,9 +110,9 @@ taler_helper_auditor_aggregation_LDADD = \ -lgnunetutil \ $(XLIB) -taler_helper_auditor_deposits_SOURCES = \ - taler-helper-auditor-deposits.c -taler_helper_auditor_deposits_LDADD = \ +taler_helper_auditor_purses_SOURCES = \ + taler-helper-auditor-purses.c +taler_helper_auditor_purses_LDADD = \ $(LIBGCRYPT_LIBS) \ $(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/json/libtalerjson.la \ @@ -125,6 +125,23 @@ taler_helper_auditor_deposits_LDADD = \ -lgnunetutil \ $(XLIB) +taler_helper_auditor_reserves_SOURCES = \ + taler-helper-auditor-reserves.c +taler_helper_auditor_reserves_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/bank-lib/libtalerbank.la \ + $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ + $(top_builddir)/src/auditordb/libtalerauditordb.la \ + libauditorreport.la \ + -ljansson \ + -lgnunetjson \ + -lgnunetutil \ + $(XLIB) + + + taler_helper_auditor_wire_SOURCES = \ taler-helper-auditor-wire.c taler_helper_auditor_wire_LDADD = \ diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c index 803328f2e..3c4c7e4c7 100644 --- a/src/auditor/taler-auditor-sync.c +++ b/src/auditor/taler-auditor-sync.c @@ -119,7 +119,7 @@ static struct Table tables[] = { { .rt = TALER_EXCHANGEDB_RT_EXTENSIONS}, { .rt = TALER_EXCHANGEDB_RT_EXTENSION_DETAILS }, { .rt = TALER_EXCHANGEDB_RT_PURSE_REQUESTS}, - { .rt = TALER_EXCHANGEDB_RT_PURSE_REFUNDS}, + { .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION}, { .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES}, { .rt = TALER_EXCHANGEDB_RT_PURSE_DEPOSITS}, { .rt = TALER_EXCHANGEDB_RT_ACCOUNT_MERGES}, diff --git a/src/auditor/taler-auditor.in b/src/auditor/taler-auditor.in index 6a8e88daa..e883acb44 100644 --- a/src/auditor/taler-auditor.in +++ b/src/auditor/taler-auditor.in @@ -91,6 +91,7 @@ done taler-helper-auditor-wire $INF ${ARGS[*]} > ${DIR}/wire.json +echo "Generating auditor report in ${DIR}." taler-helper-auditor-render.py \ ${DIR}/aggregation.json \ ${DIR}/coins.json \ diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c index 23d543dcb..f263e5732 100644 --- a/src/auditor/taler-helper-auditor-coins.c +++ b/src/auditor/taler-helper-auditor-coins.c @@ -113,39 +113,9 @@ static struct TALER_Amount reported_emergency_loss; static struct TALER_Amount reported_emergency_loss_by_count; /** - * Expected balance in the escrow account. + * Global coin balance sheet (for coins). */ -static struct TALER_Amount total_escrow_balance; - -/** - * Active risk exposure. - */ -static struct TALER_Amount total_risk; - -/** - * Actualized risk (= loss) from recoups. - */ -static struct TALER_Amount total_recoup_loss; - -/** - * Recoups we made on denominations that were not revoked (!?). - */ -static struct TALER_Amount total_irregular_recoups; - -/** - * Total deposit fees earned. - */ -static struct TALER_Amount total_deposit_fee_income; - -/** - * Total melt fees earned. - */ -static struct TALER_Amount total_melt_fee_income; - -/** - * Total refund fees earned. - */ -static struct TALER_Amount total_refund_fee_income; +static struct TALER_AUDITORDB_GlobalCoinBalance balance; /** * Array of reports about coin operations with bad signatures. @@ -153,11 +123,6 @@ static struct TALER_Amount total_refund_fee_income; 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; - -/** * Array of refresh transactions where the /refresh/reveal has not yet * happened (and may of course never happen). */ @@ -605,33 +570,9 @@ check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, struct DenominationSummary { /** - * Total value of outstanding (not deposited) coins issued with this - * denomination key. - */ - struct TALER_Amount denom_balance; - - /** - * Total losses made (once coins deposited exceed - * coins withdrawn and thus the @e denom_balance is - * effectively negative). + * Information about the circulation. */ - struct TALER_Amount denom_loss; - - /** - * Total value of coins issued with this denomination key. - */ - struct TALER_Amount denom_risk; - - /** - * Total value of coins subjected to recoup with this denomination key. - */ - struct TALER_Amount denom_recoup; - - /** - * How many coins (not their amount!) of this denomination - * did the exchange issue overall? - */ - uint64_t num_issued; + struct TALER_AUDITORDB_DenominationCirculationData dcd; /** * Denomination key information for this denomination. @@ -694,11 +635,7 @@ init_denomination (const struct TALER_DenominationHashP *denom_hash, qs = TALER_ARL_adb->get_denomination_balance (TALER_ARL_adb->cls, denom_hash, - &ds->denom_balance, - &ds->denom_loss, - &ds->denom_risk, - &ds->denom_recoup, - &ds->num_issued); + &ds->dcd); if (0 > qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); @@ -712,22 +649,22 @@ init_denomination (const struct TALER_DenominationHashP *denom_hash, { GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &ds->denom_balance)); + &ds->dcd.denom_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &ds->denom_loss)); + &ds->dcd.denom_loss)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &ds->denom_risk)); + &ds->dcd.denom_risk)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &ds->denom_recoup)); + &ds->dcd.recoup_loss)); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting balance for denomination `%s' is %s (%llu)\n", GNUNET_h2s (&denom_hash->hash), - TALER_amount2s (&ds->denom_balance), - (unsigned long long) ds->num_issued); + TALER_amount2s (&ds->dcd.denom_balance), + (unsigned long long) ds->dcd.num_issued); qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls, denom_hash, &msig, @@ -842,15 +779,14 @@ sync_denomination (void *cls, else qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && - ( (0 != ds->denom_risk.value) || - (0 != ds->denom_risk.fraction) ) ) + (! TALER_amount_is_zero (&ds->dcd.denom_risk)) ) { /* The denomination expired and carried a balance; we can now book the remaining balance as profit, and reduce our risk exposure by the accumulated risk of the denomination. */ - TALER_ARL_amount_subtract (&total_risk, - &total_risk, - &ds->denom_risk); + TALER_ARL_amount_subtract (&balance.risk, + &balance.risk, + &ds->dcd.denom_risk); /* If the above fails, our risk assessment is inconsistent! This is really, really bad (auditor-internal invariant would be violated). Hence we can "safely" assert. If @@ -858,22 +794,21 @@ sync_denomination (void *cls, in the auditor _or_ the auditor's database is corrupt. */ } if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && - ( (0 != ds->denom_balance.value) || - (0 != ds->denom_balance.fraction) ) ) + (! TALER_amount_is_zero (&ds->dcd.denom_balance)) ) { /* book denom_balance coin expiration profits! */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Denomination `%s' expired, booking %s in expiration profits\n", GNUNET_h2s (denom_hash), - TALER_amount2s (&ds->denom_balance)); + TALER_amount2s (&ds->dcd.denom_balance)); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != (qs = TALER_ARL_adb->insert_historic_denom_revenue ( TALER_ARL_adb->cls, &TALER_ARL_master_pub, &denom_h, expire_deposit, - &ds->denom_balance, - &ds->denom_recoup))) + &ds->dcd.denom_balance, + &ds->dcd.recoup_loss))) { /* Failed to store profits? Bad database */ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); @@ -890,8 +825,8 @@ sync_denomination (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Final balance for denomination `%s' is %s (%llu)\n", GNUNET_h2s (denom_hash), - TALER_amount2s (&ds->denom_balance), - (unsigned long long) ds->num_issued); + TALER_amount2s (&ds->dcd.denom_balance), + (unsigned long long) ds->dcd.num_issued); cnt = TALER_ARL_edb->count_known_coins (TALER_ARL_edb->cls, &denom_h); if (0 > cnt) @@ -903,39 +838,31 @@ sync_denomination (void *cls, } else { - if (ds->num_issued < (uint64_t) cnt) + if (ds->dcd.num_issued < (uint64_t) cnt) { /* more coins deposited than issued! very bad */ report_emergency_by_count (issue, - ds->num_issued, + ds->dcd.num_issued, cnt, - &ds->denom_risk); + &ds->dcd.denom_risk); } if (ds->report_emergency) { /* Value of coins deposited exceed value of coins issued! Also very bad! */ report_emergency_by_amount (issue, - &ds->denom_risk, - &ds->denom_loss); + &ds->dcd.denom_risk, + &ds->dcd.denom_loss); } if (ds->in_db) qs = TALER_ARL_adb->update_denomination_balance (TALER_ARL_adb->cls, &denom_h, - &ds->denom_balance, - &ds->denom_loss, - &ds->denom_risk, - &ds->denom_recoup, - ds->num_issued); + &ds->dcd); else qs = TALER_ARL_adb->insert_denomination_balance (TALER_ARL_adb->cls, &denom_h, - &ds->denom_balance, - &ds->denom_loss, - &ds->denom_risk, - &ds->denom_recoup, - ds->num_issued); + &ds->dcd); } } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) @@ -1032,22 +959,22 @@ withdraw_cb (void *cls, "Issued coin in denomination `%s' of total value %s\n", GNUNET_h2s (&dh.hash), TALER_amount2s (&issue->value)); - ds->num_issued++; - TALER_ARL_amount_add (&ds->denom_balance, - &ds->denom_balance, - &issue->value); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' is %s\n", GNUNET_h2s (&dh.hash), - TALER_amount2s (&ds->denom_balance)); - TALER_ARL_amount_add (&total_escrow_balance, - &total_escrow_balance, + TALER_amount2s (&ds->dcd.denom_balance)); + TALER_ARL_amount_add (&balance.total_escrowed, + &balance.total_escrowed, + &issue->value); + TALER_ARL_amount_add (&balance.risk, + &balance.risk, &issue->value); - TALER_ARL_amount_add (&total_risk, - &total_risk, + ds->dcd.num_issued++; + TALER_ARL_amount_add (&ds->dcd.denom_balance, + &ds->dcd.denom_balance, &issue->value); - TALER_ARL_amount_add (&ds->denom_risk, - &ds->denom_risk, + TALER_ARL_amount_add (&ds->dcd.denom_risk, + &ds->dcd.denom_risk, &issue->value); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -1201,8 +1128,8 @@ check_known_coin ( loss_potential), GNUNET_JSON_pack_data_auto ("coin_pub", coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, loss_potential); } TALER_denom_sig_free (&ci.denom_sig); @@ -1228,19 +1155,19 @@ reduce_denom_balance (struct DenominationSummary *dso, if (TALER_ARL_SR_INVALID_NEGATIVE == TALER_ARL_amount_subtract_neg (&tmp, - &dso->denom_balance, + &dso->dcd.denom_balance, amount_with_fee)) { - TALER_ARL_amount_add (&dso->denom_loss, - &dso->denom_loss, + TALER_ARL_amount_add (&dso->dcd.denom_loss, + &dso->dcd.denom_loss, amount_with_fee); dso->report_emergency = true; } else { - dso->denom_balance = tmp; + dso->dcd.denom_balance = tmp; } - if (-1 == TALER_amount_cmp (&total_escrow_balance, + if (-1 == TALER_amount_cmp (&balance.total_escrowed, amount_with_fee)) { /* This can theoretically happen if for example the exchange @@ -1252,20 +1179,20 @@ reduce_denom_balance (struct DenominationSummary *dso, report_amount_arithmetic_inconsistency ( "subtracting amount from escrow balance", rowid, - &total_escrow_balance, + &balance.total_escrowed, amount_with_fee, 0); } else { - TALER_ARL_amount_subtract (&total_escrow_balance, - &total_escrow_balance, + TALER_ARL_amount_subtract (&balance.total_escrowed, + &balance.total_escrowed, amount_with_fee); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' is %s\n", GNUNET_h2s (&dso->issue->denom_hash.hash), - TALER_amount2s (&dso->denom_balance)); + TALER_amount2s (&dso->dcd.denom_balance)); } @@ -1365,8 +1292,8 @@ refresh_session_cb (void *cls, amount_with_fee), GNUNET_JSON_pack_data_auto ("coin_pub", coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount_with_fee); } } @@ -1501,22 +1428,22 @@ refresh_session_cb (void *cls, "Created fresh coin in denomination `%s' of value %s\n", GNUNET_h2s (&ni->denom_hash.hash), TALER_amount2s (&ni->value)); - dsi->num_issued++; - TALER_ARL_amount_add (&dsi->denom_balance, - &dsi->denom_balance, + dsi->dcd.num_issued++; + TALER_ARL_amount_add (&dsi->dcd.denom_balance, + &dsi->dcd.denom_balance, &ni->value); - TALER_ARL_amount_add (&dsi->denom_risk, - &dsi->denom_risk, + TALER_ARL_amount_add (&dsi->dcd.denom_risk, + &dsi->dcd.denom_risk, &ni->value); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' is %s\n", GNUNET_h2s (&ni->denom_hash.hash), - TALER_amount2s (&dsi->denom_balance)); - TALER_ARL_amount_add (&total_escrow_balance, - &total_escrow_balance, + TALER_amount2s (&dsi->dcd.denom_balance)); + TALER_ARL_amount_add (&balance.total_escrowed, + &balance.total_escrowed, &ni->value); - TALER_ARL_amount_add (&total_risk, - &total_risk, + TALER_ARL_amount_add (&balance.risk, + &balance.risk, &ni->value); } } @@ -1541,8 +1468,8 @@ refresh_session_cb (void *cls, } /* update global melt fees */ - TALER_ARL_amount_add (&total_melt_fee_income, - &total_melt_fee_income, + TALER_ARL_amount_add (&balance.melt_fee_balance, + &balance.melt_fee_balance, &issue->fees.refresh); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -1657,8 +1584,8 @@ deposit_cb (void *cls, &deposit->amount_with_fee), GNUNET_JSON_pack_data_auto ("coin_pub", &deposit->coin.coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, &deposit->amount_with_fee); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -1689,8 +1616,8 @@ deposit_cb (void *cls, } /* update global deposit fees */ - TALER_ARL_amount_add (&total_deposit_fee_income, - &total_deposit_fee_income, + TALER_ARL_amount_add (&balance.deposit_fee_balance, + &balance.deposit_fee_balance, &issue->fees.deposit); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -1774,8 +1701,8 @@ refund_cb (void *cls, amount_with_fee), GNUNET_JSON_pack_data_auto ("coin_pub", coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount_with_fee); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -1815,31 +1742,31 @@ refund_cb (void *cls, } else { - TALER_ARL_amount_add (&ds->denom_balance, - &ds->denom_balance, + TALER_ARL_amount_add (&ds->dcd.denom_balance, + &ds->dcd.denom_balance, &amount_without_fee); - TALER_ARL_amount_add (&ds->denom_risk, - &ds->denom_risk, + TALER_ARL_amount_add (&ds->dcd.denom_risk, + &ds->dcd.denom_risk, &amount_without_fee); - TALER_ARL_amount_add (&total_escrow_balance, - &total_escrow_balance, + TALER_ARL_amount_add (&balance.total_escrowed, + &balance.total_escrowed, &amount_without_fee); - TALER_ARL_amount_add (&total_risk, - &total_risk, + TALER_ARL_amount_add (&balance.risk, + &balance.risk, &amount_without_fee); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' after refund is %s\n", GNUNET_h2s (&issue->denom_hash.hash), - TALER_amount2s (&ds->denom_balance)); + TALER_amount2s (&ds->dcd.denom_balance)); } /* update total refund fee balance */ - TALER_ARL_amount_add (&total_refund_fee_income, - &total_refund_fee_income, + TALER_ARL_amount_add (&balance.refund_fee_balance, + &balance.refund_fee_balance, &issue->fees.refund); if (full_refund) { - TALER_ARL_amount_subtract (&total_deposit_fee_income, - &total_deposit_fee_income, + TALER_ARL_amount_subtract (&balance.deposit_fee_balance, + &balance.deposit_fee_balance, &issue->fees.deposit); } if (TALER_ARL_do_abort ()) @@ -1907,26 +1834,26 @@ purse_refund_coin_cb ( } else { - TALER_ARL_amount_add (&ds->denom_balance, - &ds->denom_balance, + TALER_ARL_amount_add (&ds->dcd.denom_balance, + &ds->dcd.denom_balance, amount_with_fee); - TALER_ARL_amount_add (&ds->denom_risk, - &ds->denom_risk, + TALER_ARL_amount_add (&ds->dcd.denom_risk, + &ds->dcd.denom_risk, amount_with_fee); - TALER_ARL_amount_add (&total_escrow_balance, - &total_escrow_balance, + TALER_ARL_amount_add (&balance.total_escrowed, + &balance.total_escrowed, amount_with_fee); - TALER_ARL_amount_add (&total_risk, - &total_risk, + TALER_ARL_amount_add (&balance.risk, + &balance.risk, amount_with_fee); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' after purse-refund is %s\n", GNUNET_h2s (&issue->denom_hash.hash), - TALER_amount2s (&ds->denom_balance)); + TALER_amount2s (&ds->dcd.denom_balance)); } /* update total deposit fee balance */ - TALER_ARL_amount_subtract (&total_deposit_fee_income, - &total_deposit_fee_income, + TALER_ARL_amount_subtract (&balance.deposit_fee_balance, + &balance.deposit_fee_balance, &issue->fees.deposit); return GNUNET_OK; @@ -1941,16 +1868,22 @@ purse_refund_coin_cb ( * @param cls closure * @param rowid unique serial ID for the refund in our DB * @param purse_pub public key of the purse + * @param reserve_pub public key of the targeted reserve (ignored) + * @param val targeted amount to be in the reserve (ignored) * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ static enum GNUNET_GenericReturnValue purse_refund_cb (void *cls, uint64_t rowid, - const struct TALER_PurseContractPublicKeyP *purse_pub) + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_Amount *val) { struct CoinContext *cc = cls; enum GNUNET_DB_QueryStatus qs; + (void) val; /* irrelevant on refund */ + (void) reserve_pub; /* irrelevant, may even be NULL */ GNUNET_assert (rowid >= ppc.last_purse_refunds_serial_id); /* should be monotonically increasing */ ppc.last_purse_refunds_serial_id = rowid + 1; qs = TALER_ARL_edb->select_purse_deposits_by_purse (TALER_ARL_edb->cls, @@ -2020,8 +1953,8 @@ check_recoup (struct CoinContext *cc, amount), GNUNET_JSON_pack_data_auto ("coin_pub", &coin->denom_pub_hash))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount); } qs = TALER_ARL_get_denomination_info_by_hash (&coin->denom_pub_hash, @@ -2081,15 +2014,15 @@ check_recoup (struct CoinContext *cc, amount), GNUNET_JSON_pack_data_auto ("coin_pub", &coin->coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount); } - TALER_ARL_amount_add (&ds->denom_recoup, - &ds->denom_recoup, + TALER_ARL_amount_add (&ds->dcd.recoup_loss, + &ds->dcd.recoup_loss, amount); - TALER_ARL_amount_add (&total_recoup_loss, - &total_recoup_loss, + TALER_ARL_amount_add (&balance.loss, + &balance.loss, amount); } if (TALER_ARL_do_abort ()) @@ -2145,8 +2078,8 @@ recoup_cb (void *cls, amount), GNUNET_JSON_pack_data_auto ("coin_pub", &coin->coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -2233,13 +2166,13 @@ recoup_refresh_cb (void *cls, } else { - TALER_ARL_amount_add (&dso->denom_balance, - &dso->denom_balance, + TALER_ARL_amount_add (&dso->dcd.denom_balance, + &dso->dcd.denom_balance, amount); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "New balance of denomination `%s' after refresh-recoup is %s\n", GNUNET_h2s (&issue->denom_hash.hash), - TALER_amount2s (&dso->denom_balance)); + TALER_amount2s (&dso->dcd.denom_balance)); } } @@ -2259,8 +2192,8 @@ recoup_refresh_cb (void *cls, amount), GNUNET_JSON_pack_data_auto ("coin_pub", &coin->coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, amount); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -2432,8 +2365,8 @@ purse_deposit_cb ( &deposit->amount), GNUNET_JSON_pack_data_auto ("coin_pub", &deposit->coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, + TALER_ARL_amount_add (&balance.irregular_loss, + &balance.irregular_loss, &deposit->amount); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -2458,8 +2391,8 @@ purse_deposit_cb ( } /* update global deposit fees */ - TALER_ARL_amount_add (&total_deposit_fee_income, - &total_deposit_fee_income, + TALER_ARL_amount_add (&balance.deposit_fee_balance, + &balance.deposit_fee_balance, &issue->fees.deposit); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; @@ -2511,13 +2444,15 @@ analyze_coins (void *cls) { ppc_start = ppc; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu\n", + "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", (unsigned long long) ppc.last_deposit_serial_id, (unsigned long long) ppc.last_melt_serial_id, (unsigned long long) ppc.last_refund_serial_id, (unsigned long long) ppc.last_withdraw_serial_id, (unsigned long long) ppc.last_recoup_refresh_serial_id, - (unsigned long long) ppc.last_purse_deposits_serial_id); + (unsigned long long) ppc.last_open_deposits_serial_id, + (unsigned long long) ppc.last_purse_deposits_serial_id, + (unsigned long long) ppc.last_purse_refunds_serial_id); } /* setup 'cc' */ @@ -2526,13 +2461,7 @@ analyze_coins (void *cls) GNUNET_NO); qsx = TALER_ARL_adb->get_balance_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_deposit_fee_income, - &total_melt_fee_income, - &total_refund_fee_income, - &total_risk, - &total_recoup_loss, - &total_irregular_recoups); + &balance); if (0 > qsx) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); @@ -2569,9 +2498,10 @@ analyze_coins (void *cls) /* process purse_refunds */ if (0 > - (qs = TALER_ARL_edb->select_purse_refunds_above_serial_id ( + (qs = TALER_ARL_edb->select_purse_decisions_above_serial_id ( TALER_ARL_edb->cls, ppc.last_purse_refunds_serial_id, + true, /* only go for refunds! */ &purse_refund_cb, &cc))) { @@ -2663,23 +2593,11 @@ analyze_coins (void *cls) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx) qs = TALER_ARL_adb->update_balance_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_deposit_fee_income, - &total_melt_fee_income, - &total_refund_fee_income, - &total_risk, - &total_recoup_loss, - &total_irregular_recoups); + &balance); else qs = TALER_ARL_adb->insert_balance_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_deposit_fee_income, - &total_melt_fee_income, - &total_refund_fee_income, - &total_risk, - &total_recoup_loss, - &total_irregular_recoups); + &balance); if (0 >= qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); @@ -2702,13 +2620,15 @@ analyze_coins (void *cls) return qs; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu\n", + "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", (unsigned long long) ppc.last_deposit_serial_id, (unsigned long long) ppc.last_melt_serial_id, (unsigned long long) ppc.last_refund_serial_id, (unsigned long long) ppc.last_withdraw_serial_id, (unsigned long long) ppc.last_recoup_refresh_serial_id, - (unsigned long long) ppc.last_purse_deposits_serial_id); + (unsigned long long) ppc.last_open_deposits_serial_id, + (unsigned long long) ppc.last_purse_deposits_serial_id, + (unsigned long long) ppc.last_purse_refunds_serial_id); return qs; } @@ -2754,34 +2674,37 @@ run (void *cls, &reported_emergency_loss_by_count)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_escrow_balance)); + &balance.total_escrowed)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_risk)); + &balance.deposit_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_recoup_loss)); + &balance.melt_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_irregular_recoups)); + &balance.refund_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_deposit_fee_income)); + &balance.purse_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_melt_fee_income)); + &balance.open_deposit_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_refund_fee_income)); + &balance.risk)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_arithmetic_delta_plus)); + &balance.loss)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_arithmetic_delta_minus)); + &balance.irregular_loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_arithmetic_delta_plus)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_bad_sig_loss)); + &total_arithmetic_delta_minus)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, &total_refresh_hanging)); @@ -2812,27 +2735,30 @@ run (void *cls, TALER_ARL_done ( GNUNET_JSON_PACK ( TALER_JSON_pack_amount ("total_escrow_balance", - &total_escrow_balance), - TALER_JSON_pack_amount ("total_active_risk", - &total_risk), + &balance.total_escrowed), TALER_JSON_pack_amount ("total_deposit_fee_income", - &total_deposit_fee_income), + &balance.deposit_fee_balance), TALER_JSON_pack_amount ("total_melt_fee_income", - &total_melt_fee_income), + &balance.melt_fee_balance), TALER_JSON_pack_amount ("total_refund_fee_income", - &total_refund_fee_income), + &balance.refund_fee_balance), + TALER_JSON_pack_amount ("total_purse_fee_income", + &balance.purse_fee_balance), + TALER_JSON_pack_amount ("total_open_deposit_fee_income", + &balance.open_deposit_fee_balance), + TALER_JSON_pack_amount ("total_active_risk", + &balance.risk), + TALER_JSON_pack_amount ("total_recoup_loss", + &balance.loss), + /* Tested in test-auditor.sh #4/#5/#6/#13/#26 */ + TALER_JSON_pack_amount ("irregular_loss", + &balance.irregular_loss), /* Tested in test-auditor.sh #18 */ GNUNET_JSON_pack_array_steal ("emergencies", report_emergencies), /* Tested in test-auditor.sh #18 */ TALER_JSON_pack_amount ("emergencies_risk_by_amount", &reported_emergency_risk_by_amount), - /* Tested in test-auditor.sh #4/#5/#6/#13/#26 */ - GNUNET_JSON_pack_array_steal ("bad_sig_losses", - report_bad_sig_losses), - /* Tested in test-auditor.sh #4/#5/#6/#13/#26 */ - TALER_JSON_pack_amount ("total_bad_sig_loss", - &total_bad_sig_loss), /* Tested in test-auditor.sh #31 */ GNUNET_JSON_pack_array_steal ("row_inconsistencies", report_row_inconsistencies), @@ -2845,11 +2771,11 @@ run (void *cls, &total_arithmetic_delta_minus), TALER_JSON_pack_amount ("total_refresh_hanging", &total_refresh_hanging), + GNUNET_JSON_pack_array_steal ("bad_sig_losses", + report_bad_sig_losses), /* Tested in test-auditor.sh #12 */ GNUNET_JSON_pack_array_steal ("refresh_hanging", report_refreshs_hanging), - TALER_JSON_pack_amount ("total_recoup_loss", - &total_recoup_loss), /* Tested in test-auditor.sh #18 */ GNUNET_JSON_pack_array_steal ("emergencies_by_count", report_emergencies_by_count), @@ -2898,8 +2824,6 @@ run (void *cls, start_time), TALER_JSON_pack_time_abs_human ("auditor_end_time", GNUNET_TIME_absolute_get ()), - TALER_JSON_pack_amount ("total_irregular_recoups", - &total_irregular_recoups), GNUNET_JSON_pack_array_steal ("unsigned_denominations", report_denominations_without_sigs))); } diff --git a/src/auditor/taler-helper-auditor-purses.c b/src/auditor/taler-helper-auditor-purses.c new file mode 100644 index 000000000..e0c939faa --- /dev/null +++ b/src/auditor/taler-helper-auditor-purses.c @@ -0,0 +1,1123 @@ +/* + This file is part of TALER + Copyright (C) 2016-2022 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 + 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 Affero Public License for more details. + + You should have received a copy of the GNU Affero Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file auditor/taler-helper-auditor-purses.c + * @brief audits the purses of an exchange database + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_auditordb_plugin.h" +#include "taler_exchangedb_lib.h" +#include "taler_json_lib.h" +#include "taler_bank_service.h" +#include "taler_signatures.h" +#include "report-lib.h" + + +/** + * Use a 1 day grace period to deal with clocks not being perfectly synchronized. + */ +#define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS + +/** + * Return value from main(). + */ +static int global_ret; + +/** + * Checkpointing our progress for purses. + */ +static struct TALER_AUDITORDB_ProgressPointPurse ppp; + +/** + * Checkpointing our progress for purses. + */ +static struct TALER_AUDITORDB_ProgressPointPurse ppp_start; + +/** + * Array of reports about row inconsitencies. + */ +static json_t *report_row_inconsistencies; + +/** + * Array of reports about purse balance insufficient inconsitencies. + */ +static json_t *report_purse_balance_insufficient_inconsistencies; + +/** + * Total amount purses were merged with insufficient balance. + */ +static struct TALER_Amount total_balance_insufficient_loss; + +/** + * Array of reports about purses's not being closed inconsitencies. + */ +static json_t *report_purse_not_closed_inconsistencies; + +/** + * Total amount affected by purses not having been closed on time. + */ +static struct TALER_Amount total_balance_purse_not_closed; + +/** + * 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; + +/** + * 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; + +/** + * Should we run checks that only work for exchange-internal audits? + */ +static int internal_checks; + +/* ***************************** Report logic **************************** */ + + +/** + * 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, 0 if row is missing + * @param exchange amount calculated by exchange + * @param auditor amount calculated by auditor + * @param profitable 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 */ + TALER_ARL_amount_subtract (&delta, + exchange, + auditor); + } + else + { + /* auditor < exchange */ + profitable = -profitable; + TALER_ARL_amount_subtract (&delta, + auditor, + exchange); + } + TALER_ARL_report (report_amount_arithmetic_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + operation), + GNUNET_JSON_pack_uint64 ("rowid", + rowid), + TALER_JSON_pack_amount ("exchange", + exchange), + TALER_JSON_pack_amount ("auditor", + auditor), + GNUNET_JSON_pack_int64 ("profitable", + profitable))); + if (0 != profitable) + { + target = (1 == profitable) + ? &total_arithmetic_delta_plus + : &total_arithmetic_delta_minus; + TALER_ARL_amount_add (target, + target, + &delta); + } +} + + +/** + * Report a (serious) inconsistency in the exchange's database. + * + * @param table affected table + * @param rowid affected row, 0 if row is missing + * @param diagnostic message explaining the problem + */ +static void +report_row_inconsistency (const char *table, + uint64_t rowid, + const char *diagnostic) +{ + TALER_ARL_report (report_row_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("table", + table), + GNUNET_JSON_pack_uint64 ("row", + rowid), + GNUNET_JSON_pack_string ("diagnostic", + diagnostic))); +} + + +/* ***************************** Analyze purses ************************ */ +/* This logic checks the purses_requests, purse_deposits, + purse_refunds, purse_merges and account_merges */ + +/** + * Summary data we keep per purse. + */ +struct PurseSummary +{ + /** + * Public key of the purse. + * Always set when the struct is first initialized. + */ + struct TALER_PursePublicKeyP purse_pub; + + /** + * Sum of all incoming transfers during this transaction. + * Updated only in #handle_purse_deposits(). + */ + struct TALER_Amount total_in; + + /** + * Expected value of the purse. + */ + struct TALER_Amount total_value; + + /** + * Purse expiration date. + */ + struct GNUNET_TIME_Timestamp expiration_date; + + /** + * Did we have a previous purse info? Used to decide between UPDATE and + * INSERT later. Initialized in #load_auditor_purse_summary(). + */ + bool had_pi; + +}; + + +/** + * Load the auditor's remembered state about the purse into @a ps. + * + * @param[in,out] ps purse summary to (fully) initialize + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +load_auditor_purse_summary (struct PurseSummary *ps) +{ + enum GNUNET_DB_QueryStatus qs; + uint64_t rowid; + + qs = TALER_ARL_adb->get_purse_info (TALER_ARL_adb->cls, + &ps->purse_pub, + &TALER_ARL_master_pub, + &rowid, + &ps->total_in, + &ps->expiration_date); + if (0 > qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + ps->had_pi = false; + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &ps->prev_balance.reserve_balance)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Creating fresh purse `%s'\n", + TALER_B2S (&ps->purse_pub)); + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + ps->had_ri = true; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Auditor remembers purse `%s' has balance %s\n", + TALER_B2S (&ps->purse_pub), + TALER_amount2s (&ps->total_in)); + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +} + + +/** + * Closure to the various callbacks we make while checking a purse. + */ +struct PurseContext +{ + /** + * Map from hash of purse's public key to a `struct PurseSummary`. + */ + struct GNUNET_CONTAINER_MultiHashMap *purses; + + /** + * Transaction status code, set to error codes if applicable. + */ + enum GNUNET_DB_QueryStatus qs; + +}; + + +/** + * Create a new reserve for @a reserve_pub in @a rc. + * + * @param[in,out] pc context to update + * @param purse_pub key for which to create a purse + * @return NULL on error + */ +static struct PurseSummary * +setup_purse (struct PurseContext *pc, + const struct TALER_PursePublicKeyP *purse_pub) +{ + struct PurseSummary *rs; + struct GNUNET_HashCode key; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_CRYPTO_hash (purse_pub, + sizeof (*purse_pub), + &key); + ps = GNUNET_CONTAINER_multihashmap_get (rc->purses, + &key); + if (NULL != ps) + return ps; + ps = GNUNET_new (struct PurseSummary); + ps->purse_pub = *purse_pub; + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &ps->total_in)); + if (0 > (qs = load_auditor_purse_summary (ps))) + { + GNUNET_free (ps); + rc->qs = qs; + return NULL; + } + GNUNET_assert (GNUNET_OK == + GNUNET_CONTAINER_multihashmap_put (rc->purses, + &key, + ps, + GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); + return ps; +} + + +/** + * Check that the purse summary matches what the exchange database + * thinks about the purse, and update our own state of the purse. + * + * Remove all purses that we are happy with from the DB. + * + * @param cls our `struct PurseContext` + * @param key hash of the purse public key + * @param value a `struct PurseSummary` + * @return #GNUNET_OK to process more entries + */ +static enum GNUNET_GenericReturnValue +verify_purse_balance (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct PurseContext *rc = cls; + struct PurseSummary *ps = value; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_OK; + // FIXME: implement! + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (rc->purses, + key, + rp)); + GNUNET_free (ps); + return ret; +} + + +/** + * Function called with details about purse deposits that have been made, with + * the goal of auditing the deposit's execution. + * + * @param cls closure + * @param rowid unique serial ID for the deposit in our DB + * @param deposit deposit details + * @param reserve_pub which reserve is the purse merged into, NULL if unknown + * @param flags purse flags + * @param auditor_balance purse balance (according to the + * auditor during auditing) + * @param purse_total target amount the purse should reach + * @param denom_pub denomination public key of @a coin_pub + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_purse_deposits ( + void *cls, + uint64_t rowid, + const struct TALER_EXCHANGEDB_PurseDeposit *deposit, + const struct TALER_ReservePublicKeyP *reserve_pub, + enum TALER_WalletAccountMergeFlags flags, + const struct TALER_Amount *auditor_balance, + const struct TALER_Amount *purse_total, + const struct TALER_DenominationPublicKey *denom_pub) +{ + struct PurseContext *rc = cls; + const char *base_url + = (NULL == deposit->exchange_base_url) + ? TALER_ARL_exchange_url + : deposit->exchange_base_url; + enum GNUNET_DB_QueryStatus qs; + struct TALER_Amount amount_minus_fee; + struct TALER_Amount new_balance; + struct PurseSummary *rs; + struct TALER_DenominationHashP h_denom_pub; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_purse_deposits_serial_id); + ppr.last_purse_deposits_serial_id = rowid + 1; + + { + const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; + enum GNUNET_DB_QueryStatus qs; + + qs = TALER_ARL_get_denomination_info (denom_pub, + &issue, + &h_denom_pub); + if (0 > qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Hard database error trying to get denomination %s from database!\n", + TALER_B2S (denom_pub)); + rc->qs = qs; + return GNUNET_SYSERR; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + report_row_inconsistency ("purse-deposit", + rowid, + "denomination key not found"); + if (TALER_ARL_do_abort ()) + return GNUNET_SYSERR; + return GNUNET_OK; + } + TALER_ARL_amount_subtract (&amount_minus_fee, + &deposit->amount, + &issue->fees.deposit); + } + + if (GNUNET_OK != + TALER_wallet_purse_deposit_verify (base_url, + &deposit->purse_pub, + &deposit->amount, + &h_denom_pub, + &deposit->h_age_commitment, + &deposit->coin_pub, + &deposit->coin_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "purse-deposit"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + &deposit->amount), + GNUNET_JSON_pack_data_auto ("key_pub", + &deposit->coin_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + &deposit->amount); + return GNUNET_OK; + } + + TALER_ARL_amount_add (&new_balance, + auditor_balance, + &amount_minus_fee); + qs = TALER_ARL_edb->set_purse_balance (TALER_ARL_edb->cls, + &deposit->purse_pub, + &new_balance); + GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + rc->qs = qs; + return GNUNET_SYSERR; + } + if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE != + (flags & TALER_WAMF_MERGE_MODE_MASK)) + { + /* This just created the purse, actual credit to + the reserve will be done in handle_account_merged() */ + return GNUNET_OK; + } + if ( (NULL != deposit->exchange_base_url) && + (0 != strcmp (deposit->exchange_base_url, + TALER_ARL_exchange_url)) ) + { + /* credited reserve is at another exchange, do NOT credit here! */ + return GNUNET_OK; + } + + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if ( (-1 != TALER_amount_cmp (&new_balance, + purse_total)) && + (-1 == TALER_amount_cmp (auditor_balance, + purse_total)) ) + { + /* new balance at or above purse_total + (and previous balance was below); thus + credit reserve with purse value! */ + TALER_ARL_amount_add (&rs->total_in, + &rs->total_in, + purse_total); + } + return GNUNET_OK; +} + + +/** + * Function called with details about purse + * merges that have been made, with + * the goal of auditing the purse merge execution. + * + * @param cls closure + * @param rowid unique serial ID for the deposit in our DB + * @param partner_base_url where is the reserve, NULL for this exchange + * @param amount total amount expected in the purse + * @param balance current balance in the purse (according to the auditor) + * @param flags purse flags + * @param merge_pub merge capability key + * @param reserve_pub reserve the merge affects + * @param merge_sig signature affirming the merge + * @param purse_pub purse key + * @param merge_timestamp when did the merge happen + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_purse_merged ( + void *cls, + uint64_t rowid, + const char *partner_base_url, + const struct TALER_Amount *amount, + const struct TALER_Amount *balance, + enum TALER_WalletAccountMergeFlags flags, + const struct TALER_PurseMergePublicKeyP *merge_pub, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_PurseMergeSignatureP *merge_sig, + const struct TALER_PurseContractPublicKeyP *purse_pub, + struct GNUNET_TIME_Timestamp merge_timestamp) +{ + struct PurseContext *rc = cls; + struct PurseSummary *rs; + char *reserve_url; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_purse_merges_serial_id); + ppr.last_purse_merges_serial_id = rowid + 1; + reserve_url + = TALER_reserve_make_payto (NULL == partner_base_url + ? TALER_ARL_exchange_url + : partner_base_url, + reserve_pub); + if (GNUNET_OK != + TALER_wallet_purse_merge_verify (reserve_url, + merge_timestamp, + purse_pub, + merge_pub, + merge_sig)) + { + GNUNET_free (reserve_url); + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "merge-purse"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + amount), + GNUNET_JSON_pack_data_auto ("key_pub", + merge_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + amount); + return GNUNET_OK; + } + GNUNET_free (reserve_url); + if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE == + (flags & TALER_WAMF_MERGE_MODE_MASK)) + { + /* This just created the purse, actual credit to + the reserve will be done in handle_purse_deposits() */ + return GNUNET_OK; + } + if ( (NULL != partner_base_url) && + (0 != strcmp (partner_base_url, + TALER_ARL_exchange_url)) ) + { + /* credited reserve is at another exchange, do NOT credit here! */ + return GNUNET_OK; + } + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (-1 == TALER_amount_cmp (balance, + amount)) + { + struct TALER_Amount loss; + + TALER_ARL_amount_subtract (&loss, + amount, + balance); + /* illegal merge, balance is still below total purse value */ + TALER_ARL_report (report_purse_balance_insufficient_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "merge-purse"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + &loss), + GNUNET_JSON_pack_data_auto ("purse_pub", + purse_pub))); + TALER_ARL_amount_add (&total_balance_insufficient_loss, + &total_balance_insufficient_loss, + &loss); + return GNUNET_OK; + } + TALER_ARL_amount_add (&rs->total_in, + &rs->total_in, + amount); + // rs->a_expiration_date = FIXME: do we care? If so, set to what (so that the auditor no longer complains about the reserve not being closed) + return GNUNET_OK; +} + + +/** + * Function called with details about + * account merge requests that have been made, with + * the goal of auditing the account merge execution. + * + * @param cls closure + * @param rowid unique serial ID for the deposit in our DB + * @param reserve_pub reserve affected by the merge + * @param purse_pub purse being merged + * @param h_contract_terms hash over contract of the purse + * @param purse_expiration when would the purse expire + * @param amount total amount in the purse + * @param min_age minimum age of all coins deposited into the purse + * @param flags how was the purse created + * @param purse_fee if a purse fee was paid, how high is it + * @param merge_timestamp when was the merge approved + * @param reserve_sig signature by reserve approving the merge + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_account_merged ( + void *cls, + uint64_t rowid, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_PrivateContractHashP *h_contract_terms, + struct GNUNET_TIME_Timestamp purse_expiration, + const struct TALER_Amount *amount, + uint32_t min_age, + enum TALER_WalletAccountMergeFlags flags, + const struct TALER_Amount *purse_fee, + struct GNUNET_TIME_Timestamp merge_timestamp, + const struct TALER_PursesignatureP *reserve_sig) +{ + struct PurseContext *rc = cls; + struct PurseSummary *rs; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_account_merges_serial_id); + ppr.last_account_merges_serial_id = rowid + 1; + if (GNUNET_OK != + TALER_wallet_account_merge_verify (merge_timestamp, + purse_pub, + purse_expiration, + h_contract_terms, + amount, + purse_fee, + min_age, + flags, + reserve_pub, + reserve_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "account-merge"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + purse_fee), + GNUNET_JSON_pack_data_auto ("key_pub", + reserve_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + purse_fee); + return GNUNET_OK; + } + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + // FIXME: This is to be done per purse_requests, + // not per account_merges! + if ( (flags & TALER_WAMF_MERGE_MODE_MASK) == + TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE) + { + TALER_ARL_amount_add (&balance.purse_fee_balance, + &balance.purse_fee_balance, + purse_fee); + TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance, + &rs->curr_balance.purse_fee_balance, + purse_fee); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + purse_fee); + } + TALER_ARL_amount_add (&rs->total_in, + &rs->total_in, + amount); + return GNUNET_OK; +} + + +/** + * Analyze purses for being well-formed. + * + * @param cls NULL + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +analyze_purses (void *cls) +{ + struct PurseContext rc; + enum GNUNET_DB_QueryStatus qsx; + enum GNUNET_DB_QueryStatus qs; + enum GNUNET_DB_QueryStatus qsp; + + (void) cls; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Analyzing purses\n"); + qsp = TALER_ARL_adb->get_auditor_progress_purse (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &ppp); + if (0 > qsp) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp); + return qsp; + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsp) + { + GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, + "First analysis using this auditor, starting audit from scratch\n"); + } + else + { + ppr_start = ppr; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming purse audit at %llu/%llu/%llu\n", + (unsigned long long) ppp.last_purse_merges_serial_id, + (unsigned long long) ppp.last_purse_deposits_serial_id, + (unsigned long long) ppp.last_account_merges_serial_id); + } + rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; + qsx = TALER_ARL_adb->get_reserve_summary (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &balance); + if (qsx < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); + return qsx; + } + rc.purses = GNUNET_CONTAINER_multihashmap_create (512, + GNUNET_NO); + qs = TALER_ARL_edb->select_purse_merges_above_serial_id ( + TALER_ARL_edb->cls, + ppr.last_purse_merges_serial_id, + &handle_purse_merged, + &rc); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + qs = TALER_ARL_edb->select_purse_deposits_above_serial_id ( + TALER_ARL_edb->cls, + ppr.last_purse_deposits_serial_id, + &handle_purse_deposits, + &rc); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + /* Charge purse fee! */ + qs = TALER_ARL_edb->select_account_merges_above_serial_id ( + TALER_ARL_edb->cls, + ppr.last_account_merges_serial_id, + &handle_account_merged, + &rc); + if (qs < 0) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + + GNUNET_CONTAINER_multihashmap_iterate (rc.purses, + &verify_purse_balance, + &rc); + GNUNET_break (0 == + GNUNET_CONTAINER_multihashmap_size (rc.purses)); + GNUNET_CONTAINER_multihashmap_destroy (rc.purses); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs) + return qs; + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx) + { + qs = TALER_ARL_adb->insert_reserve_summary (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &balance); + } + else + { + qs = TALER_ARL_adb->update_reserve_summary (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &balance); + } + if (0 >= qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + return qs; + } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp) + qs = TALER_ARL_adb->update_auditor_progress_purse (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &ppp); + else + qs = TALER_ARL_adb->insert_auditor_progress_purse (TALER_ARL_adb->cls, + &TALER_ARL_master_pub, + &ppp); + 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; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Concluded purse audit step at %llu/%llu/%llu\n", + (unsigned long long) ppp.last_purse_merges_serial_id, + (unsigned long long) ppp.last_purse_deposits_serial_id, + (unsigned long long) ppp.last_account_merges_serial_id); + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +} + + +/** + * 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) +{ + (void) cls; + (void) args; + (void) cfgfile; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Launching auditor\n"); + if (GNUNET_OK != + TALER_ARL_init (c)) + { + global_ret = EXIT_FAILURE; + return; + } + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.reserve_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.reserve_loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.withdraw_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.close_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.purse_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.open_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.history_fee_balance)); + // REVIEW: + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_balance_summary_delta_plus)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_balance_summary_delta_minus)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_arithmetic_delta_plus)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_arithmetic_delta_minus)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_balance_reserve_not_closed)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &total_bad_sig_loss)); + + GNUNET_assert (NULL != + (report_row_inconsistencies = json_array ())); + GNUNET_assert (NULL != + (denomination_key_validity_withdraw_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_reserve_balance_summary_wrong_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_reserve_balance_insufficient_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_purse_balance_insufficient_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_reserve_not_closed_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_amount_arithmetic_inconsistencies + = json_array ())); + GNUNET_assert (NULL != + (report_bad_sig_losses = json_array ())); + if (GNUNET_OK != + TALER_ARL_setup_sessions_and_run (&analyze_purses, + NULL)) + { + global_ret = EXIT_FAILURE; + return; + } + TALER_ARL_done ( + GNUNET_JSON_PACK ( + /* Globals (REVIEW!) */ + /* Tested in test-auditor.sh #3 */ + TALER_JSON_pack_amount ("total_loss_balance_insufficient", + &total_balance_insufficient_loss), + /* Tested in test-auditor.sh #3 */ + GNUNET_JSON_pack_array_steal ( + "reserve_balance_summary_wrong_inconsistencies", + report_reserve_balance_summary_wrong_inconsistencies), + TALER_JSON_pack_amount ("total_balance_summary_delta_plus", + &total_balance_summary_delta_plus), + TALER_JSON_pack_amount ("total_balance_summary_delta_minus", + &total_balance_summary_delta_minus), + /* Tested in test-auditor.sh #21 */ + TALER_JSON_pack_amount ("total_balance_reserve_not_closed", + &total_balance_reserve_not_closed), + /* Tested in test-auditor.sh #7 */ + TALER_JSON_pack_amount ("total_bad_sig_loss", + &total_bad_sig_loss), + TALER_JSON_pack_amount ("total_arithmetic_delta_plus", + &total_arithmetic_delta_plus), + TALER_JSON_pack_amount ("total_arithmetic_delta_minus", + &total_arithmetic_delta_minus), + + /* Global 'balances' */ + TALER_JSON_pack_amount ("total_escrow_balance", + &balance.reserve_balance), + TALER_JSON_pack_amount ("total_irregular_loss", + &balance.reserve_loss), + TALER_JSON_pack_amount ("total_withdraw_fee_income", + &balance.withdraw_fee_balance), + TALER_JSON_pack_amount ("total_close_fee_income", + &balance.close_fee_balance), + TALER_JSON_pack_amount ("total_purse_fee_income", + &balance.purse_fee_balance), + TALER_JSON_pack_amount ("total_open_fee_income", + &balance.open_fee_balance), + TALER_JSON_pack_amount ("total_history_fee_income", + &balance.history_fee_balance), + + /* Detailed report tables */ + GNUNET_JSON_pack_array_steal ( + "reserve_balance_insufficient_inconsistencies", + report_reserve_balance_insufficient_inconsistencies), + GNUNET_JSON_pack_array_steal ( + "purse_balance_insufficient_inconsistencies", + report_purse_balance_insufficient_inconsistencies), + /* Tested in test-auditor.sh #21 */ + GNUNET_JSON_pack_array_steal ("reserve_not_closed_inconsistencies", + report_reserve_not_closed_inconsistencies), + /* Tested in test-auditor.sh #7 */ + GNUNET_JSON_pack_array_steal ("bad_sig_losses", + report_bad_sig_losses), + /* Tested in test-revocation.sh #4 */ + GNUNET_JSON_pack_array_steal ("row_inconsistencies", + report_row_inconsistencies), + /* Tested in test-auditor.sh #23 */ + GNUNET_JSON_pack_array_steal ( + "denomination_key_validity_withdraw_inconsistencies", + denomination_key_validity_withdraw_inconsistencies), + GNUNET_JSON_pack_array_steal ("amount_arithmetic_inconsistencies", + report_amount_arithmetic_inconsistencies), + + /* Information about audited range ... */ + TALER_JSON_pack_time_abs_human ("auditor_start_time", + start_time), + TALER_JSON_pack_time_abs_human ("auditor_end_time", + GNUNET_TIME_absolute_get ()), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_in_serial_id", + ppr_start.last_reserve_in_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_out_serial_id", + ppr_start.last_reserve_out_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_recoup_serial_id", + ppr_start.last_reserve_recoup_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_open_serial_id", + ppr_start.last_reserve_open_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_close_serial_id", + ppr_start.last_reserve_close_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_purse_merges_serial_id", + ppr_start.last_purse_merges_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_purse_deposits_serial_id", + ppr_start.last_purse_deposits_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_account_merges_serial_id", + ppr_start.last_account_merges_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_history_requests_serial_id", + ppr_start.last_history_requests_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_in_serial_id", + ppr.last_reserve_in_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_out_serial_id", + ppr.last_reserve_out_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_recoup_serial_id", + ppr.last_reserve_recoup_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_open_serial_id", + ppr.last_reserve_open_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_close_serial_id", + ppr.last_reserve_close_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_purse_merges_serial_id", + ppr.last_purse_merges_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_purse_deposits_serial_id", + ppr.last_purse_deposits_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_account_merges_serial_id", + ppr.last_account_merges_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_history_requests_serial_id", + ppr.last_history_requests_serial_id))); +} + + +/** + * The main function to check the database's handling of purses. + * + * @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[] = { + GNUNET_GETOPT_option_flag ('i', + "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_timetravel ('T', + "timetravel"), + GNUNET_GETOPT_OPTION_END + }; + enum GNUNET_GenericReturnValue ret; + + /* 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 (); + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return EXIT_INVALIDARGUMENT; + ret = GNUNET_PROGRAM_run ( + argc, + argv, + "taler-helper-auditor-purses", + gettext_noop ("Audit Taler exchange purse handling"), + options, + &run, + NULL); + GNUNET_free_nz ((void *) argv); + if (GNUNET_SYSERR == ret) + return EXIT_INVALIDARGUMENT; + if (GNUNET_NO == ret) + return EXIT_SUCCESS; + return global_ret; +} + + +/* end of taler-helper-auditor-purses.c */ diff --git a/src/auditor/taler-helper-auditor-render.py b/src/auditor/taler-helper-auditor-render.py index 4b086cb62..b9c92b29c 100644 --- a/src/auditor/taler-helper-auditor-render.py +++ b/src/auditor/taler-helper-auditor-render.py @@ -53,4 +53,14 @@ jinjaEnv = jinja2.Environment(loader=StdinLoader(), autoescape=False) tmpl = jinjaEnv.get_template('stdin'); -print(tmpl.render(aggregation = jsonData1, coins = jsonData2, deposits = jsonData3, reserves = jsonData4, wire = jsonData5)) +try: + print(tmpl.render(aggregation = jsonData1, coins = jsonData2, deposits = jsonData3, reserves = jsonData4, wire = jsonData5)) +except jinja2.TemplateSyntaxError as error: + print("Template syntax error: {error.message} on line {error.lineno}.".format(error=error)) + exit(1) +except jinja2.UndefinedError as error: + print("Template undefined error: {error.message}.".format(error=error)) + exit(1) +except TypeError as error: + print("Template type error: {0}.".format(error.args[0])) + exit(1) diff --git a/src/auditor/taler-helper-auditor-reserves.c b/src/auditor/taler-helper-auditor-reserves.c index 331dfab77..2a1e990c0 100644 --- a/src/auditor/taler-helper-auditor-reserves.c +++ b/src/auditor/taler-helper-auditor-reserves.c @@ -75,24 +75,21 @@ static json_t *report_reserve_balance_insufficient_inconsistencies; static json_t *report_purse_balance_insufficient_inconsistencies; /** - * Total amount reserves were charged beyond their balance. - */ -static struct TALER_Amount total_balance_insufficient_loss; - -/** * Array of reports about reserve balance summary wrong in database. */ static json_t *report_reserve_balance_summary_wrong_inconsistencies; /** * Total delta between expected and stored reserve balance summaries, - * for positive deltas. + * for positive deltas. Used only when internal checks are + * enabled. */ static struct TALER_Amount total_balance_summary_delta_plus; /** * Total delta between expected and stored reserve balance summaries, - * for negative deltas. + * for negative deltas. Used only when internal checks are + * enabled. */ static struct TALER_Amount total_balance_summary_delta_minus; @@ -123,29 +120,9 @@ static struct TALER_Amount total_arithmetic_delta_plus; static struct TALER_Amount total_arithmetic_delta_minus; /** - * Expected balance in the escrow account. - */ -static struct TALER_Amount total_escrow_balance; - -/** - * Recoups we made on denominations that were not revoked (!?). - */ -static struct TALER_Amount total_irregular_recoups; - -/** - * Total withdraw fees earned. - */ -static struct TALER_Amount total_withdraw_fee_income; - -/** - * Total purse fees earned. - */ -static struct TALER_Amount total_purse_fee_income; - -/** - * Total history fees earned. + * Expected reserve balances. */ -static struct TALER_Amount total_history_fee_income; +static struct TALER_AUDITORDB_ReserveFeeBalance balance; /** * Array of reports about coin operations with bad signatures. @@ -220,8 +197,8 @@ report_amount_arithmetic_inconsistency ( if (0 != profitable) { target = (1 == profitable) - ? &total_arithmetic_delta_plus - : &total_arithmetic_delta_minus; + ? &total_arithmetic_delta_plus + : &total_arithmetic_delta_minus; TALER_ARL_amount_add (target, target, &delta); @@ -279,21 +256,15 @@ struct ReserveSummary struct TALER_Amount total_out; /** - * Sum of withdraw fees encountered during this transaction. + * Sum of balance and fees encountered during this transaction. */ - struct TALER_Amount total_fee; + struct TALER_AUDITORDB_ReserveFeeBalance curr_balance; /** - * Previous balance of the reserve as remembered by the auditor. + * Previous balances of the reserve as remembered by the auditor. * (updated based on @e total_in and @e total_out at the end). */ - struct TALER_Amount balance_at_previous_audit; - - /** - * Previous withdraw fee balance of the reserve, as remembered by the auditor. - * (updated based on @e total_fee at the end). - */ - struct TALER_Amount a_withdraw_fee_balance; + struct TALER_AUDITORDB_ReserveFeeBalance prev_balance; /** * Previous reserve expiration data, as remembered by the auditor. @@ -335,8 +306,7 @@ load_auditor_reserve_summary (struct ReserveSummary *rs) &rs->reserve_pub, &TALER_ARL_master_pub, &rowid, - &rs->balance_at_previous_audit, - &rs->a_withdraw_fee_balance, + &rs->prev_balance, &rs->a_expiration_date, &rs->sender_account); if (0 > qs) @@ -349,31 +319,35 @@ load_auditor_reserve_summary (struct ReserveSummary *rs) rs->had_ri = false; GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (rs->total_in.currency, - &rs->balance_at_previous_audit)); + &rs->prev_balance.reserve_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rs->total_in.currency, + &rs->prev_balance.reserve_loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rs->total_in.currency, + &rs->prev_balance.withdraw_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (rs->total_in.currency, - &rs->a_withdraw_fee_balance)); + &rs->prev_balance.close_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rs->total_in.currency, + &rs->prev_balance.purse_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rs->total_in.currency, + &rs->prev_balance.open_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (rs->total_in.currency, + &rs->prev_balance.history_fee_balance)); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating fresh reserve `%s' with starting balance %s\n", - TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&rs->balance_at_previous_audit)); + "Creating fresh reserve `%s'\n", + TALER_B2S (&rs->reserve_pub)); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } rs->had_ri = true; - if ( (GNUNET_YES != - TALER_amount_cmp_currency (&rs->balance_at_previous_audit, - &rs->a_withdraw_fee_balance)) || - (GNUNET_YES != - TALER_amount_cmp_currency (&rs->total_in, - &rs->balance_at_previous_audit)) ) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Auditor remembers reserve `%s' has balance %s\n", TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&rs->balance_at_previous_audit)); + TALER_amount2s (&rs->prev_balance.reserve_balance)); return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -436,7 +410,25 @@ setup_reserve (struct ReserveContext *rc, &rs->total_out)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &rs->total_fee)); + &rs->curr_balance.reserve_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.reserve_loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.withdraw_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.close_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.purse_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.open_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.history_fee_balance)); if (0 > (qs = load_auditor_reserve_summary (rs))) { GNUNET_free (rs); @@ -481,7 +473,6 @@ handle_reserve_in (void *cls, /* should be monotonically increasing */ GNUNET_assert (rowid >= ppr.last_reserve_in_serial_id); ppr.last_reserve_in_serial_id = rowid + 1; - rs = setup_reserve (rc, reserve_pub); if (NULL == rs) @@ -489,9 +480,6 @@ handle_reserve_in (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - TALER_ARL_amount_add (&rs->total_in, - &rs->total_in, - credit); if (NULL == rs->sender_account) rs->sender_account = GNUNET_strdup (sender_account_details); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -503,6 +491,9 @@ handle_reserve_in (void *cls, idle_reserve_expiration_time)); rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date, expiry); + TALER_ARL_amount_add (&rs->total_in, + &rs->total_in, + credit); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; return GNUNET_OK; @@ -618,7 +609,7 @@ handle_reserve_out (void *cls, amount_with_fee); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; - return GNUNET_OK; /* exit function here, we cannot add this to the legitimate withdrawals */ + return GNUNET_OK; /* exit function here, we cannot add this to the legitimate withdrawals */ } TALER_ARL_amount_add (&auditor_amount_with_fee, @@ -639,9 +630,6 @@ handle_reserve_out (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - TALER_ARL_amount_add (&rs->total_out, - &rs->total_out, - &auditor_amount_with_fee); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Reserve `%s' reduced by %s from withdraw\n", TALER_B2S (reserve_pub), @@ -649,9 +637,15 @@ handle_reserve_out (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Increasing withdraw profits by fee %s\n", TALER_amount2s (&issue->fees.withdraw)); - TALER_ARL_amount_add (&rs->total_fee, - &rs->total_fee, + TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance, + &rs->curr_balance.withdraw_fee_balance, &issue->fees.withdraw); + TALER_ARL_amount_add (&balance.withdraw_fee_balance, + &balance.withdraw_fee_balance, + &issue->fees.withdraw); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + &auditor_amount_with_fee); if (TALER_ARL_do_abort ()) return GNUNET_SYSERR; return GNUNET_OK; @@ -741,8 +735,8 @@ handle_recoup_by_reserve ( report_row_inconsistency ("recoup", rowid, "denomination key not in revocation set"); - TALER_ARL_amount_add (&total_irregular_recoups, - &total_irregular_recoups, + TALER_ARL_amount_add (&balance.reserve_loss, + &balance.reserve_loss, amount); } else @@ -770,10 +764,11 @@ handle_recoup_by_reserve ( } else { - rev_rowid = 0; /* reported elsewhere */ + rev_rowid = 0; /* reported elsewhere */ } if ( (NULL != rev) && - (0 == strcmp (rev, "master signature invalid")) ) + (0 == strcmp (rev, + "master signature invalid")) ) { TALER_ARL_report (report_bad_sig_losses, GNUNET_JSON_PACK ( @@ -868,6 +863,88 @@ get_closing_fee (const char *receiver_account, /** + * Function called about reserve opening operations. + * + * @param cls closure + * @param rowid row identifier used to uniquely identify the reserve closing operation + * @param reserve_payment how much to pay from the + * reserve's own balance for opening the reserve + * @param request_timestamp when was the request created + * @param reserve_expiration desired expiration time for the reserve + * @param purse_limit minimum number of purses the client + * wants to have concurrently open for this reserve + * @param reserve_pub public key of the reserve + * @param reserve_sig signature affirming the operation + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_reserve_open ( + void *cls, + uint64_t rowid, + const struct TALER_Amount *reserve_payment, + struct GNUNET_TIME_Timestamp request_timestamp, + struct GNUNET_TIME_Timestamp reserve_expiration, + uint32_t purse_limit, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct ReserveContext *rc = cls; + struct ReserveSummary *rs; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_reserve_open_serial_id); + ppr.last_reserve_open_serial_id = rowid + 1; + + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_wallet_reserve_open_verify (reserve_payment, + request_timestamp, + reserve_expiration, + purse_limit, + reserve_pub, + reserve_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "reserve-open"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + reserve_payment), + GNUNET_JSON_pack_data_auto ("reserve_pub", + reserve_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + reserve_payment); + return GNUNET_OK; + } + TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance, + &rs->curr_balance.open_fee_balance, + reserve_payment); + TALER_ARL_amount_add (&balance.open_fee_balance, + &balance.open_fee_balance, + reserve_payment); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + reserve_payment); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Additional open operation for reserve `%s' of %s\n", + TALER_B2S (reserve_pub), + TALER_amount2s (reserve_payment)); + if (TALER_ARL_do_abort ()) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** * Function called about reserve closing operations * the aggregator triggered. * @@ -890,7 +967,8 @@ handle_reserve_closed ( const struct TALER_Amount *closing_fee, const struct TALER_ReservePublicKeyP *reserve_pub, const char *receiver_account, - const struct TALER_WireTransferIdentifierRawP *transfer_details) + const struct TALER_WireTransferIdentifierRawP *transfer_details, + uint64_t close_request_row) { struct ReserveContext *rc = cls; struct ReserveSummary *rs; @@ -910,12 +988,6 @@ handle_reserve_closed ( { struct TALER_Amount expected_fee; - TALER_ARL_amount_add (&rs->total_out, - &rs->total_out, - amount_with_fee); - TALER_ARL_amount_add (&rs->total_fee, - &rs->total_fee, - closing_fee); /* verify closing_fee is correct! */ if (GNUNET_OK != get_closing_fee (receiver_account, @@ -935,20 +1007,117 @@ handle_reserve_closed ( 1); } } - if (NULL == rs->sender_account) - { - GNUNET_break (! rs->had_ri); - report_row_inconsistency ("reserves_close", - rowid, - "target account not verified, auditor does not know reserve"); + + TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance, + &rs->curr_balance.close_fee_balance, + closing_fee); + TALER_ARL_amount_add (&balance.close_fee_balance, + &balance.close_fee_balance, + closing_fee); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + amount_with_fee); + if (0 != close_request_row) + { + struct TALER_ReserveSignatureP reserve_sig; + struct GNUNET_TIME_Timestamp request_timestamp; + struct TALER_Amount close_balance; + struct TALER_Amount close_fee; + char *payto_uri; + enum GNUNET_DB_QueryStatus qs; + + qs = TALER_ARL_edb->select_reserve_close_request_info ( + TALER_ARL_edb->cls, + reserve_pub, + close_request_row, + &reserve_sig, + &request_timestamp, + &close_balance, + &close_fee, + &payto_uri); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + report_row_inconsistency ("reserves_close", + rowid, + "reserve close request unknown"); + } + else + { + struct TALER_PaytoHashP h_payto; + + TALER_payto_hash (payto_uri, + &h_payto); + if (GNUNET_OK != + TALER_wallet_reserve_close_verify ( + request_timestamp, + &h_payto, + reserve_pub, + &reserve_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "close-request"), + GNUNET_JSON_pack_uint64 ("row", + close_request_row), + TALER_JSON_pack_amount ("loss", + amount_with_fee), + GNUNET_JSON_pack_data_auto ("reserve_pub", + reserve_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + amount_with_fee); + } + } + if ( (NULL == payto_uri) && + (NULL == rs->sender_account) ) + { + GNUNET_break (! rs->had_ri); + report_row_inconsistency ("reserves_close", + rowid, + "target account not verified, auditor does not know reserve"); + } + if (NULL == payto_uri) + { + if (0 != strcmp (rs->sender_account, + receiver_account)) + { + report_row_inconsistency ("reserves_close", + rowid, + "target account does not match origin account"); + } + } + else + { + if (0 != strcmp (payto_uri, + receiver_account)) + { + report_row_inconsistency ("reserves_close", + rowid, + "target account does not match origin account"); + } + } + GNUNET_free (payto_uri); } - else if (0 != strcmp (rs->sender_account, - receiver_account)) + else { - report_row_inconsistency ("reserves_close", - rowid, - "target account does not match origin account"); + if (NULL == rs->sender_account) + { + GNUNET_break (! rs->had_ri); + report_row_inconsistency ("reserves_close", + rowid, + "target account not verified, auditor does not know reserve"); + } + if (0 != strcmp (rs->sender_account, + receiver_account)) + { + report_row_inconsistency ("reserves_close", + rowid, + "target account does not match origin account"); + } } + + // FIXME: support/check for reserve close requests here! GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Additional closing operation for reserve `%s' of %s\n", TALER_B2S (reserve_pub), @@ -960,6 +1129,203 @@ handle_reserve_closed ( /** + * Function called with details about account merge requests that have been + * made, with the goal of accounting for the merge fee paid by the reserve (if + * applicable). + * + * @param cls closure + * @param rowid unique serial ID for the deposit in our DB + * @param reserve_pub reserve affected by the merge + * @param purse_pub purse being merged + * @param h_contract_terms hash over contract of the purse + * @param purse_expiration when would the purse expire + * @param amount total amount in the purse + * @param min_age minimum age of all coins deposited into the purse + * @param flags how was the purse created + * @param purse_fee if a purse fee was paid, how high is it + * @param merge_timestamp when was the merge approved + * @param reserve_sig signature by reserve approving the merge + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_account_merged ( + void *cls, + uint64_t rowid, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_PrivateContractHashP *h_contract_terms, + struct GNUNET_TIME_Timestamp purse_expiration, + const struct TALER_Amount *amount, + uint32_t min_age, + enum TALER_WalletAccountMergeFlags flags, + const struct TALER_Amount *purse_fee, + struct GNUNET_TIME_Timestamp merge_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct ReserveContext *rc = cls; + struct ReserveSummary *rs; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_account_merges_serial_id); + ppr.last_account_merges_serial_id = rowid + 1; + if (GNUNET_OK != + TALER_wallet_account_merge_verify (merge_timestamp, + purse_pub, + purse_expiration, + h_contract_terms, + amount, + purse_fee, + min_age, + flags, + reserve_pub, + reserve_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "account-merge"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + purse_fee), + GNUNET_JSON_pack_data_auto ("key_pub", + reserve_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + purse_fee); + return GNUNET_OK; + } + if ( (flags & TALER_WAMF_MERGE_MODE_MASK) != + TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE) + return GNUNET_OK; /* no impact on reserve balance */ + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_ARL_amount_add (&balance.purse_fee_balance, + &balance.purse_fee_balance, + purse_fee); + TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance, + &rs->curr_balance.purse_fee_balance, + purse_fee); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + purse_fee); + return GNUNET_OK; +} + + +/** + * Function called with details about a purse that was merged into an account. + * Only updates the reserve balance, the actual verifications are done in the + * purse helper. + * + * @param cls closure + * @param rowid unique serial ID for the refund in our DB + * @param purse_pub public key of the purse + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +purse_decision_cb (void *cls, + uint64_t rowid, + const struct TALER_PurseContractPublicKeyP *purse_pub, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_Amount *purse_value) +{ + struct ReserveContext *rc = cls; + struct ReserveSummary *rs; + + GNUNET_assert (rowid >= ppr.last_purse_decisions_serial_id); /* should be monotonically increasing */ + ppr.last_purse_decisions_serial_id = rowid + 1; + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_ARL_amount_add (&rs->total_in, + &rs->total_in, + purse_value); + if (TALER_ARL_do_abort ()) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Function called with details about + * history requests that have been made, with + * the goal of auditing the history request execution. + * + * @param cls closure + * @param rowid unique serial ID for the deposit in our DB + * @param history_fee fee paid for the request + * @param ts timestamp of the request + * @param reserve_pub reserve history was requested for + * @param reserve_sig signature approving the @a history_fee + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +static enum GNUNET_GenericReturnValue +handle_history_request ( + void *cls, + uint64_t rowid, + const struct TALER_Amount *history_fee, + const struct GNUNET_TIME_Timestamp ts, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig) +{ + struct ReserveContext *rc = cls; + struct ReserveSummary *rs; + + /* should be monotonically increasing */ + GNUNET_assert (rowid >= ppr.last_history_requests_serial_id); + ppr.last_history_requests_serial_id = rowid + 1; + if (GNUNET_OK != + TALER_wallet_reserve_history_verify (ts, + history_fee, + reserve_pub, + reserve_sig)) + { + TALER_ARL_report (report_bad_sig_losses, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("operation", + "account-history"), + GNUNET_JSON_pack_uint64 ("row", + rowid), + TALER_JSON_pack_amount ("loss", + history_fee), + GNUNET_JSON_pack_data_auto ("key_pub", + reserve_pub))); + TALER_ARL_amount_add (&total_bad_sig_loss, + &total_bad_sig_loss, + history_fee); + return GNUNET_OK; + } + rs = setup_reserve (rc, + reserve_pub); + if (NULL == rs) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_ARL_amount_add (&balance.history_fee_balance, + &balance.history_fee_balance, + history_fee); + TALER_ARL_amount_add (&rs->curr_balance.history_fee_balance, + &rs->curr_balance.history_fee_balance, + history_fee); + TALER_ARL_amount_add (&rs->total_out, + &rs->total_out, + history_fee); + return GNUNET_OK; +} + + +/** * Check that the reserve summary matches what the exchange database * thinks about the reserve, and update our own state of the reserve. * @@ -977,29 +1343,32 @@ verify_reserve_balance (void *cls, { struct ReserveContext *rc = cls; struct ReserveSummary *rs = value; - struct TALER_Amount balance; + struct TALER_Amount mbalance; struct TALER_Amount nbalance; enum GNUNET_DB_QueryStatus qs; - int ret; + enum GNUNET_GenericReturnValue ret; ret = GNUNET_OK; /* Check our reserve summary balance calculation shows that the reserve balance is acceptable (i.e. non-negative) */ - TALER_ARL_amount_add (&balance, + TALER_ARL_amount_add (&mbalance, &rs->total_in, - &rs->balance_at_previous_audit); + &rs->prev_balance.reserve_balance); if (TALER_ARL_SR_INVALID_NEGATIVE == TALER_ARL_amount_subtract_neg (&nbalance, - &balance, + &mbalance, &rs->total_out)) { struct TALER_Amount loss; TALER_ARL_amount_subtract (&loss, &rs->total_out, - &balance); - TALER_ARL_amount_add (&total_balance_insufficient_loss, - &total_balance_insufficient_loss, + &mbalance); + TALER_ARL_amount_add (&rs->curr_balance.reserve_loss, + &rs->prev_balance.reserve_loss, + &loss); + TALER_ARL_amount_add (&balance.reserve_loss, + &balance.reserve_loss, &loss); TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies, GNUNET_JSON_PACK ( @@ -1009,8 +1378,13 @@ verify_reserve_balance (void *cls, &loss))); /* Continue with a reserve balance of zero */ GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (balance.currency, - &nbalance)); + TALER_amount_set_zero (TALER_ARL_currency, + &rs->curr_balance.reserve_balance)); + } + else + { + /* Update remaining reserve balance! */ + rs->curr_balance.reserve_balance = nbalance; } if (internal_checks) @@ -1048,17 +1422,17 @@ verify_reserve_balance (void *cls, else { /* Check that exchange's balance matches our expected balance for the reserve */ - if (0 != TALER_amount_cmp (&nbalance, + if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance, &reserve.balance)) { struct TALER_Amount delta; - if (0 < TALER_amount_cmp (&nbalance, + if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance, &reserve.balance)) { /* balance > reserve.balance */ TALER_ARL_amount_subtract (&delta, - &nbalance, + &rs->curr_balance.reserve_balance, &reserve.balance); TALER_ARL_amount_add (&total_balance_summary_delta_plus, &total_balance_summary_delta_plus, @@ -1069,7 +1443,7 @@ verify_reserve_balance (void *cls, /* balance < reserve.balance */ TALER_ARL_amount_subtract (&delta, &reserve.balance, - &nbalance); + &rs->curr_balance.reserve_balance); TALER_ARL_amount_add (&total_balance_summary_delta_minus, &total_balance_summary_delta_minus, &delta); @@ -1081,10 +1455,11 @@ verify_reserve_balance (void *cls, TALER_JSON_pack_amount ("exchange", &reserve.balance), TALER_JSON_pack_amount ("auditor", - &nbalance))); + &rs->curr_balance. + reserve_balance))); } } - } /* end of 'if (internal_checks)' */ + } /* end of 'if (internal_checks)' */ /* Check that reserve is being closed if it is past its expiration date (and the closing fee would not exceed the remaining balance) */ @@ -1141,26 +1516,42 @@ verify_reserve_balance (void *cls, } } - /* Add withdraw fees we encountered to totals */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Reserve reserve `%s' made %s in withdraw fees\n", - TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&rs->total_fee)); - TALER_ARL_amount_add (&rs->a_withdraw_fee_balance, - &rs->a_withdraw_fee_balance, - &rs->total_fee); - TALER_ARL_amount_add (&total_escrow_balance, - &total_escrow_balance, + /* We already computed the 'new' balance in 'curr_balance' + to include the previous balance, so this one is just + an assignment, not adding up! */ + rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance; + + /* Add up new totals to previous totals */ + TALER_ARL_amount_add (&rs->prev_balance.reserve_loss, + &rs->prev_balance.reserve_loss, + &rs->curr_balance.reserve_loss); + TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance, + &rs->prev_balance.withdraw_fee_balance, + &rs->curr_balance.withdraw_fee_balance); + TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance, + &rs->prev_balance.close_fee_balance, + &rs->curr_balance.close_fee_balance); + TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance, + &rs->prev_balance.purse_fee_balance, + &rs->curr_balance.purse_fee_balance); + TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance, + &rs->prev_balance.open_fee_balance, + &rs->curr_balance.open_fee_balance); + TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance, + &rs->prev_balance.history_fee_balance, + &rs->curr_balance.history_fee_balance); + + /* Update global balance: add incoming first, then try + to subtract outgoing... */ + TALER_ARL_amount_add (&balance.reserve_balance, + &balance.reserve_balance, &rs->total_in); - TALER_ARL_amount_add (&total_withdraw_fee_income, - &total_withdraw_fee_income, - &rs->total_fee); { struct TALER_Amount r; if (TALER_ARL_SR_INVALID_NEGATIVE == TALER_ARL_amount_subtract_neg (&r, - &total_escrow_balance, + &balance.reserve_balance, &rs->total_out)) { /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!) @@ -1168,31 +1559,29 @@ verify_reserve_balance (void *cls, went negative!). Woopsie. Calculate how badly it went and log. */ report_amount_arithmetic_inconsistency ("global escrow balance", 0, - &total_escrow_balance, /* what we had */ - &rs->total_out, /* what we needed */ + &balance.reserve_balance, /* what we had */ + &rs->total_out, /* what we needed */ 0 /* specific profit/loss does not apply to the total summary */); /* We unexpectedly went negative, so a sane value to continue from would be zero. */ GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_escrow_balance)); + &balance.reserve_balance)); } else { - total_escrow_balance = r; + balance.reserve_balance = r; } } - if ( (0ULL == balance.value) && - (0U == balance.fraction) ) + if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance)) { /* balance is zero, drop reserve details (and then do not update/insert) */ if (rs->had_ri) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Final balance of reserve `%s' is %s, dropping it\n", - TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&nbalance)); + "Final balance of reserve `%s' is zero, dropping it\n", + TALER_B2S (&rs->reserve_pub)); qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls, &rs->reserve_pub, &TALER_ARL_master_pub); @@ -1206,9 +1595,8 @@ verify_reserve_balance (void *cls, else { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Final balance of reserve `%s' is %s, no need to remember it\n", - TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&nbalance)); + "Final balance of reserve `%s' is zero, no need to remember it\n", + TALER_B2S (&rs->reserve_pub)); } } else @@ -1217,20 +1605,18 @@ verify_reserve_balance (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Remembering final balance of reserve `%s' as %s\n", TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&nbalance)); + TALER_amount2s (&rs->prev_balance.reserve_balance)); if (rs->had_ri) qs = TALER_ARL_adb->update_reserve_info (TALER_ARL_adb->cls, &rs->reserve_pub, &TALER_ARL_master_pub, - &nbalance, - &rs->a_withdraw_fee_balance, + &rs->prev_balance, rs->a_expiration_date); else qs = TALER_ARL_adb->insert_reserve_info (TALER_ARL_adb->cls, &rs->reserve_pub, &TALER_ARL_master_pub, - &nbalance, - &rs->a_withdraw_fee_balance, + &rs->prev_balance, rs->a_expiration_date, rs->sender_account); if (0 >= qs) @@ -1240,7 +1626,7 @@ verify_reserve_balance (void *cls, rc->qs = qs; } } - + /* now we can discard the cached entry */ GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (rc->reserves, key, @@ -1252,426 +1638,6 @@ verify_reserve_balance (void *cls, /** - * Function called with details about purse deposits that have been made, with - * the goal of auditing the deposit's execution. - * - * @param cls closure - * @param rowid unique serial ID for the deposit in our DB - * @param deposit deposit details - * @param reserve_pub which reserve is the purse merged into, NULL if unknown - * @param flags purse flags - * @param auditor_balance purse balance (according to the - * auditor during auditing) - * @param purse_total target amount the purse should reach - * @param denom_pub denomination public key of @a coin_pub - * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop - */ -static enum GNUNET_GenericReturnValue -handle_purse_deposits ( - void *cls, - uint64_t rowid, - const struct TALER_EXCHANGEDB_PurseDeposit *deposit, - const struct TALER_ReservePublicKeyP *reserve_pub, - enum TALER_WalletAccountMergeFlags flags, - const struct TALER_Amount *auditor_balance, - const struct TALER_Amount *purse_total, - const struct TALER_DenominationPublicKey *denom_pub) -{ - struct ReserveContext *rc = cls; - const char *base_url - = (NULL == deposit->exchange_base_url) - ? TALER_ARL_exchange_url - : deposit->exchange_base_url; - enum GNUNET_DB_QueryStatus qs; - struct TALER_Amount amount_minus_fee; - struct TALER_Amount new_balance; - struct ReserveSummary *rs; - struct TALER_DenominationHashP h_denom_pub; - - /* should be monotonically increasing */ - GNUNET_assert (rowid >= ppr.last_purse_deposits_serial_id); - ppr.last_purse_deposits_serial_id = rowid + 1; - - { - const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; - enum GNUNET_DB_QueryStatus qs; - - qs = TALER_ARL_get_denomination_info (denom_pub, - &issue, - &h_denom_pub); - if (0 > qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (GNUNET_DB_STATUS_HARD_ERROR == qs) - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Hard database error trying to get denomination %s from database!\n", - TALER_B2S (denom_pub)); - rc->qs = qs; - return GNUNET_SYSERR; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - report_row_inconsistency ("purse-deposit", - rowid, - "denomination key not found"); - if (TALER_ARL_do_abort ()) - return GNUNET_SYSERR; - return GNUNET_OK; - } - TALER_ARL_amount_subtract (&amount_minus_fee, - &deposit->amount, - &issue->fees.deposit); - } - - if (GNUNET_OK != - TALER_wallet_purse_deposit_verify (base_url, - &deposit->purse_pub, - &deposit->amount, - &h_denom_pub, - &deposit->h_age_commitment, - &deposit->coin_pub, - &deposit->coin_sig)) - { - TALER_ARL_report (report_bad_sig_losses, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("operation", - "purse-deposit"), - GNUNET_JSON_pack_uint64 ("row", - rowid), - TALER_JSON_pack_amount ("loss", - &deposit->amount), - GNUNET_JSON_pack_data_auto ("key_pub", - &deposit->coin_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, - &deposit->amount); - return GNUNET_OK; - } - - TALER_ARL_amount_add (&new_balance, - auditor_balance, - &amount_minus_fee); - qs = TALER_ARL_edb->set_purse_balance (TALER_ARL_edb->cls, - &deposit->purse_pub, - &new_balance); - GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); - if (qs < 0) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - rc->qs = qs; - return GNUNET_SYSERR; - } - if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE != - (flags & TALER_WAMF_MERGE_MODE_MASK)) - { - /* This just created the purse, actual credit to - the reserve will be done in handle_account_merged() */ - return GNUNET_OK; - } - if ( (NULL != deposit->exchange_base_url) && - (0 != strcmp (deposit->exchange_base_url, - TALER_ARL_exchange_url)) ) - { - /* credited reserve is at another exchange, do NOT credit here! */ - return GNUNET_OK; - } - - rs = setup_reserve (rc, - reserve_pub); - if (NULL == rs) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if ( (-1 != TALER_amount_cmp (&new_balance, - purse_total)) && - (-1 == TALER_amount_cmp (auditor_balance, - purse_total)) ) - { - /* new balance at or above purse_total - (and previous balance was below); thus - credit reserve with purse value! */ - TALER_ARL_amount_add (&rs->total_in, - &rs->total_in, - purse_total); - } - return GNUNET_OK; -} - - -/** - * Function called with details about purse - * merges that have been made, with - * the goal of auditing the purse merge execution. - * - * @param cls closure - * @param rowid unique serial ID for the deposit in our DB - * @param partner_base_url where is the reserve, NULL for this exchange - * @param amount total amount expected in the purse - * @param balance current balance in the purse (according to the auditor) - * @param flags purse flags - * @param merge_pub merge capability key - * @param reserve_pub reserve the merge affects - * @param merge_sig signature affirming the merge - * @param purse_pub purse key - * @param merge_timestamp when did the merge happen - * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop - */ -static enum GNUNET_GenericReturnValue -handle_purse_merged ( - void *cls, - uint64_t rowid, - const char *partner_base_url, - const struct TALER_Amount *amount, - const struct TALER_Amount *balance, - enum TALER_WalletAccountMergeFlags flags, - const struct TALER_PurseMergePublicKeyP *merge_pub, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_PurseMergeSignatureP *merge_sig, - const struct TALER_PurseContractPublicKeyP *purse_pub, - struct GNUNET_TIME_Timestamp merge_timestamp) -{ - struct ReserveContext *rc = cls; - struct ReserveSummary *rs; - char *reserve_url; - - /* should be monotonically increasing */ - GNUNET_assert (rowid >= ppr.last_purse_merges_serial_id); - ppr.last_purse_merges_serial_id = rowid + 1; - reserve_url - = TALER_reserve_make_payto (NULL == partner_base_url - ? TALER_ARL_exchange_url - : partner_base_url, - reserve_pub); - if (GNUNET_OK != - TALER_wallet_purse_merge_verify (reserve_url, - merge_timestamp, - purse_pub, - merge_pub, - merge_sig)) - { - GNUNET_free (reserve_url); - TALER_ARL_report (report_bad_sig_losses, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("operation", - "merge-purse"), - GNUNET_JSON_pack_uint64 ("row", - rowid), - TALER_JSON_pack_amount ("loss", - amount), - GNUNET_JSON_pack_data_auto ("key_pub", - merge_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, - amount); - return GNUNET_OK; - } - GNUNET_free (reserve_url); - if (TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE == - (flags & TALER_WAMF_MERGE_MODE_MASK)) - { - /* This just created the purse, actual credit to - the reserve will be done in handle_purse_deposits() */ - return GNUNET_OK; - } - if ( (NULL != partner_base_url) && - (0 != strcmp (partner_base_url, - TALER_ARL_exchange_url)) ) - { - /* credited reserve is at another exchange, do NOT credit here! */ - return GNUNET_OK; - } - rs = setup_reserve (rc, - reserve_pub); - if (NULL == rs) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (-1 == TALER_amount_cmp (balance, - amount)) - { - struct TALER_Amount loss; - - TALER_ARL_amount_subtract (&loss, - amount, - balance); - /* illegal merge, balance is still below total purse value */ - TALER_ARL_report (report_purse_balance_insufficient_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("operation", - "merge-purse"), - GNUNET_JSON_pack_uint64 ("row", - rowid), - TALER_JSON_pack_amount ("loss", - &loss), - GNUNET_JSON_pack_data_auto ("purse_pub", - purse_pub))); - TALER_ARL_amount_add (&total_balance_insufficient_loss, - &total_balance_insufficient_loss, - &loss); - return GNUNET_OK; - } - TALER_ARL_amount_add (&rs->total_in, - &rs->total_in, - amount); - // rs->a_expiration_date = FIXME: do we care? If so, set to what (so that the auditor no longer complains about the reserve not being closed) - return GNUNET_OK; -} - - -/** - * Function called with details about - * account merge requests that have been made, with - * the goal of auditing the account merge execution. - * - * @param cls closure - * @param rowid unique serial ID for the deposit in our DB - * @param reserve_pub reserve affected by the merge - * @param purse_pub purse being merged - * @param h_contract_terms hash over contract of the purse - * @param purse_expiration when would the purse expire - * @param amount total amount in the purse - * @param min_age minimum age of all coins deposited into the purse - * @param flags how was the purse created - * @param purse_fee if a purse fee was paid, how high is it - * @param merge_timestamp when was the merge approved - * @param reserve_sig signature by reserve approving the merge - * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop - */ -static enum GNUNET_GenericReturnValue -handle_account_merged ( - void *cls, - uint64_t rowid, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_PurseContractPublicKeyP *purse_pub, - const struct TALER_PrivateContractHashP *h_contract_terms, - struct GNUNET_TIME_Timestamp purse_expiration, - const struct TALER_Amount *amount, - uint32_t min_age, - enum TALER_WalletAccountMergeFlags flags, - const struct TALER_Amount *purse_fee, - struct GNUNET_TIME_Timestamp merge_timestamp, - const struct TALER_ReserveSignatureP *reserve_sig) -{ - struct ReserveContext *rc = cls; - struct ReserveSummary *rs; - - /* should be monotonically increasing */ - GNUNET_assert (rowid >= ppr.last_account_merges_serial_id); - ppr.last_account_merges_serial_id = rowid + 1; - if (GNUNET_OK != - TALER_wallet_account_merge_verify (merge_timestamp, - purse_pub, - purse_expiration, - h_contract_terms, - amount, - purse_fee, - min_age, - flags, - reserve_pub, - reserve_sig)) - { - TALER_ARL_report (report_bad_sig_losses, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("operation", - "account-merge"), - GNUNET_JSON_pack_uint64 ("row", - rowid), - TALER_JSON_pack_amount ("loss", - purse_fee), - GNUNET_JSON_pack_data_auto ("key_pub", - reserve_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, - purse_fee); - return GNUNET_OK; - } - rs = setup_reserve (rc, - reserve_pub); - if (NULL == rs) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_ARL_amount_add (&total_purse_fee_income, - &total_purse_fee_income, - purse_fee); - TALER_ARL_amount_add (&rs->total_out, - &rs->total_out, - purse_fee); - return GNUNET_OK; -} - - -/** - * Function called with details about - * history requests that have been made, with - * the goal of auditing the history request execution. - * - * @param cls closure - * @param rowid unique serial ID for the deposit in our DB - * @param history_fee fee paid for the request - * @param ts timestamp of the request - * @param reserve_pub reserve history was requested for - * @param reserve_sig signature approving the @a history_fee - * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop - */ -static enum GNUNET_GenericReturnValue -handle_history_request ( - void *cls, - uint64_t rowid, - const struct TALER_Amount *history_fee, - const struct GNUNET_TIME_Timestamp ts, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_ReserveSignatureP *reserve_sig) -{ - struct ReserveContext *rc = cls; - struct ReserveSummary *rs; - - /* should be monotonically increasing */ - GNUNET_assert (rowid >= ppr.last_history_requests_serial_id); - ppr.last_history_requests_serial_id = rowid + 1; - if (GNUNET_OK != - TALER_wallet_reserve_history_verify (ts, - history_fee, - reserve_pub, - reserve_sig)) - { - TALER_ARL_report (report_bad_sig_losses, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("operation", - "account-history"), - GNUNET_JSON_pack_uint64 ("row", - rowid), - TALER_JSON_pack_amount ("loss", - history_fee), - GNUNET_JSON_pack_data_auto ("key_pub", - reserve_pub))); - TALER_ARL_amount_add (&total_bad_sig_loss, - &total_bad_sig_loss, - history_fee); - return GNUNET_OK; - } - rs = setup_reserve (rc, - reserve_pub); - if (NULL == rs) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_ARL_amount_add (&total_history_fee_income, - &total_history_fee_income, - history_fee); - TALER_ARL_amount_add (&rs->total_out, - &rs->total_out, - history_fee); - return GNUNET_OK; -} - - -/** * Analyze reserves for being well-formed. * * @param cls NULL @@ -1705,24 +1671,20 @@ analyze_reserves (void *cls) { ppr_start = ppr; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", + "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", (unsigned long long) ppr.last_reserve_in_serial_id, (unsigned long long) ppr.last_reserve_out_serial_id, (unsigned long long) ppr.last_reserve_recoup_serial_id, + (unsigned long long) ppr.last_reserve_open_serial_id, (unsigned long long) ppr.last_reserve_close_serial_id, - (unsigned long long) ppr.last_purse_merges_serial_id, - (unsigned long long) ppr.last_purse_deposits_serial_id, + (unsigned long long) ppr.last_purse_decisions_serial_id, (unsigned long long) ppr.last_account_merges_serial_id, - (unsigned long long) ppr.last_history_requests_serial_id, - (unsigned long long) ppr.last_close_requests_serial_id); + (unsigned long long) ppr.last_history_requests_serial_id); } rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; qsx = TALER_ARL_adb->get_reserve_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_withdraw_fee_income, - &total_purse_fee_income, - &total_history_fee_income); + &balance); if (qsx < 0) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); @@ -1732,7 +1694,6 @@ analyze_reserves (void *cls) GNUNET_NO); rc.revoked = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_NO); - qs = TALER_ARL_edb->select_reserves_in_above_serial_id ( TALER_ARL_edb->cls, ppr.last_reserve_in_serial_id, @@ -1763,42 +1724,45 @@ analyze_reserves (void *cls) GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } - qs = TALER_ARL_edb->select_reserve_closed_above_serial_id ( + qs = TALER_ARL_edb->select_reserve_open_above_serial_id ( TALER_ARL_edb->cls, - ppr.last_reserve_close_serial_id, - &handle_reserve_closed, + ppr.last_reserve_open_serial_id, + &handle_reserve_open, &rc); if (qs < 0) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } - qs = TALER_ARL_edb->select_purse_deposits_above_serial_id ( + qs = TALER_ARL_edb->select_reserve_closed_above_serial_id ( TALER_ARL_edb->cls, - ppr.last_purse_deposits_serial_id, - &handle_purse_deposits, + ppr.last_reserve_close_serial_id, + &handle_reserve_closed, &rc); if (qs < 0) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } - /* Charge purse fee! */ - qs = TALER_ARL_edb->select_account_merges_above_serial_id ( - TALER_ARL_edb->cls, - ppr.last_account_merges_serial_id, - &handle_account_merged, - &rc); - if (qs < 0) + /* process purse_decisions (to credit reserve) */ + if (0 > + (qs = TALER_ARL_edb->select_purse_decisions_above_serial_id ( + TALER_ARL_edb->cls, + ppr.last_purse_decisions_serial_id, + false, /* only go for merged purses! */ + &purse_decision_cb, + &rc))) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } - /* Credit purse value (if last op)! */ - qs = TALER_ARL_edb->select_purse_merges_above_serial_id ( + if (0 > rc.qs) + return rc.qs; + /* Charge purse fee! */ + qs = TALER_ARL_edb->select_account_merges_above_serial_id ( TALER_ARL_edb->cls, - ppr.last_purse_merges_serial_id, - &handle_purse_merged, + ppr.last_account_merges_serial_id, + &handle_account_merged, &rc); if (qs < 0) { @@ -1817,7 +1781,7 @@ analyze_reserves (void *cls) return qs; } #if 0 - /* FIXME #7269 (support for explicit reserve closure request) */ + /* FIXME #7269 (support for explicit reserve closure request) -- needed??? */ qs = TALER_ARL_edb->select_close_requests_above_serial_id ( TALER_ARL_edb->cls, ppr.last_close_requests_serial_id, @@ -1836,27 +1800,19 @@ analyze_reserves (void *cls) GNUNET_CONTAINER_multihashmap_size (rc.reserves)); GNUNET_CONTAINER_multihashmap_destroy (rc.reserves); GNUNET_CONTAINER_multihashmap_destroy (rc.revoked); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs) return qs; - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx) { qs = TALER_ARL_adb->insert_reserve_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_withdraw_fee_income, - &total_purse_fee_income, - &total_history_fee_income); + &balance); } else { qs = TALER_ARL_adb->update_reserve_summary (TALER_ARL_adb->cls, &TALER_ARL_master_pub, - &total_escrow_balance, - &total_withdraw_fee_income, - &total_purse_fee_income, - &total_history_fee_income); + &balance); } if (0 >= qs) { @@ -1879,16 +1835,15 @@ analyze_reserves (void *cls) return qs; } GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", + "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", (unsigned long long) ppr.last_reserve_in_serial_id, (unsigned long long) ppr.last_reserve_out_serial_id, (unsigned long long) ppr.last_reserve_recoup_serial_id, + (unsigned long long) ppr.last_reserve_open_serial_id, (unsigned long long) ppr.last_reserve_close_serial_id, - (unsigned long long) ppr.last_purse_merges_serial_id, - (unsigned long long) ppr.last_purse_deposits_serial_id, + (unsigned long long) ppr.last_purse_decisions_serial_id, (unsigned long long) ppr.last_account_merges_serial_id, - (unsigned long long) ppr.last_history_requests_serial_id, - (unsigned long long) ppr.last_close_requests_serial_id); + (unsigned long long) ppr.last_history_requests_serial_id); return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -1934,22 +1889,26 @@ run (void *cls, "Starting audit\n"); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_escrow_balance)); + &balance.reserve_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_irregular_recoups)); + &balance.reserve_loss)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_withdraw_fee_income)); + &balance.withdraw_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_history_fee_income)); + &balance.close_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_purse_fee_income)); + &balance.purse_fee_balance)); GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, - &total_balance_insufficient_loss)); + &balance.open_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &balance.history_fee_balance)); + // REVIEW: GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, &total_balance_summary_delta_plus)); @@ -1968,6 +1927,7 @@ run (void *cls, GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, &total_bad_sig_loss)); + GNUNET_assert (NULL != (report_row_inconsistencies = json_array ())); GNUNET_assert (NULL != @@ -1999,15 +1959,6 @@ run (void *cls, } TALER_ARL_done ( GNUNET_JSON_PACK ( - GNUNET_JSON_pack_array_steal ( - "reserve_balance_insufficient_inconsistencies", - report_reserve_balance_insufficient_inconsistencies), - GNUNET_JSON_pack_array_steal ( - "purse_balance_insufficient_inconsistencies", - report_purse_balance_insufficient_inconsistencies), - /* Tested in test-auditor.sh #3 */ - TALER_JSON_pack_amount ("total_loss_balance_insufficient", - &total_balance_insufficient_loss), /* Tested in test-auditor.sh #3 */ GNUNET_JSON_pack_array_steal ( "reserve_balance_summary_wrong_inconsistencies", @@ -2016,26 +1967,47 @@ run (void *cls, &total_balance_summary_delta_plus), TALER_JSON_pack_amount ("total_balance_summary_delta_minus", &total_balance_summary_delta_minus), + /* Tested in test-auditor.sh #21 */ + TALER_JSON_pack_amount ("total_balance_reserve_not_closed", + &total_balance_reserve_not_closed), + /* Tested in test-auditor.sh #7 */ + TALER_JSON_pack_amount ("total_bad_sig_loss", + &total_bad_sig_loss), + TALER_JSON_pack_amount ("total_arithmetic_delta_plus", + &total_arithmetic_delta_plus), + TALER_JSON_pack_amount ("total_arithmetic_delta_minus", + &total_arithmetic_delta_minus), + + /* Global 'balances' */ TALER_JSON_pack_amount ("total_escrow_balance", - &total_escrow_balance), + &balance.reserve_balance), + /* Tested in test-auditor.sh #3 */ + TALER_JSON_pack_amount ("total_irregular_loss", + &balance.reserve_loss), TALER_JSON_pack_amount ("total_withdraw_fee_income", - &total_withdraw_fee_income), - TALER_JSON_pack_amount ("total_history_fee_income", - &total_history_fee_income), + &balance.withdraw_fee_balance), + TALER_JSON_pack_amount ("total_close_fee_income", + &balance.close_fee_balance), TALER_JSON_pack_amount ("total_purse_fee_income", - &total_purse_fee_income), + &balance.purse_fee_balance), + TALER_JSON_pack_amount ("total_open_fee_income", + &balance.open_fee_balance), + TALER_JSON_pack_amount ("total_history_fee_income", + &balance.history_fee_balance), + + /* Detailed report tables */ + GNUNET_JSON_pack_array_steal ( + "reserve_balance_insufficient_inconsistencies", + report_reserve_balance_insufficient_inconsistencies), + GNUNET_JSON_pack_array_steal ( + "purse_balance_insufficient_inconsistencies", + report_purse_balance_insufficient_inconsistencies), /* Tested in test-auditor.sh #21 */ GNUNET_JSON_pack_array_steal ("reserve_not_closed_inconsistencies", report_reserve_not_closed_inconsistencies), - /* Tested in test-auditor.sh #21 */ - TALER_JSON_pack_amount ("total_balance_reserve_not_closed", - &total_balance_reserve_not_closed), /* Tested in test-auditor.sh #7 */ GNUNET_JSON_pack_array_steal ("bad_sig_losses", report_bad_sig_losses), - /* Tested in test-auditor.sh #7 */ - TALER_JSON_pack_amount ("total_bad_sig_loss", - &total_bad_sig_loss), /* Tested in test-revocation.sh #4 */ GNUNET_JSON_pack_array_steal ("row_inconsistencies", report_row_inconsistencies), @@ -2045,42 +2017,44 @@ run (void *cls, denomination_key_validity_withdraw_inconsistencies), GNUNET_JSON_pack_array_steal ("amount_arithmetic_inconsistencies", report_amount_arithmetic_inconsistencies), - TALER_JSON_pack_amount ("total_arithmetic_delta_plus", - &total_arithmetic_delta_plus), - TALER_JSON_pack_amount ("total_arithmetic_delta_minus", - &total_arithmetic_delta_minus), + + /* Information about audited range ... */ TALER_JSON_pack_time_abs_human ("auditor_start_time", start_time), TALER_JSON_pack_time_abs_human ("auditor_end_time", GNUNET_TIME_absolute_get ()), - TALER_JSON_pack_amount ("total_irregular_recoups", - &total_irregular_recoups), GNUNET_JSON_pack_uint64 ("start_ppr_reserve_in_serial_id", ppr_start.last_reserve_in_serial_id), GNUNET_JSON_pack_uint64 ("start_ppr_reserve_out_serial_id", ppr_start.last_reserve_out_serial_id), GNUNET_JSON_pack_uint64 ("start_ppr_reserve_recoup_serial_id", ppr_start.last_reserve_recoup_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_reserve_open_serial_id", + ppr_start.last_reserve_open_serial_id), GNUNET_JSON_pack_uint64 ("start_ppr_reserve_close_serial_id", ppr_start.last_reserve_close_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_purse_decisions_serial_id", + ppr_start.last_purse_decisions_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_account_merges_serial_id", + ppr_start.last_account_merges_serial_id), + GNUNET_JSON_pack_uint64 ("start_ppr_history_requests_serial_id", + ppr_start.last_history_requests_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_reserve_in_serial_id", ppr.last_reserve_in_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_reserve_out_serial_id", ppr.last_reserve_out_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_reserve_recoup_serial_id", ppr.last_reserve_recoup_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_reserve_open_serial_id", + ppr.last_reserve_open_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_reserve_close_serial_id", ppr.last_reserve_close_serial_id), - GNUNET_JSON_pack_uint64 ("end_ppr_purse_merges_serial_id", - ppr.last_purse_merges_serial_id), - GNUNET_JSON_pack_uint64 ("end_ppr_purse_deposits_serial_id", - ppr.last_purse_deposits_serial_id), + GNUNET_JSON_pack_uint64 ("end_ppr_purse_decisions_serial_id", + ppr.last_purse_decisions_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_account_merges_serial_id", ppr.last_account_merges_serial_id), GNUNET_JSON_pack_uint64 ("end_ppr_history_requests_serial_id", - ppr.last_history_requests_serial_id), - GNUNET_JSON_pack_uint64 ("end_ppr_close_requests_serial_id", - ppr.last_close_requests_serial_id))); + ppr.last_history_requests_serial_id))); } diff --git a/src/auditor/taler-helper-auditor-wire.c b/src/auditor/taler-helper-auditor-wire.c index 7d5e8a933..081ee01e6 100644 --- a/src/auditor/taler-helper-auditor-wire.c +++ b/src/auditor/taler-helper-auditor-wire.c @@ -93,14 +93,9 @@ struct WireAccount struct TALER_AUDITORDB_WireAccountProgressPoint start_pp; /** - * Where we are in the inbound (CREDIT) transaction history. + * Where we are in the transaction history. */ - uint64_t in_wire_off; - - /** - * Where we are in the inbound (DEBIT) transaction history. - */ - uint64_t out_wire_off; + struct TALER_AUDITORDB_BankAccountProgressPoint wire_off; /** * Return value when we got this account's progress point. @@ -772,16 +767,14 @@ commit (enum GNUNET_DB_QueryStatus qs) &TALER_ARL_master_pub, wa->ai->section_name, &wa->pp, - wa->in_wire_off, - wa->out_wire_off); + &wa->wire_off); else qs = TALER_ARL_adb->insert_wire_auditor_account_progress ( TALER_ARL_adb->cls, &TALER_ARL_master_pub, wa->ai->section_name, &wa->pp, - wa->in_wire_off, - wa->out_wire_off); + &wa->wire_off); if (0 >= qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -1520,7 +1513,7 @@ history_debit_cb (void *cls, TALER_amount2s (&details->amount), TALER_B2S (&details->wtid)); /* Update offset */ - wa->out_wire_off = row_off; + wa->wire_off.out_wire_off = row_off; slen = strlen (details->credit_account_uri) + 1; roi = GNUNET_malloc (sizeof (struct ReserveOutInfo) + slen); @@ -1594,7 +1587,7 @@ process_debits (void *cls) // (CG: used to be INT64_MAX, changed by MS to INT32_MAX, why? To be discussed with him!) wa->dhh = TALER_BANK_debit_history (ctx, wa->ai->auth, - wa->out_wire_off, + wa->wire_off.out_wire_off, INT32_MAX, GNUNET_TIME_UNIT_ZERO, &history_debit_cb, @@ -1846,7 +1839,7 @@ history_credit_cb (void *cls, } /* Update offset */ - wa->in_wire_off = row_off; + wa->wire_off.in_wire_off = row_off; /* compare records with expected data */ if (0 != GNUNET_memcmp (&details->reserve_pub, &rii->details.reserve_pub)) @@ -2032,7 +2025,7 @@ process_credits (void *cls) // (CG: used to be INT64_MAX, changed by MS to INT32_MAX, why? To be discussed with him!) wa->chh = TALER_BANK_credit_history (ctx, wa->ai->auth, - wa->in_wire_off, + wa->wire_off.in_wire_off, INT32_MAX, GNUNET_TIME_UNIT_ZERO, &history_credit_cb, @@ -2084,12 +2077,14 @@ reserve_closed_cb (void *cls, const struct TALER_Amount *closing_fee, const struct TALER_ReservePublicKeyP *reserve_pub, const char *receiver_account, - const struct TALER_WireTransferIdentifierRawP *wtid) + const struct TALER_WireTransferIdentifierRawP *wtid, + uint64_t close_request_row) { struct ReserveClosure *rc; struct GNUNET_HashCode key; (void) cls; + (void) close_request_row; rc = GNUNET_new (struct ReserveClosure); if (TALER_ARL_SR_INVALID_NEGATIVE == TALER_ARL_amount_subtract_neg (&rc->amount, @@ -2210,8 +2205,7 @@ begin_transaction (void) &TALER_ARL_master_pub, wa->ai->section_name, &wa->pp, - &wa->in_wire_off, - &wa->out_wire_off); + &wa->wire_off); if (0 > wa->qsx) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == wa->qsx); diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh index c9895b6f3..34a3980d9 100755 --- a/src/auditor/test-auditor.sh +++ b/src/auditor/test-auditor.sh @@ -388,7 +388,7 @@ function test_0() { then exit_fail "Wrong total bad sig loss from aggregation, got unexpected loss of $LOSS" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:0" then exit_fail "Wrong total bad sig loss from coins, got unexpected loss of $LOSS" @@ -613,7 +613,7 @@ function test_3() { exit_fail "Expected reserve balance summary amount wrong, got $EXPECTED (exchange)" fi - WIRED=`jq -r .total_loss_balance_insufficient < test-audit-reserves.json` + WIRED=`jq -r .total_irregular_loss < test-audit-reserves.json` if test $WIRED != "TESTKUDOS:0" then exit_fail "Wrong total loss from insufficient balance, got $WIRED" @@ -690,7 +690,7 @@ function test_4() { exit_fail "Wrong operation, got $OP" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:3" then exit_fail "Wrong total bad sig loss, got $LOSS" @@ -734,7 +734,7 @@ function test_5() { exit_fail "Wrong operation, got $OP" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:3" then exit_fail "Wrong total bad sig loss, got $LOSS" @@ -776,7 +776,7 @@ function test_6() { exit_fail "Wrong operation, got $OP" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS == "TESTKUDOS:0" then exit_fail "Wrong total bad sig loss, got $LOSS" @@ -1093,7 +1093,7 @@ function test_13() { fi LOSS=`jq -er .bad_sig_losses[0].loss < test-audit-coins.json` - TOTAL_LOSS=`jq -er .total_bad_sig_loss < test-audit-coins.json` + TOTAL_LOSS=`jq -er .irregular_loss < test-audit-coins.json` if test x$LOSS != x$TOTAL_LOSS then exit_fail "Loss inconsistent, got $LOSS and $TOTAL_LOSS" @@ -1644,7 +1644,7 @@ function test_26() { exit_fail "Wrong operation, got $OP" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:3" then exit_fail "Wrong total bad sig loss, got $LOSS" @@ -1805,7 +1805,7 @@ function test_31() { run_audit aggregator echo -n "Testing inconsistency detection... " - AMOUNT=`jq -r .total_bad_sig_loss < test-audit-coins.json` + AMOUNT=`jq -r .irregular_loss < test-audit-coins.json` if test "x$AMOUNT" == "xTESTKUDOS:0" then exit_fail "Reported total amount wrong: $AMOUNT" @@ -1892,7 +1892,7 @@ function test_33() { then exit_fail "Wrong total bad sig loss from aggregation, got unexpected loss of $LOSS" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:0" then exit_fail "Wrong total bad sig loss from coins, got unexpected loss of $LOSS" diff --git a/src/auditor/test-revocation.sh b/src/auditor/test-revocation.sh index e21b94878..22d1c86ab 100755 --- a/src/auditor/test-revocation.sh +++ b/src/auditor/test-revocation.sh @@ -337,7 +337,7 @@ function test_0() { then exit_fail "Wrong total bad sig loss from aggregation, got unexpected loss of $LOSS" fi - LOSS=`jq -r .total_bad_sig_loss < test-audit-coins.json` + LOSS=`jq -r .irregular_loss < test-audit-coins.json` if test $LOSS != "TESTKUDOS:0" then exit_fail "Wrong total bad sig loss from coins, got unexpected loss of $LOSS" @@ -561,7 +561,7 @@ function test_4() { echo -n "Testing inconsistency detection... " # Coin spent exceeded coin's value jq -e .bad_sig_losses[0] < test-audit-coins.json > /dev/null || exit_fail "Bad recoup not detected" - AMOUNT=`jq -r .total_bad_sig_losses < test-audit-coins.json` + AMOUNT=`jq -r .irregular_loss < test-audit-coins.json` if test $AMOUNT == "TESTKUDOS:0" then exit_fail "Total bad sig losses are wrong" |