-towards coin audits with purse deposits

This commit is contained in:
Christian Grothoff 2022-06-13 15:31:52 +02:00
parent 70a5ceecc1
commit 58a0882909
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
26 changed files with 3358 additions and 2168 deletions

View File

@ -1 +1 @@
1655064974
1655124520

View File

@ -113,7 +113,7 @@ currency = TESTKUDOS
[merchant-exchange-default]
CURRENCY = TESTKUDOS
EXCHANGE_BASE_URL = http://localhost:8081/
MASTER_KEY = QRZFN2H2C1FANGHV76FBN11ESTK72VFCG3BVSRZA6PHJNT9WG3SG
MASTER_KEY = GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G
[merchant-account-merchant]
ACTIVE_default = YES
@ -157,7 +157,7 @@ CONFIG = postgres:///auditor-basedb
[exchange]
LOOKAHEAD_SIGN = 32 weeks 1 day
SIGNKEY_DURATION = 4 weeks
MASTER_PUBLIC_KEY = QRZFN2H2C1FANGHV76FBN11ESTK72VFCG3BVSRZA6PHJNT9WG3SG
MASTER_PUBLIC_KEY = GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G
SIGNKEY_LEGAL_DURATION = 4 weeks
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
@ -175,7 +175,7 @@ DATABASE = postgres:///auditor-basedb
CONFIG = postgres:///auditor-basedb
[auditor]
PUBLIC_KEY = X25D3Y3K4SJZHP3KPP48R3PM9ZSYCEVEZK1SBXAMCRTTGN6CV32G
PUBLIC_KEY = FBVV6V14GCCR95X2W2PMCBTFYZ4S3SX3JN504M8SJTMK1JBBV99G
TINY_AMOUNT = TESTKUDOS:0.01
BASE_URL = http://localhost:8083/

View File

@ -1 +1 @@
QRZFN2H2C1FANGHV76FBN11ESTK72VFCG3BVSRZA6PHJNT9WG3SG
GPB1CSPV6E49MET2PX6BFWVBGFF8BAE14M05RJB54C28EY1HX42G

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,11 @@ struct TALER_AuditorPublicKeyP TALER_ARL_auditor_pub;
*/
char *TALER_ARL_auditor_url;
/**
* REST API endpoint of the exchange.
*/
char *TALER_ARL_exchange_url;
/**
* At what time did the auditor process start?
*/
@ -371,10 +376,13 @@ test_master_present (void *cls,
{
int *found = cls;
(void) exchange_url;
if (0 == GNUNET_memcmp (mpub,
&TALER_ARL_master_pub))
{
*found = GNUNET_YES;
GNUNET_free (TALER_ARL_exchange_url);
TALER_ARL_exchange_url = GNUNET_strdup (exchange_url);
}
}
@ -765,6 +773,7 @@ TALER_ARL_done (json_t *report)
JSON_INDENT (2));
json_decref (report);
}
GNUNET_free (TALER_ARL_exchange_url);
GNUNET_free (TALER_ARL_auditor_url);
}

View File

@ -74,6 +74,11 @@ extern struct TALER_AuditorPublicKeyP TALER_ARL_auditor_pub;
*/
extern char *TALER_ARL_auditor_url;
/**
* REST API endpoint of the exchange.
*/
extern char *TALER_ARL_exchange_url;
/**
* At what time did the auditor process start?
*/

View File

@ -1 +1 @@
1655066138
1655124599

View File

@ -1 +1 @@
RJD4G3ERAWDZRH8W4PMNKB9X75HB3N1A3MFKMHBH4FTAAD71GA90
6CF031C2JWXQB98SNHARWSPJMYR8JM26BVDV08CPH63T2M9JXV30

File diff suppressed because it is too large Load Diff

View File

@ -115,6 +115,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_MERGES},
{ .rt = TALER_EXCHANGEDB_RT_PURSE_DEPOSITS},
{ .rt = TALER_EXCHANGEDB_RT_ACCOUNT_MERGES},

View File

