diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2 index c31af135f..d40dc0c8f 100644 --- a/contrib/auditor-report.tex.j2 +++ b/contrib/auditor-report.tex.j2 @@ -140,9 +140,9 @@ In that time, the wire auditor processed the following table ranges: \end{center} The total credits to the exchange processed in -this audit run was {\bf {{ wire.total_wire_in }}. +this audit run was {\bf {{ wire.total_wire_in }}}. The total debits initiated by the exchange processed in -this audit run was {\bf {{ wire.total_wire_out }}. +this audit run was {\bf {{ wire.total_wire_out }}}. \section{Operations} @@ -169,9 +169,8 @@ Loss (actualized risk from recoups) is {\bf {{ coins.total_recoup_loss }}}. \noindent -Recoups of non-revoked coins are at -{\bf {{ coins.total_irregular_recoups }}} (coins) -plus {\bf {{ reserves.total_irregular_recoups }}} (reserves). +Losses from irregular reserve operations are at +{\bf {{ reserves.total_irregular_loss }}} (reserves). \section{Income} @@ -613,7 +612,7 @@ compromise resulting in proportional financial losses to the exchange. \endfoot \hline {\bf Total loss} & - {{ reserves.total_loss_balance_insufficient }} \\ + {{ reserves.total_irregular_loss }} \\ \caption{Reserves with withdrawals higher than reserve funding.} \label{table:reserve:balance_insufficient} \endlastfoot @@ -782,7 +781,7 @@ invalid and the amount involved should be considered lost. \endfoot \hline \multicolumn{2}{l}{ {\bf Total losses} } & - {\bf {{ coins.total_bad_sig_loss}} } \\ + {\bf {{ coins.irregular_loss}} } \\ \caption{Losses from operations performed on coins without proper signatures.} \label{table:bad_signature_losses} \endlastfoot diff --git a/contrib/gana b/contrib/gana index 3a2899981..9657bf77d 160000 --- a/contrib/gana +++ b/contrib/gana @@ -1 +1 @@ -Subproject commit 3a2899981056f1d349730464b7f7172ffcc9671f +Subproject commit 9657bf77de05c0ac17ff39629306a604066b21de 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,21 +65,6 @@ 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 = \ - $(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_coins_SOURCES = \ taler-helper-auditor-coins.c taler_helper_auditor_coins_LDADD = \ @@ -125,6 +110,38 @@ taler_helper_auditor_deposits_LDADD = \ -lgnunetutil \ $(XLIB) +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 \ + $(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_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,50 +113,15 @@ 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. */ 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. + * Information about the circulation. */ - struct TALER_Amount denom_balance; - - /** - * Total losses made (once coins deposited exceed - * coins withdrawn and thus the @e denom_balance is - * effectively negative). - */ - 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 (&total_risk, - &total_risk, + TALER_ARL_amount_add (&balance.risk, + &balance.risk, &issue->value); - TALER_ARL_amount_add (&ds->denom_risk, - &ds->denom_risk, + ds->dcd.num_issued++; + TALER_ARL_amount_add (&ds->dcd.denom_balance, + &ds->dcd.denom_balance, + &issue->value); + 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, + &balance.loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_set_zero (TALER_ARL_currency, + &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_arithmetic_delta_minus)); - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (TALER_ARL_currency, - &total_bad_sig_loss)); 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 +*/ +/** + * @file auditor/taler-helper-auditor-purses.c + * @brief audits the purses of an exchange database + * @author Christian Grothoff + */ +#include "platform.h" +#include +#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 @@ -74,11 +74,6 @@ 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. */ @@ -86,13 +81,15 @@ 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. + * Expected reserve balances. */ -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. - */ -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->a_withdraw_fee_balance)); + &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->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 ( @@ -867,6 +862,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) + + 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) { - GNUNET_break (! rs->had_ri); - report_row_inconsistency ("reserves_close", - rowid, - "target account not verified, auditor does not know reserve"); + 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,571 +1129,9 @@ handle_reserve_closed ( /** - * Check that the reserve summary matches what the exchange database - * thinks about the reserve, and update our own state of the reserve. - * - * Remove all reserves that we are happy with from the DB. - * - * @param cls our `struct ReserveContext` - * @param key hash of the reserve public key - * @param value a `struct ReserveSummary` - * @return #GNUNET_OK to process more entries - */ -static enum GNUNET_GenericReturnValue -verify_reserve_balance (void *cls, - const struct GNUNET_HashCode *key, - void *value) -{ - struct ReserveContext *rc = cls; - struct ReserveSummary *rs = value; - struct TALER_Amount balance; - struct TALER_Amount nbalance; - enum GNUNET_DB_QueryStatus qs; - int 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, - &rs->total_in, - &rs->balance_at_previous_audit); - if (TALER_ARL_SR_INVALID_NEGATIVE == - TALER_ARL_amount_subtract_neg (&nbalance, - &balance, - &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, - &loss); - TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - &rs->reserve_pub), - TALER_JSON_pack_amount ("loss", - &loss))); - /* Continue with a reserve balance of zero */ - GNUNET_assert (GNUNET_OK == - TALER_amount_set_zero (balance.currency, - &nbalance)); - } - - if (internal_checks) - { - /* Now check OUR balance calculation vs. the one the exchange has - in its database. This can only be done when we are doing an - internal audit, as otherwise the balance of the 'reserves' table - is not replicated at the auditor. */ - struct TALER_EXCHANGEDB_Reserve reserve; - - reserve.pub = rs->reserve_pub; - qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls, - &reserve); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - /* If the exchange doesn't have this reserve in the summary, it - is like the exchange 'lost' that amount from its records, - making an illegitimate gain over the amount it dropped. - We don't add the amount to some total simply because it is - not an actualized gain and could be trivially corrected by - restoring the summary. */ - TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - &rs->reserve_pub), - TALER_JSON_pack_amount ("gain", - &nbalance))); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - GNUNET_break (0); - qs = GNUNET_DB_STATUS_HARD_ERROR; - } - rc->qs = qs; - } - else - { - /* Check that exchange's balance matches our expected balance for the reserve */ - if (0 != TALER_amount_cmp (&nbalance, - &reserve.balance)) - { - struct TALER_Amount delta; - - if (0 < TALER_amount_cmp (&nbalance, - &reserve.balance)) - { - /* balance > reserve.balance */ - TALER_ARL_amount_subtract (&delta, - &nbalance, - &reserve.balance); - TALER_ARL_amount_add (&total_balance_summary_delta_plus, - &total_balance_summary_delta_plus, - &delta); - } - else - { - /* balance < reserve.balance */ - TALER_ARL_amount_subtract (&delta, - &reserve.balance, - &nbalance); - TALER_ARL_amount_add (&total_balance_summary_delta_minus, - &total_balance_summary_delta_minus, - &delta); - } - TALER_ARL_report (report_reserve_balance_summary_wrong_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - &rs->reserve_pub), - TALER_JSON_pack_amount ("exchange", - &reserve.balance), - TALER_JSON_pack_amount ("auditor", - &nbalance))); - } - } - } /* 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) */ - if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD, - <, - GNUNET_TIME_absolute_get_duration ( - rs->a_expiration_date.abs_time))) - { - /* Reserve is expired */ - struct TALER_Amount cfee; - - if ( (NULL != rs->sender_account) && - (GNUNET_OK == - get_closing_fee (rs->sender_account, - rs->a_expiration_date, - &cfee)) ) - { - /* We got the closing fee */ - if (1 == TALER_amount_cmp (&nbalance, - &cfee)) - { - /* remaining balance (according to us) exceeds closing fee */ - TALER_ARL_amount_add (&total_balance_reserve_not_closed, - &total_balance_reserve_not_closed, - &nbalance); - TALER_ARL_report ( - report_reserve_not_closed_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - &rs->reserve_pub), - TALER_JSON_pack_amount ("balance", - &nbalance), - TALER_JSON_pack_time_abs_human ("expiration_time", - rs->a_expiration_date.abs_time))); - } - } - else - { - /* We failed to determine the closing fee, complain! */ - TALER_ARL_amount_add (&total_balance_reserve_not_closed, - &total_balance_reserve_not_closed, - &nbalance); - TALER_ARL_report ( - report_reserve_not_closed_inconsistencies, - GNUNET_JSON_PACK ( - GNUNET_JSON_pack_data_auto ("reserve_pub", - &rs->reserve_pub), - TALER_JSON_pack_amount ("balance", - &nbalance), - TALER_JSON_pack_time_abs_human ("expiration_time", - rs->a_expiration_date.abs_time), - GNUNET_JSON_pack_string ("diagnostic", - "could not determine closing fee"))); - } - } - - /* 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, - &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, - &rs->total_out)) - { - /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!) - to be withdrawn more than it was IN TOTAL ever given (exchange balance - 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 */ - 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)); - } - else - { - total_escrow_balance = r; - } - } - - if ( (0ULL == balance.value) && - (0U == balance.fraction) ) - { - /* 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)); - qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls, - &rs->reserve_pub, - &TALER_ARL_master_pub); - if (0 >= qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - ret = GNUNET_SYSERR; - rc->qs = qs; - } - } - 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)); - } - } - else - { - /* balance is non-zero, persist for future audits */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Remembering final balance of reserve `%s' as %s\n", - TALER_B2S (&rs->reserve_pub), - TALER_amount2s (&nbalance)); - 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->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->a_expiration_date, - rs->sender_account); - if (0 >= qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - ret = GNUNET_SYSERR; - rc->qs = qs; - } - } - - GNUNET_assert (GNUNET_YES == - GNUNET_CONTAINER_multihashmap_remove (rc->reserves, - key, - rs)); - GNUNET_free (rs->sender_account); - GNUNET_free (rs); - 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 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. + * 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 @@ -1588,6 +1195,9 @@ handle_account_merged ( 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) @@ -1595,8 +1205,11 @@ handle_account_merged ( GNUNET_break (0); return GNUNET_SYSERR; } - TALER_ARL_amount_add (&total_purse_fee_income, - &total_purse_fee_income, + 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, @@ -1605,6 +1218,44 @@ handle_account_merged ( } +/** + * 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 @@ -1661,8 +1312,11 @@ handle_history_request ( GNUNET_break (0); return GNUNET_SYSERR; } - TALER_ARL_amount_add (&total_history_fee_income, - &total_history_fee_income, + 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, @@ -1671,6 +1325,318 @@ handle_history_request ( } +/** + * Check that the reserve summary matches what the exchange database + * thinks about the reserve, and update our own state of the reserve. + * + * Remove all reserves that we are happy with from the DB. + * + * @param cls our `struct ReserveContext` + * @param key hash of the reserve public key + * @param value a `struct ReserveSummary` + * @return #GNUNET_OK to process more entries + */ +static enum GNUNET_GenericReturnValue +verify_reserve_balance (void *cls, + const struct GNUNET_HashCode *key, + void *value) +{ + struct ReserveContext *rc = cls; + struct ReserveSummary *rs = value; + struct TALER_Amount mbalance; + struct TALER_Amount nbalance; + enum GNUNET_DB_QueryStatus qs; + 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 (&mbalance, + &rs->total_in, + &rs->prev_balance.reserve_balance); + if (TALER_ARL_SR_INVALID_NEGATIVE == + TALER_ARL_amount_subtract_neg (&nbalance, + &mbalance, + &rs->total_out)) + { + struct TALER_Amount loss; + + TALER_ARL_amount_subtract (&loss, + &rs->total_out, + &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 ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + &rs->reserve_pub), + TALER_JSON_pack_amount ("loss", + &loss))); + /* Continue with a reserve balance of zero */ + GNUNET_assert (GNUNET_OK == + 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) + { + /* Now check OUR balance calculation vs. the one the exchange has + in its database. This can only be done when we are doing an + internal audit, as otherwise the balance of the 'reserves' table + is not replicated at the auditor. */ + struct TALER_EXCHANGEDB_Reserve reserve; + + reserve.pub = rs->reserve_pub; + qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls, + &reserve); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + /* If the exchange doesn't have this reserve in the summary, it + is like the exchange 'lost' that amount from its records, + making an illegitimate gain over the amount it dropped. + We don't add the amount to some total simply because it is + not an actualized gain and could be trivially corrected by + restoring the summary. */ + TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + &rs->reserve_pub), + TALER_JSON_pack_amount ("gain", + &nbalance))); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + GNUNET_break (0); + qs = GNUNET_DB_STATUS_HARD_ERROR; + } + rc->qs = qs; + } + else + { + /* Check that exchange's balance matches our expected balance for the reserve */ + if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance, + &reserve.balance)) + { + struct TALER_Amount delta; + + if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance, + &reserve.balance)) + { + /* balance > reserve.balance */ + TALER_ARL_amount_subtract (&delta, + &rs->curr_balance.reserve_balance, + &reserve.balance); + TALER_ARL_amount_add (&total_balance_summary_delta_plus, + &total_balance_summary_delta_plus, + &delta); + } + else + { + /* balance < reserve.balance */ + TALER_ARL_amount_subtract (&delta, + &reserve.balance, + &rs->curr_balance.reserve_balance); + TALER_ARL_amount_add (&total_balance_summary_delta_minus, + &total_balance_summary_delta_minus, + &delta); + } + TALER_ARL_report (report_reserve_balance_summary_wrong_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + &rs->reserve_pub), + TALER_JSON_pack_amount ("exchange", + &reserve.balance), + TALER_JSON_pack_amount ("auditor", + &rs->curr_balance. + reserve_balance))); + } + } + } /* 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) */ + if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD, + <, + GNUNET_TIME_absolute_get_duration ( + rs->a_expiration_date.abs_time))) + { + /* Reserve is expired */ + struct TALER_Amount cfee; + + if ( (NULL != rs->sender_account) && + (GNUNET_OK == + get_closing_fee (rs->sender_account, + rs->a_expiration_date, + &cfee)) ) + { + /* We got the closing fee */ + if (1 == TALER_amount_cmp (&nbalance, + &cfee)) + { + /* remaining balance (according to us) exceeds closing fee */ + TALER_ARL_amount_add (&total_balance_reserve_not_closed, + &total_balance_reserve_not_closed, + &nbalance); + TALER_ARL_report ( + report_reserve_not_closed_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + &rs->reserve_pub), + TALER_JSON_pack_amount ("balance", + &nbalance), + TALER_JSON_pack_time_abs_human ("expiration_time", + rs->a_expiration_date.abs_time))); + } + } + else + { + /* We failed to determine the closing fee, complain! */ + TALER_ARL_amount_add (&total_balance_reserve_not_closed, + &total_balance_reserve_not_closed, + &nbalance); + TALER_ARL_report ( + report_reserve_not_closed_inconsistencies, + GNUNET_JSON_PACK ( + GNUNET_JSON_pack_data_auto ("reserve_pub", + &rs->reserve_pub), + TALER_JSON_pack_amount ("balance", + &nbalance), + TALER_JSON_pack_time_abs_human ("expiration_time", + rs->a_expiration_date.abs_time), + GNUNET_JSON_pack_string ("diagnostic", + "could not determine closing fee"))); + } + } + + /* 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); + { + struct TALER_Amount r; + + if (TALER_ARL_SR_INVALID_NEGATIVE == + TALER_ARL_amount_subtract_neg (&r, + &balance.reserve_balance, + &rs->total_out)) + { + /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!) + to be withdrawn more than it was IN TOTAL ever given (exchange balance + went negative!). Woopsie. Calculate how badly it went and log. */ + report_amount_arithmetic_inconsistency ("global escrow balance", + 0, + &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, + &balance.reserve_balance)); + } + else + { + balance.reserve_balance = r; + } + } + + 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 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); + if (0 >= qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + ret = GNUNET_SYSERR; + rc->qs = qs; + } + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Final balance of reserve `%s' is zero, no need to remember it\n", + TALER_B2S (&rs->reserve_pub)); + } + } + else + { + /* balance is non-zero, persist for future audits */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Remembering final balance of reserve `%s' as %s\n", + TALER_B2S (&rs->reserve_pub), + 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, + &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, + &rs->prev_balance, + rs->a_expiration_date, + rs->sender_account); + if (0 >= qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + ret = GNUNET_SYSERR; + rc->qs = qs; + } + } + /* now we can discard the cached entry */ + GNUNET_assert (GNUNET_YES == + GNUNET_CONTAINER_multihashmap_remove (rc->reserves, + key, + rs)); + GNUNET_free (rs->sender_account); + GNUNET_free (rs); + return ret; +} + + /** * Analyze reserves for being well-formed. * @@ -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,6 +1724,16 @@ analyze_reserves (void *cls) GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); return qs; } + qs = TALER_ARL_edb->select_reserve_open_above_serial_id ( + TALER_ARL_edb->cls, + 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_reserve_closed_above_serial_id ( TALER_ARL_edb->cls, ppr.last_reserve_close_serial_id, @@ -1773,16 +1744,20 @@ analyze_reserves (void *cls) 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) + /* 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; } + if (0 > rc.qs) + return rc.qs; /* Charge purse fee! */ qs = TALER_ARL_edb->select_account_merges_above_serial_id ( TALER_ARL_edb->cls, @@ -1794,17 +1769,6 @@ analyze_reserves (void *cls) 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 ( - 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; - } /* Charge history fee! */ qs = TALER_ARL_edb->select_history_requests_above_serial_id ( TALER_ARL_edb->cls, @@ -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), - TALER_JSON_pack_amount ("total_escrow_balance", - &total_escrow_balance), - TALER_JSON_pack_amount ("total_withdraw_fee_income", - &total_withdraw_fee_income), - TALER_JSON_pack_amount ("total_history_fee_income", - &total_history_fee_income), - TALER_JSON_pack_amount ("total_purse_fee_income", - &total_purse_fee_income), - /* 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), + 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), + /* Tested in test-auditor.sh #3 */ + 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), @@ -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" diff --git a/src/auditordb/9999.sql b/src/auditordb/9999.sql deleted file mode 100644 index d6add4b20..000000000 --- a/src/auditordb/9999.sql +++ /dev/null @@ -1,53 +0,0 @@ --- --- This file is part of TALER --- Copyright (C) 2014--2020 Taler Systems SA --- --- TALER is free software; you can redistribute it and/or modify it under the --- terms of the GNU General Public License as published by the Free Software --- Foundation; either version 3, or (at your option) any later version. --- --- TALER is distributed in the hope that it will be useful, but WITHOUT ANY --- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR --- A PARTICULAR PURPOSE. See the GNU General Public License for more details. --- --- You should have received a copy of the GNU General Public License along with --- TALER; see the file COPYING. If not, see --- - --- Everything in one big transaction -BEGIN; - -NOTE: This code is not yet ready / in use. It was archived here -as we might want this kind of table in the future. It is NOT -to be installed in a production system (hence in EXTRA_DIST and -not in the SQL target!) - --- Check patch versioning is in place. -SELECT _v.register_patch('auditor-9999', NULL, NULL); - - --- Table with historic business ledger; basically, when the exchange --- operator decides to use operating costs for anything but wire --- transfers to merchants, it goes in here. This happens when the --- operator users transaction fees for business expenses. purpose --- is free-form but should be a human-readable wire transfer --- identifier. This is NOT yet used and outside of the scope of --- the core auditing logic. However, once we do take fees to use --- operating costs, and if we still want auditor_predicted_result to match --- the tables overall, we'll need a command-line tool to insert rows --- into this table and update auditor_predicted_result accordingly. --- (So this table for now just exists as a reminder of what we'll --- need in the long term.) -CREATE TABLE IF NOT EXISTS auditor_historic_ledger - (master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE - ,purpose VARCHAR NOT NULL - ,timestamp INT8 NOT NULL - ,balance_val INT8 NOT NULL - ,balance_frac INT4 NOT NULL - ); -CREATE INDEX history_ledger_by_master_pub_and_time - ON auditor_historic_ledger - (master_pub - ,timestamp); - -COMMIT; diff --git a/src/auditordb/auditor-0001.sql b/src/auditordb/auditor-0001.sql index 483f4f1e3..7bd5531e8 100644 --- a/src/auditordb/auditor-0001.sql +++ b/src/auditordb/auditor-0001.sql @@ -1,6 +1,6 @@ -- -- This file is part of TALER --- Copyright (C) 2014--2020 Taler Systems SA +-- Copyright (C) 2014--2022 Taler Systems SA -- -- TALER is free software; you can redistribute it and/or modify it under the -- terms of the GNU General Public License as published by the Free Software @@ -52,16 +52,29 @@ CREATE TABLE IF NOT EXISTS auditor_progress_reserve ,last_reserve_in_serial_id INT8 NOT NULL DEFAULT 0 ,last_reserve_out_serial_id INT8 NOT NULL DEFAULT 0 ,last_reserve_recoup_serial_id INT8 NOT NULL DEFAULT 0 + ,last_reserve_open_serial_id INT8 NOT NULL DEFAULT 0 ,last_reserve_close_serial_id INT8 NOT NULL DEFAULT 0 - ,last_purse_merges_serial_id INT8 NOT NULL DEFAULT 0 - ,last_purse_deposits_serial_id INT8 NOT NULL DEFAULT 0 + ,last_purse_decision_serial_id INT8 NOT NULL DEFAULT 0 ,last_account_merges_serial_id INT8 NOT NULL DEFAULT 0 ,last_history_requests_serial_id INT8 NOT NULL DEFAULT 0 - ,last_close_requests_serial_id INT8 NOT NULL DEFAULT 0 ,PRIMARY KEY (master_pub) ); COMMENT ON TABLE auditor_progress_reserve - IS 'information as to which transactions the auditor has processed in the exchange database. Used for SELECTing the + IS 'information as to which transactions the reserve auditor has processed in the exchange database. Used for SELECTing the + statements to process. The indices include the last serial ID from the respective tables that we have processed. Thus, we need to select those table entries that are strictly larger (and process in monotonically increasing order).'; + + +CREATE TABLE IF NOT EXISTS auditor_progress_purse + (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE + ,last_purse_request_serial_id INT8 NOT NULL DEFAULT 0 + ,last_purse_decision_serial_id INT8 NOT NULL DEFAULT 0 + ,last_purse_merges_serial_id INT8 NOT NULL DEFAULT 0 + ,last_account_merges_serial_id INT8 NOT NULL DEFAULT 0 + ,last_purse_deposits_serial_id INT8 NOT NULL DEFAULT 0 + ,PRIMARY KEY (master_pub) + ); +COMMENT ON TABLE auditor_progress_purse + IS 'information as to which purses the purse auditor has processed in the exchange database. Used for SELECTing the statements to process. The indices include the last serial ID from the respective tables that we have processed. Thus, we need to select those table entries that are strictly larger (and process in monotonically increasing order).'; @@ -93,8 +106,9 @@ CREATE TABLE IF NOT EXISTS auditor_progress_coin ,last_refund_serial_id INT8 NOT NULL DEFAULT 0 ,last_recoup_serial_id INT8 NOT NULL DEFAULT 0 ,last_recoup_refresh_serial_id INT8 NOT NULL DEFAULT 0 + ,last_open_deposits_serial_id INT8 NOT NULL DEFAULT 0 ,last_purse_deposits_serial_id INT8 NOT NULL DEFAULT 0 - ,last_purse_refunds_serial_id INT8 NOT NULL DEFAULT 0 + ,last_purse_decision_serial_id INT8 NOT NULL DEFAULT 0 ,PRIMARY KEY (master_pub) ); COMMENT ON TABLE auditor_progress_coin @@ -129,10 +143,20 @@ CREATE TABLE IF NOT EXISTS auditor_reserves ,master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE ,reserve_balance_val INT8 NOT NULL ,reserve_balance_frac INT4 NOT NULL + ,reserve_loss_val INT8 NOT NULL + ,reserve_loss_frac INT4 NOT NULL ,withdraw_fee_balance_val INT8 NOT NULL ,withdraw_fee_balance_frac INT4 NOT NULL + ,close_fee_balance_val INT8 NOT NULL + ,close_fee_balance_frac INT4 NOT NULL + ,purse_fee_balance_val INT8 NOT NULL + ,purse_fee_balance_frac INT4 NOT NULL + ,open_fee_balance_val INT8 NOT NULL + ,open_fee_balance_frac INT4 NOT NULL + ,history_fee_balance_val INT8 NOT NULL + ,history_fee_balance_frac INT4 NOT NULL ,expiration_date INT8 NOT NULL - ,auditor_reserves_rowid BIGSERIAL UNIQUE + ,auditor_reserves_rowid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,origin_account TEXT ); COMMENT ON TABLE auditor_reserves @@ -143,14 +167,38 @@ CREATE INDEX IF NOT EXISTS auditor_reserves_by_reserve_pub (reserve_pub); +CREATE TABLE IF NOT EXISTS auditor_purses + (purse_pub BYTEA NOT NULL CHECK(LENGTH(purse_pub)=32) + ,master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE + ,balance_val INT8 NOT NULL + ,balance_frac INT4 NOT NULL + ,target_val INT8 NOT NULL + ,target_frac INT4 NOT NULL + ,expiration_date INT8 NOT NULL + ,auditor_purses_rowid BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE + ); +COMMENT ON TABLE auditor_purses + IS 'all of the purses and their respective balances that the auditor is aware of'; + +CREATE INDEX IF NOT EXISTS auditor_purses_by_purse_pub + ON auditor_purses + (purse_pub); + + CREATE TABLE IF NOT EXISTS auditor_reserve_balance (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE ,reserve_balance_val INT8 NOT NULL ,reserve_balance_frac INT4 NOT NULL + ,reserve_loss_val INT8 NOT NULL + ,reserve_loss_frac INT4 NOT NULL ,withdraw_fee_balance_val INT8 NOT NULL ,withdraw_fee_balance_frac INT4 NOT NULL + ,close_fee_balance_val INT8 NOT NULL + ,close_fee_balance_frac INT4 NOT NULL ,purse_fee_balance_val INT8 NOT NULL ,purse_fee_balance_frac INT4 NOT NULL + ,open_fee_balance_val INT8 NOT NULL + ,open_fee_balance_frac INT4 NOT NULL ,history_fee_balance_val INT8 NOT NULL ,history_fee_balance_frac INT4 NOT NULL ); @@ -185,8 +233,10 @@ COMMENT ON COLUMN auditor_denomination_pending.num_issued IS 'counts the number of coins issued (withdraw, refresh) of this denomination'; COMMENT ON COLUMN auditor_denomination_pending.denom_risk_val IS 'amount that could theoretically be lost in the future due to recoup operations'; +COMMENT ON COLUMN auditor_denomination_pending.denom_loss_val + IS 'amount that was lost due to failures by the exchange'; COMMENT ON COLUMN auditor_denomination_pending.recoup_loss_val - IS 'amount actually lost due to recoup operations past revocation'; + IS 'amount actually lost due to recoup operations after a revocation'; CREATE TABLE IF NOT EXISTS auditor_balance_summary @@ -199,15 +249,21 @@ CREATE TABLE IF NOT EXISTS auditor_balance_summary ,melt_fee_balance_frac INT4 NOT NULL ,refund_fee_balance_val INT8 NOT NULL ,refund_fee_balance_frac INT4 NOT NULL + ,purse_fee_balance_val INT8 NOT NULL + ,purse_fee_balance_frac INT4 NOT NULL + ,open_deposit_fee_balance_val INT8 NOT NULL + ,open_deposit_fee_balance_frac INT4 NOT NULL ,risk_val INT8 NOT NULL ,risk_frac INT4 NOT NULL ,loss_val INT8 NOT NULL ,loss_frac INT4 NOT NULL - ,irregular_recoup_val INT8 NOT NULL - ,irregular_recoup_frac INT4 NOT NULL + ,irregular_loss_val INT8 NOT NULL + ,irregular_loss_frac INT4 NOT NULL ); COMMENT ON TABLE auditor_balance_summary IS 'the sum of the outstanding coins from auditor_denomination_pending (denom_pubs must belong to the respectives exchange master public key); it represents the auditor_balance_summary of the exchange at this point (modulo unexpected historic_loss-style events where denomination keys are compromised)'; +COMMENT ON COLUMN auditor_balance_summary.denom_balance_frac + IS 'total amount we should have in escrow for all denominations'; CREATE TABLE IF NOT EXISTS auditor_historic_denomination_revenue @@ -243,7 +299,7 @@ CREATE INDEX IF NOT EXISTS auditor_historic_reserve_summary_by_master_pub_start_ CREATE TABLE IF NOT EXISTS deposit_confirmations (master_pub BYTEA NOT NULL CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE - ,serial_id BIGSERIAL NOT NULL UNIQUE + ,serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64) ,h_extensions BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64) ,h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64) diff --git a/src/auditordb/plugin_auditordb_postgres.c b/src/auditordb/plugin_auditordb_postgres.c index cf8d0b5d0..9a4f95f35 100644 --- a/src/auditordb/plugin_auditordb_postgres.c +++ b/src/auditordb/plugin_auditordb_postgres.c @@ -229,13 +229,12 @@ setup_connection (struct PostgresClosure *pg) " last_reserve_in_serial_id=$1" ",last_reserve_out_serial_id=$2" ",last_reserve_recoup_serial_id=$3" - ",last_reserve_close_serial_id=$4" - ",last_purse_merges_serial_id=$5" - ",last_purse_deposits_serial_id=$6" + ",last_reserve_open_serial_id=$4" + ",last_reserve_close_serial_id=$5" + ",last_purse_decision_serial_id=$6" ",last_account_merges_serial_id=$7" ",last_history_requests_serial_id=$8" - ",last_close_requests_serial_id=$9" - " WHERE master_pub=$10"), + " WHERE master_pub=$9"), /* Used in #postgres_get_auditor_progress_reserve() */ GNUNET_PQ_make_prepare ("auditor_progress_select_reserve", "SELECT" @@ -243,11 +242,10 @@ setup_connection (struct PostgresClosure *pg) ",last_reserve_out_serial_id" ",last_reserve_recoup_serial_id" ",last_reserve_close_serial_id" - ",last_purse_merges_serial_id" - ",last_purse_deposits_serial_id" + ",last_purse_decision_serial_id" ",last_account_merges_serial_id" ",last_history_requests_serial_id" - ",last_close_requests_serial_id" + ",last_reserve_open_serial_id" " FROM auditor_progress_reserve" " WHERE master_pub=$1;"), /* Used in #postgres_insert_auditor_progress_reserve() */ @@ -257,13 +255,41 @@ setup_connection (struct PostgresClosure *pg) ",last_reserve_in_serial_id" ",last_reserve_out_serial_id" ",last_reserve_recoup_serial_id" + ",last_reserve_open_serial_id" ",last_reserve_close_serial_id" - ",last_purse_merges_serial_id" - ",last_purse_deposits_serial_id" + ",last_purse_decision_serial_id" ",last_account_merges_serial_id" ",last_history_requests_serial_id" - ",last_close_requests_serial_id" - ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);"), + ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);"), + /* Used in #postgres_update_auditor_progress_purse() */ + GNUNET_PQ_make_prepare ("auditor_progress_update_purse", + "UPDATE auditor_progress_purse SET " + " last_purse_request_serial_id=$1" + ",last_purse_decision_serial_id=$2" + ",last_purse_merges_serial_id=$3" + ",last_account_merges_serial_id=$4" + ",last_purse_deposits_serial_id=$5" + " WHERE master_pub=$6"), + /* Used in #postgres_get_auditor_progress_purse() */ + GNUNET_PQ_make_prepare ("auditor_progress_select_purse", + "SELECT" + " last_purse_request_serial_id" + ",last_purse_decision_serial_id" + ",last_purse_merges_serial_id" + ",last_account_merges_serial_id" + ",last_purse_deposits_serial_id" + " FROM auditor_progress_purse" + " WHERE master_pub=$1;"), + /* Used in #postgres_insert_auditor_progress_purse() */ + GNUNET_PQ_make_prepare ("auditor_progress_insert_purse", + "INSERT INTO auditor_progress_purse " + "(master_pub" + ",last_purse_request_serial_id" + ",last_purse_decision_serial_id" + ",last_purse_merges_serial_id" + ",last_account_merges_serial_id" + ",last_purse_deposits_serial_id" + ") VALUES ($1,$2,$3,$4,$5,$6);"), /* Used in #postgres_update_auditor_progress_aggregation() */ GNUNET_PQ_make_prepare ("auditor_progress_update_aggregation", "UPDATE auditor_progress_aggregation SET " @@ -308,7 +334,7 @@ setup_connection (struct PostgresClosure *pg) ",last_recoup_serial_id=$5" ",last_recoup_refresh_serial_id=$6" ",last_purse_deposits_serial_id=$7" - ",last_purse_refunds_serial_id=$8" + ",last_purse_decision_serial_id=$8" " WHERE master_pub=$9"), /* Used in #postgres_get_auditor_progress_coin() */ GNUNET_PQ_make_prepare ("auditor_progress_select_coin", @@ -320,7 +346,7 @@ setup_connection (struct PostgresClosure *pg) ",last_recoup_serial_id" ",last_recoup_refresh_serial_id" ",last_purse_deposits_serial_id" - ",last_purse_refunds_serial_id" + ",last_purse_decision_serial_id" " FROM auditor_progress_coin" " WHERE master_pub=$1;"), /* Used in #postgres_insert_auditor_progress() */ @@ -334,7 +360,7 @@ setup_connection (struct PostgresClosure *pg) ",last_recoup_serial_id" ",last_recoup_refresh_serial_id" ",last_purse_deposits_serial_id" - ",last_purse_refunds_serial_id" + ",last_purse_decision_serial_id" ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);"), /* Used in #postgres_insert_wire_auditor_account_progress() */ GNUNET_PQ_make_prepare ("wire_auditor_account_progress_insert", @@ -390,27 +416,57 @@ setup_connection (struct PostgresClosure *pg) ",master_pub" ",reserve_balance_val" ",reserve_balance_frac" + ",reserve_loss_val" + ",reserve_loss_frac" ",withdraw_fee_balance_val" ",withdraw_fee_balance_frac" + ",close_fee_balance_val" + ",close_fee_balance_frac" + ",purse_fee_balance_val" + ",purse_fee_balance_frac" + ",open_fee_balance_val" + ",open_fee_balance_frac" + ",history_fee_balance_val" + ",history_fee_balance_frac" ",expiration_date" ",origin_account" - ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8);"), + ") VALUES " + "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18);"), /* Used in #postgres_update_reserve_info() */ GNUNET_PQ_make_prepare ("auditor_reserves_update", "UPDATE auditor_reserves SET" " reserve_balance_val=$1" ",reserve_balance_frac=$2" - ",withdraw_fee_balance_val=$3" - ",withdraw_fee_balance_frac=$4" - ",expiration_date=$5" - " WHERE reserve_pub=$6 AND master_pub=$7;"), + ",reserve_loss_val=$3" + ",reserve_loss_frac=$4" + ",withdraw_fee_balance_val=$5" + ",withdraw_fee_balance_frac=$6" + ",purse_fee_balance_val=$7" + ",purse_fee_balance_frac=$8" + ",open_fee_balance_val=$9" + ",open_fee_balance_frac=$10" + ",history_fee_balance_val=$11" + ",history_fee_balance_frac=$12" + ",expiration_date=$13" + " WHERE reserve_pub=$14" + " AND master_pub=$15;"), /* Used in #postgres_get_reserve_info() */ GNUNET_PQ_make_prepare ("auditor_reserves_select", "SELECT" " reserve_balance_val" ",reserve_balance_frac" + ",reserve_loss_val" + ",reserve_loss_frac" ",withdraw_fee_balance_val" ",withdraw_fee_balance_frac" + ",close_fee_balance_val" + ",close_fee_balance_frac" + ",purse_fee_balance_val" + ",purse_fee_balance_frac" + ",open_fee_balance_val" + ",open_fee_balance_frac" + ",history_fee_balance_val" + ",history_fee_balance_frac" ",expiration_date" ",auditor_reserves_rowid" ",origin_account" @@ -427,34 +483,53 @@ setup_connection (struct PostgresClosure *pg) "(master_pub" ",reserve_balance_val" ",reserve_balance_frac" + ",reserve_loss_val" + ",reserve_loss_frac" ",withdraw_fee_balance_val" ",withdraw_fee_balance_frac" + ",close_fee_balance_val" + ",close_fee_balance_frac" ",purse_fee_balance_val" ",purse_fee_balance_frac" + ",open_fee_balance_val" + ",open_fee_balance_frac" ",history_fee_balance_val" ",history_fee_balance_frac" - ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)"), + ") VALUES " + "($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)"), /* Used in #postgres_update_reserve_summary() */ GNUNET_PQ_make_prepare ("auditor_reserve_balance_update", "UPDATE auditor_reserve_balance SET" " reserve_balance_val=$1" ",reserve_balance_frac=$2" - ",withdraw_fee_balance_val=$3" - ",withdraw_fee_balance_frac=$4" - ",purse_fee_balance_val=$5" - ",purse_fee_balance_frac=$6" - ",history_fee_balance_val=$7" - ",history_fee_balance_frac=$8" - " WHERE master_pub=$9;"), + ",reserve_loss_val=$3" + ",reserve_loss_frac=$4" + ",withdraw_fee_balance_val=$5" + ",withdraw_fee_balance_frac=$6" + ",close_fee_balance_val=$7" + ",close_fee_balance_frac=$8" + ",purse_fee_balance_val=$9" + ",purse_fee_balance_frac=$10" + ",open_fee_balance_val=$11" + ",open_fee_balance_frac=$12" + ",history_fee_balance_val=$13" + ",history_fee_balance_frac=$14" + " WHERE master_pub=$15;"), /* Used in #postgres_get_reserve_summary() */ GNUNET_PQ_make_prepare ("auditor_reserve_balance_select", "SELECT" " reserve_balance_val" ",reserve_balance_frac" + ",reserve_loss_val" + ",reserve_loss_frac" ",withdraw_fee_balance_val" ",withdraw_fee_balance_frac" + ",close_fee_balance_val" + ",close_fee_balance_frac" ",purse_fee_balance_val" ",purse_fee_balance_frac" + ",open_fee_balance_val" + ",open_fee_balance_frac" ",history_fee_balance_val" ",history_fee_balance_frac" " FROM auditor_reserve_balance" @@ -534,14 +609,18 @@ setup_connection (struct PostgresClosure *pg) ",melt_fee_balance_frac" ",refund_fee_balance_val" ",refund_fee_balance_frac" + ",purse_fee_balance_val" + ",purse_fee_balance_frac" + ",open_deposit_fee_balance_val" + ",open_deposit_fee_balance_frac" ",risk_val" ",risk_frac" ",loss_val" ",loss_frac" - ",irregular_recoup_val" - ",irregular_recoup_frac" + ",irregular_loss_val" + ",irregular_loss_frac" ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10," - " $11,$12,$13,$14,$15);"), + " $11,$12,$13,$14,$15,$16,$17,$18,$19);"), /* Used in #postgres_update_balance_summary() */ GNUNET_PQ_make_prepare ("auditor_balance_summary_update", "UPDATE auditor_balance_summary SET" @@ -553,13 +632,17 @@ setup_connection (struct PostgresClosure *pg) ",melt_fee_balance_frac=$6" ",refund_fee_balance_val=$7" ",refund_fee_balance_frac=$8" - ",risk_val=$9" - ",risk_frac=$10" - ",loss_val=$11" - ",loss_frac=$12" - ",irregular_recoup_val=$13" - ",irregular_recoup_frac=$14" - " WHERE master_pub=$15;"), + ",purse_fee_balance_val=$9" + ",purse_fee_balance_frac=$10" + ",open_deposit_fee_balance_val=$11" + ",open_deposit_fee_balance_frac=$12" + ",risk_val=$13" + ",risk_frac=$14" + ",loss_val=$15" + ",loss_frac=$16" + ",irregular_loss_val=$17" + ",irregular_loss_frac=$18" + " WHERE master_pub=$19;"), /* Used in #postgres_get_balance_summary() */ GNUNET_PQ_make_prepare ("auditor_balance_summary_select", "SELECT" @@ -571,12 +654,16 @@ setup_connection (struct PostgresClosure *pg) ",melt_fee_balance_frac" ",refund_fee_balance_val" ",refund_fee_balance_frac" + ",purse_fee_balance_val" + ",purse_fee_balance_frac" + ",open_deposit_fee_balance_val" + ",open_deposit_fee_balance_frac" ",risk_val" ",risk_frac" ",loss_val" ",loss_frac" - ",irregular_recoup_val" - ",irregular_recoup_frac" + ",irregular_loss_val" + ",irregular_loss_frac" " FROM auditor_balance_summary" " WHERE master_pub=$1;"), /* Used in #postgres_insert_historic_denom_revenue() */ @@ -1230,12 +1317,11 @@ postgres_insert_auditor_progress_reserve ( GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_out_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_recoup_serial_id), + GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_open_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_close_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_purse_merges_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_purse_deposits_serial_id), + GNUNET_PQ_query_param_uint64 (&ppr->last_purse_decisions_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_account_merges_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_history_requests_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_close_requests_serial_id), GNUNET_PQ_query_param_end }; @@ -1265,12 +1351,11 @@ postgres_update_auditor_progress_reserve ( GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_out_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_recoup_serial_id), + GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_open_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_reserve_close_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_purse_merges_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_purse_deposits_serial_id), + GNUNET_PQ_query_param_uint64 (&ppr->last_purse_decisions_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_account_merges_serial_id), GNUNET_PQ_query_param_uint64 (&ppr->last_history_requests_serial_id), - GNUNET_PQ_query_param_uint64 (&ppr->last_close_requests_serial_id), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_end }; @@ -1307,18 +1392,16 @@ postgres_get_auditor_progress_reserve ( &ppr->last_reserve_out_serial_id), GNUNET_PQ_result_spec_uint64 ("last_reserve_recoup_serial_id", &ppr->last_reserve_recoup_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_reserve_open_serial_id", + &ppr->last_reserve_open_serial_id), GNUNET_PQ_result_spec_uint64 ("last_reserve_close_serial_id", &ppr->last_reserve_close_serial_id), - GNUNET_PQ_result_spec_uint64 ("last_purse_merges_serial_id", - &ppr->last_purse_merges_serial_id), - GNUNET_PQ_result_spec_uint64 ("last_purse_deposits_serial_id", - &ppr->last_purse_deposits_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_purse_decision_serial_id", + &ppr->last_purse_decisions_serial_id), GNUNET_PQ_result_spec_uint64 ("last_account_merges_serial_id", &ppr->last_account_merges_serial_id), GNUNET_PQ_result_spec_uint64 ("last_history_requests_serial_id", &ppr->last_history_requests_serial_id), - GNUNET_PQ_result_spec_uint64 ("last_close_requests_serial_id", - &ppr->last_close_requests_serial_id), GNUNET_PQ_result_spec_end }; @@ -1329,6 +1412,110 @@ postgres_get_auditor_progress_reserve ( } +/** + * Insert information about the auditor's progress with an exchange's + * data. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param ppp where is the auditor in processing + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_insert_auditor_progress_purse ( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ProgressPointPurse *ppp) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (master_pub), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_request_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_decision_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_merge_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_account_merge_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_deposits_serial_id), + GNUNET_PQ_query_param_end + }; + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "auditor_progress_insert_purse", + params); +} + + +/** + * Update information about the progress of the auditor. There + * must be an existing record for the exchange. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param ppp where is the auditor in processing + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_update_auditor_progress_purse ( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ProgressPointPurse *ppp) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_request_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_decision_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_merge_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_account_merge_serial_id), + GNUNET_PQ_query_param_uint64 (&ppp->last_purse_deposits_serial_id), + GNUNET_PQ_query_param_auto_from_type (master_pub), + GNUNET_PQ_query_param_end + }; + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "auditor_progress_update_purse", + params); +} + + +/** + * Get information about the progress of the auditor. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param[out] ppp set to where the auditor is in processing + * @return transaction status code + */ +static enum GNUNET_DB_QueryStatus +postgres_get_auditor_progress_purse ( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + struct TALER_AUDITORDB_ProgressPointPurse *ppp) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (master_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("last_purse_request_serial_id", + &ppp->last_purse_request_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_purse_decision_serial_id", + &ppp->last_purse_decision_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_purse_merges_serial_id", + &ppp->last_purse_merge_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_account_merges_serial_id", + &ppp->last_account_merge_serial_id), + GNUNET_PQ_result_spec_uint64 ("last_purse_deposits_serial_id", + &ppp->last_purse_deposits_serial_id), + GNUNET_PQ_result_spec_end + }; + + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "auditor_progress_select_purse", + params, + rs); +} + + /** * Insert information about the auditor's progress with an exchange's * data. @@ -1609,7 +1796,7 @@ postgres_get_auditor_progress_coin ( &ppc->last_recoup_refresh_serial_id), GNUNET_PQ_result_spec_uint64 ("last_purse_deposits_serial_id", &ppc->last_purse_deposits_serial_id), - GNUNET_PQ_result_spec_uint64 ("last_purse_refunds_serial_id", + GNUNET_PQ_result_spec_uint64 ("last_purse_decision_serial_id", &ppc->last_purse_refunds_serial_id), GNUNET_PQ_result_spec_end }; @@ -1629,8 +1816,7 @@ postgres_get_auditor_progress_coin ( * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param pp how far are we in the auditor's tables - * @param in_wire_off how far are we in the incoming wire transfers - * @param out_wire_off how far are we in the outgoing wire transfers + * @param bapp progress in wire transaction histories * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -1639,8 +1825,7 @@ postgres_insert_wire_auditor_account_progress ( const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t in_wire_off, - uint64_t out_wire_off) + const struct TALER_AUDITORDB_BankAccountProgressPoint *bapp) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -1648,8 +1833,8 @@ postgres_insert_wire_auditor_account_progress ( GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), - GNUNET_PQ_query_param_uint64 (&in_wire_off), - GNUNET_PQ_query_param_uint64 (&out_wire_off), + GNUNET_PQ_query_param_uint64 (&bapp->in_wire_off), + GNUNET_PQ_query_param_uint64 (&bapp->out_wire_off), GNUNET_PQ_query_param_end }; @@ -1667,8 +1852,7 @@ postgres_insert_wire_auditor_account_progress ( * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param pp where is the auditor in processing - * @param in_wire_off how far are we in the incoming wire transaction history - * @param out_wire_off how far are we in the outgoing wire transaction history + * @param bapp progress in wire transaction histories * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -1677,15 +1861,14 @@ postgres_update_wire_auditor_account_progress ( const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t in_wire_off, - uint64_t out_wire_off) + const struct TALER_AUDITORDB_BankAccountProgressPoint *bapp) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), - GNUNET_PQ_query_param_uint64 (&in_wire_off), - GNUNET_PQ_query_param_uint64 (&out_wire_off), + GNUNET_PQ_query_param_uint64 (&bapp->in_wire_off), + GNUNET_PQ_query_param_uint64 (&bapp->out_wire_off), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_end @@ -1704,8 +1887,7 @@ postgres_update_wire_auditor_account_progress ( * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param[out] pp where is the auditor in processing - * @param[out] in_wire_off how far are we in the incoming wire transaction history - * @param[out] out_wire_off how far are we in the outgoing wire transaction history + * @param[out] bapp how far are we in the wire transaction histories * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -1714,8 +1896,7 @@ postgres_get_wire_auditor_account_progress ( const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t *in_wire_off, - uint64_t *out_wire_off) + struct TALER_AUDITORDB_BankAccountProgressPoint *bapp) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -1729,9 +1910,9 @@ postgres_get_wire_auditor_account_progress ( GNUNET_PQ_result_spec_uint64 ("last_wire_wire_out_serial_id", &pp->last_wire_out_serial_id), GNUNET_PQ_result_spec_uint64 ("wire_in_off", - in_wire_off), + &bapp->in_wire_off), GNUNET_PQ_result_spec_uint64 ("wire_out_off", - out_wire_off), + &bapp->out_wire_off), GNUNET_PQ_result_spec_end }; @@ -1842,36 +2023,37 @@ postgres_get_wire_auditor_progress ( * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve - * @param expiration_date expiration date of the reserve + * @param rfb amounts for the reserve * @param origin_account where did the money in the reserve originally come from * @return transaction status code */ static enum GNUNET_DB_QueryStatus -postgres_insert_reserve_info (void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - struct GNUNET_TIME_Timestamp expiration_date, - const char *origin_account) +postgres_insert_reserve_info ( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb, + struct GNUNET_TIME_Timestamp expiration_date, + const char *origin_account) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_auto_from_type (master_pub), - TALER_PQ_query_param_amount (reserve_balance), - TALER_PQ_query_param_amount (withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->reserve_balance), + TALER_PQ_query_param_amount (&rfb->reserve_loss), + TALER_PQ_query_param_amount (&rfb->withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->close_fee_balance), + TALER_PQ_query_param_amount (&rfb->purse_fee_balance), + TALER_PQ_query_param_amount (&rfb->open_fee_balance), + TALER_PQ_query_param_amount (&rfb->history_fee_balance), GNUNET_PQ_query_param_timestamp (&expiration_date), - GNUNET_PQ_query_param_string (origin_account), + NULL == origin_account + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string (origin_account), GNUNET_PQ_query_param_end }; - GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (reserve_balance, - withdraw_fee_balance)); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, "auditor_reserves_insert", params); @@ -1885,34 +2067,32 @@ postgres_insert_reserve_info (void *cls, * @param cls the @e cls of this struct with the plugin-specific state * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve + * @param rfb amounts for the reserve * @param expiration_date expiration date of the reserve * @return transaction status code */ static enum GNUNET_DB_QueryStatus -postgres_update_reserve_info (void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - struct GNUNET_TIME_Timestamp expiration_date) +postgres_update_reserve_info ( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb, + struct GNUNET_TIME_Timestamp expiration_date) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_amount (reserve_balance), - TALER_PQ_query_param_amount (withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->reserve_balance), + TALER_PQ_query_param_amount (&rfb->reserve_loss), + TALER_PQ_query_param_amount (&rfb->withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->purse_fee_balance), + TALER_PQ_query_param_amount (&rfb->open_fee_balance), + TALER_PQ_query_param_amount (&rfb->history_fee_balance), GNUNET_PQ_query_param_timestamp (&expiration_date), GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_end }; - GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (reserve_balance, - withdraw_fee_balance)); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, "auditor_reserves_update", params); @@ -1952,9 +2132,7 @@ postgres_del_reserve_info (void *cls, * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange * @param[out] rowid which row did we get the information from - * @param[out] reserve_balance amount stored in the reserve - * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve + * @param[out] rfb where to store the reserve balance summary * @param[out] expiration_date expiration date of the reserve * @param[out] sender_account from where did the money in the reserve originally come from * @return transaction status code @@ -1964,8 +2142,7 @@ postgres_get_reserve_info (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_MasterPublicKeyP *master_pub, uint64_t *rowid, - struct TALER_Amount *reserve_balance, - struct TALER_Amount *withdraw_fee_balance, + struct TALER_AUDITORDB_ReserveFeeBalance *rfb, struct GNUNET_TIME_Timestamp *expiration_date, char **sender_account) { @@ -1976,14 +2153,32 @@ postgres_get_reserve_info (void *cls, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance", reserve_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("withdraw_fee_balance", withdraw_fee_balance), - GNUNET_PQ_result_spec_timestamp ("expiration_date", expiration_date), - GNUNET_PQ_result_spec_uint64 ("auditor_reserves_rowid", rowid), - GNUNET_PQ_result_spec_string ("origin_account", sender_account), + TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance", + &rfb->reserve_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_loss", + &rfb->reserve_loss), + TALER_PQ_RESULT_SPEC_AMOUNT ("withdraw_fee_balance", + &rfb->withdraw_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("close_fee_balance", + &rfb->close_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee_balance", + &rfb->purse_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("open_fee_balance", + &rfb->open_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee_balance", + &rfb->history_fee_balance), + GNUNET_PQ_result_spec_timestamp ("expiration_date", + expiration_date), + GNUNET_PQ_result_spec_uint64 ("auditor_reserves_rowid", + rowid), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("origin_account", + sender_account), + NULL), GNUNET_PQ_result_spec_end }; + *sender_account = NULL; return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, "auditor_reserves_select", params, @@ -1997,34 +2192,31 @@ postgres_get_reserve_info (void *cls, * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * @param purse_fee_balance amount the exchange gained in purse fees - * @param history_fee_balance amount the exchange gained in history fees + * @param rfb balances to be stored for the reserve * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_insert_reserve_summary ( void *cls, const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - const struct TALER_Amount *purse_fee_balance, - const struct TALER_Amount *history_fee_balance) + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (master_pub), - TALER_PQ_query_param_amount (reserve_balance), - TALER_PQ_query_param_amount (withdraw_fee_balance), - TALER_PQ_query_param_amount (purse_fee_balance), - TALER_PQ_query_param_amount (history_fee_balance), + TALER_PQ_query_param_amount (&rfb->reserve_balance), + TALER_PQ_query_param_amount (&rfb->reserve_loss), + TALER_PQ_query_param_amount (&rfb->withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->close_fee_balance), + TALER_PQ_query_param_amount (&rfb->purse_fee_balance), + TALER_PQ_query_param_amount (&rfb->open_fee_balance), + TALER_PQ_query_param_amount (&rfb->history_fee_balance), GNUNET_PQ_query_param_end }; GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (reserve_balance, - withdraw_fee_balance)); + TALER_amount_cmp_currency (&rfb->reserve_balance, + &rfb->withdraw_fee_balance)); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "auditor_reserve_balance_insert", @@ -2038,28 +2230,24 @@ postgres_insert_reserve_summary ( * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve - * @param purse_fee_balance amount the exchange gained in purse fees - * @param history_fee_balance amount the exchange gained in history fees + * @param rfb balances to be stored for the reserve * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_update_reserve_summary ( void *cls, const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - const struct TALER_Amount *purse_fee_balance, - const struct TALER_Amount *history_fee_balance) + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_amount (reserve_balance), - TALER_PQ_query_param_amount (withdraw_fee_balance), - TALER_PQ_query_param_amount (purse_fee_balance), - TALER_PQ_query_param_amount (history_fee_balance), + TALER_PQ_query_param_amount (&rfb->reserve_balance), + TALER_PQ_query_param_amount (&rfb->reserve_loss), + TALER_PQ_query_param_amount (&rfb->withdraw_fee_balance), + TALER_PQ_query_param_amount (&rfb->close_fee_balance), + TALER_PQ_query_param_amount (&rfb->purse_fee_balance), + TALER_PQ_query_param_amount (&rfb->open_fee_balance), + TALER_PQ_query_param_amount (&rfb->history_fee_balance), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_end }; @@ -2075,19 +2263,13 @@ postgres_update_reserve_summary ( * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param[out] reserve_balance amount stored in reserves - * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees - * @param[out] purse_fee_balance amount the exchange gained in purse fees - * @param[out] history_fee_balance amount the exchange gained in history fees + * @param[out] rfb balances are returned here * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_get_reserve_summary (void *cls, const struct TALER_MasterPublicKeyP *master_pub, - struct TALER_Amount *reserve_balance, - struct TALER_Amount *withdraw_fee_balance, - struct TALER_Amount *purse_fee_balance, - struct TALER_Amount *history_fee_balance) + struct TALER_AUDITORDB_ReserveFeeBalance *rfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -2095,10 +2277,20 @@ postgres_get_reserve_summary (void *cls, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance", reserve_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("withdraw_fee_balance", withdraw_fee_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee_balance", purse_fee_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee_balance", history_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_balance", + &rfb->reserve_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("reserve_loss", + &rfb->reserve_loss), + TALER_PQ_RESULT_SPEC_AMOUNT ("withdraw_fee_balance", + &rfb->withdraw_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("close_fee_balance", + &rfb->close_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee_balance", + &rfb->purse_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("open_fee_balance", + &rfb->open_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee_balance", + &rfb->history_fee_balance), GNUNET_PQ_result_spec_end }; @@ -2202,31 +2394,23 @@ postgres_get_wire_fee_summary (void *cls, * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param denom_balance value of coins outstanding with this denomination key - * @param denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) - * @param denom_risk value of coins issued with this denomination key - * @param recoup_loss losses from recoup (if this denomination was revoked) - * @param num_issued how many coins of this denomination did the exchange blind-sign + * @param dcd circulation data to store * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_insert_denomination_balance ( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *denom_loss, - const struct TALER_Amount *denom_risk, - const struct TALER_Amount *recoup_loss, - uint64_t num_issued) + const struct TALER_AUDITORDB_DenominationCirculationData *dcd) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (denom_pub_hash), - TALER_PQ_query_param_amount (denom_balance), - TALER_PQ_query_param_amount (denom_loss), - GNUNET_PQ_query_param_uint64 (&num_issued), - TALER_PQ_query_param_amount (denom_risk), - TALER_PQ_query_param_amount (recoup_loss), + TALER_PQ_query_param_amount (&dcd->denom_balance), + TALER_PQ_query_param_amount (&dcd->denom_loss), + GNUNET_PQ_query_param_uint64 (&dcd->num_issued), + TALER_PQ_query_param_amount (&dcd->denom_risk), + TALER_PQ_query_param_amount (&dcd->recoup_loss), GNUNET_PQ_query_param_end }; @@ -2242,30 +2426,22 @@ postgres_insert_denomination_balance ( * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param denom_balance value of coins outstanding with this denomination key - * @param denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) -* @param denom_risk value of coins issued with this denomination key - * @param recoup_loss losses from recoup (if this denomination was revoked) - * @param num_issued how many coins of this denomination did the exchange blind-sign + * @param dcd circulation data to store * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_update_denomination_balance ( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *denom_loss, - const struct TALER_Amount *denom_risk, - const struct TALER_Amount *recoup_loss, - uint64_t num_issued) + const struct TALER_AUDITORDB_DenominationCirculationData *dcd) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_amount (denom_balance), - TALER_PQ_query_param_amount (denom_loss), - GNUNET_PQ_query_param_uint64 (&num_issued), - TALER_PQ_query_param_amount (denom_risk), - TALER_PQ_query_param_amount (recoup_loss), + TALER_PQ_query_param_amount (&dcd->denom_balance), + TALER_PQ_query_param_amount (&dcd->denom_loss), + GNUNET_PQ_query_param_uint64 (&dcd->num_issued), + TALER_PQ_query_param_amount (&dcd->denom_risk), + TALER_PQ_query_param_amount (&dcd->recoup_loss), GNUNET_PQ_query_param_auto_from_type (denom_pub_hash), GNUNET_PQ_query_param_end }; @@ -2281,22 +2457,14 @@ postgres_update_denomination_balance ( * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param[out] denom_balance value of coins outstanding with this denomination key - * @param[out] denom_risk value of coins issued with this denomination key - * @param[out] denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) - * @param[out] recoup_loss losses from recoup (if this denomination was revoked) - * @param[out] num_issued how many coins of this denomination did the exchange blind-sign + * @param[out] dcd circulation data to initialize * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_get_denomination_balance ( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - struct TALER_Amount *denom_balance, - struct TALER_Amount *denom_loss, - struct TALER_Amount *denom_risk, - struct TALER_Amount *recoup_loss, - uint64_t *num_issued) + struct TALER_AUDITORDB_DenominationCirculationData *dcd) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -2304,11 +2472,16 @@ postgres_get_denomination_balance ( GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("denom_balance", denom_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("denom_loss", denom_loss), - TALER_PQ_RESULT_SPEC_AMOUNT ("denom_risk", denom_risk), - TALER_PQ_RESULT_SPEC_AMOUNT ("recoup_loss", recoup_loss), - GNUNET_PQ_result_spec_uint64 ("num_issued", num_issued), + TALER_PQ_RESULT_SPEC_AMOUNT ("denom_balance", + &dcd->denom_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("denom_loss", + &dcd->denom_loss), + TALER_PQ_RESULT_SPEC_AMOUNT ("denom_risk", + &dcd->denom_risk), + TALER_PQ_RESULT_SPEC_AMOUNT ("recoup_loss", + &dcd->recoup_loss), + GNUNET_PQ_result_spec_uint64 ("num_issued", + &dcd->num_issued), GNUNET_PQ_result_spec_end }; @@ -2325,51 +2498,30 @@ postgres_get_denomination_balance ( * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param denom_balance value of coins outstanding with this denomination key - * @param deposit_fee_balance total deposit fees collected for this DK - * @param melt_fee_balance total melt fees collected for this DK - * @param refund_fee_balance total refund fees collected for this DK - * @param risk maximum risk exposure of the exchange - * @param loss materialized @a risk from recoup - * @param irregular_recoup recoups on non-revoked coins + * @param dfb denomination balance data to store * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_insert_balance_summary ( void *cls, const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *deposit_fee_balance, - const struct TALER_Amount *melt_fee_balance, - const struct TALER_Amount *refund_fee_balance, - const struct TALER_Amount *risk, - const struct TALER_Amount *loss, - const struct TALER_Amount *irregular_recoup) + const struct TALER_AUDITORDB_GlobalCoinBalance *dfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_auto_from_type (master_pub), - TALER_PQ_query_param_amount (denom_balance), - TALER_PQ_query_param_amount (deposit_fee_balance), - TALER_PQ_query_param_amount (melt_fee_balance), - TALER_PQ_query_param_amount (refund_fee_balance), - TALER_PQ_query_param_amount (risk), - TALER_PQ_query_param_amount (loss), - TALER_PQ_query_param_amount (irregular_recoup), + TALER_PQ_query_param_amount (&dfb->total_escrowed), + TALER_PQ_query_param_amount (&dfb->deposit_fee_balance), + TALER_PQ_query_param_amount (&dfb->melt_fee_balance), + TALER_PQ_query_param_amount (&dfb->refund_fee_balance), + TALER_PQ_query_param_amount (&dfb->purse_fee_balance), + TALER_PQ_query_param_amount (&dfb->open_deposit_fee_balance), + TALER_PQ_query_param_amount (&dfb->risk), + TALER_PQ_query_param_amount (&dfb->loss), + TALER_PQ_query_param_amount (&dfb->irregular_loss), GNUNET_PQ_query_param_end }; - GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (denom_balance, - deposit_fee_balance)); - GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (denom_balance, - melt_fee_balance)); - - GNUNET_assert (GNUNET_YES == - TALER_amount_cmp_currency (denom_balance, - refund_fee_balance)); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, "auditor_balance_summary_insert", params); @@ -2382,36 +2534,26 @@ postgres_insert_balance_summary ( * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param denom_balance value of coins outstanding with this denomination key - * @param deposit_fee_balance total deposit fees collected for this DK - * @param melt_fee_balance total melt fees collected for this DK - * @param refund_fee_balance total refund fees collected for this DK - * @param risk maximum risk exposure of the exchange - * @param loss materialized @a risk from recoup - * @param irregular_recoup recoups made on non-revoked coins + * @param dfb denomination balance data to store * @return transaction status code */ static enum GNUNET_DB_QueryStatus postgres_update_balance_summary ( void *cls, const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *deposit_fee_balance, - const struct TALER_Amount *melt_fee_balance, - const struct TALER_Amount *refund_fee_balance, - const struct TALER_Amount *risk, - const struct TALER_Amount *loss, - const struct TALER_Amount *irregular_recoup) + const struct TALER_AUDITORDB_GlobalCoinBalance *dfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - TALER_PQ_query_param_amount (denom_balance), - TALER_PQ_query_param_amount (deposit_fee_balance), - TALER_PQ_query_param_amount (melt_fee_balance), - TALER_PQ_query_param_amount (refund_fee_balance), - TALER_PQ_query_param_amount (risk), - TALER_PQ_query_param_amount (loss), - TALER_PQ_query_param_amount (irregular_recoup), + TALER_PQ_query_param_amount (&dfb->total_escrowed), + TALER_PQ_query_param_amount (&dfb->deposit_fee_balance), + TALER_PQ_query_param_amount (&dfb->melt_fee_balance), + TALER_PQ_query_param_amount (&dfb->refund_fee_balance), + TALER_PQ_query_param_amount (&dfb->purse_fee_balance), + TALER_PQ_query_param_amount (&dfb->open_deposit_fee_balance), + TALER_PQ_query_param_amount (&dfb->risk), + TALER_PQ_query_param_amount (&dfb->loss), + TALER_PQ_query_param_amount (&dfb->irregular_loss), GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_end }; @@ -2427,25 +2569,14 @@ postgres_update_balance_summary ( * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param[out] denom_balance value of coins outstanding with this denomination key - * @param[out] deposit_fee_balance total deposit fees collected for this DK - * @param[out] melt_fee_balance total melt fees collected for this DK - * @param[out] refund_fee_balance total refund fees collected for this DK - * @param[out] risk maximum risk exposure of the exchange - * @param[out] loss losses from recoup (on revoked denominations) - * @param[out] irregular_recoup recoups on NOT revoked denominations + * @param[out] dfb where to return the denomination balances * @return transaction status code */ static enum GNUNET_DB_QueryStatus -postgres_get_balance_summary (void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - struct TALER_Amount *denom_balance, - struct TALER_Amount *deposit_fee_balance, - struct TALER_Amount *melt_fee_balance, - struct TALER_Amount *refund_fee_balance, - struct TALER_Amount *risk, - struct TALER_Amount *loss, - struct TALER_Amount *irregular_recoup) +postgres_get_balance_summary ( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + struct TALER_AUDITORDB_GlobalCoinBalance *dfb) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { @@ -2453,13 +2584,24 @@ postgres_get_balance_summary (void *cls, GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_RESULT_SPEC_AMOUNT ("denom_balance", denom_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee_balance", deposit_fee_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("melt_fee_balance", melt_fee_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee_balance", refund_fee_balance), - TALER_PQ_RESULT_SPEC_AMOUNT ("risk", risk), - TALER_PQ_RESULT_SPEC_AMOUNT ("loss", loss), - TALER_PQ_RESULT_SPEC_AMOUNT ("irregular_recoup", irregular_recoup), + TALER_PQ_RESULT_SPEC_AMOUNT ("denom_balance", + &dfb->total_escrowed), + TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee_balance", + &dfb->deposit_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("melt_fee_balance", + &dfb->melt_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee_balance", + &dfb->refund_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee_balance", + &dfb->purse_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("open_deposit_fee_balance", + &dfb->open_deposit_fee_balance), + TALER_PQ_RESULT_SPEC_AMOUNT ("risk", + &dfb->risk), + TALER_PQ_RESULT_SPEC_AMOUNT ("loss", + &dfb->loss), + TALER_PQ_RESULT_SPEC_AMOUNT ("irregular_loss", + &dfb->irregular_loss), GNUNET_PQ_result_spec_end }; @@ -2920,33 +3062,45 @@ libtaler_plugin_auditordb_postgres_init (void *cls) plugin->insert_deposit_confirmation = &postgres_insert_deposit_confirmation; plugin->get_deposit_confirmations = &postgres_get_deposit_confirmations; - plugin->get_auditor_progress_reserve = &postgres_get_auditor_progress_reserve; - plugin->update_auditor_progress_reserve = - &postgres_update_auditor_progress_reserve; - plugin->insert_auditor_progress_reserve = - &postgres_insert_auditor_progress_reserve; - plugin->get_auditor_progress_aggregation = - &postgres_get_auditor_progress_aggregation; - plugin->update_auditor_progress_aggregation = - &postgres_update_auditor_progress_aggregation; - plugin->insert_auditor_progress_aggregation = - &postgres_insert_auditor_progress_aggregation; - plugin->get_auditor_progress_deposit_confirmation = - &postgres_get_auditor_progress_deposit_confirmation; - plugin->update_auditor_progress_deposit_confirmation = - &postgres_update_auditor_progress_deposit_confirmation; - plugin->insert_auditor_progress_deposit_confirmation = - &postgres_insert_auditor_progress_deposit_confirmation; + plugin->get_auditor_progress_reserve + = &postgres_get_auditor_progress_reserve; + plugin->update_auditor_progress_reserve + = &postgres_update_auditor_progress_reserve; + plugin->insert_auditor_progress_reserve + = &postgres_insert_auditor_progress_reserve; + + plugin->get_auditor_progress_purse + = &postgres_get_auditor_progress_purse; + plugin->update_auditor_progress_purse + = &postgres_update_auditor_progress_purse; + plugin->insert_auditor_progress_purse + = &postgres_insert_auditor_progress_purse; + + plugin->get_auditor_progress_aggregation + = &postgres_get_auditor_progress_aggregation; + plugin->update_auditor_progress_aggregation + = &postgres_update_auditor_progress_aggregation; + plugin->insert_auditor_progress_aggregation + = &postgres_insert_auditor_progress_aggregation; + + plugin->get_auditor_progress_deposit_confirmation + = &postgres_get_auditor_progress_deposit_confirmation; + plugin->update_auditor_progress_deposit_confirmation + = &postgres_update_auditor_progress_deposit_confirmation; + plugin->insert_auditor_progress_deposit_confirmation + = &postgres_insert_auditor_progress_deposit_confirmation; + plugin->get_auditor_progress_coin = &postgres_get_auditor_progress_coin; plugin->update_auditor_progress_coin = &postgres_update_auditor_progress_coin; plugin->insert_auditor_progress_coin = &postgres_insert_auditor_progress_coin; - plugin->get_wire_auditor_account_progress = - &postgres_get_wire_auditor_account_progress; - plugin->update_wire_auditor_account_progress = - &postgres_update_wire_auditor_account_progress; - plugin->insert_wire_auditor_account_progress = - &postgres_insert_wire_auditor_account_progress; + plugin->get_wire_auditor_account_progress + = &postgres_get_wire_auditor_account_progress; + plugin->update_wire_auditor_account_progress + = &postgres_update_wire_auditor_account_progress; + plugin->insert_wire_auditor_account_progress + = &postgres_insert_wire_auditor_account_progress; + plugin->get_wire_auditor_progress = &postgres_get_wire_auditor_progress; plugin->update_wire_auditor_progress = &postgres_update_wire_auditor_progress; plugin->insert_wire_auditor_progress = &postgres_insert_wire_auditor_progress; @@ -2972,15 +3126,15 @@ libtaler_plugin_auditordb_postgres_init (void *cls) plugin->update_balance_summary = &postgres_update_balance_summary; plugin->insert_balance_summary = &postgres_insert_balance_summary; - plugin->select_historic_denom_revenue = - &postgres_select_historic_denom_revenue; - plugin->insert_historic_denom_revenue = - &postgres_insert_historic_denom_revenue; + plugin->select_historic_denom_revenue + = &postgres_select_historic_denom_revenue; + plugin->insert_historic_denom_revenue + = &postgres_insert_historic_denom_revenue; - plugin->select_historic_reserve_revenue = - &postgres_select_historic_reserve_revenue; - plugin->insert_historic_reserve_revenue = - &postgres_insert_historic_reserve_revenue; + plugin->select_historic_reserve_revenue + = &postgres_select_historic_reserve_revenue; + plugin->insert_historic_reserve_revenue + = &postgres_insert_historic_reserve_revenue; plugin->get_predicted_balance = &postgres_get_predicted_balance; plugin->update_predicted_result = &postgres_update_predicted_result; diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c index 1e5ca7553..d39d08b91 100644 --- a/src/auditordb/test_auditordb.c +++ b/src/auditordb/test_auditordb.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016 Taler Systems SA + 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 General Public License as published by the Free Software @@ -17,17 +17,17 @@ * @file auditordb/test_auditordb.c * @brief test cases for DB interaction functions * @author Gabor X Toth + * @author Christian Grothoff */ #include "platform.h" #include #include "taler_auditordb_lib.h" #include "taler_auditordb_plugin.h" - /** - * Global result from the testcase. + * Currency we use, must match CURRENCY in "test-auditor-db-postgres.conf". */ -static int result = -1; +#define CURRENCY "EUR" /** * Report line of error if @a cond is true, and jump to label "drop". @@ -39,7 +39,6 @@ static int result = -1; goto drop; \ } while (0) - /** * Initializes @a ptr with random data. */ @@ -54,15 +53,124 @@ static int result = -1; /** - * Currency we use, must match CURRENCY in "test-auditor-db-postgres.conf". + * Global result from the testcase. */ -#define CURRENCY "EUR" +static int result = -1; + +/** + * Hash of denomination public key. + */ +static struct TALER_DenominationHashP denom_pub_hash; + +/** + * Another hash of a denomination public key. + */ +static struct TALER_DenominationHashP rnd_hash; + +/** + * Current time. + */ +static struct GNUNET_TIME_Timestamp now; + +/** + * Timestamp in the past. + */ +static struct GNUNET_TIME_Timestamp past; + +/** + * Timestamp in the future. + */ +static struct GNUNET_TIME_Timestamp future; /** * Database plugin under test. */ static struct TALER_AUDITORDB_Plugin *plugin; +/** + * Historic denomination revenue value. + */ +static struct TALER_Amount rbalance; + +/** + * Historic denomination loss value. + */ +static struct TALER_Amount rloss; + +/** + * Reserve profit value we are using. + */ +static struct TALER_Amount reserve_profits; + + +static enum GNUNET_GenericReturnValue +select_historic_denom_revenue_result ( + void *cls, + const struct TALER_DenominationHashP *denom_pub_hash2, + struct GNUNET_TIME_Timestamp revenue_timestamp2, + const struct TALER_Amount *revenue_balance2, + const struct TALER_Amount *loss2) +{ + static int n = 0; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "select_historic_denom_revenue_result: row %u\n", n); + + if ( (2 <= n++) + || (cls != NULL) + || ((0 != GNUNET_memcmp (&revenue_timestamp2, + &past)) + && (0 != GNUNET_memcmp (&revenue_timestamp2, + &now))) + || ((0 != GNUNET_memcmp (denom_pub_hash2, + &denom_pub_hash)) + && (0 != GNUNET_memcmp (denom_pub_hash2, + &rnd_hash))) + || (0 != TALER_amount_cmp (revenue_balance2, + &rbalance)) + || (0 != TALER_amount_cmp (loss2, + &rloss))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "select_historic_denom_revenue_result: result does not match\n"); + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +static enum GNUNET_GenericReturnValue +select_historic_reserve_revenue_result ( + void *cls, + struct GNUNET_TIME_Timestamp start_time2, + struct GNUNET_TIME_Timestamp end_time2, + const struct TALER_Amount *reserve_profits2) +{ + static int n = 0; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "select_historic_reserve_revenue_result: row %u\n", n); + + if ((2 <= n++) + || (cls != NULL) + || ((0 != GNUNET_memcmp (&start_time2, + &past)) + && (0 != GNUNET_memcmp (&start_time2, + &now))) + || (0 != GNUNET_memcmp (&end_time2, + &future)) + || (0 != TALER_amount_cmp (reserve_profits2, + &reserve_profits))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "select_historic_reserve_revenue_result: result does not match\n"); + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + /** * Main function that will be run by the scheduler. @@ -130,13 +238,8 @@ run (void *cls) struct TALER_MasterPublicKeyP master_pub; struct TALER_ReservePublicKeyP reserve_pub; - struct TALER_DenominationHashP rnd_hash; struct TALER_DenominationPrivateKey denom_priv; struct TALER_DenominationPublicKey denom_pub; - struct TALER_DenominationHashP denom_pub_hash; - struct GNUNET_TIME_Timestamp now; - struct GNUNET_TIME_Timestamp past; - struct GNUNET_TIME_Timestamp future; struct GNUNET_TIME_Timestamp date; RND_BLK (&master_pub); @@ -217,472 +320,364 @@ run (void *cls) (ppc.last_refund_serial_id != ppc2.last_refund_serial_id) || (ppc.last_withdraw_serial_id != ppc2.last_withdraw_serial_id) ); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: insert_reserve_info\n"); + { + struct TALER_AUDITORDB_ReserveFeeBalance rfb; + struct TALER_AUDITORDB_ReserveFeeBalance rfb2; - struct TALER_Amount reserve_balance; - struct TALER_Amount withdraw_fee_balance; - struct TALER_Amount purse_fee_balance; - struct TALER_Amount history_fee_balance; - struct TALER_Amount reserve_balance2 = {}; - struct TALER_Amount withdraw_fee_balance2 = {}; - struct TALER_Amount purse_fee_balance2 = {}; - struct TALER_Amount history_fee_balance2 = {}; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: insert_reserve_info\n"); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":12.345678", + &rfb.reserve_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":11.245678", + &rfb.reserve_loss)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":23.456789", + &rfb.withdraw_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":23.456719", + &rfb.close_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":33.456789", + &rfb.purse_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":43.456789", + &rfb.open_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":53.456789", + &rfb.history_fee_balance)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_reserve_info (plugin->cls, + &reserve_pub, + &master_pub, + &rfb, + past, + "payto://bla/blub")); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: update_reserve_info\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_reserve_info (plugin->cls, + &reserve_pub, + &master_pub, + &rfb, + future)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: get_reserve_info\n"); + { + char *payto; - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":12.345678", - &reserve_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":23.456789", - &withdraw_fee_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":23.456789", - &purse_fee_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":23.456789", - &history_fee_balance)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_reserve_info (plugin->cls, + &reserve_pub, + &master_pub, + &rowid, + &rfb2, + &date, + &payto)); + FAILIF (0 != strcmp (payto, + "payto://bla/blub")); + GNUNET_free (payto); + } + FAILIF ( (0 != GNUNET_memcmp (&date, + &future)) + || (0 != TALER_amount_cmp (&rfb2.reserve_balance, + &rfb.reserve_balance)) + || (0 != TALER_amount_cmp (&rfb2.withdraw_fee_balance, + &rfb.withdraw_fee_balance)) + || (0 != TALER_amount_cmp (&rfb2.close_fee_balance, + &rfb.close_fee_balance)) + || (0 != TALER_amount_cmp (&rfb2.purse_fee_balance, + &rfb.purse_fee_balance)) + || (0 != TALER_amount_cmp (&rfb2.open_fee_balance, + &rfb.open_fee_balance)) + || (0 != TALER_amount_cmp (&rfb2.history_fee_balance, + &rfb.history_fee_balance)) + ); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: insert_reserve_summary\n"); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_reserve_info (plugin->cls, - &reserve_pub, - &master_pub, - &reserve_balance, - &withdraw_fee_balance, - past, - "payto://bla/blub")); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_reserve_summary (plugin->cls, + &master_pub, + &rfb)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: update_reserve_summary\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_reserve_summary (plugin->cls, + &master_pub, + &rfb)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: get_reserve_summary\n"); + ZR_BLK (&rfb2); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_reserve_summary (plugin->cls, + &master_pub, + &rfb2)); + FAILIF ( (0 != TALER_amount_cmp (&rfb2.reserve_balance, + &rfb.reserve_balance) || + (0 != TALER_amount_cmp (&rfb2.withdraw_fee_balance, + &rfb.withdraw_fee_balance)) || + (0 != TALER_amount_cmp (&rfb2.close_fee_balance, + &rfb.close_fee_balance)) || + (0 != TALER_amount_cmp (&rfb2.purse_fee_balance, + &rfb.purse_fee_balance)) || + (0 != TALER_amount_cmp (&rfb2.open_fee_balance, + &rfb.open_fee_balance)) || + (0 != TALER_amount_cmp (&rfb2.history_fee_balance, + &rfb.history_fee_balance)))); + } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: update_reserve_info\n"); + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: insert_denomination_balance\n"); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_reserve_info (plugin->cls, - &reserve_pub, - &master_pub, - &reserve_balance, - &withdraw_fee_balance, - future)); + struct TALER_AUDITORDB_DenominationCirculationData dcd; + struct TALER_AUDITORDB_DenominationCirculationData dcd2; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: get_reserve_info\n"); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":12.345678", + &dcd.denom_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.1", + &dcd.denom_loss)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":13.57986", + &dcd.denom_risk)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":12.57986", + &dcd.recoup_loss)); + dcd.num_issued = 62; + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_denomination_balance (plugin->cls, + &denom_pub_hash, + &dcd)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: update_denomination_balance\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_denomination_balance (plugin->cls, + &denom_pub_hash, + &dcd)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: get_denomination_balance\n"); - char *payto; + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_denomination_balance (plugin->cls, + &denom_pub_hash, + &dcd2)); + FAILIF (0 != TALER_amount_cmp (&dcd2.denom_balance, + &dcd.denom_balance)); + FAILIF (0 != TALER_amount_cmp (&dcd2.denom_loss, + &dcd.denom_loss)); + FAILIF (0 != TALER_amount_cmp (&dcd2.denom_risk, + &dcd.denom_risk)); + FAILIF (0 != TALER_amount_cmp (&dcd2.recoup_loss, + &dcd.recoup_loss)); + FAILIF (dcd2.num_issued != dcd.num_issued); + } - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_reserve_info (plugin->cls, - &reserve_pub, - &master_pub, - &rowid, - &reserve_balance2, - &withdraw_fee_balance2, - &date, - &payto)); - FAILIF (0 != strcmp (payto, - "payto://bla/blub")); - GNUNET_free (payto); - FAILIF (0 != GNUNET_memcmp (&date, - &future) - || 0 != TALER_amount_cmp (&reserve_balance2, - &reserve_balance) - || 0 != TALER_amount_cmp (&withdraw_fee_balance2, - &withdraw_fee_balance)); + { + struct TALER_AUDITORDB_GlobalCoinBalance gcb; + struct TALER_AUDITORDB_GlobalCoinBalance gcb2; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: insert_reserve_summary\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_reserve_summary (plugin->cls, - &master_pub, - &reserve_balance, - &withdraw_fee_balance, - &purse_fee_balance, - &history_fee_balance)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: update_reserve_summary\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_reserve_summary (plugin->cls, - &master_pub, - &reserve_balance, - &withdraw_fee_balance, - &purse_fee_balance, - &history_fee_balance)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: get_reserve_summary\n"); - - ZR_BLK (&reserve_balance2); - ZR_BLK (&withdraw_fee_balance2); - ZR_BLK (&purse_fee_balance2); - ZR_BLK (&history_fee_balance2); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_reserve_summary (plugin->cls, - &master_pub, - &reserve_balance2, - &withdraw_fee_balance2, - &purse_fee_balance2, - &history_fee_balance2)); - - FAILIF ( (0 != TALER_amount_cmp (&reserve_balance2, - &reserve_balance) || - (0 != TALER_amount_cmp (&withdraw_fee_balance2, - &withdraw_fee_balance)) || - (0 != TALER_amount_cmp (&purse_fee_balance2, - &purse_fee_balance)) || - (0 != TALER_amount_cmp (&history_fee_balance2, - &history_fee_balance)))); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: insert_denomination_balance\n"); - - struct TALER_Amount denom_balance; - struct TALER_Amount denom_loss; - struct TALER_Amount denom_loss2; - struct TALER_Amount deposit_fee_balance; - struct TALER_Amount melt_fee_balance; - struct TALER_Amount refund_fee_balance; - struct TALER_Amount denom_balance2; - struct TALER_Amount deposit_fee_balance2; - struct TALER_Amount melt_fee_balance2; - struct TALER_Amount refund_fee_balance2; - struct TALER_Amount rbalance; - struct TALER_Amount dbalance; - struct TALER_Amount rbalance2; - struct TALER_Amount dbalance2; - struct TALER_Amount loss; - struct TALER_Amount loss2; - struct TALER_Amount iirp; - struct TALER_Amount iirp2; - uint64_t nissued; - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":12.345678", - &denom_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":0.1", - &denom_loss)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":23.456789", - &deposit_fee_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":34.567890", - &melt_fee_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":45.678901", - &refund_fee_balance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":13.57986", - &rbalance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":12.57986", - &dbalance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1.6", - &loss)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":1.1", - &iirp)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_denomination_balance (plugin->cls, - &denom_pub_hash, - &denom_balance, - &denom_loss, - &rbalance, - &loss, - 42)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: update_denomination_balance\n"); - - ppc.last_withdraw_serial_id++; - ppc.last_deposit_serial_id++; - ppc.last_melt_serial_id++; - ppc.last_refund_serial_id++; - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_denomination_balance (plugin->cls, - &denom_pub_hash, - &denom_balance, - &denom_loss, - &rbalance, - &loss, - 62)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: get_denomination_balance\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_denomination_balance (plugin->cls, - &denom_pub_hash, - &denom_balance2, - &denom_loss2, - &rbalance2, - &loss2, - &nissued)); - - FAILIF (0 != GNUNET_memcmp (&denom_balance2, &denom_balance)); - FAILIF (0 != GNUNET_memcmp (&denom_loss2, &denom_loss)); - FAILIF (0 != GNUNET_memcmp (&rbalance2, &rbalance)); - FAILIF (62 != nissued); - - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: insert_balance_summary\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_balance_summary (plugin->cls, - &master_pub, - &refund_fee_balance, - &melt_fee_balance, - &deposit_fee_balance, - &denom_balance, - &rbalance, - &loss, - &iirp)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: update_balance_summary\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_balance_summary (plugin->cls, - &master_pub, - &denom_balance, - &deposit_fee_balance, - &melt_fee_balance, - &refund_fee_balance, - &rbalance, - &loss, - &iirp)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: get_balance_summary\n"); - - ZR_BLK (&denom_balance2); - ZR_BLK (&deposit_fee_balance2); - ZR_BLK (&melt_fee_balance2); - ZR_BLK (&refund_fee_balance2); - ZR_BLK (&rbalance2); - ZR_BLK (&loss2); - ZR_BLK (&iirp2); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_balance_summary (plugin->cls, - &master_pub, - &denom_balance2, - &deposit_fee_balance2, - &melt_fee_balance2, - &refund_fee_balance2, - &rbalance2, - &loss2, - &iirp2)); - - FAILIF ( (0 != GNUNET_memcmp (&denom_balance2, - &denom_balance) ) || - (0 != GNUNET_memcmp (&deposit_fee_balance2, - &deposit_fee_balance) ) || - (0 != GNUNET_memcmp (&melt_fee_balance2, - &melt_fee_balance) ) || - (0 != GNUNET_memcmp (&refund_fee_balance2, - &refund_fee_balance)) ); - FAILIF (0 != GNUNET_memcmp (&rbalance2, - &rbalance)); - FAILIF (0 != GNUNET_memcmp (&loss2, - &loss)); - FAILIF (0 != GNUNET_memcmp (&iirp2, - &iirp)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":12.345678", + &gcb.total_escrowed)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":23.456789", + &gcb.deposit_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":34.567890", + &gcb.melt_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":45.678901", + &gcb.refund_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":55.678901", + &gcb.purse_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":65.678901", + &gcb.open_deposit_fee_balance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":13.57986", + &gcb.risk)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":0.1", + &gcb.loss)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":1.1", + &gcb.irregular_loss)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: insert_balance_summary\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_balance_summary (plugin->cls, + &master_pub, + &gcb)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: update_balance_summary\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_balance_summary (plugin->cls, + &master_pub, + &gcb)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: get_balance_summary\n"); + ZR_BLK (&gcb2); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_balance_summary (plugin->cls, + &master_pub, + &gcb2)); + FAILIF (0 != TALER_amount_cmp (&gcb2.total_escrowed, + &gcb.total_escrowed)); + FAILIF (0 != TALER_amount_cmp (&gcb2.deposit_fee_balance, + &gcb.deposit_fee_balance) ); + FAILIF (0 != TALER_amount_cmp (&gcb2.melt_fee_balance, + &gcb.melt_fee_balance) ); + FAILIF (0 != TALER_amount_cmp (&gcb2.refund_fee_balance, + &gcb.refund_fee_balance)); + FAILIF (0 != TALER_amount_cmp (&gcb2.purse_fee_balance, + &gcb.purse_fee_balance)); + FAILIF (0 != TALER_amount_cmp (&gcb2.open_deposit_fee_balance, + &gcb.open_deposit_fee_balance)); + FAILIF (0 != TALER_amount_cmp (&gcb2.risk, + &gcb.risk)); + FAILIF (0 != TALER_amount_cmp (&gcb2.loss, + &gcb.loss)); + FAILIF (0 != TALER_amount_cmp (&gcb2.irregular_loss, + &gcb.irregular_loss)); + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test: insert_historic_denom_revenue\n"); - + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":12.345678", + &rbalance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":23.456789", + &rloss)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->insert_historic_denom_revenue (plugin->cls, &master_pub, &denom_pub_hash, past, &rbalance, - &loss)); - + &rloss)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->insert_historic_denom_revenue (plugin->cls, &master_pub, &rnd_hash, now, &rbalance, - &loss)); - + &rloss)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test: select_historic_denom_revenue\n"); - - int - select_historic_denom_revenue_result ( - void *cls, - const struct TALER_DenominationHashP *denom_pub_hash2, - struct GNUNET_TIME_Timestamp revenue_timestamp2, - const struct TALER_Amount *revenue_balance2, - const struct TALER_Amount *loss2) - { - static int n = 0; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "select_historic_denom_revenue_result: row %u\n", n); - - if ((2 <= n++) - || (cls != NULL) - || ((0 != GNUNET_memcmp (&revenue_timestamp2, &past)) - && (0 != GNUNET_memcmp (&revenue_timestamp2, &now))) - || ((0 != GNUNET_memcmp (denom_pub_hash2, &denom_pub_hash)) - && (0 != GNUNET_memcmp (denom_pub_hash2, &rnd_hash))) - || (0 != GNUNET_memcmp (revenue_balance2, &rbalance)) - || (0 != GNUNET_memcmp (loss2, &loss))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "select_historic_denom_revenue_result: result does not match\n"); - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; - } - - FAILIF (0 >= - plugin->select_historic_denom_revenue (plugin->cls, - &master_pub, - & - select_historic_denom_revenue_result, - NULL)); - + plugin->select_historic_denom_revenue ( + plugin->cls, + &master_pub, + &select_historic_denom_revenue_result, + NULL)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test: insert_historic_reserve_revenue\n"); - - struct TALER_Amount reserve_profits; GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":56.789012", &reserve_profits)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->insert_historic_reserve_revenue (plugin->cls, &master_pub, past, future, &reserve_profits)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->insert_historic_reserve_revenue (plugin->cls, &master_pub, now, future, &reserve_profits)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Test: select_historic_reserve_revenue\n"); - - int - select_historic_reserve_revenue_result ( - void *cls, - struct GNUNET_TIME_Timestamp start_time2, - struct GNUNET_TIME_Timestamp end_time2, - const struct TALER_Amount *reserve_profits2) - { - static int n = 0; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "select_historic_reserve_revenue_result: row %u\n", n); - - if ((2 <= n++) - || (cls != NULL) - || ((0 != GNUNET_memcmp (&start_time2, &past)) - && (0 != GNUNET_memcmp (&start_time2, &now))) - || (0 != GNUNET_memcmp (&end_time2, &future)) - || (0 != GNUNET_memcmp (reserve_profits2, &reserve_profits))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "select_historic_reserve_revenue_result: result does not match\n"); - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; - } - - FAILIF (0 >= plugin->select_historic_reserve_revenue (plugin->cls, &master_pub, select_historic_reserve_revenue_result, NULL)); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: insert_predicted_result\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_predicted_result (plugin->cls, - &master_pub, - &rbalance, - &dbalance)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: update_predicted_result\n"); - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":78.901234", - &rbalance)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (CURRENCY ":73.901234", - &dbalance)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_predicted_result (plugin->cls, - &master_pub, - &rbalance, - &dbalance)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_wire_fee_summary (plugin->cls, - &master_pub, - &rbalance)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_wire_fee_summary (plugin->cls, - &master_pub, - &reserve_profits)); { - struct TALER_Amount rprof; + struct TALER_Amount dbalance; + struct TALER_Amount dbalance2; + struct TALER_Amount rbalance2; + + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":2.535678", + &dbalance)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: insert_predicted_result\n"); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_predicted_result (plugin->cls, + &master_pub, + &rbalance, + &dbalance)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: update_predicted_result\n"); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":78.901234", + &rbalance)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":73.901234", + &dbalance)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_predicted_result (plugin->cls, + &master_pub, + &rbalance, + &dbalance)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_wire_fee_summary (plugin->cls, + &master_pub, + &rbalance)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->update_wire_fee_summary (plugin->cls, + &master_pub, + &reserve_profits)); + { + struct TALER_Amount rprof; + + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->get_wire_fee_summary (plugin->cls, + &master_pub, + &rprof)); + FAILIF (0 != + TALER_amount_cmp (&rprof, + &reserve_profits)); + } + FAILIF (0 > + plugin->commit (plugin->cls)); + + + FAILIF (GNUNET_OK != + plugin->start (plugin->cls)); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Test: get_predicted_balance\n"); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_wire_fee_summary (plugin->cls, - &master_pub, - &rprof)); - FAILIF (0 != - TALER_amount_cmp (&rprof, - &reserve_profits)); + plugin->get_predicted_balance (plugin->cls, + &master_pub, + &rbalance2, + &dbalance2)); + + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->del_reserve_info (plugin->cls, + &reserve_pub, + &master_pub)); + + FAILIF (0 != TALER_amount_cmp (&rbalance2, + &rbalance)); + FAILIF (0 != TALER_amount_cmp (&dbalance2, + &dbalance)); + + plugin->rollback (plugin->cls); } - FAILIF (0 > - plugin->commit (plugin->cls)); - - - FAILIF (GNUNET_OK != - plugin->start (plugin->cls)); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Test: get_predicted_balance\n"); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_predicted_balance (plugin->cls, - &master_pub, - &rbalance2, - &dbalance2)); - - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->del_reserve_info (plugin->cls, - &reserve_pub, - &master_pub)); - - FAILIF (0 != TALER_amount_cmp (&rbalance2, - &rbalance)); - FAILIF (0 != TALER_amount_cmp (&dbalance2, - &dbalance)); - - plugin->rollback (plugin->cls); #if GC_IMPLEMENTED FAILIF (GNUNET_OK != diff --git a/src/exchange/taler-exchange-closer.c b/src/exchange/taler-exchange-closer.c index eacfa5d50..41c6436a1 100644 --- a/src/exchange/taler-exchange-closer.c +++ b/src/exchange/taler-exchange-closer.c @@ -204,6 +204,8 @@ commit_or_warn (void) * @param account_payto_uri information about the bank account that initially * caused the reserve to be created * @param expiration_date when did the reserve expire + * @param close_request_row row of request asking for + * closure, 0 for expired reserves * @return #GNUNET_OK on success (continue) * #GNUNET_NO on non-fatal errors (try again) * #GNUNET_SYSERR on fatal errors (abort) @@ -213,7 +215,8 @@ expired_reserve_cb (void *cls, const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *left, const char *account_payto_uri, - struct GNUNET_TIME_Timestamp expiration_date) + struct GNUNET_TIME_Timestamp expiration_date, + uint64_t close_request_row) { struct GNUNET_TIME_Timestamp now; struct TALER_WireTransferIdentifierRawP wtid; @@ -319,7 +322,8 @@ expired_reserve_cb (void *cls, account_payto_uri, &wtid, left, - &closing_fee); + &closing_fee, + close_request_row); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Closing reserve %s over %s (%d, %d)\n", TALER_B2S (reserve_pub), diff --git a/src/exchange/taler-exchange-httpd_purses_create.c b/src/exchange/taler-exchange-httpd_purses_create.c index 078357899..2bccd11f7 100644 --- a/src/exchange/taler-exchange-httpd_purses_create.c +++ b/src/exchange/taler-exchange-httpd_purses_create.c @@ -231,6 +231,10 @@ create_transaction (void *cls, } if (! balance_ok) { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Coin %s has insufficient balance for purse deposit of amount %s\n", + TALER_B2S (&coin->cpi.coin_pub), + TALER_amount2s (&coin->amount)); *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds ( connection, diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am index 54f51e9a3..f4ea5df9c 100644 --- a/src/exchangedb/Makefile.am +++ b/src/exchangedb/Makefile.am @@ -79,7 +79,9 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \ pg_iterate_reserve_close_info.c pg_iterate_reserve_close_info.h \ pg_lookup_records_by_table.c pg_lookup_records_by_table.h \ pg_lookup_serial_by_table.c pg_lookup_serial_by_table.h \ - pg_select_reserve_close_info.c pg_select_reserve_close_info.h + pg_select_reserve_close_info.c pg_select_reserve_close_info.h \ + pg_select_reserve_closed_above_serial_id.c pg_select_reserve_closed_above_serial_id.h \ + pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h libtaler_plugin_exchangedb_postgres_la_LIBADD = \ $(LTLIBINTL) libtaler_plugin_exchangedb_postgres_la_LDFLAGS = \ diff --git a/src/exchangedb/bench-db-postgres.conf b/src/exchangedb/bench-db-postgres.conf index 837ae41e2..d51cf9175 100644 --- a/src/exchangedb/bench-db-postgres.conf +++ b/src/exchangedb/bench-db-postgres.conf @@ -8,3 +8,7 @@ CONFIG = postgres:///talercheck # Where are the SQL files to setup our tables? # Important: this MUST end with a "/"! SQL_DIR = $DATADIR/sql/exchange/ + +[exchangedb] +# Number of purses per account by default. +DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql index 9f32ede74..ab4f8ea91 100644 --- a/src/exchangedb/common-0001.sql +++ b/src/exchangedb/common-0001.sql @@ -346,6 +346,7 @@ BEGIN ',amount_frac INT4 NOT NULL' ',closing_fee_val INT8 NOT NULL' ',closing_fee_frac INT4 NOT NULL' + ',close_request_row INT8 NOT NULL DEFAULT(0)' ') %s ;' ,table_name ,'PARTITION BY HASH (reserve_pub)' @@ -383,6 +384,79 @@ END $$; +--------------------------- close_requests --------------------------- + +CREATE OR REPLACE FUNCTION create_table_close_requests( + IN shard_suffix VARCHAR DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name VARCHAR DEFAULT 'close_requests'; +BEGIN + + PERFORM create_partitioned_table( + 'CREATE TABLE IF NOT EXISTS %I ' + '(close_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE' + ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)' -- REFERENCES reserves(reserve_pub) ON DELETE CASCADE + ',close_timestamp INT8 NOT NULL' + ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)' + ',close_val INT8 NOT NULL' + ',close_frac INT4 NOT NULL' + ',close_fee_val INT8 NOT NULL' + ',close_fee_frac INT4 NOT NULL' + ',payto_uri VARCHAR NOT NULL' + ',done BOOL NOT NULL DEFAULT(FALSE)' + ',PRIMARY KEY (reserve_pub,close_timestamp)' + ') %s ;' + ,table_name + ,'PARTITION BY HASH (reserve_pub)' + ,shard_suffix + ); +END +$$; + + +CREATE OR REPLACE FUNCTION add_constraints_to_close_requests( + IN partition_suffix VARCHAR +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name VARCHAR DEFAULT 'close_requests'; +BEGIN + + EXECUTE FORMAT ( + 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index ' + 'ON ' || table_name || ' ' + '(close_request_serial_id);' + ); + EXECUTE FORMAT ( + 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_done_index ' + 'ON ' || table_name || ' ' + '(done);' + ); +END +$$; + +CREATE OR REPLACE FUNCTION add_constraints_to_close_requests_partition( + IN partition_suffix VARCHAR +) +RETURNS void +LANGUAGE plpgsql +AS $$ +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE close_requests_' || partition_suffix || ' ' + 'ADD CONSTRAINT close_requests_' || partition_suffix || '_close_request_uuid_pkey ' + 'UNIQUE (close_request_serial_id)' + ); +END +$$; + + --------------------------- reserves_open_requests ------------------------------- CREATE OR REPLACE FUNCTION create_table_reserves_open_requests( @@ -1391,8 +1465,6 @@ BEGIN ',h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)' ',age_limit INT4 NOT NULL' ',flags INT4 NOT NULL' - ',refunded BOOLEAN NOT NULL DEFAULT(FALSE)' - ',finished BOOLEAN NOT NULL DEFAULT(FALSE)' ',in_reserve_quota BOOLEAN NOT NULL DEFAULT(FALSE)' ',amount_with_fee_val INT8 NOT NULL' ',amount_with_fee_frac INT4 NOT NULL' @@ -1444,53 +1516,6 @@ END $$; -------------------------------- purse_refunds ---------------------------------------- - -CREATE OR REPLACE FUNCTION create_table_purse_refunds( - IN shard_suffix VARCHAR DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name VARCHAR DEFAULT 'purse_refunds'; -BEGIN - - PERFORM create_partitioned_table( - 'CREATE TABLE IF NOT EXISTS %I ' - '(purse_refunds_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE - ',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)' - ',PRIMARY KEY (purse_pub)' - ') %s ;' - ,table_name - ,'PARTITION BY HASH (purse_pub)' - ,shard_suffix - ); - - table_name = concat_ws('_', table_name, shard_suffix); - -END -$$; - -CREATE OR REPLACE FUNCTION add_constraints_to_purse_refunds_partition( - IN partition_suffix VARCHAR -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -BEGIN - EXECUTE FORMAT ( - 'ALTER TABLE purse_refunds_' || partition_suffix || ' ' - 'ADD CONSTRAINT purse_refunds_' || partition_suffix || '_purse_refunds_serial_id_key ' - 'UNIQUE (purse_refunds_serial_id) ' - ); -END -$$; - - - - - ---------------------------- purse_merges ----------------------------- CREATE OR REPLACE FUNCTION create_table_purse_merges( @@ -1602,6 +1627,53 @@ BEGIN END $$; + +------------------------------- purse_decision ---------------------------------------- + +CREATE OR REPLACE FUNCTION create_table_purse_decision( + IN shard_suffix VARCHAR DEFAULT NULL +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +DECLARE + table_name VARCHAR DEFAULT 'purse_decision'; +BEGIN + + PERFORM create_partitioned_table( + 'CREATE TABLE IF NOT EXISTS %I ' + '(purse_decision_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE + ',purse_pub BYTEA NOT NULL CHECK (LENGTH(purse_pub)=32)' + ',action_timestamp INT8 NOT NULL' + ',refunded BOOL NOT NULL' + ',PRIMARY KEY (purse_pub)' + ') %s ;' + ,table_name + ,'PARTITION BY HASH (purse_pub)' + ,shard_suffix + ); + + table_name = concat_ws('_', table_name, shard_suffix); + +END +$$; + +CREATE OR REPLACE FUNCTION add_constraints_to_purse_decision_partition( + IN partition_suffix VARCHAR +) +RETURNS VOID +LANGUAGE plpgsql +AS $$ +BEGIN + EXECUTE FORMAT ( + 'ALTER TABLE purse_decision_' || partition_suffix || ' ' + 'ADD CONSTRAINT purse_decision_' || partition_suffix || '_purse_action_serial_id_key ' + 'UNIQUE (purse_decision_serial_id) ' + ); +END +$$; + + ------------------------- contracts ------------------------------- CREATE OR REPLACE FUNCTION create_table_contracts( @@ -1678,80 +1750,6 @@ BEGIN END $$; ---------------------------- close_requests --------------------------- - -CREATE OR REPLACE FUNCTION create_table_close_requests( - IN shard_suffix VARCHAR DEFAULT NULL -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name VARCHAR DEFAULT 'close_requests'; -BEGIN - - PERFORM create_partitioned_table( - 'CREATE TABLE IF NOT EXISTS %I ' - '(close_request_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' --UNIQUE' - ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)' -- REFERENCES reserves(reserve_pub) ON DELETE CASCADE - ',close_timestamp INT8 NOT NULL' - ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)' - ',close_val INT8 NOT NULL' - ',close_frac INT4 NOT NULL' - ',close_fee_val INT8 NOT NULL' - ',close_fee_frac INT4 NOT NULL' - ',payto_uri VARCHAR NOT NULL' - ',done BOOL NOT NULL DEFAULT(FALSE)' - ',PRIMARY KEY (reserve_pub,close_timestamp)' - ') %s ;' - ,table_name - ,'PARTITION BY HASH (reserve_pub)' - ,shard_suffix - ); -END -$$; - - -CREATE OR REPLACE FUNCTION add_constraints_to_close_requests( - IN partition_suffix VARCHAR -) -RETURNS VOID -LANGUAGE plpgsql -AS $$ -DECLARE - table_name VARCHAR DEFAULT 'close_requests'; -BEGIN - - EXECUTE FORMAT ( - 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_uuid_index ' - 'ON ' || table_name || ' ' - '(close_request_serial_id);' - ); - EXECUTE FORMAT ( - 'CREATE INDEX IF NOT EXISTS ' || table_name || '_by_close_request_done_index ' - 'ON ' || table_name || ' ' - '(done);' - ); -END -$$; - -CREATE OR REPLACE FUNCTION add_constraints_to_close_requests_partition( - IN partition_suffix VARCHAR -) -RETURNS void -LANGUAGE plpgsql -AS $$ -BEGIN - EXECUTE FORMAT ( - 'ALTER TABLE close_requests_' || partition_suffix || ' ' - 'ADD CONSTRAINT close_requests_' || partition_suffix || '_close_request_uuid_pkey ' - 'UNIQUE (close_request_serial_id)' - ); -END -$$; - - - ------------------------------- purse_deposits ------------------------------- CREATE OR REPLACE FUNCTION create_table_purse_deposits( @@ -2087,6 +2085,15 @@ BEGIN ALTER TABLE IF EXISTS reserves_close DETACH PARTITION reserves_close_default; + ALTER TABLE IF EXISTS history_requests + DETACH partition history_requests_default; + + ALTER TABLE IF EXISTS close_requests + DETACH partition close_requests_default; + + ALTER TABLE IF EXISTS reserves_open_requests + DETACH partition reserves_open_requests_default; + ALTER TABLE IF EXISTS reserves_out DETACH PARTITION reserves_out_default; @@ -2145,8 +2152,8 @@ BEGIN ALTER TABLE IF EXISTS purse_requests DETACH partition purse_requests_default; - ALTER TABLE IF EXISTS purse_refunds - DETACH partition purse_refunds_default; + ALTER TABLE IF EXISTS purse_decision + DETACH partition purse_decision_default; ALTER TABLE IF EXISTS purse_merges DETACH partition purse_merges_default; @@ -2157,12 +2164,6 @@ BEGIN ALTER TABLE IF EXISTS contracts DETACH partition contracts_default; - ALTER TABLE IF EXISTS history_requests - DETACH partition history_requests_default; - - ALTER TABLE IF EXISTS close_requests - DETACH partition close_requests_default; - ALTER TABLE IF EXISTS purse_deposits DETACH partition purse_deposits_default; @@ -2194,6 +2195,10 @@ BEGIN DROP TABLE IF EXISTS reserves_default; DROP TABLE IF EXISTS reserves_in_default; DROP TABLE IF EXISTS reserves_close_default; + DROP TABLE IF EXISTS reserves_open_requests_default; + DROP TABLE IF EXISTS history_requests_default; + DROP TABLE IF EXISTS close_requests_default; + DROP TABLE IF EXISTS reserves_out_default; DROP TABLE IF EXISTS reserves_out_by_reserve_default; DROP TABLE IF EXISTS known_coins_default; @@ -2214,13 +2219,12 @@ BEGIN DROP TABLE IF EXISTS cs_nonce_locks_default; DROP TABLE IF EXISTS purse_requests_default; - DROP TABLE IF EXISTS purse_refunds_default; + DROP TABLE IF EXISTS purse_decision_default; DROP TABLE IF EXISTS purse_merges_default; DROP TABLE IF EXISTS account_merges_default; - DROP TABLE IF EXISTS contracts_default; - DROP TABLE IF EXISTS history_requests_default; - DROP TABLE IF EXISTS close_requests_default; DROP TABLE IF EXISTS purse_deposits_default; + DROP TABLE IF EXISTS contracts_default; + DROP TABLE IF EXISTS wad_out_entries_default; DROP TABLE IF EXISTS wads_in_default; DROP TABLE IF EXISTS wad_in_entries_default; @@ -2417,6 +2421,27 @@ BEGIN ); PERFORM add_constraints_to_cs_nonce_locks_partition(num_partitions::varchar); + + PERFORM create_hash_partition( + 'close_requests' + ,modulus + ,num_partitions + ); + + PERFORM create_hash_partition( + 'reserves_open_requests' + ,modulus + ,num_partitions + ); + PERFORM add_constraints_to_reserves_open_request_partition(num_partitions::varchar); + + PERFORM create_hash_partition( + 'history_requests' + ,modulus + ,num_partitions + ); + + ---------------- P2P ---------------------- PERFORM create_hash_partition( @@ -2427,11 +2452,11 @@ BEGIN PERFORM add_constraints_to_purse_requests_partition(num_partitions::varchar); PERFORM create_hash_partition( - 'purse_refunds' + 'purse_decision' ,modulus ,num_partitions ); - PERFORM add_constraints_to_purse_refunds_partition(num_partitions::varchar); + PERFORM add_constraints_to_purse_decision_partition(num_partitions::varchar); PERFORM create_hash_partition( 'purse_merges' @@ -2454,18 +2479,6 @@ BEGIN ); PERFORM add_constraints_to_contracts_partition(num_partitions::varchar); - PERFORM create_hash_partition( - 'history_requests' - ,modulus - ,num_partitions - ); - - PERFORM create_hash_partition( - 'close_requests' - ,modulus - ,num_partitions - ); - PERFORM create_hash_partition( 'purse_deposits' ,modulus @@ -2644,8 +2657,8 @@ BEGIN DROP CONSTRAINT IF EXISTS purse_requests_pkey CASCADE ; - ALTER TABLE IF EXISTS purse_refunds - DROP CONSTRAINT IF EXISTS purse_refunds_pkey CASCADE + ALTER TABLE IF EXISTS purse_decision + DROP CONSTRAINT IF EXISTS purse_decision_pkey CASCADE ; ALTER TABLE IF EXISTS purse_merges @@ -2781,6 +2794,27 @@ BEGIN ,current_shard_num ,local_user ); + PERFORM create_foreign_hash_partition( + 'history_requests' + ,total_num_shards + ,shard_suffix + ,current_shard_num + ,local_user + ); + PERFORM create_foreign_hash_partition( + 'close_requests' + ,total_num_shards + ,shard_suffix + ,current_shard_num + ,local_user + ); + PERFORM create_foreign_hash_partition( + 'open_requests' + ,total_num_shards + ,shard_suffix + ,current_shard_num + ,local_user + ); PERFORM create_foreign_hash_partition( 'known_coins' ,total_num_shards @@ -2904,7 +2938,7 @@ BEGIN ,local_user ); PERFORM create_foreign_hash_partition( - 'purse_refunds' + 'purse_decision' ,total_num_shards ,shard_suffix ,current_shard_num @@ -2931,20 +2965,7 @@ BEGIN ,current_shard_num ,local_user ); - PERFORM create_foreign_hash_partition( - 'history_requests' - ,total_num_shards - ,shard_suffix - ,current_shard_num - ,local_user - ); - PERFORM create_foreign_hash_partition( - 'close_requests' - ,total_num_shards - ,shard_suffix - ,current_shard_num - ,local_user - ); + PERFORM create_foreign_hash_partition( 'purse_deposits' ,total_num_shards diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index 760acd98b..4903b8879 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -1135,10 +1135,6 @@ COMMENT ON COLUMN purse_requests.h_contract_terms IS 'Hash of the contract the parties are to agree to'; COMMENT ON COLUMN purse_requests.flags IS 'see the enum TALER_WalletAccountMergeFlags'; -COMMENT ON COLUMN purse_requests.finished - IS 'set to TRUE once the purse has been merged (into reserve or wad) or the coins were refunded (transfer aborted)'; -COMMENT ON COLUMN purse_requests.refunded - IS 'set to TRUE if the purse could not be merged and thus all deposited coins were refunded'; COMMENT ON COLUMN purse_requests.in_reserve_quota IS 'set to TRUE if this purse currently counts against the number of free purses in the respective reserve'; COMMENT ON COLUMN purse_requests.amount_with_fee_val @@ -1157,20 +1153,20 @@ CREATE TABLE IF NOT EXISTS purse_requests_default SELECT add_constraints_to_purse_requests_partition('default'); --- ------------------------------ purse_refunds ---------------------------------------- +-- ------------------------------ purse_decisions ---------------------------------------- -SELECT create_table_purse_refunds(); +SELECT create_table_purse_decision(); -COMMENT ON TABLE purse_refunds - IS 'Purses that were refunded due to expiration'; -COMMENT ON COLUMN purse_refunds.purse_pub +COMMENT ON TABLE purse_decision + IS 'Purses that were decided upon (refund or merge)'; +COMMENT ON COLUMN purse_decision.purse_pub IS 'Public key of the purse'; -CREATE TABLE IF NOT EXISTS purse_refunds_default - PARTITION OF purse_refunds +CREATE TABLE IF NOT EXISTS purse_decision_default + PARTITION OF purse_decision FOR VALUES WITH (MODULUS 1, REMAINDER 0); -SELECT add_constraints_to_purse_refunds_partition('default'); +SELECT add_constraints_to_purse_decision_partition('default'); -- ------------------------------ purse_merges ---------------------------------------- @@ -1462,7 +1458,6 @@ CREATE OR REPLACE FUNCTION purse_requests_insert_trigger() LANGUAGE plpgsql AS $$ BEGIN - ASSERT NOT NEW.finished,'Internal invariant violated'; INSERT INTO purse_actions (purse_pub @@ -1482,45 +1477,3 @@ CREATE TRIGGER purse_requests_on_insert COMMENT ON TRIGGER purse_requests_on_insert ON purse_requests IS 'Here we install an entry for the purse expiration.'; - - -CREATE OR REPLACE FUNCTION purse_requests_on_update_trigger() - RETURNS trigger - LANGUAGE plpgsql - AS $$ -BEGIN - IF (NEW.finished AND NOT OLD.finished) - THEN - -- If this purse counted against the reserve's - -- quota of purses, decrement the reserve accounting. - IF (NEW.in_reserve_quota) - THEN - UPDATE reserves - SET purses_active=purses_active-1 - WHERE reserve_pub IN - (SELECT reserve_pub - FROM exchange.purse_merges - WHERE purse_pub=NEW.purse_pub - LIMIT 1); - NEW.in_reserve_quota=FALSE; - END IF; - -- Delete from the purse_actions table, we are done - -- with this purse for good. - DELETE FROM exchange.purse_actions - WHERE purse_pub=NEW.purse_pub; - RETURN NEW; - END IF; - - RETURN NEW; -END $$; - -COMMENT ON FUNCTION purse_requests_on_update_trigger() - IS 'Trigger the router if the purse is ready. Also removes the entry from the router watchlist once the purse is finished.'; - -CREATE TRIGGER purse_requests_on_update - BEFORE UPDATE - ON purse_requests - FOR EACH ROW EXECUTE FUNCTION purse_requests_on_update_trigger(); -COMMENT ON TRIGGER purse_requests_on_update - ON purse_requests - IS 'This covers the case where a deposit is made into a purse, which inherently then changes the purse balance via an UPDATE. If the merge is already present and the balance matches the total, we trigger the router. Once the router sets the purse to finished, the trigger will remove the purse from the watchlist of the router.'; diff --git a/src/exchangedb/exchangedb-postgres.conf b/src/exchangedb/exchangedb-postgres.conf index 7d600586f..e481940ce 100644 --- a/src/exchangedb/exchangedb-postgres.conf +++ b/src/exchangedb/exchangedb-postgres.conf @@ -4,3 +4,6 @@ CONFIG = "postgres:///taler" # Where are the SQL files to setup our tables? # Important: this MUST end with a "/"! SQL_DIR = $DATADIR/sql/exchange/ + +# Number of purses per account by default. +DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c index 27bd513fd..f24c9be4a 100644 --- a/src/exchangedb/pg_get_coin_transactions.c +++ b/src/exchangedb/pg_get_coin_transactions.c @@ -169,6 +169,7 @@ add_coin_purse_deposit (void *cls, chc->have_deposit_or_melt = true; deposit = GNUNET_new (struct TALER_EXCHANGEDB_PurseDepositListEntry); { + bool not_finished; struct GNUNET_PQ_ResultSpec rs[] = { TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee", &deposit->amount), @@ -186,8 +187,10 @@ add_coin_purse_deposit (void *cls, &deposit->coin_sig), GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash", &deposit->h_age_commitment), - GNUNET_PQ_result_spec_bool ("refunded", - &deposit->refunded), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_bool ("refunded", + &deposit->refunded), + ¬_finished), GNUNET_PQ_result_spec_end }; @@ -201,6 +204,8 @@ add_coin_purse_deposit (void *cls, chc->failed = true; return; } + if (not_finished) + deposit->refunded = false; deposit->no_age_commitment = GNUNET_is_zero (&deposit->h_age_commitment); } tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList); @@ -352,9 +357,9 @@ add_coin_refund (void *cls, * @param num_results the number of results in @a result */ static void -add_coin_purse_refund (void *cls, - PGresult *result, - unsigned int num_results) +add_coin_purse_decision (void *cls, + PGresult *result, + unsigned int num_results) { struct CoinHistoryContext *chc = cls; struct PostgresClosure *pg = chc->pg; @@ -374,7 +379,7 @@ add_coin_purse_refund (void *cls, &prefund->refund_amount), TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refund", &prefund->refund_fee), - GNUNET_PQ_result_spec_uint64 ("purse_refunds_serial_id", + GNUNET_PQ_result_spec_uint64 ("purse_decision_serial_id", &serial_id), GNUNET_PQ_result_spec_end }; @@ -687,8 +692,8 @@ TEH_PG_get_coin_transactions ( { "get_purse_deposit_by_coin_pub", &add_coin_purse_deposit }, /** #TALER_EXCHANGEDB_TT_PURSE_REFUND */ - { "get_purse_refund_by_coin_pub", - &add_coin_purse_refund }, + { "get_purse_decision_by_coin_pub", + &add_coin_purse_decision }, /** #TALER_EXCHANGEDB_TT_REFUND */ { "get_refunds_by_coin", &add_coin_refund }, @@ -775,12 +780,14 @@ TEH_PG_get_coin_transactions ( ",kc.age_commitment_hash" ",pd.coin_sig" ",pd.purse_deposit_serial_id" - ",pr.refunded" + ",pdes.refunded" " FROM purse_deposits pd" " LEFT JOIN partners" " USING (partner_serial_id)" " JOIN purse_requests pr" " USING (purse_pub)" + " LEFT JOIN purse_decision pdes" + " USING (purse_pub)" " JOIN known_coins kc" " ON (pd.coin_pub = kc.coin_pub)" " JOIN denominations denoms" @@ -809,22 +816,23 @@ TEH_PG_get_coin_transactions ( " USING (denominations_serial)" " WHERE ref.coin_pub=$1;"); PREPARE (pg, - "get_purse_refund_by_coin_pub", + "get_purse_decision_by_coin_pub", "SELECT" - " pr.purse_pub" + " pdes.purse_pub" ",pd.amount_with_fee_val" ",pd.amount_with_fee_frac" ",denom.fee_refund_val " ",denom.fee_refund_frac " - ",pr.purse_refunds_serial_id" + ",pdes.purse_decision_serial_id" " FROM purse_deposits pd" - " JOIN purse_refunds pr" + " JOIN purse_decision pdes" " USING (purse_pub)" " JOIN known_coins kc" " ON (pd.coin_pub = kc.coin_pub)" " JOIN denominations denom" " USING (denominations_serial)" - " WHERE pd.coin_pub=$1;"); + " WHERE pd.coin_pub=$1" + " AND pdes.refunded;"); PREPARE (pg, "recoup_by_old_coin", "SELECT" diff --git a/src/exchangedb/pg_get_expired_reserves.c b/src/exchangedb/pg_get_expired_reserves.c index 07a739115..c7162dc6b 100644 --- a/src/exchangedb/pg_get_expired_reserves.c +++ b/src/exchangedb/pg_get_expired_reserves.c @@ -102,7 +102,8 @@ reserve_expired_cb (void *cls, &reserve_pub, &remaining_balance, account_details, - exp_date); + exp_date, + 0); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != ret) break; diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c index 893ba7db4..8b8e280a6 100644 --- a/src/exchangedb/pg_get_reserve_history.c +++ b/src/exchangedb/pg_get_reserve_history.c @@ -797,13 +797,14 @@ TEH_PG_get_reserve_history (void *cls, " FROM purse_merges pm" " JOIN purse_requests pr" " USING (purse_pub)" + " JOIN purse_decision pdes" + " USING (purse_pub)" " JOIN account_merges am" " ON (am.purse_pub = pm.purse_pub AND" " am.reserve_pub = pm.reserve_pub)" " WHERE pm.reserve_pub=$1" " AND pm.partner_serial_id=0" /* must be local! */ - " AND pr.finished" - " AND NOT pr.refunded;"); + " AND NOT pdes.refunded;"); PREPARE (pg, "history_by_reserve", "SELECT" @@ -1089,14 +1090,15 @@ TEH_PG_get_reserve_status (void *cls, " FROM purse_merges pm" " JOIN purse_requests pr" " USING (purse_pub)" + " JOIN purse_decision pdes" + " USING (purse_pub)" " JOIN account_merges am" " ON (am.purse_pub = pm.purse_pub AND" " am.reserve_pub = pm.reserve_pub)" " WHERE pm.reserve_pub=$1" " AND pm.merge_timestamp >= $2" " AND pm.partner_serial_id=0" /* must be local! */ - " AND pr.finished" - " AND NOT pr.refunded;"); + " AND NOT pdes.refunded;"); PREPARE (pg, "history_by_reserve_truncated", "SELECT" diff --git a/src/exchangedb/pg_get_unfinished_close_requests.c b/src/exchangedb/pg_get_unfinished_close_requests.c index d9da6a7c0..fa8abdf8b 100644 --- a/src/exchangedb/pg_get_unfinished_close_requests.c +++ b/src/exchangedb/pg_get_unfinished_close_requests.c @@ -77,6 +77,7 @@ reserve_cb (void *cls, char *account_details; struct TALER_ReservePublicKeyP reserve_pub; struct TALER_Amount remaining_balance; + uint64_t close_request_row; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_timestamp ("expiration_date", &exp_date), @@ -86,6 +87,8 @@ reserve_cb (void *cls, &reserve_pub), TALER_PQ_RESULT_SPEC_AMOUNT ("close", &remaining_balance), + GNUNET_PQ_result_spec_uint64 ("close_request_serial_id", + &close_request_row), GNUNET_PQ_result_spec_end }; @@ -102,7 +105,8 @@ reserve_cb (void *cls, &reserve_pub, &remaining_balance, account_details, - exp_date); + exp_date, + close_request_row); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != ret) break; @@ -136,6 +140,7 @@ TEH_PG_get_unfinished_close_requests ( " WHERE done=FALSE" " RETURNING" " reserve_pub" + " ,close_request_serial_id" " ,close_timestamp AS expiration_date" " ,close_val" " ,close_frac" diff --git a/src/exchangedb/pg_insert_records_by_table.c b/src/exchangedb/pg_insert_records_by_table.c index 5613166cd..99173cc60 100644 --- a/src/exchangedb/pg_insert_records_by_table.c +++ b/src/exchangedb/pg_insert_records_by_table.c @@ -1336,19 +1336,23 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg, /** - * Function called with purse_refunds records to insert into table. + * Function called with purse_decision records to insert into table. * * @param pg plugin context * @param td record to insert */ static enum GNUNET_DB_QueryStatus -irbt_cb_table_purse_refunds (struct PostgresClosure *pg, - const struct TALER_EXCHANGEDB_TableData *td) +irbt_cb_table_purse_decision (struct PostgresClosure *pg, + const struct TALER_EXCHANGEDB_TableData *td) { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&td->serial), GNUNET_PQ_query_param_auto_from_type ( - &td->details.purse_refunds.purse_pub), + &td->details.purse_decision.purse_pub), + GNUNET_PQ_query_param_timestamp ( + &td->details.purse_decision.action_timestamp), + GNUNET_PQ_query_param_bool ( + &td->details.purse_decision.refunded), GNUNET_PQ_query_param_end }; @@ -1357,10 +1361,12 @@ irbt_cb_table_purse_refunds (struct PostgresClosure *pg, "INSERT INTO purse_refunds" "(purse_refunds_serial_id" ",purse_pub" + ",action_timestamp" + ",refunded" ") VALUES " - "($1, $2);"); + "($1, $2, $3, $4);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_into_table_purse_refunds", + "insert_into_table_purse_decision", params); } @@ -1910,8 +1916,8 @@ TEH_PG_insert_records_by_table (void *cls, case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: rh = &irbt_cb_table_purse_requests; break; - case TALER_EXCHANGEDB_RT_PURSE_REFUNDS: - rh = &irbt_cb_table_purse_refunds; + case TALER_EXCHANGEDB_RT_PURSE_DECISION: + rh = &irbt_cb_table_purse_decision; break; case TALER_EXCHANGEDB_RT_PURSE_MERGES: rh = &irbt_cb_table_purse_merges; diff --git a/src/exchangedb/pg_lookup_records_by_table.c b/src/exchangedb/pg_lookup_records_by_table.c index dc1f17caa..b7435bc2b 100644 --- a/src/exchangedb/pg_lookup_records_by_table.c +++ b/src/exchangedb/pg_lookup_records_by_table.c @@ -1575,20 +1575,20 @@ lrbt_cb_table_purse_requests (void *cls, /** - * Function called with purse_refunds table entries. + * Function called with purse_decision table entries. * * @param cls closure * @param result the postgres result * @param num_results the number of results in @a result */ static void -lrbt_cb_table_purse_refunds (void *cls, - PGresult *result, - unsigned int num_results) +lrbt_cb_table_purse_decision (void *cls, + PGresult *result, + unsigned int num_results) { struct LookupRecordsByTableContext *ctx = cls; struct TALER_EXCHANGEDB_TableData td = { - .table = TALER_EXCHANGEDB_RT_PURSE_REFUNDS + .table = TALER_EXCHANGEDB_RT_PURSE_DECISION }; for (unsigned int i = 0; i $1" - " ORDER BY purse_refunds_serial_id ASC;"); - rh = &lrbt_cb_table_purse_refunds; + " FROM purse_decision" + " WHERE purse_decision_serial_id > $1" + " ORDER BY purse_decision_serial_id ASC;"); + rh = &lrbt_cb_table_purse_decision; break; case TALER_EXCHANGEDB_RT_PURSE_MERGES: XPREPARE ("select_above_serial_by_table_purse_merges", diff --git a/src/exchangedb/pg_lookup_serial_by_table.c b/src/exchangedb/pg_lookup_serial_by_table.c index b8d254791..202be30f8 100644 --- a/src/exchangedb/pg_lookup_serial_by_table.c +++ b/src/exchangedb/pg_lookup_serial_by_table.c @@ -293,12 +293,12 @@ TEH_PG_lookup_serial_by_table (void *cls, " ORDER BY purse_requests_serial_id DESC" " LIMIT 1;") break; - case TALER_EXCHANGEDB_RT_PURSE_REFUNDS: - XPREPARE ("select_serial_by_table_purse_refunds", + case TALER_EXCHANGEDB_RT_PURSE_DECISION: + XPREPARE ("select_serial_by_table_purse_decision", "SELECT" - " purse_refunds_serial_id AS serial" - " FROM purse_refunds" - " ORDER BY purse_refunds_serial_id DESC" + " purse_decision_serial_id AS serial" + " FROM purse_decision" + " ORDER BY purse_decision_serial_id DESC" " LIMIT 1;"); break; case TALER_EXCHANGEDB_RT_PURSE_MERGES: diff --git a/src/exchangedb/pg_select_reserve_closed_above_serial_id.c b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c new file mode 100644 index 000000000..985c6792c --- /dev/null +++ b/src/exchangedb/pg_select_reserve_closed_above_serial_id.c @@ -0,0 +1,180 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file pg_select_reserve_closed_above_serial_id.c + * @brief Low-level (statement-level) Postgres database access for the exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_get_reserve_history.h" +#include "plugin_exchangedb_common.h" +#include "pg_helper.h" + +/** + * Closure for #reserve_closed_serial_helper_cb(). + */ +struct ReserveClosedSerialContext +{ + + /** + * Callback to call. + */ + TALER_EXCHANGEDB_ReserveClosedCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin's context. + */ + struct PostgresClosure *pg; + + /** + * Status code, set to #GNUNET_SYSERR on hard errors. + */ + enum GNUNET_GenericReturnValue status; +}; + + +/** + * Helper function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct ReserveClosedSerialContext` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +reserve_closed_serial_helper_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveClosedSerialContext *rcsc = cls; + struct PostgresClosure *pg = rcsc->pg; + + for (unsigned int i = 0; istatus = GNUNET_SYSERR; + return; + } + ret = rcsc->cb (rcsc->cb_cls, + rowid, + execution_date, + &amount_with_fee, + &closing_fee, + &reserve_pub, + receiver_account, + &wtid, + close_request_row); + GNUNET_PQ_cleanup_result (rs); + if (GNUNET_OK != ret) + break; + } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_select_reserve_closed_above_serial_id ( + void *cls, + uint64_t serial_id, + TALER_EXCHANGEDB_ReserveClosedCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&serial_id), + GNUNET_PQ_query_param_end + }; + struct ReserveClosedSerialContext rcsc = { + .cb = cb, + .cb_cls = cb_cls, + .pg = pg, + .status = GNUNET_OK + }; + enum GNUNET_DB_QueryStatus qs; + + /* Used in #postgres_select_reserve_closed_above_serial_id() to + obtain information about closed reserves */ + PREPARE ( + pg, + "reserves_close_get_incr", + "SELECT" + " close_uuid" + ",reserves.reserve_pub" + ",execution_date" + ",wtid" + ",payto_uri AS receiver_account" + ",amount_val" + ",amount_frac" + ",closing_fee_val" + ",closing_fee_frac" + ",close_request_row" + " FROM reserves_close" + " JOIN wire_targets" + " USING (wire_target_h_payto)" + " JOIN reserves" + " USING (reserve_pub)" + " WHERE close_uuid>=$1" + " ORDER BY close_uuid ASC;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "reserves_close_get_incr", + params, + &reserve_closed_serial_helper_cb, + &rcsc); + if (GNUNET_OK != rcsc.status) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} diff --git a/src/exchangedb/pg_select_reserve_closed_above_serial_id.h b/src/exchangedb/pg_select_reserve_closed_above_serial_id.h new file mode 100644 index 000000000..af3c8631e --- /dev/null +++ b/src/exchangedb/pg_select_reserve_closed_above_serial_id.h @@ -0,0 +1,47 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file pg_select_reserve_closed_above_serial_id.h + * @brief implementation of the select_reserve_closed_above_serial_id function + * @author Christian Grothoff + */ +#ifndef PG_SELECT_RESERVE_CLOSED_ABOVE_SERIAL_ID_H +#define PG_SELECT_RESERVE_CLOSED_ABOVE_SERIAL_ID_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Function called to select reserve close operations the aggregator + * triggered, ordered by serial ID (monotonically increasing). + * + * @param cls closure + * @param serial_id lowest serial ID to include (select larger or equal) + * @param cb function to call for ONE unfinished item + * @param cb_cls closure for @a cb + * @return transaction status code + */ +enum GNUNET_DB_QueryStatus +TEH_PG_select_reserve_closed_above_serial_id ( + void *cls, + uint64_t serial_id, + TALER_EXCHANGEDB_ReserveClosedCallback cb, + void *cb_cls); + + +#endif diff --git a/src/exchangedb/pg_select_reserve_open_above_serial_id.c b/src/exchangedb/pg_select_reserve_open_above_serial_id.c new file mode 100644 index 000000000..cc33bc48c --- /dev/null +++ b/src/exchangedb/pg_select_reserve_open_above_serial_id.c @@ -0,0 +1,169 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file pg_select_reserve_open_above_serial_id.c + * @brief Low-level (statement-level) Postgres database access for the exchange + * @author Christian Grothoff + */ +#include "platform.h" +#include "taler_error_codes.h" +#include "taler_dbevents.h" +#include "taler_pq_lib.h" +#include "pg_select_reserve_open_above_serial_id.h" +#include "plugin_exchangedb_common.h" +#include "pg_helper.h" + + +/** + * Closure for #reserve_open_serial_helper_cb(). + */ +struct ReserveOpenSerialContext +{ + + /** + * Callback to call. + */ + TALER_EXCHANGEDB_ReserveOpenCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Plugin's context. + */ + struct PostgresClosure *pg; + + /** + * Status code, set to #GNUNET_SYSERR on hard errors. + */ + enum GNUNET_GenericReturnValue status; +}; + + +/** + * Helper function to be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct ReserveOpenSerialContext` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +reserve_open_serial_helper_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveOpenSerialContext *rcsc = cls; + struct PostgresClosure *pg = rcsc->pg; + + for (unsigned int i = 0; istatus = GNUNET_SYSERR; + return; + } + ret = rcsc->cb (rcsc->cb_cls, + rowid, + &reserve_payment, + request_timestamp, + reserve_expiration, + requested_purse_limit, + &reserve_pub, + &reserve_sig); + GNUNET_PQ_cleanup_result (rs); + if (GNUNET_OK != ret) + break; + } +} + + +enum GNUNET_DB_QueryStatus +TEH_PG_select_reserve_open_above_serial_id ( + void *cls, + uint64_t serial_id, + TALER_EXCHANGEDB_ReserveOpenCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&serial_id), + GNUNET_PQ_query_param_end + }; + struct ReserveOpenSerialContext rcsc = { + .cb = cb, + .cb_cls = cb_cls, + .pg = pg, + .status = GNUNET_OK + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE ( + pg, + "reserves_open_get_incr", + "SELECT" + " open_request_uuid" + ",reserve_pub" + ",request_timestamp" + ",expiration_date" + ",reserve_sig" + ",reserve_payment_val" + ",reserve_payment_frac" + ",requested_purse_limit" + " FROM reserves_open_requests" + " WHERE open_request_uuid>=$1" + " ORDER BY open_request_uuid ASC;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "reserves_open_get_incr", + params, + &reserve_open_serial_helper_cb, + &rcsc); + if (GNUNET_OK != rcsc.status) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} diff --git a/src/exchangedb/pg_select_reserve_open_above_serial_id.h b/src/exchangedb/pg_select_reserve_open_above_serial_id.h new file mode 100644 index 000000000..4ec5b705a --- /dev/null +++ b/src/exchangedb/pg_select_reserve_open_above_serial_id.h @@ -0,0 +1,47 @@ +/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file pg_select_reserve_open_above_serial_id.h + * @brief implementation of the select_reserve_open_above_serial_id function + * @author Christian Grothoff + */ +#ifndef PG_SELECT_RESERVE_OPEN_ABOVE_SERIAL_ID_H +#define PG_SELECT_RESERVE_OPEN_ABOVE_SERIAL_ID_H + +#include "taler_util.h" +#include "taler_json_lib.h" +#include "taler_exchangedb_plugin.h" + + +/** + * Function called to select reserve open operations, ordered by serial ID + * (monotonically increasing). + * + * @param cls closure + * @param serial_id lowest serial ID to include (select larger or equal) + * @param cb function to call for ONE unfinished item + * @param cb_cls closure for @a cb + * @return transaction status code + */ +enum GNUNET_DB_QueryStatus +TEH_PG_select_reserve_open_above_serial_id ( + void *cls, + uint64_t serial_id, + TALER_EXCHANGEDB_ReserveOpenCallback cb, + void *cb_cls); + + +#endif diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index af441d95d..c4957c912 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -44,6 +44,8 @@ #include "pg_lookup_records_by_table.h" #include "pg_lookup_serial_by_table.h" #include "pg_select_reserve_close_info.h" +#include "pg_select_reserve_closed_above_serial_id.h" +#include "pg_select_reserve_open_above_serial_id.h" #include #include #include @@ -574,7 +576,8 @@ prepare_statements (struct PostgresClosure *pg) ",amount_frac" ",closing_fee_val" ",closing_fee_frac" - ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8);"), + ",close_request_row" + ") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);"), /* Used in #postgres_insert_drain_profit() */ GNUNET_PQ_make_prepare ( "drain_profit_insert", @@ -741,7 +744,7 @@ prepare_statements (struct PostgresClosure *pg) " out_balance_ok AS balance_ok" ",out_conflict AS conflict" " FROM exchange_do_purse_deposit" - " ($1,$2,$3,$4,$5,$6,$7,$8,$9);"), + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);"), /* Used in #postgres_update_aggregation_transient() */ GNUNET_PQ_make_prepare ( "set_purse_balance", @@ -755,7 +758,7 @@ prepare_statements (struct PostgresClosure *pg) "SELECT " " out_found AS found" " FROM exchange_do_expire_purse" - " ($1,$2);"), + " ($1,$2,$3);"), /* Used in #postgres_do_melt() to melt a coin. */ GNUNET_PQ_make_prepare ( "call_melt", @@ -1304,15 +1307,21 @@ prepare_statements (struct PostgresClosure *pg) " JOIN denominations denom USING (denominations_serial)" " WHERE purse_pub=$1;"), GNUNET_PQ_make_prepare ( - "audit_get_purse_refunds_incr", + "audit_get_purse_decisions_incr", "SELECT" - " purse_pub" - ",purse_refunds_serial_id" - " FROM purse_refunds" + " pd.purse_pub" + ",pm.reserve_pub" + ",pd.purse_decision_serial_id" + ",pr.amount_with_fee_val" + ",pr.amount_with_fee_frac" + " FROM purse_decision pd" + " JOIN purse_requests pr ON (pd.purse_pub = pr.purse_pub)" + " LEFT JOIN purse_merges pm ON (pm.purse_pub = pd.purse_pub)" " WHERE (" - " (purse_refunds_serial_id>=$1)" + " (purse_decision_serial_id>=$1) AND " + " (refunded=$2)" " )" - " ORDER BY purse_refunds_serial_id ASC;"), + " ORDER BY purse_decision_serial_id ASC;"), /* Fetch an existing deposit request. Used in #postgres_lookup_transfer_by_deposit(). */ GNUNET_PQ_make_prepare ( @@ -1845,27 +1854,6 @@ prepare_statements (struct PostgresClosure *pg) " ON (old_coins.denominations_serial = old_denoms.denominations_serial)" " WHERE recoup_refresh_uuid>=$1" " ORDER BY recoup_refresh_uuid ASC;"), - /* Used in #postgres_select_reserve_closed_above_serial_id() to - obtain information about closed reserves */ - GNUNET_PQ_make_prepare ( - "reserves_close_get_incr", - "SELECT" - " close_uuid" - ",reserves.reserve_pub" - ",execution_date" - ",wtid" - ",payto_uri AS receiver_account" - ",amount_val" - ",amount_frac" - ",closing_fee_val" - ",closing_fee_frac" - " FROM reserves_close" - " JOIN wire_targets" - " USING (wire_target_h_payto)" - " JOIN reserves" - " USING (reserve_pub)" - " WHERE close_uuid>=$1" - " ORDER BY close_uuid ASC;"), /* Used in #postgres_get_reserve_by_h_blind() */ GNUNET_PQ_make_prepare ( "reserve_by_h_blind", @@ -2491,9 +2479,10 @@ prepare_statements (struct PostgresClosure *pg) " FROM account_merges" " JOIN purse_merges USING (purse_pub)" " JOIN purse_requests USING (purse_pub)" + " JOIN purse_decision USING (purse_pub)" " WHERE wallet_h_payto=$1" " AND merge_timestamp >= $2" - " AND finished" + " AND NOT refunded" " ORDER BY merge_timestamp DESC"), GNUNET_PQ_PREPARED_STATEMENT_END @@ -6725,6 +6714,7 @@ postgres_insert_global_fee (void *cls, * @param wtid wire transfer details * @param amount_with_fee amount we charged to the reserve * @param closing_fee how high is the closing fee + * @param close_request_row identifies explicit close request, 0 for none * @return transaction status code */ static enum GNUNET_DB_QueryStatus @@ -6735,7 +6725,8 @@ postgres_insert_reserve_closed ( const char *receiver_account, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *amount_with_fee, - const struct TALER_Amount *closing_fee) + const struct TALER_Amount *closing_fee, + uint64_t close_request_row) { struct PostgresClosure *pg = cls; struct TALER_EXCHANGEDB_Reserve reserve; @@ -6752,6 +6743,7 @@ postgres_insert_reserve_closed ( GNUNET_PQ_query_param_auto_from_type (&h_payto), TALER_PQ_query_param_amount (amount_with_fee), TALER_PQ_query_param_amount (closing_fee), + GNUNET_PQ_query_param_uint64 (&close_request_row), GNUNET_PQ_query_param_end }; @@ -7879,15 +7871,15 @@ postgres_select_history_requests_above_serial_id ( /** - * Closure for #purse_refund_serial_helper_cb(). + * Closure for #purse_decision_serial_helper_cb(). */ -struct PurseRefundSerialContext +struct PurseDecisionSerialContext { /** * Callback to call. */ - TALER_EXCHANGEDB_PurseRefundCallback cb; + TALER_EXCHANGEDB_PurseDecisionCallback cb; /** * Closure for @e cb. @@ -7915,19 +7907,29 @@ struct PurseRefundSerialContext * @param num_results the number of results in @a result */ static void -purse_refund_serial_helper_cb (void *cls, - PGresult *result, - unsigned int num_results) +purse_decision_serial_helper_cb (void *cls, + PGresult *result, + unsigned int num_results) { - struct PurseRefundSerialContext *dsc = cls; + struct PurseDecisionSerialContext *dsc = cls; + struct PostgresClosure *pg = dsc->pg; for (unsigned int i = 0; icb (dsc->cb_cls, rowid, - &purse_pub); + &purse_pub, + no_reserve ? NULL : &reserve_pub, + &val); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != ret) break; @@ -7954,28 +7958,31 @@ purse_refund_serial_helper_cb (void *cls, /** - * Select purse refunds above @a serial_id in monotonically increasing + * Select purse decisions above @a serial_id in monotonically increasing * order. * * @param cls closure * @param serial_id highest serial ID to exclude (select strictly larger) + * @param refunded which refund status to select for * @param cb function to call on each result * @param cb_cls closure for @a cb * @return transaction status code */ static enum GNUNET_DB_QueryStatus -postgres_select_purse_refunds_above_serial_id ( +postgres_select_purse_decisions_above_serial_id ( void *cls, uint64_t serial_id, - TALER_EXCHANGEDB_PurseRefundCallback cb, + bool refunded, + TALER_EXCHANGEDB_PurseDecisionCallback cb, void *cb_cls) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&serial_id), + GNUNET_PQ_query_param_bool (refunded), GNUNET_PQ_query_param_end }; - struct PurseRefundSerialContext dsc = { + struct PurseDecisionSerialContext dsc = { .cb = cb, .cb_cls = cb_cls, .pg = pg, @@ -7984,9 +7991,9 @@ postgres_select_purse_refunds_above_serial_id ( enum GNUNET_DB_QueryStatus qs; qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "audit_get_purse_refunds_incr", + "audit_get_purse_decisions_incr", params, - &purse_refund_serial_helper_cb, + &purse_decision_serial_helper_cb, &dsc); if (GNUNET_OK != dsc.status) return GNUNET_DB_STATUS_HARD_ERROR; @@ -9232,143 +9239,6 @@ postgres_select_recoup_refresh_above_serial_id ( } -/** - * Closure for #reserve_closed_serial_helper_cb(). - */ -struct ReserveClosedSerialContext -{ - - /** - * Callback to call. - */ - TALER_EXCHANGEDB_ReserveClosedCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Plugin's context. - */ - struct PostgresClosure *pg; - - /** - * Status code, set to #GNUNET_SYSERR on hard errors. - */ - enum GNUNET_GenericReturnValue status; -}; - - -/** - * Helper function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure of type `struct ReserveClosedSerialContext` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -reserve_closed_serial_helper_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct ReserveClosedSerialContext *rcsc = cls; - struct PostgresClosure *pg = rcsc->pg; - - for (unsigned int i = 0; istatus = GNUNET_SYSERR; - return; - } - ret = rcsc->cb (rcsc->cb_cls, - rowid, - execution_date, - &amount_with_fee, - &closing_fee, - &reserve_pub, - receiver_account, - &wtid); - GNUNET_PQ_cleanup_result (rs); - if (GNUNET_OK != ret) - break; - } -} - - -/** - * Function called to select reserve close operations the aggregator - * triggered, ordered by serial ID (monotonically increasing). - * - * @param cls closure - * @param serial_id lowest serial ID to include (select larger or equal) - * @param cb function to call for ONE unfinished item - * @param cb_cls closure for @a cb - * @return transaction status code - */ -static enum GNUNET_DB_QueryStatus -postgres_select_reserve_closed_above_serial_id ( - void *cls, - uint64_t serial_id, - TALER_EXCHANGEDB_ReserveClosedCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&serial_id), - GNUNET_PQ_query_param_end - }; - struct ReserveClosedSerialContext rcsc = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg, - .status = GNUNET_OK - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "reserves_close_get_incr", - params, - &reserve_closed_serial_helper_cb, - &rcsc); - if (GNUNET_OK != rcsc.status) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - /** * Obtain information about which reserve a coin was generated * from given the hash of the blinded coin. @@ -11639,9 +11509,11 @@ postgres_expire_purse ( struct GNUNET_TIME_Absolute end_time) { struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_absolute_time (&start_time), GNUNET_PQ_query_param_absolute_time (&end_time), + GNUNET_PQ_query_param_absolute_time (&now), GNUNET_PQ_query_param_end }; bool found = false; @@ -11806,6 +11678,7 @@ postgres_do_purse_deposit ( bool *conflict) { struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); struct GNUNET_TIME_Timestamp reserve_expiration; uint64_t partner_id = 0; /* FIXME #7271: WAD support... */ struct GNUNET_PQ_QueryParam params[] = { @@ -11816,6 +11689,7 @@ postgres_do_purse_deposit ( GNUNET_PQ_query_param_auto_from_type (coin_sig), TALER_PQ_query_param_amount (amount_minus_fee), GNUNET_PQ_query_param_timestamp (&reserve_expiration), + GNUNET_PQ_query_param_timestamp (&now), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -13166,8 +13040,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &postgres_select_purse_merges_above_serial_id; plugin->select_history_requests_above_serial_id = &postgres_select_history_requests_above_serial_id; - plugin->select_purse_refunds_above_serial_id - = &postgres_select_purse_refunds_above_serial_id; + plugin->select_purse_decisions_above_serial_id + = &postgres_select_purse_decisions_above_serial_id; plugin->select_purse_deposits_by_purse = &postgres_select_purse_deposits_by_purse; plugin->select_refreshes_above_serial_id @@ -13188,8 +13062,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &postgres_select_recoup_above_serial_id; plugin->select_recoup_refresh_above_serial_id = &postgres_select_recoup_refresh_above_serial_id; - plugin->select_reserve_closed_above_serial_id - = &postgres_select_reserve_closed_above_serial_id; plugin->get_reserve_by_h_blind = &postgres_get_reserve_by_h_blind; plugin->get_old_coin_by_h_blind @@ -13347,6 +13219,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) = &TEH_PG_lookup_serial_by_table; plugin->select_reserve_close_info = &TEH_PG_select_reserve_close_info; + plugin->select_reserve_closed_above_serial_id + = &TEH_PG_select_reserve_closed_above_serial_id; + plugin->select_reserve_open_above_serial_id + = &TEH_PG_select_reserve_open_above_serial_id; return plugin; } diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql index 912a37dbc..e9075775c 100644 --- a/src/exchangedb/procedures.sql +++ b/src/exchangedb/procedures.sql @@ -1428,6 +1428,7 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_deposit( IN in_amount_without_fee_val INT8, IN in_amount_without_fee_frac INT4, IN in_reserve_expiration INT8, + IN in_now INT8, OUT out_balance_ok BOOLEAN, OUT out_conflict BOOLEAN) LANGUAGE plpgsql @@ -1442,6 +1443,8 @@ DECLARE my_amount_frac INT4; -- total in purse DECLARE was_paid BOOLEAN; +DECLARE + my_in_reserve_quota BOOLEAN; DECLARE my_reserve_pub BYTEA; BEGIN @@ -1548,9 +1551,11 @@ END IF; SELECT amount_with_fee_val ,amount_with_fee_frac + ,in_reserve_quota INTO my_amount_val ,my_amount_frac + ,my_in_reserve_quota FROM exchange.purse_requests WHERE (purse_pub=in_purse_pub) AND ( ( ( (amount_with_fee_val <= balance_val) @@ -1561,6 +1566,28 @@ THEN RETURN; END IF; +-- Remember how this purse was finished. +INSERT INTO purse_decision + (purse_pub + ,action_timestamp + ,refunded) +VALUES + (in_purse_pub + ,in_now + ,FALSE); + +IF (my_in_reserve_quota) +THEN + UPDATE reserves + SET purses_active=purses_active-1 + WHERE reserve_pub IN + (SELECT reserve_pub + FROM exchange.purse_merges + WHERE purse_pub=my_purse_pub + LIMIT 1); +END IF; + + IF (0 != psi) THEN -- The taler-exchange-router will take care of this. @@ -1606,11 +1633,6 @@ ELSE WHERE reserve_pub=my_reserve_pub; END IF; - -- ... and mark purse as finished. - -- FIXME: combine with UPDATE above? - UPDATE purse_requests - SET finished=true - WHERE purse_pub=in_purse_pub; END IF; @@ -1644,7 +1666,7 @@ DECLARE DECLARE my_partner_serial_id INT8; DECLARE - my_finished BOOLEAN; + my_in_reserve_quota BOOLEAN; BEGIN IF in_partner_url IS NULL @@ -1675,12 +1697,12 @@ SELECT amount_with_fee_val ,amount_with_fee_frac ,purse_fee_val ,purse_fee_frac - ,finished + ,in_reserve_quota INTO my_amount_val ,my_amount_frac ,my_purse_fee_val ,my_purse_fee_frac - ,my_finished + ,my_in_reserve_quota FROM exchange.purse_requests WHERE purse_pub=in_purse_pub AND balance_val >= amount_with_fee_val @@ -1731,8 +1753,6 @@ THEN END IF; out_conflict=FALSE; -ASSERT NOT my_finished, 'internal invariant failed'; - -- Initialize reserve, if not yet exists. INSERT INTO reserves @@ -1745,8 +1765,26 @@ INSERT INTO reserves ,in_expiration_date) ON CONFLICT DO NOTHING; +-- Remember how this purse was finished. +INSERT INTO purse_decision + (purse_pub + ,action_timestamp + ,refunded) +VALUES + (in_purse_pub + ,in_merge_timestamp + ,FALSE); - +IF (my_in_reserve_quota) +THEN + UPDATE reserves + SET purses_active=purses_active-1 + WHERE reserve_pub IN + (SELECT reserve_pub + FROM exchange.purse_merges + WHERE purse_pub=my_purse_pub + LIMIT 1); +END IF; -- Store account merge signature. INSERT INTO exchange.account_merges @@ -1794,10 +1832,6 @@ ELSE END WHERE reserve_pub=in_reserve_pub; - -- ... and mark purse as finished. - UPDATE exchange.purse_requests - SET finished=true - WHERE purse_pub=in_purse_pub; END IF; @@ -1969,6 +2003,7 @@ END $$; CREATE OR REPLACE FUNCTION exchange_do_expire_purse( IN in_start_time INT8, IN in_end_time INT8, + IN in_now INT8, OUT out_found BOOLEAN) LANGUAGE plpgsql AS $$ @@ -1976,15 +2011,21 @@ DECLARE my_purse_pub BYTEA; DECLARE my_deposit record; +DECLARE + my_in_reserve_quota BOOLEAN; BEGIN +-- FIXME: we should probably do this in a loop +-- and expire all at once, instead of one per query SELECT purse_pub + ,in_reserve_quota INTO my_purse_pub + ,my_in_reserve_quota FROM exchange.purse_requests WHERE (purse_expiration >= in_start_time) AND (purse_expiration < in_end_time) AND - (NOT finished) AND - (NOT refunded) + purse_pub NOT IN (SELECT purse_pub + FROM purse_decision) ORDER BY purse_expiration ASC LIMIT 1; out_found = FOUND; @@ -1993,15 +2034,25 @@ THEN RETURN; END IF; -UPDATE exchange.purse_requests - SET refunded=TRUE, - finished=TRUE - WHERE purse_pub=my_purse_pub; +INSERT INTO purse_decision + (purse_pub + ,action_timestamp + ,refunded) +VALUES + (my_purse_pub + ,in_now + ,TRUE); -INSERT INTO exchange.purse_refunds - (purse_pub) - VALUES - (my_purse_pub); +IF (my_in_reserve_quota) +THEN + UPDATE reserves + SET purses_active=purses_active-1 + WHERE reserve_pub IN + (SELECT reserve_pub + FROM exchange.purse_merges + WHERE purse_pub=my_purse_pub + LIMIT 1); +END IF; -- restore balance to each coin deposited into the purse FOR my_deposit IN @@ -2028,7 +2079,7 @@ LOOP END LOOP; END $$; -COMMENT ON FUNCTION exchange_do_expire_purse(INT8,INT8) +COMMENT ON FUNCTION exchange_do_expire_purse(INT8,INT8,INT8) IS 'Finds an expired purse in the given time range and refunds the coins (if any).'; diff --git a/src/exchangedb/shard-0001-part.sql b/src/exchangedb/shard-0001-part.sql index a54eb8dc8..8b6ec07f9 100644 --- a/src/exchangedb/shard-0001-part.sql +++ b/src/exchangedb/shard-0001-part.sql @@ -101,8 +101,8 @@ BEGIN PERFORM create_table_purse_requests(shard_suffix); PERFORM add_constraints_to_purse_requests_partition(shard_suffix); - PERFORM create_table_purse_refunds(shard_suffix); - PERFORM add_constraints_to_purse_refunds_partition(shard_suffix); + PERFORM create_table_purse_decision(shard_suffix); + PERFORM add_constraints_to_purse_decision_partition(shard_suffix); PERFORM create_table_purse_merges(shard_suffix); PERFORM add_constraints_to_purse_merges_partition(shard_suffix); diff --git a/src/exchangedb/test-exchange-db-postgres.conf b/src/exchangedb/test-exchange-db-postgres.conf index ab70bcfce..05104cb7f 100644 --- a/src/exchangedb/test-exchange-db-postgres.conf +++ b/src/exchangedb/test-exchange-db-postgres.conf @@ -30,4 +30,7 @@ IDLE_RESERVE_EXPIRATION_TIME = 4 weeks LEGAL_RESERVE_EXPIRATION_TIME = 7 years # Shift to apply before aggregating. -AGGREGATOR_SHIFT = 1s \ No newline at end of file +AGGREGATOR_SHIFT = 1s + +# Number of purses per account by default. +DEFAULT_PURSE_LIMIT = 1 \ No newline at end of file diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index d19f91a44..77f3dab26 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1809,7 +1809,8 @@ run (void *cls) sndr, &wire_out_wtid, &amount_with_fee, - &fee_closing)); + &fee_closing, + 0)); FAILIF (GNUNET_OK != check_reserve (&reserve_pub2, 0, @@ -1823,7 +1824,8 @@ run (void *cls) sndr, &wire_out_wtid, &value, - &fee_closing)); + &fee_closing, + 0)); FAILIF (GNUNET_OK != check_reserve (&reserve_pub, 0, diff --git a/src/include/taler_auditordb_plugin.h b/src/include/taler_auditordb_plugin.h index dff96700f..d26e42b78 100644 --- a/src/include/taler_auditordb_plugin.h +++ b/src/include/taler_auditordb_plugin.h @@ -129,6 +129,25 @@ struct TALER_AUDITORDB_WireAccountProgressPoint }; +/** + * Structure for remembering the wire auditor's progress + * with respect to the bank transaction histories. + */ +struct TALER_AUDITORDB_BankAccountProgressPoint +{ + /** + * How far are we in the incoming wire transaction history + */ + uint64_t in_wire_off; + + /** + * How far are we in the outgoing wire transaction history + */ + uint64_t out_wire_off; + +}; + + /** * Structure for remembering the auditor's progress over the various * tables and (auditor) transactions when analyzing reserves. @@ -141,7 +160,7 @@ struct TALER_AUDITORDB_ProgressPointReserve uint64_t last_reserve_in_serial_id; /** - * serial ID of the last reserve_out the auditor processed + * serial ID of the last reserve_out (withdraw) the auditor processed */ uint64_t last_reserve_out_serial_id; @@ -152,40 +171,64 @@ struct TALER_AUDITORDB_ProgressPointReserve uint64_t last_reserve_recoup_serial_id; /** - * serial ID of the last reserve_close - * entry the auditor processed. + * serial ID of the last open_requests entry the auditor processed. + */ + uint64_t last_reserve_open_serial_id; + + /** + * serial ID of the last reserve_close entry the auditor processed. */ uint64_t last_reserve_close_serial_id; /** - * serial ID of the last purse_merges - * entry the auditor processed. + * Serial ID of the last purse_decisions entry the auditor processed. */ - uint64_t last_purse_merges_serial_id; + uint64_t last_purse_decisions_serial_id; /** - * Serial ID of the last purse_deposits - * entry the auditor processed. - */ - uint64_t last_purse_deposits_serial_id; - - /** - * serial ID of the last account_merges - * entry the auditor processed. + * serial ID of the last account_merges entry the auditor processed. */ uint64_t last_account_merges_serial_id; /** - * serial ID of the last history_requests - * entry the auditor processed. + * serial ID of the last history_requests entry the auditor processed. */ uint64_t last_history_requests_serial_id; +}; + + +/** + * Structure for remembering the auditor's progress over the various + * tables and (auditor) transactions when analyzing purses. + */ +struct TALER_AUDITORDB_ProgressPointPurse +{ /** - * serial ID of the last close_requests - * entry the auditor processed. + * serial ID of the last purse_request transfer the auditor processed */ - uint64_t last_close_requests_serial_id; + uint64_t last_purse_request_serial_id; + + /** + * serial ID of the last purse_decision the auditor processed + */ + uint64_t last_purse_decision_serial_id; + + /** + * serial ID of the last purse_merge entry the auditor processed when + * considering reserves. + */ + uint64_t last_purse_merge_serial_id; + + /** + * serial ID of the last account_merge entry the auditor processed. + */ + uint64_t last_account_merge_serial_id; + + /** + * serial ID of the last purse_deposits entry the auditor processed. + */ + uint64_t last_purse_deposits_serial_id; }; @@ -254,6 +297,11 @@ struct TALER_AUDITORDB_ProgressPointCoin */ uint64_t last_recoup_refresh_serial_id; + /** + * Serial ID of the last reserve_open_deposits operation the auditor processed. + */ + uint64_t last_open_deposits_serial_id; + /** * Serial ID of the last purse_deposits operation the auditor processed. */ @@ -389,6 +437,140 @@ struct TALER_AUDITORDB_DepositConfirmation }; +/** + * Balance values for a reserve (or all reserves). + */ +struct TALER_AUDITORDB_ReserveFeeBalance +{ + /** + * Remaining funds. + */ + struct TALER_Amount reserve_balance; + + /** + * Losses from operations that should not have + * happened (e.g. negative balance). + */ + struct TALER_Amount reserve_loss; + + /** + * Fees charged for withdraw. + */ + struct TALER_Amount withdraw_fee_balance; + + /** + * Fees charged for closing. + */ + struct TALER_Amount close_fee_balance; + + /** + * Fees charged for purse creation. + */ + struct TALER_Amount purse_fee_balance; + + /** + * Opening fees charged. + */ + struct TALER_Amount open_fee_balance; + + /** + * History fees charged. + */ + struct TALER_Amount history_fee_balance; +}; + + +/** + * Balance data for denominations in circulation. + */ +struct TALER_AUDITORDB_DenominationCirculationData +{ + /** + * Amount of outstanding coins in circulation. + */ + struct TALER_Amount denom_balance; + + /** + * Amount lost due coins illicitly accepted (effectively, a + * negative @a denom_balance). + */ + struct TALER_Amount denom_loss; + + /** + * Total amount that could still be theoretically lost in the future due to + * recoup operations. (Total put into circulation minus @e recoup_loss). + */ + struct TALER_Amount denom_risk; + + /** + * Amount lost due to recoups. + */ + struct TALER_Amount recoup_loss; + + /** + * Number of coins of this denomination that the exchange signed into + * existence. + */ + uint64_t num_issued; +}; + + +/** + * Balance values for all denominations. + */ +struct TALER_AUDITORDB_GlobalCoinBalance +{ + /** + * Amount of outstanding coins in circulation. + */ + struct TALER_Amount total_escrowed; + + /** + * Amount collected in deposit fees. + */ + struct TALER_Amount deposit_fee_balance; + + /** + * Amount collected in melt fees. + */ + struct TALER_Amount melt_fee_balance; + + /** + * Amount collected in refund fees. + */ + struct TALER_Amount refund_fee_balance; + + /** + * Amount collected in purse fees from coins. + */ + struct TALER_Amount purse_fee_balance; + + /** + * Amount collected in reserve open deposit fees from coins. + */ + struct TALER_Amount open_deposit_fee_balance; + + /** + * Total amount that could still be theoretically + * lost in the future due to recoup operations. + * (Total put into circulation minus @e loss + * and @e irregular_recoup.) + */ + struct TALER_Amount risk; + + /** + * Amount lost due to recoups. + */ + struct TALER_Amount loss; + + /** + * Amount lost due to coin operations that the exchange + * should not have permitted. + */ + struct TALER_Amount irregular_loss; +}; + + /** * Function called with deposit confirmations stored in * the auditor's database. @@ -639,14 +821,14 @@ struct TALER_AUDITORDB_Plugin struct TALER_AUDITORDB_ProgressPointCoin *ppc); /** - * Insert information about the auditor's progress with an exchange's - * data. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param master_pub master key of the exchange - * @param ppr where is the auditor in processing - * @return transaction status code - */ + * Insert information about the auditor's progress with an exchange's + * data. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param ppr where is the auditor in processing + * @return transaction status code + */ enum GNUNET_DB_QueryStatus (*insert_auditor_progress_reserve)( void *cls, @@ -684,6 +866,53 @@ struct TALER_AUDITORDB_Plugin const struct TALER_MasterPublicKeyP *master_pub, struct TALER_AUDITORDB_ProgressPointReserve *ppr); + /** + * Insert information about the auditor's progress with an exchange's + * data. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param ppp where is the auditor in processing + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*insert_auditor_progress_purse)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ProgressPointPurse *ppp); + + + /** + * Update information about the progress of the auditor. There + * must be an existing record for the exchange. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param ppp where is the auditor in processing + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*update_auditor_progress_purse)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ProgressPointPurse *ppp); + + + /** + * Get information about the progress of the auditor. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param master_pub master key of the exchange + * @param[out] ppp set to where the auditor is in processing + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*get_auditor_progress_purse)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + struct TALER_AUDITORDB_ProgressPointPurse *ppp); + + /** * Insert information about the auditor's progress with an exchange's * data. @@ -786,8 +1015,7 @@ struct TALER_AUDITORDB_Plugin * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param pp where is the auditor in processing - * @param in_wire_off how far are we in the incoming wire transaction history - * @param out_wire_off how far are we in the outgoing wire transaction history + * @param bapp progress in wire transaction histories * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -796,8 +1024,7 @@ struct TALER_AUDITORDB_Plugin const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t in_wire_off, - uint64_t out_wire_off); + const struct TALER_AUDITORDB_BankAccountProgressPoint *bapp); /** @@ -808,8 +1035,7 @@ struct TALER_AUDITORDB_Plugin * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param pp where is the auditor in processing - * @param in_wire_off how far are we in the incoming wire transaction history - * @param out_wire_off how far are we in the outgoing wire transaction history + * @param bapp progress in wire transaction histories * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -818,8 +1044,7 @@ struct TALER_AUDITORDB_Plugin const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, const struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t in_wire_off, - uint64_t out_wire_off); + const struct TALER_AUDITORDB_BankAccountProgressPoint *bapp); /** @@ -829,8 +1054,7 @@ struct TALER_AUDITORDB_Plugin * @param master_pub master key of the exchange * @param account_name name of the wire account we are auditing * @param[out] pp where is the auditor in processing - * @param[out] in_wire_off how far are we in the incoming wire transaction history - * @param[out] out_wire_off how far are we in the outgoing wire transaction history + * @param[out] bapp how far are we in the wire transaction histories * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -839,8 +1063,7 @@ struct TALER_AUDITORDB_Plugin const struct TALER_MasterPublicKeyP *master_pub, const char *account_name, struct TALER_AUDITORDB_WireAccountProgressPoint *pp, - uint64_t *in_wire_off, - uint64_t *out_wire_off); + struct TALER_AUDITORDB_BankAccountProgressPoint *bapp); /** @@ -887,9 +1110,10 @@ struct TALER_AUDITORDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*get_wire_auditor_progress)(void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - struct TALER_AUDITORDB_WireProgressPoint *pp); + (*get_wire_auditor_progress)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + struct TALER_AUDITORDB_WireProgressPoint *pp); /** @@ -899,21 +1123,19 @@ struct TALER_AUDITORDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve + * @param rfb balance amounts for the reserve * @param expiration_date expiration date of the reserve * @param origin_account where did the money in the reserve originally come from * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*insert_reserve_info)(void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - struct GNUNET_TIME_Timestamp expiration_date, - const char *origin_account); + (*insert_reserve_info)( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb, + struct GNUNET_TIME_Timestamp expiration_date, + const char *origin_account); /** @@ -923,19 +1145,17 @@ struct TALER_AUDITORDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve + * @param rfb balance amounts for the reserve * @param expiration_date expiration date of the reserve * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*update_reserve_info)(void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - struct GNUNET_TIME_Timestamp expiration_date); + (*update_reserve_info)( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb, + struct GNUNET_TIME_Timestamp expiration_date); /** @@ -945,22 +1165,20 @@ struct TALER_AUDITORDB_Plugin * @param reserve_pub public key of the reserve * @param master_pub master public key of the exchange * @param[out] rowid which row did we get the information from - * @param[out] reserve_balance amount stored in the reserve - * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees - * due to withdrawals from this reserve + * @param[out] rfb set to balances associated with the reserve * @param[out] expiration_date expiration date of the reserve * @param[out] sender_account from where did the money in the reserve originally come from * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*get_reserve_info)(void *cls, - const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_MasterPublicKeyP *master_pub, - uint64_t *rowid, - struct TALER_Amount *reserve_balance, - struct TALER_Amount *withdraw_fee_balance, - struct GNUNET_TIME_Timestamp *expiration_date, - char **sender_account); + (*get_reserve_info)( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_MasterPublicKeyP *master_pub, + uint64_t *rowid, + struct TALER_AUDITORDB_ReserveFeeBalance *rfb, + struct GNUNET_TIME_Timestamp *expiration_date, + char **sender_account); /** @@ -983,19 +1201,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * @param purse_fee_balance amount the exchange gained in purse fees - * @param history_fee_balance amount the exchange gained in history fees + * @param rfb reserve balances summary to store * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*insert_reserve_summary)(void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - const struct TALER_Amount *purse_fee_balance, - const struct TALER_Amount *history_fee_balance); + (*insert_reserve_summary)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb); /** @@ -1004,19 +1217,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param reserve_balance amount stored in the reserve - * @param withdraw_fee_balance amount the exchange gained in withdraw fees - * @param purse_fee_balance amount the exchange gained in purse fees - * @param history_fee_balance amount the exchange gained in history fees + * @param rfb reserve balances summary to store * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*update_reserve_summary)(void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *reserve_balance, - const struct TALER_Amount *withdraw_fee_balance, - const struct TALER_Amount *purse_fee_balance, - const struct TALER_Amount *history_fee_balance); + (*update_reserve_summary)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_ReserveFeeBalance *rfb); /** @@ -1024,19 +1232,13 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master public key of the exchange - * @param[out] reserve_balance amount stored in reserves - * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees - * @param[out] purse_fee_balance amount the exchange gained in purse fees - * @param[out] history_fee_balance amount the exchange gained in history fees + * @param[out] rfb reserve balances summary to initialize * @return transaction status code */ enum GNUNET_DB_QueryStatus (*get_reserve_summary)(void *cls, const struct TALER_MasterPublicKeyP *master_pub, - struct TALER_Amount *reserve_balance, - struct TALER_Amount *withdraw_fee_balance, - struct TALER_Amount *purse_fee_balance, - struct TALER_Amount *history_fee_balance); + struct TALER_AUDITORDB_ReserveFeeBalance *rfb); /** @@ -1089,22 +1291,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param denom_balance value of coins outstanding with this denomination key - * @param denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) - * @param denom_risk value of coins issued with this denomination key - * @param denom_recoup value of coins paid back if this denomination key was revoked - * @param num_issued how many coins of this denomination did the exchange blind-sign + * @param dcd denomination circulation data to store * @return transaction status code */ enum GNUNET_DB_QueryStatus (*insert_denomination_balance)( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *denom_loss, - const struct TALER_Amount *denom_risk, - const struct TALER_Amount *recoup_loss, - uint64_t num_issued); + const struct TALER_AUDITORDB_DenominationCirculationData *dcd); /** @@ -1113,22 +1307,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param denom_balance value of coins outstanding with this denomination key - * @param denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) - * @param denom_risk value of coins issued with this denomination key - * @param denom_recoup value of coins paid back if this denomination key was revoked - * @param num_issued how many coins of this denomination did the exchange blind-sign + * @param dcd denomination circulation data to store * @return transaction status code */ enum GNUNET_DB_QueryStatus (*update_denomination_balance)( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *denom_loss, - const struct TALER_Amount *denom_risk, - const struct TALER_Amount *recoup_loss, - uint64_t num_issued); + const struct TALER_AUDITORDB_DenominationCirculationData *dcd); /** @@ -1136,22 +1322,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param denom_pub_hash hash of the denomination public key - * @param[out] denom_balance value of coins outstanding with this denomination key - * @param[out] denom_loss value of coins redeemed that were not outstanding (effectively, negative @a denom_balance) - * @param[out] denom_risk value of coins issued with this denomination key - * @param[out] denom_recoup value of coins paid back if this denomination key was revoked - * @param[out] num_issued how many coins of this denomination did the exchange blind-sign + * @param[out] dcd denomination circulation data to initialize * @return transaction status code */ enum GNUNET_DB_QueryStatus (*get_denomination_balance)( void *cls, const struct TALER_DenominationHashP *denom_pub_hash, - struct TALER_Amount *denom_balance, - struct TALER_Amount *denom_loss, - struct TALER_Amount *denom_risk, - struct TALER_Amount *recoup_loss, - uint64_t *num_issued); + struct TALER_AUDITORDB_DenominationCirculationData *dcd); /** @@ -1173,26 +1351,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param denom_balance value of coins outstanding with this denomination key - * @param deposit_fee_balance total deposit fees collected for this DK - * @param melt_fee_balance total melt fees collected for this DK - * @param refund_fee_balance total refund fees collected for this DK - * @param risk maximum risk exposure of the exchange - * @param recoup_loss actual losses from recoup (actualized @a risk) - * @param irregular_recoups recoups made of non-revoked coins (reduces - * risk, but should never happen) + * @param dfb denomination balance data to store * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*insert_balance_summary)(void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *deposit_fee_balance, - const struct TALER_Amount *melt_fee_balance, - const struct TALER_Amount *refund_fee_balance, - const struct TALER_Amount *risk, - const struct TALER_Amount *recoup_loss, - const struct TALER_Amount *irregular_recoups); + (*insert_balance_summary)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_GlobalCoinBalance *dfb); /** @@ -1201,26 +1367,14 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param denom_balance value of coins outstanding with this denomination key - * @param deposit_fee_balance total deposit fees collected for this DK - * @param melt_fee_balance total melt fees collected for this DK - * @param refund_fee_balance total refund fees collected for this DK - * @param risk maximum risk exposure of the exchange - * @param recoup_loss actual losses from recoup (actualized @a risk) - * @param irregular_recoups recoups made of non-revoked coins (reduces - * risk, but should never happen) + * @param dfb denomination balance data to store * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*update_balance_summary)(void *cls, - const struct TALER_MasterPublicKeyP *master_pub, - const struct TALER_Amount *denom_balance, - const struct TALER_Amount *deposit_fee_balance, - const struct TALER_Amount *melt_fee_balance, - const struct TALER_Amount *refund_fee_balance, - const struct TALER_Amount *risk, - const struct TALER_Amount *recoup_loss, - const struct TALER_Amount *irregular_recoups); + (*update_balance_summary)( + void *cls, + const struct TALER_MasterPublicKeyP *master_pub, + const struct TALER_AUDITORDB_GlobalCoinBalance *dfb); /** @@ -1228,26 +1382,13 @@ struct TALER_AUDITORDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param master_pub master key of the exchange - * @param[out] denom_balance value of coins outstanding with this denomination key - * @param[out] deposit_fee_balance total deposit fees collected for this DK - * @param[out] melt_fee_balance total melt fees collected for this DK - * @param[out] refund_fee_balance total refund fees collected for this DK - * @param[out] risk maximum risk exposure of the exchange - * @param[out] recoup_loss actual losses from recoup (actualized @a risk) - * @param[out] irregular_recoups recoups made of non-revoked coins (reduces - * risk, but should never happen) + * @param[out] dfb where to return the denomination balances * @return transaction status code */ enum GNUNET_DB_QueryStatus (*get_balance_summary)(void *cls, const struct TALER_MasterPublicKeyP *master_pub, - struct TALER_Amount *denom_balance, - struct TALER_Amount *deposit_fee_balance, - struct TALER_Amount *melt_fee_balance, - struct TALER_Amount *refund_fee_balance, - struct TALER_Amount *risk, - struct TALER_Amount *recoup_loss, - struct TALER_Amount *irregular_recoup); + struct TALER_AUDITORDB_GlobalCoinBalance *dfb); /** diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 107d88cca..4965a27b1 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -222,7 +222,7 @@ enum TALER_EXCHANGEDB_ReplicatedTable TALER_EXCHANGEDB_RT_EXTENSIONS, TALER_EXCHANGEDB_RT_EXTENSION_DETAILS, TALER_EXCHANGEDB_RT_PURSE_REQUESTS, - TALER_EXCHANGEDB_RT_PURSE_REFUNDS, + TALER_EXCHANGEDB_RT_PURSE_DECISION, TALER_EXCHANGEDB_RT_PURSE_MERGES, TALER_EXCHANGEDB_RT_PURSE_DEPOSITS, TALER_EXCHANGEDB_RT_ACCOUNT_MERGES, @@ -536,7 +536,9 @@ struct TALER_EXCHANGEDB_TableData struct { struct TALER_PurseContractPublicKeyP purse_pub; - } purse_refunds; + struct GNUNET_TIME_Timestamp action_timestamp; + bool refunded; + } purse_decision; struct { @@ -2348,19 +2350,23 @@ typedef enum GNUNET_GenericReturnValue /** - * Function called with details about purse refunds that have been made, with - * the goal of auditing the purse refund's execution. + * Function called with details about purse decisions that have been made, with + * the goal of auditing the purse's execution. * * @param cls closure * @param rowid unique serial ID for the deposit in our DB - * @param purse_pub public key of the refunded purse + * @param purse_pub public key of the purse + * @param reserve_pub public key of the target reserve, NULL if not known + * @param purse_value what is the (target) value of the purse * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ typedef enum GNUNET_GenericReturnValue -(*TALER_EXCHANGEDB_PurseRefundCallback)( +(*TALER_EXCHANGEDB_PurseDecisionCallback)( 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 *purse_value); /** @@ -2849,6 +2855,33 @@ typedef enum GNUNET_GenericReturnValue const union TALER_DenominationBlindingKeyP *coin_blind); +/** + * 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 + */ +typedef enum GNUNET_GenericReturnValue +(*TALER_EXCHANGEDB_ReserveOpenCallback)( + 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); + + /** * Function called about reserve closing operations * the aggregator triggered. @@ -2861,6 +2894,8 @@ typedef enum GNUNET_GenericReturnValue * @param reserve_pub public key of the reserve * @param receiver_account where did we send the funds, in payto://-format * @param wtid identifier used for the wire transfer + * @param close_request_row row with the responsible close + * request, 0 if regular expiration triggered close * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ typedef enum GNUNET_GenericReturnValue @@ -2872,7 +2907,8 @@ typedef enum GNUNET_GenericReturnValue 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); /** @@ -2895,6 +2931,8 @@ typedef void * @param left amount left in the reserve * @param account_details information about the reserve's bank account, in payto://-format * @param expiration_date when did the reserve expire + * @param close_request_row row that caused the reserve + * to be closed, 0 if it expired without request * @return #GNUNET_OK on success, * #GNUNET_NO to retry * #GNUNET_SYSERR on hard failures (exit) @@ -2905,7 +2943,8 @@ typedef enum GNUNET_GenericReturnValue const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_Amount *left, const char *account_details, - struct GNUNET_TIME_Timestamp expiration_date); + struct GNUNET_TIME_Timestamp expiration_date, + uint64_t close_request_row); /** @@ -4361,6 +4400,33 @@ struct TALER_EXCHANGEDB_Plugin char **payto_uri); + /** + * Select information about reserve close requests. + * + * @param cls closure + * @param reserve_pub which reserve is this about? + * @param rowid row ID of the close request + * @param[out] reserve_sig reserve signature affirming + * @param[out] request_timestamp when was the request made + * @param[out] close_balance reserve balance at close time + * @param[out] close_fee closing fee to be charged + * @param[out] payto_uri set to URL of account that + * should receive the money; + * could be set to NULL for origin + * @return transaction status code, 0 if reserve unknown + */ + enum GNUNET_DB_QueryStatus + (*select_reserve_close_request_info)( + void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + uint64_t rowid, + struct TALER_ReserveSignatureP *reserve_sig, + struct GNUNET_TIME_Timestamp *request_timestamp, + struct TALER_Amount *close_balance, + struct TALER_Amount *close_fee, + char **payto_uri); + + /** * Select information needed for KYC checks on reserve close: historic * reserve closures going to the same account. @@ -4392,6 +4458,7 @@ struct TALER_EXCHANGEDB_Plugin * @param wtid identifier for the wire transfer * @param amount_with_fee amount we charged to the reserve * @param closing_fee how high is the closing fee + * @param close_request_row identifies explicit close request, 0 for none * @return transaction status code */ enum GNUNET_DB_QueryStatus @@ -4401,7 +4468,8 @@ struct TALER_EXCHANGEDB_Plugin const char *receiver_account, const struct TALER_WireTransferIdentifierRawP *wtid, const struct TALER_Amount *amount_with_fee, - const struct TALER_Amount *closing_fee); + const struct TALER_Amount *closing_fee, + uint64_t close_request_row); /** @@ -4604,15 +4672,17 @@ struct TALER_EXCHANGEDB_Plugin * * @param cls closure * @param serial_id highest serial ID to exclude (select strictly larger) + * @param refunded which refund status to select for * @param cb function to call on each result * @param cb_cls closure for @a cb * @return transaction status code */ enum GNUNET_DB_QueryStatus - (*select_purse_refunds_above_serial_id)( + (*select_purse_decisions_above_serial_id)( void *cls, uint64_t serial_id, - TALER_EXCHANGEDB_PurseRefundCallback cb, + bool refunded, + TALER_EXCHANGEDB_PurseDecisionCallback cb, void *cb_cls); @@ -4795,8 +4865,8 @@ struct TALER_EXCHANGEDB_Plugin /** - * Function called to select reserve close operations the aggregator - * triggered, ordered by serial ID (monotonically increasing). + * Function called to select reserve open operations, ordered by serial ID + * (monotonically increasing). * * @param cls closure * @param serial_id lowest serial ID to include (select larger or equal) @@ -4805,6 +4875,24 @@ struct TALER_EXCHANGEDB_Plugin * @return transaction status code */ enum GNUNET_DB_QueryStatus + (*select_reserve_open_above_serial_id)( + void *cls, + uint64_t serial_id, + TALER_EXCHANGEDB_ReserveOpenCallback cb, + void *cb_cls); + + + /** + * Function called to select reserve close operations the aggregator + * triggered, ordered by serial ID (monotonically increasing). + * + * @param cls closure + * @param serial_id lowest serial ID to include (select larger or equal) + * @param cb function to call + * @param cb_cls closure for @a cb + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus (*select_reserve_closed_above_serial_id)( void *cls, uint64_t serial_id, diff --git a/src/lib/exchange_api_purse_create_with_deposit.c b/src/lib/exchange_api_purse_create_with_deposit.c index ce0221aa6..a2618d639 100644 --- a/src/lib/exchange_api_purse_create_with_deposit.c +++ b/src/lib/exchange_api_purse_create_with_deposit.c @@ -303,7 +303,7 @@ handle_purse_create_deposit_finished (void *cls, TALER_amount_cmp (&left, &deposit->contribution)) { - /* Balance was sufficient after all; recoup MAY have still been possible */ + /* Balance was sufficient after all; operation MAY have still been possible */ GNUNET_break_op (0); continue; }