@ -17,9 +17,6 @@
* @file auditor/taler-helper-auditor-coins.c
* @brief audits coins in an exchange database.
* @author Christian Grothoff
*
* UNDECIDED:
* - do we care about checking the 'done' flag in deposit_cb?
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
@ -248,8 +245,9 @@ get_cached_history (const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
unsigned int i = coin_history_index (coin_pub);
if (0 == GNUNET_memcmp (coin_pub,
&coin_histories[i].coin_pub))
if (0 ==
GNUNET_memcmp (coin_pub,
&coin_histories[i].coin_pub))
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Found verification of %s in cache\n",
@ -479,7 +477,6 @@ check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&tl);
if (0 >= qs)
return qs;
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (value->currency,
&refunded));
@ -538,6 +535,7 @@ check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
&pos->details.recoup_refresh->value);
break;
case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
/* spent += pos->value */
TALER_ARL_amount_add (&spent,
&spent,
&pos->details.purse_deposit->amount);
@ -628,22 +626,22 @@ struct DenominationSummary
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
/**
* #GNUNET_YES if this record already existed in the DB.
* True if this record already existed in the DB.
* Used to decide between insert/update in
* #sync_denomination().
*/
int in_db;
bool in_db;
/**
* Should we report an emergency for this denomination, causing it to be
* revoked (because more coins were deposited than issued)?
*/
int report_emergency;
bool report_emergency;
/**
* #GNUNET_YES if this denomination was revoked.
* True if this denomination was revoked.
*/
int was_revoked;
bool was_revoked;
};
@ -695,7 +693,7 @@ init_denomination (const struct TALER_DenominationHashP *denom_hash,
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
ds->in_db = GNUNET_YES;
ds->in_db = true;
}
else
{
@ -741,10 +739,10 @@ init_denomination (const struct TALER_DenominationHashP *denom_hash,
}
else
{
ds->was_revoked = GNUNET_YES;
ds->was_revoked = true;
}
}
return (GNUNET_YES == ds->in_db)
return ds->in_db
? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
: GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
@ -900,7 +898,7 @@ sync_denomination (void *cls,
cnt,
&ds->denom_risk);
}
if (GNUNET_YES == ds->report_emergency)
if (ds->report_emergency)
{
/* Value of coins deposited exceed value of coins
issued! Also very bad! */
@ -1139,13 +1137,13 @@ reveal_data_cb (void *cls,
* #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
*/
static enum GNUNET_DB_QueryStatus
check_known_coin (const char *operation,
const struct
TALER_EXCHANGEDB_DenominationKeyInformation *issue,
uint64_t rowid,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_Amount *loss_potential)
check_known_coin (
const char *operation,
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
uint64_t rowid,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_Amount *loss_potential)
{
struct TALER_CoinPublicInfo ci;
enum GNUNET_DB_QueryStatus qs;
@ -1463,6 +1461,7 @@ refresh_session_cb (void *cls,
}
else
{
// FIXME: refactor: repeated logic!
if (TALER_ARL_SR_INVALID_NEGATIVE ==
TALER_ARL_amount_subtract_neg (&tmp,
&dso->denom_balance,
@ -1471,7 +1470,7 @@ refresh_session_cb (void *cls,
TALER_ARL_amount_add (&dso->denom_loss,
&dso->denom_loss,
amount_with_fee);
dso->report_emergency = GNUNET_YES;
dso->report_emergency = true;
}
else
{
@ -1650,6 +1649,7 @@ deposit_cb (void *cls,
{
struct TALER_Amount tmp;
// FIXME: refactor: repeated logic!
if (TALER_ARL_SR_INVALID_NEGATIVE ==
TALER_ARL_amount_subtract_neg (&tmp,
&ds->denom_balance,
@ -1658,7 +1658,7 @@ deposit_cb (void *cls,
TALER_ARL_amount_add (&ds->denom_loss,
&ds->denom_loss,
&deposit->amount_with_fee);
ds->report_emergency = GNUNET_YES;
ds->report_emergency = true;
}
else
{
@ -1718,6 +1718,7 @@ deposit_cb (void *cls,
* @param merchant_sig signature of the merchant
* @param h_contract_terms hash of the proposal data known to merchant and customer
* @param rtransaction_id refund transaction ID chosen by the merchant
* @param full_refund true if the refunds total up to the entire deposited value
* @param amount_with_fee amount that was deposited including fee
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -1730,6 +1731,7 @@ refund_cb (void *cls,
const struct TALER_MerchantSignatureP *merchant_sig,
const struct TALER_PrivateContractHashP *h_contract_terms,
uint64_t rtransaction_id,
bool full_refund,
const struct TALER_Amount *amount_with_fee)
{
struct CoinContext *cc = cls;
@ -1840,6 +1842,153 @@ refund_cb (void *cls,
TALER_ARL_amount_add (&total_refund_fee_income,
&total_refund_fee_income,
&issue->fees.refund);
if (full_refund)
{
TALER_ARL_amount_subtract (&total_deposit_fee_income,
&total_deposit_fee_income,
&issue->fees.deposit);
}
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
/**
* Function called with details about purse refunds that have been made, with
* the goal of auditing the purse refund's execution.
*
* @param cls closure
* @param amount_with_fee amount of the deposit into the purse
* @param coin_pub coin that is to be refunded the @a given amount_with_fee
* @param denom_pub denomination of @a coin_pub
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static enum GNUNET_GenericReturnValue
purse_refund_coin_cb (
void *cls,
const struct TALER_Amount *amount_with_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationPublicKey *denom_pub)
{
struct CoinContext *cc = cls;
#if FIXME
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
struct DenominationSummary *ds;
struct TALER_Amount amount_without_fee;
enum GNUNET_DB_QueryStatus qs;
qs = TALER_ARL_get_denomination_info (denom_pub,
&issue,
NULL);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("purse-refunds",
rowid,
"denomination key not found");
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return GNUNET_SYSERR;
}
if (TALER_ARL_SR_INVALID_NEGATIVE ==
TALER_ARL_amount_subtract_neg (&amount_without_fee,
amount_with_fee,
&issue->fees.refund))
{
report_amount_arithmetic_inconsistency ("refund (fee)",
rowid,
&amount_without_fee,
&issue->fees.refund,
-1);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Refunding coin %s in denomination `%s' value %s\n",
TALER_B2S (coin_pub),
GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (amount_with_fee));
/* update coin's denomination balance */
ds = get_denomination_summary (cc,
issue,
&issue->denom_hash);
if (NULL == ds)
{
report_row_inconsistency ("refund",
rowid,
"denomination key for refunded coin unknown to auditor");
}
else
{
TALER_ARL_amount_add (&ds->denom_balance,
&ds->denom_balance,
&amount_without_fee);
TALER_ARL_amount_add (&ds->denom_risk,
&ds->denom_risk,
&amount_without_fee);
TALER_ARL_amount_add (&total_escrow_balance,
&total_escrow_balance,
&amount_without_fee);
TALER_ARL_amount_add (&total_risk,
&total_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));
}
/* update total refund fee balance */
TALER_ARL_amount_add (&total_refund_fee_income,
&total_refund_fee_income,
&issue->fees.refund);
TALER_ARL_amount_subtract (&total_deposit_fee_income,
&total_deposit_fee_income,
&issue->fees.deposit);
#endif
return GNUNET_OK;
}
/**
* Function called with details about a purse that was refunded. Adds the
* refunded amounts back to the outstanding balance of the respective
* denominations.
*
* @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_refund_cb (void *cls,
uint64_t rowid,
const struct TALER_PurseContractPublicKeyP *purse_pub)
{
struct CoinContext *cc = cls;
enum GNUNET_DB_QueryStatus qs;
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,
purse_pub,
&purse_refund_coin_cb,
cc);
if (qs < 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return GNUNET_SYSERR;
}
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
@ -1934,7 +2083,7 @@ check_recoup (struct CoinContext *cc,
}
else
{
if (GNUNET_NO == ds->was_revoked)
if (! ds->was_revoked)
{
/* Woopsie, we allowed recoup on non-revoked denomination!? */
TALER_ARL_report (report_bad_sig_losses,
@ -2233,9 +2382,131 @@ purse_deposit_cb (
const struct TALER_DenominationPublicKey *denom_pub)
{
struct CoinContext *cc = cls;
enum GNUNET_DB_QueryStatus qs;
struct TALER_DenominationHashP dh;
const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
struct DenominationSummary *ds;
GNUNET_break (0); // FIXME: not implemented!
return GNUNET_SYSERR;
GNUNET_assert (rowid >= ppc.last_purse_deposits_serial_id);
ppc.last_purse_deposits_serial_id = rowid + 1;
qs = TALER_ARL_get_denomination_info (denom_pub,
&issue,
&dh);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
report_row_inconsistency ("purse-deposits",
rowid,
"denomination key not found");
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
qs = check_known_coin ("purse-deposit",
issue,
rowid,
&deposit->coin_pub,
denom_pub,
&deposit->amount);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
cc->qs = qs;
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (
NULL != deposit->exchange_base_url
? deposit->exchange_base_url
: TALER_ARL_exchange_url,
&deposit->purse_pub,
&deposit->amount,
&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 ("coin_pub",
&deposit->coin_pub)));
TALER_ARL_amount_add (&total_bad_sig_loss,
&total_bad_sig_loss,
&deposit->amount);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
/* update coin's denomination balance */
ds = get_denomination_summary (cc,
issue,
&issue->denom_hash);
if (NULL == ds)
{
report_row_inconsistency ("purse-deposit",
rowid,
"denomination key for purse-deposited coin unknown to auditor");
}
else
{
struct TALER_Amount tmp;
// FIXME: refactor: repeated logic!
if (TALER_ARL_SR_INVALID_NEGATIVE ==
TALER_ARL_amount_subtract_neg (&tmp,
&ds->denom_balance,
&deposit->amount))
{
TALER_ARL_amount_add (&ds->denom_loss,
&ds->denom_loss,
&deposit->amount);
ds->report_emergency = true;
}
else
{
ds->denom_balance = tmp;
}
if (-1 == TALER_amount_cmp (&total_escrow_balance,
&deposit->amount))
{
/* This can theoretically happen if for example the exchange
never issued any coins (i.e. escrow balance is zero), but
accepted a forged coin (i.e. emergency situation after
private key compromise). In that case, we cannot even
subtract the profit we make from the fee from the escrow
balance. Tested as part of test-auditor.sh, case #18 */
report_amount_arithmetic_inconsistency (
"subtracting purse deposit fee from escrow balance",
rowid,
&total_escrow_balance,
&deposit->amount,
0);
}
else
{
TALER_ARL_amount_subtract (&total_escrow_balance,
&total_escrow_balance,
&deposit->amount);
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"New balance of denomination `%s' after purse deposit is %s\n",
GNUNET_h2s (&issue->denom_hash.hash),
TALER_amount2s (&ds->denom_balance));
}
/* update global deposit fees */
TALER_ARL_amount_add (&total_deposit_fee_income,
&total_deposit_fee_income,
&issue->fees.deposit);
if (TALER_ARL_do_abort ())
return GNUNET_SYSERR;
return GNUNET_OK;
}
@ -2339,6 +2610,20 @@ analyze_coins (void *cls)
if (0 > cc.qs)
return cc.qs;
/* process purse_refunds */
if (0 >
(qs = TALER_ARL_edb->select_purse_refunds_above_serial_id (
TALER_ARL_edb->cls,
ppc.last_purse_refunds_serial_id,
&purse_refund_cb,
&cc)))
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
if (0 > cc.qs)
return cc.qs;
/* process recoups */
if (0 >
(qs = TALER_ARL_edb->select_recoup_refresh_above_serial_id (
@ -2631,8 +2916,11 @@ run (void *cls,
GNUNET_JSON_pack_uint64 ("start_ppc_recoup_serial_id",
ppc_start.last_recoup_serial_id),
GNUNET_JSON_pack_uint64 ("start_ppc_recoup_refresh_serial_id",
ppc_start.
last_recoup_refresh_serial_id),
ppc_start.last_recoup_refresh_serial_id),
GNUNET_JSON_pack_uint64 ("start_ppc_purse_deposits_serial_id",
ppc_start.last_purse_deposits_serial_id),
GNUNET_JSON_pack_uint64 ("start_ppc_purse_refunds_serial_id",
ppc_start.last_purse_refunds_serial_id),
GNUNET_JSON_pack_uint64 ("end_ppc_withdraw_serial_id",
ppc.last_withdraw_serial_id),
GNUNET_JSON_pack_uint64 ("end_ppc_deposit_serial_id",
@ -2645,6 +2933,10 @@ run (void *cls,
ppc.last_recoup_serial_id),
GNUNET_JSON_pack_uint64 ("end_ppc_recoup_refresh_serial_id",
ppc.last_recoup_refresh_serial_id),
GNUNET_JSON_pack_uint64 ("end_ppc_purse_deposits_serial_id",
ppc.last_purse_deposits_serial_id),
GNUNET_JSON_pack_uint64 ("end_ppc_purse_refunds_serial_id",
ppc.last_purse_refunds_serial_id),
TALER_JSON_pack_time_abs_human ("auditor_start_time",
start_time),
TALER_JSON_pack_time_abs_human ("auditor_end_time",

View File

@ -87,6 +87,7 @@ CREATE TABLE IF NOT EXISTS auditor_progress_coin
,last_recoup_serial_id INT8 NOT NULL DEFAULT 0
,last_recoup_refresh_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
,PRIMARY KEY (master_pub)
);
COMMENT ON TABLE auditor_progress_coin

View File

@ -314,8 +314,9 @@ setup_connection (struct PostgresClosure *pg)
",last_recoup_serial_id=$5"
",last_recoup_refresh_serial_id=$6"
",last_purse_deposits_serial_id=$7"
" WHERE master_pub=$8",
8),
",last_purse_refunds_serial_id=$8"
" WHERE master_pub=$9",
9),
/* Used in #postgres_get_auditor_progress_coin() */
GNUNET_PQ_make_prepare ("auditor_progress_select_coin",
"SELECT"
@ -326,6 +327,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"
" FROM auditor_progress_coin"
" WHERE master_pub=$1;",
1),
@ -340,8 +342,9 @@ setup_connection (struct PostgresClosure *pg)
",last_recoup_serial_id"
",last_recoup_refresh_serial_id"
",last_purse_deposits_serial_id"
") VALUES ($1,$2,$3,$4,$5,$6,$7,$8);",
8),
",last_purse_refunds_serial_id"
") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
9),
/* Used in #postgres_insert_wire_auditor_account_progress() */
GNUNET_PQ_make_prepare ("wire_auditor_account_progress_insert",
"INSERT INTO wire_auditor_account_progress "
@ -1535,6 +1538,7 @@ postgres_insert_auditor_progress_coin (
GNUNET_PQ_query_param_uint64 (&ppc->last_recoup_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_recoup_refresh_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_purse_deposits_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_purse_refunds_serial_id),
GNUNET_PQ_query_param_end
};
@ -1568,6 +1572,7 @@ postgres_update_auditor_progress_coin (
GNUNET_PQ_query_param_uint64 (&ppc->last_recoup_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_recoup_refresh_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_purse_deposits_serial_id),
GNUNET_PQ_query_param_uint64 (&ppc->last_purse_refunds_serial_id),
GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_end
};
@ -1612,6 +1617,8 @@ 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",
&ppc->last_purse_refunds_serial_id),
GNUNET_PQ_result_spec_end
};

View File

@ -14,16 +14,24 @@ pkgcfg_DATA = \
sqldir = $(prefix)/share/taler/sql/exchange/
sqlinputs = \
common-0001.sql \
drop-common.sql \
exchange-0001-part.sql \
drop0001-exchange-part.sql \
shard-0001-part.sql \
drop0001-shard-part.sql
sql_DATA = \
benchmark-0000.sql \
benchmark-0001.sql \
exchange-0000.sql \
exchange-0001-part.sql \
shard-0001-part.sql \
common-0001.sql \
drop-common.sql \
drop0001-exchange-part.sql \
drop0001-shard-part.sql
exchange-0001.sql \
drop0001.sql \
shard-0000.sql \
shard-0001.sql \
shard-drop0001.sql
BUILT_SOURCES = \
shard-0000.sql \
@ -40,22 +48,27 @@ CLEANFILES = \
shard-drop0001.sql
exchange-0001.sql: common-0001.sql exchange-0001-part.sql
chmod +w $@ || true
cat common-0001.sql exchange-0001-part.sql >$@
chmod -w $@
shard-0001.sql: common-0001.sql shard-0001-part.sql
chmod +w $@ || true
cat common-0001.sql shard-0001-part.sql >$@
chmod -w $@
shard-0000.sql: exchange-0000.sql
chmod +w $@ || true
cp exchange-0000.sql $@
chmod -w $@
drop0001.sql: drop-common.sql drop0001-exchange-part.sql
chmod +w $@ || true
cat drop-common.sql drop0001-exchange-part.sql >$@
chmod -w $@
shard-drop0001.sql: drop-common.sql drop0001-shard-part.sql
chmod +w $@ || true
cat drop-common.sql drop0001-shard-part.sql >$@
chmod -w $@
@ -67,6 +80,7 @@ EXTRA_DIST = \
lrbt_callbacks.c \
bench-db-postgres.conf \
test-exchange-db-postgres.conf \
$(sqlinputs) \
$(sql_DATA)
plugindir = $(libdir)/taler

View File

@ -1219,6 +1219,54 @@ BEGIN
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(
@ -1828,6 +1876,9 @@ 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_merges
DETACH partition purse_merges_default;
@ -1894,6 +1945,7 @@ 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_merges_default;
DROP TABLE IF EXISTS account_merges_default;
DROP TABLE IF EXISTS contracts_default;
@ -2105,6 +2157,13 @@ BEGIN
);
PERFORM add_constraints_to_purse_requests_partition(num_partitions::varchar);
PERFORM create_hash_partition(
'purse_refunds'
,modulus
,num_partitions
);
PERFORM add_constraints_to_purse_refunds_partition(num_partitions::varchar);
PERFORM create_hash_partition(
'purse_merges'
,modulus
@ -2316,6 +2375,10 @@ 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_merges
DROP CONSTRAINT IF EXISTS purse_merges_pkey CASCADE
;
@ -2571,6 +2634,13 @@ BEGIN
,current_shard_num
,local_user
);
PERFORM create_foreign_hash_partition(
'purse_refunds'
,total_num_shards
,shard_suffix
,current_shard_num
,local_user
);
PERFORM create_foreign_hash_partition(
'purse_merges'
,total_num_shards

View File

@ -63,6 +63,8 @@ DROP FUNCTION IF EXISTS add_constraints_to_cs_nonce_locks_partition;
DROP FUNCTION IF EXISTS create_table_purse_requests;
DROP FUNCTION IF EXISTS add_constraints_to_purse_requests_partition;
DROP FUNCTION IF EXISTS create_table_purse_refunds;
DROP FUNCTION IF EXISTS add_constraints_to_purse_refunds_partition;
DROP FUNCTION IF EXISTS create_table_purse_merges;
DROP FUNCTION IF EXISTS add_constraints_to_purse_merges_partition;
DROP FUNCTION IF EXISTS create_table_account_merges;

View File

@ -73,6 +73,7 @@ DROP TABLE IF EXISTS contracts CASCADE;
DROP TABLE IF EXISTS history_requests CASCADE;
DROP TABLE IF EXISTS close_requests CASCADE;
DROP TABLE IF EXISTS purse_requests CASCADE;
DROP TABLE IF EXISTS purse_refunds CASCADE;
DROP TABLE IF EXISTS wads_out CASCADE;
DROP TABLE IF EXISTS wad_out_entries CASCADE;
DROP TABLE IF EXISTS wads_in CASCADE;

View File

@ -1040,6 +1040,21 @@ CREATE TABLE IF NOT EXISTS purse_requests_default
SELECT add_constraints_to_purse_requests_partition('default');
-- ------------------------------ purse_refunds ----------------------------------------
SELECT create_table_purse_refunds();
COMMENT ON TABLE purse_refunds
IS 'Purses that were refunded due to expiration';
COMMENT ON COLUMN purse_refunds.purse_pub
IS 'Public key of the purse';
CREATE TABLE IF NOT EXISTS purse_refunds_default
PARTITION OF purse_refunds
FOR VALUES WITH (MODULUS 1, REMAINDER 0);
SELECT add_constraints_to_purse_refunds_partition('default');
-- ------------------------------ purse_merges ----------------------------------------
@ -3485,6 +3500,11 @@ UPDATE purse_requests
finished=TRUE
WHERE purse_pub=my_purse_pub;
INSERT INTO purse_refunds
(purse_pub)
VALUES
(my_purse_pub);
-- restore balance to each coin deposited into the purse
FOR my_deposit IN
SELECT coin_pub

View File

@ -809,6 +809,8 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg,
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial),
GNUNET_PQ_query_param_auto_from_type (
&td->details.purse_requests.purse_pub),
GNUNET_PQ_query_param_auto_from_type (
&td->details.purse_requests.merge_pub),
GNUNET_PQ_query_param_timestamp (
@ -832,6 +834,29 @@ irbt_cb_table_purse_requests (struct PostgresClosure *pg,
}
/**
* Function called with purse_refunds 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)
{
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),
GNUNET_PQ_query_param_end
};
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
"insert_into_table_purse_refunds",
params);
}
/**
* Function called with purse_merges records to insert into table.
*

View File

@ -1549,6 +1549,51 @@ lrbt_cb_table_purse_requests (void *cls,
}
/**
* Function called with purse_refunds 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)
{
struct LookupRecordsByTableContext *ctx = cls;
struct TALER_EXCHANGEDB_TableData td = {
.table = TALER_EXCHANGEDB_RT_PURSE_REFUNDS
};
for (unsigned int i = 0; i<num_results; i++)
{
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 (
"purse_refunds_serial_id",
&td.serial),
GNUNET_PQ_result_spec_auto_from_type (
"purse_pub",
&td.details.purse_refunds.purse_pub),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
ctx->error = true;
return;
}
ctx->cb (ctx->cb_cls,
&td);
GNUNET_PQ_cleanup_result (rs);
}
}
/**
* Function called with purse_merges table entries.
*

View File

@ -1407,6 +1407,19 @@ prepare_statements (struct PostgresClosure *pg)
" WHERE ref.refund_serial_id>=$1"
" ORDER BY ref.refund_serial_id ASC;",
1),
GNUNET_PQ_make_prepare (
"test_refund_full",
"SELECT"
" CAST(SUM(CAST(ref.amount_with_fee_frac AS INT8)) AS INT8) AS s_f"
",CAST(SUM(ref.amount_with_fee_val) AS INT8) AS s_v"
",dep.amount_with_fee_val"
",dep.amount_with_fee_frac"
" FROM refunds ref"
" JOIN deposits dep"
" ON (ref.coin_pub=dep.coin_pub AND ref.deposit_serial_id=dep.deposit_serial_id)"
" WHERE ref.refund_serial_id=$1"
" GROUP BY (dep.amount_with_fee_val, dep.amount_with_fee_frac);",
1),
/* Store information about a /deposit the exchange is to execute.
Used in #postgres_insert_deposit(). Only used in test cases. */
@ -1508,6 +1521,29 @@ prepare_statements (struct PostgresClosure *pg)
" )"
" ORDER BY purse_deposit_serial_id ASC;",
1),
GNUNET_PQ_make_prepare (
"audit_get_purse_deposits_by_purse",
"SELECT"
" pd.amount_with_fee_val"
",pd.amount_with_fee_frac"
",pd.coin_pub"
",denom.denom_pub"
" FROM purse_deposits pd"
" JOIN known_coins kc USING (coin_pub)"
" JOIN denominations denom USING (denominations_serial)"
" WHERE purse_pub=$1;",
1),
GNUNET_PQ_make_prepare (
"audit_get_purse_refunds_incr",
"SELECT"
" purse_pub"
",purse_refunds_serial_id"
" FROM purse_refunds"
" WHERE ("
" (purse_refunds_serial_id>=$1)"
" )"
" ORDER BY purse_refunds_serial_id ASC;",
1),
/* Fetch an existing deposit request.
Used in #postgres_lookup_transfer_by_deposit(). */
GNUNET_PQ_make_prepare (
@ -2778,6 +2814,14 @@ prepare_statements (struct PostgresClosure *pg)
" ORDER BY purse_requests_serial_id DESC"
" LIMIT 1;",
0),
GNUNET_PQ_make_prepare (
"select_serial_by_table_purse_refunds",
"SELECT"
" purse_refunds_serial_id AS serial"
" FROM purse_refunds"
" ORDER BY purse_refunds_serial_id DESC"
" LIMIT 1;",
0),
GNUNET_PQ_make_prepare (
"select_serial_by_table_purse_merges",
"SELECT"
@ -3212,6 +3256,15 @@ prepare_statements (struct PostgresClosure *pg)
" WHERE purse_requests_serial_id > $1"
" ORDER BY purse_requests_serial_id ASC;",
1),
GNUNET_PQ_make_prepare (
"select_above_serial_by_table_purse_refunds",
"SELECT"
" purse_refunds_serial_id"
",purse_pub"
" FROM purse_refunds"
" WHERE purse_refunds_serial_id > $1"
" ORDER BY purse_refunds_serial_id ASC;",
1),
GNUNET_PQ_make_prepare (
"select_above_serial_by_table_purse_merges",
"SELECT"
@ -3688,6 +3741,14 @@ prepare_statements (struct PostgresClosure *pg)
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);",
13),
GNUNET_PQ_make_prepare (
"insert_into_table_purse_refunds",
"INSERT INTO purse_refunds"
"(purse_refunds_serial_id"
",purse_pub"
") VALUES "
"($1, $2);",
2),
GNUNET_PQ_make_prepare (
"insert_into_table_purse_merges",
"INSERT INTO purse_merges"
@ -10496,6 +10557,242 @@ postgres_select_purse_deposits_above_serial_id (
}
/**
* Closure for #purse_refund_serial_helper_cb().
*/
struct PurseRefundSerialContext
{
/**
* Callback to call.
*/
TALER_EXCHANGEDB_PurseRefundCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin 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 PurseRefundSerialContext`
* @param result the postgres result
* @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)
{
struct PurseRefundSerialContext *dsc = cls;
for (unsigned int i = 0; i<num_results; i++)
{
struct TALER_PurseContractPublicKeyP purse_pub;
uint64_t rowid;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("purse_pub",
&purse_pub),
GNUNET_PQ_result_spec_uint64 ("purse_deposit_serial_id",
&rowid),
GNUNET_PQ_result_spec_end
};
enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
dsc->status = GNUNET_SYSERR;
return;
}
ret = dsc->cb (dsc->cb_cls,
rowid,
&purse_pub);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
}
/**
* Select purse refunds above @a serial_id in monotonically increasing
* order.
*
* @param cls closure
* @param serial_id highest serial ID to exclude (select strictly larger)
* @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 (
void *cls,
uint64_t serial_id,
TALER_EXCHANGEDB_PurseRefundCallback 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 PurseRefundSerialContext dsc = {
.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,
"audit_get_purse_refunds_incr",
params,
&purse_refund_serial_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Closure for #purse_refund_coin_helper_cb().
*/
struct PurseRefundCoinContext
{
/**
* Callback to call.
*/
TALER_EXCHANGEDB_PurseRefundCoinCallback cb;
/**
* Closure for @e cb.
*/
void *cb_cls;
/**
* Plugin 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 PurseRefundCoinContext`
* @param result the postgres result
* @param num_results the number of results in @a result
*/
static void
purse_refund_coin_helper_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct PurseRefundCoinContext *dsc = cls;
struct PostgresClosure *pg = dsc->pg;
for (unsigned int i = 0; i<num_results; i++)
{
struct TALER_Amount amount_with_fee;
struct TALER_CoinSpendPublicKeyP coin_pub;
struct TALER_DenominationPublicKey denom_pub;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_denom_pub ("denom_pub",
&denom_pub),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&amount_with_fee),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
&coin_pub),
GNUNET_PQ_result_spec_end
};
enum GNUNET_GenericReturnValue ret;
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
dsc->status = GNUNET_SYSERR;
return;
}
ret = dsc->cb (dsc->cb_cls,
&amount_with_fee,
&coin_pub,
&denom_pub);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
break;
}
}
/**
* Select coin affected by purse refund.
*
* @param cls closure
* @param purse_pub purse that was refunded
* @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_deposits_by_purse (
void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub,
TALER_EXCHANGEDB_PurseRefundCoinCallback cb,
void *cb_cls)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (purse_pub),
GNUNET_PQ_query_param_end
};
struct PurseRefundCoinContext dsc = {
.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,
"audit_get_purse_deposits_by_purse",
params,
&purse_refund_coin_helper_cb,
&dsc);
if (GNUNET_OK != dsc.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/**
* Closure for #refreshs_serial_helper_cb().
*/
@ -10690,6 +10987,7 @@ refunds_serial_helper_cb (void *cls,
struct TALER_EXCHANGEDB_Refund refund;
struct TALER_DenominationPublicKey denom_pub;
uint64_t rowid;
bool full_refund;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
&refund.details.merchant_pub),
@ -10720,6 +11018,42 @@ refunds_serial_helper_cb (void *cls,
rsc->status = GNUNET_SYSERR;
return;
}
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&rowid),
GNUNET_PQ_query_param_end
};
struct TALER_Amount amount_with_fee;
uint64_t s_f;
uint64_t s_v;
struct GNUNET_PQ_ResultSpec rs2[] = {
GNUNET_PQ_result_spec_uint64 ("s_v",
&s_v),
GNUNET_PQ_result_spec_uint64 ("s_f",
&s_f),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&amount_with_fee),
GNUNET_PQ_result_spec_end
};
enum GNUNET_DB_QueryStatus qs;
qs = GNUNET_PQ_eval_prepared_singleton_select (
pg->conn,
"test_refund_full",
params,
rs2);
if (qs <= 0)
{
GNUNET_break (0);
rsc->status = GNUNET_SYSERR;
return;
}
/* normalize */
s_v += s_f / TALER_AMOUNT_FRAC_BASE;
s_f %= TALER_AMOUNT_FRAC_BASE;
full_refund = (s_v >= amount_with_fee.value) &&
(s_f >= amount_with_fee.fraction);
}
ret = rsc->cb (rsc->cb_cls,
rowid,
&denom_pub,
@ -10728,6 +11062,7 @@ refunds_serial_helper_cb (void *cls,
&refund.details.merchant_sig,
&refund.details.h_contract_terms,
refund.details.rtransaction_id,
full_refund,
&refund.details.refund_amount);
GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret)
@ -13129,6 +13464,9 @@ postgres_lookup_serial_by_table (void *cls,
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
statement = "select_serial_by_table_purse_requests";
break;
case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
statement = "select_serial_by_table_purse_refunds";
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
statement = "select_serial_by_table_purse_merges";
break;
@ -13337,6 +13675,10 @@ postgres_lookup_records_by_table (void *cls,
statement = "select_above_serial_by_table_purse_requests";
rh = &lrbt_cb_table_purse_requests;
break;
case TALER_EXCHANGEDB_RT_PURSE_REFUNDS:
statement = "select_above_serial_by_table_purse_refunds";
rh = &lrbt_cb_table_purse_refunds;
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
statement = "select_above_serial_by_table_purse_merges";
rh = &lrbt_cb_table_purse_merges;
@ -13510,6 +13852,9 @@ postgres_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;
break;
case TALER_EXCHANGEDB_RT_PURSE_MERGES:
rh = &irbt_cb_table_purse_merges;
break;
@ -15203,6 +15548,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_deposits_above_serial_id;
plugin->select_purse_deposits_above_serial_id
= &postgres_select_purse_deposits_above_serial_id;
plugin->select_purse_refunds_above_serial_id
= &postgres_select_purse_refunds_above_serial_id;
plugin->select_purse_deposits_by_purse
= &postgres_select_purse_deposits_by_purse;
plugin->select_refreshes_above_serial_id
= &postgres_select_refreshes_above_serial_id;
plugin->select_refunds_above_serial_id

View File

@ -26,7 +26,7 @@ CREATE OR REPLACE FUNCTION setup_shard(
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
DECLARE
shard_suffix VARCHAR;
BEGIN
@ -51,26 +51,26 @@ BEGIN
PERFORM create_table_refresh_commitments(shard_suffix);
PERFORM add_constraints_to_refresh_commitments_partition(shard_suffix);
PERFORM create_table_refresh_revealed_coins(shard_suffix);
PERFORM add_constraints_to_refresh_revealed_coins_partition(shard_suffix);
PERFORM create_table_refresh_transfer_keys(shard_suffix);
PERFORM add_constraints_to_refresh_transfer_keys_partition(shard_suffix);
PERFORM create_table_deposits(shard_suffix);
PERFORM add_constraints_to_deposits_partition(shard_suffix);
PERFORM create_table_deposits_by_ready(shard_suffix);
PERFORM create_table_deposits_for_matching(shard_suffix);
PERFORM create_table_refunds(shard_suffix);
PERFORM add_constraints_to_refunds_partition(shard_suffix);
PERFORM create_table_wire_out(shard_suffix);
PERFORM add_constraints_to_wire_out_partition(shard_suffix);
PERFORM create_table_aggregation_transient(shard_suffix);
PERFORM create_table_aggregation_tracking(shard_suffix);
@ -92,6 +92,9 @@ 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_merges(shard_suffix);
PERFORM add_constraints_to_purse_merges_partition(shard_suffix);
@ -121,15 +124,15 @@ $$;
CREATE OR REPLACE FUNCTION drop_shard(
shard_idx INTEGER
shard_idx INTEGER
)
RETURNS VOID
LANGUAGE plpgsql
AS $$
DECLARE
DECLARE
shard_suffix VARCHAR;
BEGIN
shard_suffix = shard_idx::varchar;
EXECUTE FORMAT(
@ -224,6 +227,10 @@ BEGIN
'DROP TABLE IF EXISTS %I CASCADE'
,'purse_requests_' || shard_suffix
);
EXECUTE FORMAT(
'DROP TABLE IF EXISTS %I CASCADE'
,'purse_refunds_' || shard_suffix
);
EXECUTE FORMAT(
'DROP TABLE IF EXISTS %I CASCADE'
,'purse_merges_' || shard_suffix

View File

@ -646,6 +646,7 @@ audit_deposit_cb (void *cls,
* @param h_contract_terms hash of the proposal data in
* the contract between merchant and customer
* @param rtransaction_id refund transaction ID chosen by the merchant
* @param full_refund the deposit
* @param amount_with_fee amount that was deposited including fee
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -658,6 +659,7 @@ audit_refund_cb (void *cls,
const struct TALER_MerchantSignatureP *merchant_sig,
const struct TALER_PrivateContractHashP *h_contract_terms,
uint64_t rtransaction_id,
bool full_refund,
const struct TALER_Amount *amount_with_fee)
{
(void) cls;
@ -669,6 +671,7 @@ audit_refund_cb (void *cls,
(void) h_contract_terms;
(void) rtransaction_id;
(void) amount_with_fee;
(void) full_refund;
auditor_row_cnt++;
return GNUNET_OK;
}

1
src/include/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
taler_signatures.h

View File

@ -253,6 +253,11 @@ struct TALER_AUDITORDB_ProgressPointCoin
*/
uint64_t last_purse_deposits_serial_id;
/**
* Serial ID of the last purse_refunds operation the auditor processed.
*/
uint64_t last_purse_refunds_serial_id;
};

View File

@ -218,6 +218,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_MERGES,
TALER_EXCHANGEDB_RT_PURSE_DEPOSITS,
TALER_EXCHANGEDB_RT_ACCOUNT_MERGES,
@ -495,6 +496,11 @@ struct TALER_EXCHANGEDB_TableData
struct TALER_PurseContractSignatureP purse_sig;
} purse_requests;
struct
{
struct TALER_PurseContractPublicKeyP purse_pub;
} purse_refunds;
struct
{
uint64_t partner_serial_id;
@ -1995,6 +2001,40 @@ typedef enum GNUNET_GenericReturnValue
const struct TALER_DenominationPublicKey *denom_pub);
/**
* Function called with details about purse refunds that have been made, with
* the goal of auditing the purse refund'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
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef enum GNUNET_GenericReturnValue
(*TALER_EXCHANGEDB_PurseRefundCallback)(
void *cls,
uint64_t rowid,
const struct TALER_PurseContractPublicKeyP *purse_pub);
/**
* Function called with details about purse refunds that have been made, with
* the goal of auditing the purse refund's execution.
*
* @param cls closure
* @param amount_with_fee amount of the deposit into the purse
* @param coin_pub coin that is to be refunded the @a given amount_with_fee
* @param denom_pub denomination of @a coin_pub
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef enum GNUNET_GenericReturnValue
(*TALER_EXCHANGEDB_PurseRefundCoinCallback)(
void *cls,
const struct TALER_Amount *amount_with_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_DenominationPublicKey *denom_pub);
/**
* Function called with details about coins that were melted,
* with the goal of auditing the refresh's execution.
@ -2198,6 +2238,7 @@ typedef void
* @param merchant_sig signature of the merchant
* @param h_contract_terms hash of the proposal data known to merchant and customer
* @param rtransaction_id refund transaction ID chosen by the merchant
* @param full_refund true if the refunds total up to the entire value of the deposit
* @param amount_with_fee amount that was deposited including fee
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
@ -2211,6 +2252,7 @@ typedef enum GNUNET_GenericReturnValue
const struct TALER_MerchantSignatureP *merchant_sig,
const struct TALER_PrivateContractHashP *h_contract_terms,
uint64_t rtransaction_id,
bool full_refund,
const struct TALER_Amount *amount_with_fee);
@ -4058,6 +4100,41 @@ struct TALER_EXCHANGEDB_Plugin
void *cb_cls);
/**
* Select purse refunds above @a serial_id in monotonically increasing
* order.
*
* @param cls closure
* @param serial_id highest serial ID to exclude (select strictly larger)
* @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)(
void *cls,
uint64_t serial_id,
TALER_EXCHANGEDB_PurseRefundCallback cb,
void *cb_cls);
/**
* Select coins deposited into a purse.
*
* @param cls closure
* @param purse_pub public key of the purse
* @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_deposits_by_purse)(
void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub,
TALER_EXCHANGEDB_PurseRefundCoinCallback cb,
void *cb_cls);
/**
* Select refresh sessions above @a serial_id in monotonically increasing
* order.