merge changelog

This commit is contained in:
Christian Grothoff 2018-01-15 13:27:19 +01:00
commit 9fffeee4ef
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
19 changed files with 702 additions and 126 deletions

View File

@ -1,3 +1,10 @@
Thu Jan 4 11:55:41 CET 2018
Fix issue #5234 (aggregator ignoring refunds).
Misc. minor fixes to the auditor. -CG
Mon Jan 1 23:15:37 CET 2018
Add TALER_EXCHANGE_refund2() API call to libtalerexchange. -CG
Thu Dec 14 15:32:50 CET 2017 Thu Dec 14 15:32:50 CET 2017
Replaced taler-exchange-reservemod tool with new taler-bank-transfer Replaced taler-exchange-reservemod tool with new taler-bank-transfer
tool (#5195). Removed /admin/add/incoming API, replaced by new tool (#5195). Removed /admin/add/incoming API, replaced by new

View File

@ -102,8 +102,7 @@ making wire transfers that have been due.
The total amount the exchange currently lags behind is The total amount the exchange currently lags behind is
{\bf {{ wire.total_amount_lag.value }}.{{ wire.total_amount_lag.fraction }} {\bf {{ wire.total_amount_lag.value }}.{{ wire.total_amount_lag.fraction }}
{{ wire.total_amount_lag.currency }} {{ wire.total_amount_lag.currency }}}.
}.
Note that some lag is perfectly normal, as tiny amounts that are too small to be wired Note that some lag is perfectly normal, as tiny amounts that are too small to be wired
are deferred beyond the due date, hoping that additional transfers will push them above are deferred beyond the due date, hoping that additional transfers will push them above
@ -210,7 +209,7 @@ the financial damage done to the customer).
{% if data.amount_arithmetic_inconsistencies|length() == 0 %} {% if data.amount_arithmetic_inconsistencies|length() == 0 %}
{\bf No arithmetic problems detected.} {\bf No arithmetic problems detected.}
{% else %} {% else %}
\begin{longtable}{p{4.5cm}|l|rl|rl} \begin{longtable}{p{3.5cm}|l|rl|rl}
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
\hline \hline \hline \hline
\endfirsthead \endfirsthead
@ -219,11 +218,11 @@ the financial damage done to the customer).
\hline \hline \hline \hline
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ {\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
\endfoot \endfoot
\hline \hline \hline
{\bf Total} & & \multicolumn{2}{l|}{ {\bf $\sum$ Deltas (Auditor-Exchange)} } &
{{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} & + {{ data.total_arithmetic_delta_plus.value }}.{{ data.total_arithmetic_delta_plus.fraction }} &
{{ data.total_arithmetic_delta_plus.currency }} & {{ data.total_arithmetic_delta_plus.currency }} &
{{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} & - {{ data.total_arithmetic_delta_minus.value }}.{{ data.total_arithmetic_delta_minus.fraction }} &
{{ data.total_arithmetic_delta_minus.currency }} \\ {{ data.total_arithmetic_delta_minus.currency }} \\
\caption{Arithmetic inconsistencies.} \caption{Arithmetic inconsistencies.}
\label{table:amount:arithmetic:inconsistencies} \label{table:amount:arithmetic:inconsistencies}
@ -328,17 +327,17 @@ any effects on its own balance, those entries are excluded from the total.
{% if data.coin_inconsistencies|length() == 0 %} {% if data.coin_inconsistencies|length() == 0 %}
{\bf All coin histories were unproblematic.} {\bf All coin histories were unproblematic.}
{% else %} {% else %}
\begin{longtable}{l|p{5.5cm}|rl|rl} \begin{longtable}{p{1.8cm}|p{3cm}|rl|rl}
{\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
\hline \hline \hline \hline
\endfirsthead \endfirsthead
{\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ \hline \hline {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline
\endhead \endhead
\hline \hline \hline \hline
{\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c|}{ {\bf Auditor}} \\ {\bf Operation} & {\bf Coin public key} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
\endfoot \endfoot
\hline \hline
{\bf Total} & & \multicolumn{2}{l|}{ $\sum$ {\bf Delta (Auditor-Exchange)} } &
{{ data.total_coin_delta_plus.value }}.{{ data.total_coin_delta_plus.fraction }} & {{ data.total_coin_delta_plus.value }}.{{ data.total_coin_delta_plus.fraction }} &
{{ data.total_coin_delta_plus.currency }} & {{ data.total_coin_delta_plus.currency }} &
- {{ data.total_coin_delta_minus.value }}.{{ data.total_coin_delta_minus.fraction }} & - {{ data.total_coin_delta_minus.value }}.{{ data.total_coin_delta_minus.fraction }} &
@ -593,7 +592,7 @@ have a clear financial impact.
\hline \hline
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
\caption{Other wire table issues found (by table and row).} \caption{Other wire table issues found (by table and row).}
\label{table:misc} \label{table:wire:misc}
\endlastfoot \endlastfoot
{% for item in wire.row_inconsistencies %} {% for item in wire.row_inconsistencies %}
\verb! {{ item.table }} ! & \verb! {{ item.table }} ! &
@ -687,7 +686,7 @@ impact.
{% if data.row_inconsistencies|length() == 0 %} {% if data.row_inconsistencies|length() == 0 %}
{\bf No row inconsistencies found.} {\bf No row inconsistencies found.}
{% else %} {% else %}
\begin{longtable}{p{1.5cm}|l|p{5.5}} \begin{longtable}{p{1.5cm}|l|p{5.5cm}}
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
\hline \hline \hline \hline
\endfirsthead \endfirsthead
@ -697,13 +696,13 @@ impact.
\hline \hline \hline \hline
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
\endfoot \endfoot
\hline \hline \hline
{\bf Table} & {\bf Row} & {\bf Diagnostic} \\ {\bf Table} & {\bf Row} & {\bf Diagnostic} \\
\caption{Other issues found (by table and row).} \caption{Other issues found (by table and row).}
\label{table:misc} \label{table:misc}
\endlastfoot \endlastfoot
{% for item in data.row_inconsistencies %} {% for item in data.row_inconsistencies %}
{{ item.table }} & \verb! {{ item.table }} ! &
{{ item.row }} & {{ item.row }} &
{{ item.diagnostic }} \\ \hline {{ item.diagnostic }} \\ \hline
{% endfor %} {% endfor %}
@ -728,12 +727,12 @@ reserve expired.
{\bf All expired reserves were closed.} {\bf All expired reserves were closed.}
{% else %} {% else %}
\begin{longtable}{p{1.5cm}|c|rl} \begin{longtable}{p{1.5cm}|c|rl}
{\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}} \\ \hline \hline {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}} \\ \hline \hline
\endfirsthead \endfirsthead
{\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}} \\ \hline \hline {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}} \\ \hline \hline
\endhead \endhead
\hline \hline \hline \hline
{\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c|}{ {\bf Balance}} {\bf Reserve} & {\bf Expired} & \multicolumn{2}{|c}{ {\bf Balance}}
\endfoot \endfoot
\hline \hline
{\bf Sum} & & {\bf Sum} & &
@ -854,7 +853,7 @@ This section lists issues with wire transfers related to timestamps.
\label{table:wire:bad_time} \label{table:wire:bad_time}
\endlastfoot \endlastfoot
{% for item in wire.row_minor_inconsistencies %} {% for item in wire.row_minor_inconsistencies %}
{\tt {{ item.table }} } & {{ item.row }} & {{ item.diagnostic }} \\ \hline \verb! {{ item.table }} ! & {{ item.row }} & {{ item.diagnostic }} \\ \hline
{% endfor %} {% endfor %}
\end{longtable} \end{longtable}
{% endif %} {% endif %}

View File

@ -391,7 +391,7 @@ report_amount_arithmetic_inconsistency (const char *operation,
"profitable", (json_int_t) profitable)); "profitable", (json_int_t) profitable));
if (0 != profitable) if (0 != profitable)
{ {
target = profitable target = (1 == profitable)
? &total_arithmetic_delta_plus ? &total_arithmetic_delta_plus
: &total_arithmetic_delta_minus; : &total_arithmetic_delta_minus;
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
@ -452,7 +452,7 @@ report_coin_arithmetic_inconsistency (const char *operation,
"profitable", (json_int_t) profitable)); "profitable", (json_int_t) profitable));
if (0 != profitable) if (0 != profitable)
{ {
target = profitable target = (1 == profitable)
? &total_coin_delta_plus ? &total_coin_delta_plus
: &total_coin_delta_minus; : &total_coin_delta_minus;
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
@ -1664,10 +1664,15 @@ struct WireFeeInfo
struct GNUNET_TIME_Absolute end_date; struct GNUNET_TIME_Absolute end_date;
/** /**
* How high is the fee. * How high is the wire fee.
*/ */
struct TALER_Amount wire_fee; struct TALER_Amount wire_fee;
/**
* How high is the closing fee.
*/
struct TALER_Amount closing_fee;
}; };
@ -1697,11 +1702,6 @@ struct AggregationContext
*/ */
struct WireFeeInfo *fee_tail; struct WireFeeInfo *fee_tail;
/**
* How much did we make in aggregation fees.
*/
struct TALER_Amount total_aggregation_feesX;
/** /**
* Final result status. * Final result status.
*/ */
@ -1798,7 +1798,6 @@ struct WireCheckContext
* @param dki denomination information about the coin * @param dki denomination information about the coin
* @param tl_head head of transaction history to verify * @param tl_head head of transaction history to verify
* @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant * @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
* @param[out] merchant_fees fees the exchange charged the merchant for the transaction(s)
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/ */
static int static int
@ -1807,14 +1806,16 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki, const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki,
const struct TALER_EXCHANGEDB_TransactionList *tl_head, const struct TALER_EXCHANGEDB_TransactionList *tl_head,
struct TALER_Amount *merchant_gain, struct TALER_Amount *merchant_gain)
struct TALER_Amount *merchant_fees)
{ {
struct TALER_Amount expenditures; struct TALER_Amount expenditures;
struct TALER_Amount refunds; struct TALER_Amount refunds;
struct TALER_Amount spent; struct TALER_Amount spent;
struct TALER_Amount value; struct TALER_Amount value;
struct TALER_Amount merchant_loss; struct TALER_Amount merchant_loss;
struct TALER_Amount merchant_delta;
const struct TALER_Amount *deposit_fee;
int refund_deposit_fee;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Checking transaction history of coin %s\n", "Checking transaction history of coin %s\n",
@ -1830,9 +1831,6 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency, TALER_amount_get_zero (currency,
merchant_gain)); merchant_gain));
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency,
merchant_fees));
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (currency, TALER_amount_get_zero (currency,
&merchant_loss)); &merchant_loss));
@ -1840,6 +1838,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
know the order, so instead of subtracting we compute positive know the order, so instead of subtracting we compute positive
(deposit, melt) and negative (refund) values separately here, (deposit, melt) and negative (refund) values separately here,
and then subtract the negative from the positive after the loop. */ and then subtract the negative from the positive after the loop. */
refund_deposit_fee = GNUNET_NO;
deposit_fee = NULL;
for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != tl;tl = tl->next) for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;NULL != tl;tl = tl->next)
{ {
const struct TALER_Amount *amount_with_fee; const struct TALER_Amount *amount_with_fee;
@ -1871,7 +1871,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
{ {
struct TALER_Amount amount_without_fee; struct TALER_Amount amount_without_fee;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_subtract (&amount_without_fee, TALER_amount_subtract (&amount_without_fee,
amount_with_fee, amount_with_fee,
fee)) fee))
@ -1890,14 +1890,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Detected applicable deposit of %s\n", "Detected applicable deposit of %s\n",
TALER_amount2s (&amount_without_fee)); TALER_amount2s (&amount_without_fee));
if (GNUNET_OK != deposit_fee = fee;
TALER_amount_add (merchant_fees,
merchant_fees,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
} }
/* Check that the fees given in the transaction list and in dki match */ /* Check that the fees given in the transaction list and in dki match */
TALER_amount_ntoh (&tmp, TALER_amount_ntoh (&tmp,
@ -1975,14 +1968,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Detected applicable refund of %s\n", "Detected applicable refund of %s\n",
TALER_amount2s (amount_with_fee)); TALER_amount2s (amount_with_fee));
if (GNUNET_OK != refund_deposit_fee = GNUNET_YES;
TALER_amount_add (merchant_fees,
merchant_fees,
fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
} }
/* Check that the fees given in the transaction list and in dki match */ /* Check that the fees given in the transaction list and in dki match */
TALER_amount_ntoh (&tmp, TALER_amount_ntoh (&tmp,
@ -2008,9 +1994,19 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
} }
break; break;
} }
} /* for 'tl' */ } /* for 'tl' */
if ( (GNUNET_YES == refund_deposit_fee) &&
(NULL != deposit_fee) )
{
/* We had a /deposit operation AND a /refund operation,
and should thus not charge the merchant the /deposit fee */
GNUNET_assert (GNUNET_OK ==
TALER_amount_add (merchant_gain,
merchant_gain,
deposit_fee));
}
/* Calculate total balance change, i.e. expenditures minus refunds */ /* Calculate total balance change, i.e. expenditures minus refunds */
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
TALER_amount_subtract (&spent, TALER_amount_subtract (&spent,
@ -2022,7 +2018,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
coin_pub, coin_pub,
&expenditures, &expenditures,
&refunds, &refunds,
0); 1);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
@ -2041,9 +2037,10 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
/* Finally, update @a merchant_gain by subtracting what he "lost" from refunds */ /* Finally, update @a merchant_gain by subtracting what he "lost"
from refunds */
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
TALER_amount_subtract (merchant_gain, TALER_amount_subtract (&merchant_delta,
merchant_gain, merchant_gain,
&merchant_loss)) &merchant_loss))
{ {
@ -2052,9 +2049,10 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
coin_pub, coin_pub,
merchant_gain, merchant_gain,
&merchant_loss, &merchant_loss,
0); 1);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
*merchant_gain = merchant_delta;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Coin %s contributes %s to contract %s\n", "Coin %s contributes %s to contract %s\n",
TALER_B2S (coin_pub), TALER_B2S (coin_pub),
@ -2077,7 +2075,8 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
* @param h_contract_terms which proposal was this payment about * @param h_contract_terms which proposal was this payment about
* @param coin_pub which public key was this payment about * @param coin_pub which public key was this payment about
* @param coin_value amount contributed by this coin in total (with fee) * @param coin_value amount contributed by this coin in total (with fee)
* @param coin_fee applicable fee for this coin * @param deposit_fee applicable deposit fee for this coin, actual
* fees charged may differ if coin was refunded
*/ */
static void static void
wire_transfer_information_cb (void *cls, wire_transfer_information_cb (void *cls,
@ -2089,12 +2088,11 @@ wire_transfer_information_cb (void *cls,
const struct GNUNET_HashCode *h_contract_terms, const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value, const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee) const struct TALER_Amount *deposit_fee)
{ {
struct WireCheckContext *wcc = cls; struct WireCheckContext *wcc = cls;
const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki; const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
struct TALER_Amount computed_value; struct TALER_Amount computed_value;
struct TALER_Amount computed_fees;
struct TALER_Amount coin_value_without_fee; struct TALER_Amount coin_value_without_fee;
struct TALER_EXCHANGEDB_TransactionList *tl; struct TALER_EXCHANGEDB_TransactionList *tl;
const struct TALER_CoinPublicInfo *coin; const struct TALER_CoinPublicInfo *coin;
@ -2156,18 +2154,17 @@ wire_transfer_information_cb (void *cls,
merchant_pub, merchant_pub,
dki, dki,
tl, tl,
&computed_value, &computed_value);
&computed_fees);
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
TALER_amount_subtract (&coin_value_without_fee, TALER_amount_subtract (&coin_value_without_fee,
coin_value, coin_value,
coin_fee)) deposit_fee))
{ {
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR; wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_amount_arithmetic_inconsistency ("aggregation (fee structure)", report_amount_arithmetic_inconsistency ("aggregation (fee structure)",
rowid, rowid,
coin_value, coin_value,
coin_fee, deposit_fee,
-1); -1);
return; return;
} }
@ -2182,17 +2179,6 @@ wire_transfer_information_cb (void *cls,
&computed_value, &computed_value,
-1); -1);
} }
if (0 !=
TALER_amount_cmp (&computed_fees,
coin_fee))
{
wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
report_amount_arithmetic_inconsistency ("aggregation (fee)",
rowid,
coin_fee,
&computed_fees,
1);
}
edb->free_coin_transaction_list (edb->cls, edb->free_coin_transaction_list (edb->cls,
tl); tl);
@ -2276,6 +2262,7 @@ get_wire_fee (struct AggregationContext *ac,
&wfi->start_date, &wfi->start_date,
&wfi->end_date, &wfi->end_date,
&wfi->wire_fee, &wfi->wire_fee,
&wfi->closing_fee,
&master_sig)) &master_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -2299,6 +2286,8 @@ get_wire_fee (struct AggregationContext *ac,
wp.end_date = GNUNET_TIME_absolute_hton (wfi->end_date); wp.end_date = GNUNET_TIME_absolute_hton (wfi->end_date);
TALER_amount_hton (&wp.wire_fee, TALER_amount_hton (&wp.wire_fee,
&wfi->wire_fee); &wfi->wire_fee);
TALER_amount_hton (&wp.closing_fee,
&wfi->closing_fee);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES, GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_WIRE_FEES,
&wp.purpose, &wp.purpose,

View File

@ -613,7 +613,7 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
if (GNUNET_OK != found) if (GNUNET_OK != found)
return create_bank_error (connection, return create_bank_error (connection,
MHD_HTTP_NOT_FOUND, MHD_HTTP_NOT_FOUND,
TALER_EC_BANK_REJECT_NOT_FOUND, TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND,
"transaction unknown"); "transaction unknown");
/* finally build regular response */ /* finally build regular response */
resp = MHD_create_response_from_buffer (0, resp = MHD_create_response_from_buffer (0,

View File

@ -454,7 +454,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle *exchange,
" s:o," /* merchant_pub */ " s:o," /* merchant_pub */
" s:o, s:o," /* refund_deadline, wire_deadline */ " s:o, s:o," /* refund_deadline, wire_deadline */
" s:o}", /* coin_sig */ " s:o}", /* coin_sig */
"f", TALER_JSON_from_amount (amount), "contribution", TALER_JSON_from_amount (amount),
"wire", wire_details, "wire", wire_details,
"H_wire", GNUNET_JSON_from_data_auto (&h_wire), "H_wire", GNUNET_JSON_from_data_auto (&h_wire),
"h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms), "h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms),

View File

@ -243,12 +243,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
TALER_EXCHANGE_RefundResultCallback cb, TALER_EXCHANGE_RefundResultCallback cb,
void *cb_cls) void *cb_cls)
{ {
struct TALER_EXCHANGE_RefundHandle *rh;
struct GNUNET_CURL_Context *ctx;
struct TALER_RefundRequestPS rr; struct TALER_RefundRequestPS rr;
struct TALER_MerchantSignatureP merchant_sig; struct TALER_MerchantSignatureP merchant_sig;
json_t *refund_obj;
CURL *eh;
GNUNET_assert (GNUNET_YES == GNUNET_assert (GNUNET_YES ==
MAH_handle_is_ready (exchange)); MAH_handle_is_ready (exchange));
@ -267,7 +263,68 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&rr.purpose, &rr.purpose,
&merchant_sig.eddsa_sig)); &merchant_sig.eddsa_sig));
refund_obj = json_pack ("{s:o, s:o," /* amount/fee */ return TALER_EXCHANGE_refund2 (exchange,
amount,
refund_fee,
h_contract_terms,
coin_pub,
rtransaction_id,
&rr.merchant,
&merchant_sig,
cb,
cb_cls);
}
/**
* Submit a refund request to the exchange and get the exchange's
* response. This API is used by a merchant. Note that
* while we return the response verbatim to the caller for further
* processing, we do already verify that the response is well-formed
* (i.e. that signatures included in the response are all valid). If
* the exchange's reply is not well-formed, we return an HTTP status code
* of zero to @a cb.
*
* The @a exchange must be ready to operate (i.e. have
* finished processing the /keys reply). If this check fails, we do
* NOT initiate the transaction with the exchange and instead return NULL.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
* this is needed as we may first do a partial refund and later a full refund. If both
* refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint
* refund requests different (as requests are idempotent and otherwise the 2nd refund might not work).
* @param merchant_pub public key of the merchant
* @param merchant_sig signature affirming the refund from the merchant
* @param cb the callback to call when a reply for this request is available
* @param cb_cls closure for the above callback
* @return a handle for this request; NULL if the inputs are invalid (i.e.
* signatures fail to verify). In this case, the callback is not called.
*/
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
TALER_EXCHANGE_RefundResultCallback cb,
void *cb_cls)
{
struct TALER_EXCHANGE_RefundHandle *rh;
struct GNUNET_CURL_Context *ctx;
json_t *refund_obj;
CURL *eh;
refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
" s:o, s:o," /* h_contract_terms, coin_pub */ " s:o, s:o," /* h_contract_terms, coin_pub */
" s:I," /* rtransaction id */ " s:I," /* rtransaction id */
" s:o, s:o}", /* merchant_pub, merchant_sig */ " s:o, s:o}", /* merchant_pub, merchant_sig */
@ -276,8 +333,8 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
"h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms), "h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms),
"coin_pub", GNUNET_JSON_from_data_auto (coin_pub), "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
"rtransaction_id", (json_int_t) rtransaction_id, "rtransaction_id", (json_int_t) rtransaction_id,
"merchant_pub", GNUNET_JSON_from_data_auto (&rr.merchant), "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub),
"merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig) "merchant_sig", GNUNET_JSON_from_data_auto (merchant_sig)
); );
if (NULL == refund_obj) if (NULL == refund_obj)
{ {
@ -294,7 +351,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
rh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND); rh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
rh->depconf.h_contract_terms = *h_contract_terms; rh->depconf.h_contract_terms = *h_contract_terms;
rh->depconf.coin_pub = *coin_pub; rh->depconf.coin_pub = *coin_pub;
rh->depconf.merchant = rr.merchant; rh->depconf.merchant = *merchant_pub;
rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id); rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id);
TALER_amount_hton (&rh->depconf.refund_amount, TALER_amount_hton (&rh->depconf.refund_amount,
amount); amount);

View File

@ -3562,6 +3562,65 @@ run (void *cls)
.details.refund.fee = "EUR:0.01", .details.refund.fee = "EUR:0.01",
.details.refund.deposit_ref = "deposit-refund-2", .details.refund.deposit_ref = "deposit-refund-2",
}, },
{ .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
.label = "check-empty-after-refund" },
/* Test refunded coins are never executed, even past
refund deadline */
{ .oc = OC_ADMIN_ADD_INCOMING,
.label = "create-reserve-rb",
.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.debit_account_no = 42,
.details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
.details.admin_add_incoming.auth_username = "user42",
.details.admin_add_incoming.auth_password = "pass42",
.details.admin_add_incoming.amount = "EUR:5.01" },
/* Run wirewatch to observe /admin/add/incoming */
{ .oc = OC_RUN_WIREWATCH,
.label = "wirewatch-3b" },
/* Withdraw a 5 EUR coin, at fee of 1 ct */
{ .oc = OC_WITHDRAW_SIGN,
.label = "withdraw-coin-rb",
.expected_response_code = MHD_HTTP_OK,
.details.reserve_withdraw.reserve_reference = "create-reserve-rb",
.details.reserve_withdraw.amount = "EUR:5" },
/* Spend 5 EUR of the 5 EUR coin (in full)
(merchant would receive EUR:4.99 due to 1 ct deposit fee) */
{ .oc = OC_DEPOSIT,
.label = "deposit-refund-1b",
.expected_response_code = MHD_HTTP_OK,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-rb",
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost:8082/\", \"account_number\":42 }",
.details.deposit.contract_terms = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:5\" } ] }",
.details.deposit.refund_deadline = { 0 },
},
{ .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-aai-3b",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:5.01",
.details.check_bank_transfer.account_debit = 42,
.details.check_bank_transfer.account_credit = 2
},
/* Trigger refund (before aggregator had a chance to execute
deposit, even though refund deadline was zero) */
{ .oc = OC_REFUND,
.label = "refund-ok-fast",
.expected_response_code = MHD_HTTP_OK,
.details.refund.amount = "EUR:5",
.details.refund.fee = "EUR:0.01",
.details.refund.deposit_ref = "deposit-refund-1b",
},
/* Run transfers. This will do the transfer as refund deadline
was 0, except of course because the refund succeeded, the
transfer should no longer be done. */
{ .oc = OC_RUN_AGGREGATOR,
.label = "run-aggregator-3b" },
/* check that aggregator didn't do anything, as expected */
{ .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
.label = "check-refund-fast-not-run" },
/* ************** End of refund API testing************* */ /* ************** End of refund API testing************* */

View File

@ -123,6 +123,11 @@ struct AggregationUnit
*/ */
struct GNUNET_HashCode h_wire; struct GNUNET_HashCode h_wire;
/**
* Hash code of contract we are currently looking into.
*/
const struct GNUNET_HashCode *h_contract;
/** /**
* Wire transfer identifier we use. * Wire transfer identifier we use.
*/ */
@ -374,6 +379,7 @@ update_fees (struct WirePlugin *wp,
p->start_date, p->start_date,
p->end_date, p->end_date,
&p->wire_fee, &p->wire_fee,
&p->closing_fee,
&p->master_sig); &p->master_sig);
if (qs < 0) if (qs < 0)
{ {
@ -567,6 +573,53 @@ exchange_serve_process_config ()
} }
/**
* Callback invoked with information about refunds applicable
* to a particular coin. Subtract refunded amount(s) from
* the aggregation unit's total amount.
*
* @param cls closure with a `struct AggregationUnit *`
* @param merchant_pub public key of merchant who authorized refund
* @param merchant_sig signature of merchant authorizing refund
* @param h_contract hash of contract being refunded
* @param rtransaction_id refund transaction ID
* @param amount_with_fee amount being refunded
* @param refund_fee fee the exchange keeps for the refund processing
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
refund_by_coin_cb (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
const struct GNUNET_HashCode *h_contract,
uint64_t rtransaction_id,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *refund_fee)
{
struct AggregationUnit *au = cls;
/* TODO: potential optimization: include these conditions
in the SELECT! */
if (0 != memcmp (merchant_pub,
&au->merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP)))
return GNUNET_OK; /* different merchant */
if (0 != memcmp (h_contract,
au->h_contract,
sizeof (struct GNUNET_HashCode)))
return GNUNET_OK; /* different contract */
if (GNUNET_OK !=
TALER_amount_subtract (&au->total_amount,
&au->total_amount,
amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/** /**
* Function called with details about deposits that have been made, * Function called with details about deposits that have been made,
* with the goal of executing the corresponding wire transaction. * with the goal of executing the corresponding wire transaction.
@ -609,6 +662,20 @@ deposit_cb (void *cls,
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
au->row_id = row_id; au->row_id = row_id;
au->h_contract = h_contract_terms;
qs = db_plugin->select_refunds_by_coin (db_plugin->cls,
au->session,
coin_pub,
&refund_by_coin_cb,
au);
au->h_contract = NULL;
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
GNUNET_assert (NULL == au->wire); GNUNET_assert (NULL == au->wire);
au->wire = json_incref ((json_t *) wire); au->wire = json_incref ((json_t *) wire);
if (GNUNET_OK != if (GNUNET_OK !=
@ -730,6 +797,20 @@ aggregate_cb (void *cls,
/* Skip this one, but keep going! */ /* Skip this one, but keep going! */
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
} }
au->h_contract = h_contract_terms;
qs = db_plugin->select_refunds_by_coin (db_plugin->cls,
au->session,
coin_pub,
&refund_by_coin_cb,
au);
au->h_contract = NULL;
if (0 > qs)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
return qs;
}
if (au->rows_offset >= aggregation_limit) if (au->rows_offset >= aggregation_limit)
{ {
/* Bug: we asked for at most #aggregation_limit results! */ /* Bug: we asked for at most #aggregation_limit results! */

View File

@ -149,6 +149,8 @@ deposit_transaction (void *cls,
{ {
struct TALER_Amount amount_without_fee; struct TALER_Amount amount_without_fee;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/deposit replay, accepting again!\n");
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_subtract (&amount_without_fee, TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee, &deposit->amount_with_fee,
@ -191,6 +193,8 @@ deposit_transaction (void *cls,
if (0 < TALER_amount_cmp (&spent, if (0 < TALER_amount_cmp (&spent,
&dc->value)) &dc->value))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Deposited coin has insufficient funds left!\n");
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection, *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS, TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
tl); tl);
@ -376,7 +380,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
struct GNUNET_HashCode my_h_wire; struct GNUNET_HashCode my_h_wire;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("wire", &wire), GNUNET_JSON_spec_json ("wire", &wire),
TALER_JSON_spec_amount ("f", &deposit.amount_with_fee), TALER_JSON_spec_amount ("contribution", &deposit.amount_with_fee),
TALER_JSON_spec_denomination_public_key ("denom_pub", &deposit.coin.denom_pub), TALER_JSON_spec_denomination_public_key ("denom_pub", &deposit.coin.denom_pub),
TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig), TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub), GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub),

View File

@ -285,7 +285,6 @@ refund_transaction (void *cls,
} }
/* check if we already send the money for the /deposit */ /* check if we already send the money for the /deposit */
// FIXME: DB API...
qs = TEH_plugin->test_deposit_done (TEH_plugin->cls, qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
session, session,
dep); dep);

View File

@ -352,6 +352,7 @@ track_transfer_transaction (void *cls,
struct GNUNET_TIME_Absolute wire_fee_start_date; struct GNUNET_TIME_Absolute wire_fee_start_date;
struct GNUNET_TIME_Absolute wire_fee_end_date; struct GNUNET_TIME_Absolute wire_fee_end_date;
struct TALER_MasterSignatureP wire_fee_master_sig; struct TALER_MasterSignatureP wire_fee_master_sig;
struct TALER_Amount closing_fee;
ctx->is_valid = GNUNET_NO; ctx->is_valid = GNUNET_NO;
ctx->wdd_head = NULL; ctx->wdd_head = NULL;
@ -393,6 +394,7 @@ track_transfer_transaction (void *cls,
&wire_fee_start_date, &wire_fee_start_date,
&wire_fee_end_date, &wire_fee_end_date,
&ctx->wire_fee, &ctx->wire_fee,
&closing_fee,
&wire_fee_master_sig); &wire_fee_master_sig);
if (0 >= qs) if (0 >= qs)
{ {

View File

@ -383,6 +383,9 @@ postgres_create_tables (void *cls)
",wire_fee_val INT8 NOT NULL" ",wire_fee_val INT8 NOT NULL"
",wire_fee_frac INT4 NOT NULL" ",wire_fee_frac INT4 NOT NULL"
",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",closing_fee_val INT8 NOT NULL"
",closing_fee_frac INT4 NOT NULL"
",closing_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)" ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
",PRIMARY KEY (wire_method, start_date)" /* this combo must be unique */ ",PRIMARY KEY (wire_method, start_date)" /* this combo must be unique */
");"), ");"),
@ -1170,6 +1173,9 @@ postgres_prepare (PGconn *db_conn)
",wire_fee_val" ",wire_fee_val"
",wire_fee_frac" ",wire_fee_frac"
",wire_fee_curr" ",wire_fee_curr"
",closing_fee_val"
",closing_fee_frac"
",closing_fee_curr"
",master_sig" ",master_sig"
" FROM wire_fee" " FROM wire_fee"
" WHERE wire_method=$1" " WHERE wire_method=$1"
@ -1185,10 +1191,13 @@ postgres_prepare (PGconn *db_conn)
",wire_fee_val" ",wire_fee_val"
",wire_fee_frac" ",wire_fee_frac"
",wire_fee_curr" ",wire_fee_curr"
",closing_fee_val"
",closing_fee_frac"
",closing_fee_curr"
",master_sig" ",master_sig"
") VALUES " ") VALUES "
"($1, $2, $3, $4, $5, $6, $7);", "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
7), 19),
/* Used in #postgres_store_wire_transfer_out */ /* Used in #postgres_store_wire_transfer_out */
GNUNET_PQ_make_prepare ("insert_wire_out", GNUNET_PQ_make_prepare ("insert_wire_out",
"INSERT INTO wire_out " "INSERT INTO wire_out "
@ -3032,6 +3041,128 @@ postgres_insert_refund (void *cls,
} }
/**
* Closure for #get_refunds_cb().
*/
struct SelectRefundContext
{
/**
* Function to call on each result.
*/
TALER_EXCHANGEDB_RefundCoinCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Set to #GNUNET_SYSERR on error.
*/
int status;
};
/**
* Function to be called with the results of a SELECT statement
* that has returned @a num_results results.
*
* @param cls closure of type `struct SelectRefundContext *`
* @param result the postgres result
* @param num_result the number of results in @a result
*/
static void
get_refunds_cb (void *cls,
PGresult *result,
unsigned int num_results)
{
struct SelectRefundContext *srctx = cls;
for (unsigned int i=0;i<num_results;i++)
{
struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_MerchantSignatureP merchant_sig;
struct GNUNET_HashCode h_contract;
uint64_t rtransaction_id;
struct TALER_Amount amount_with_fee;
struct TALER_Amount refund_fee;
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
&merchant_pub),
GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
&merchant_sig),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&h_contract),
GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
&rtransaction_id),
TALER_PQ_result_spec_amount ("amount_with_fee",
&amount_with_fee),
TALER_PQ_result_spec_amount ("fee_refund",
&refund_fee),
GNUNET_PQ_result_spec_end
};
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
i))
{
GNUNET_break (0);
srctx->status = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
srctx->cb (srctx->cb_cls,
&merchant_pub,
&merchant_sig,
&h_contract,
rtransaction_id,
&amount_with_fee,
&refund_fee))
return;
}
}
/**
* Select refunds by @a coin_pub.
*
* @param cls closure of plugin
* @param session database handle to use
* @param coin_pub coin to get refunds for
* @param cb function to call for each refund found
* @param cb_cls closure for @a cb
* @return query result status
*/
static enum GNUNET_DB_QueryStatus
postgres_select_refunds_by_coin (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
TALER_EXCHANGEDB_RefundCoinCallback cb,
void *cb_cls)
{
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (coin_pub),
GNUNET_PQ_query_param_end
};
struct SelectRefundContext srctx = {
.cb = cb,
.cb_cls = cb_cls,
.status = GNUNET_OK
};
qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
"get_refunds_by_coin",
params,
&get_refunds_cb,
&srctx);
if (GNUNET_SYSERR == srctx.status)
return GNUNET_DB_STATUS_HARD_ERROR;
return qs;
}
/** /**
* Lookup refresh melt commitment data under the given @a rc. * Lookup refresh melt commitment data under the given @a rc.
* *
@ -4234,6 +4365,7 @@ postgres_insert_aggregation_tracking (void *cls,
* @param[out] start_date when does the fee go into effect * @param[out] start_date when does the fee go into effect
* @param[out] end_date when does the fee end being valid * @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee * @param[out] wire_fee how high is the wire transfer fee
* @param[out] closing_fee how high is the closing fee
* @param[out] master_sig signature over the above by the exchange master key * @param[out] master_sig signature over the above by the exchange master key
* @return status of the transaction * @return status of the transaction
*/ */
@ -4245,6 +4377,7 @@ postgres_get_wire_fee (void *cls,
struct GNUNET_TIME_Absolute *start_date, struct GNUNET_TIME_Absolute *start_date,
struct GNUNET_TIME_Absolute *end_date, struct GNUNET_TIME_Absolute *end_date,
struct TALER_Amount *wire_fee, struct TALER_Amount *wire_fee,
struct TALER_Amount *closing_fee,
struct TALER_MasterSignatureP *master_sig) struct TALER_MasterSignatureP *master_sig)
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
@ -4256,6 +4389,7 @@ postgres_get_wire_fee (void *cls,
TALER_PQ_result_spec_absolute_time ("start_date", start_date), TALER_PQ_result_spec_absolute_time ("start_date", start_date),
TALER_PQ_result_spec_absolute_time ("end_date", end_date), TALER_PQ_result_spec_absolute_time ("end_date", end_date),
TALER_PQ_result_spec_amount ("wire_fee", wire_fee), TALER_PQ_result_spec_amount ("wire_fee", wire_fee),
TALER_PQ_result_spec_amount ("closing_fee", closing_fee),
GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig), GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -4276,6 +4410,7 @@ postgres_get_wire_fee (void *cls,
* @param start_date when does the fee go into effect * @param start_date when does the fee go into effect
* @param end_date when does the fee end being valid * @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee * @param wire_fee how high is the wire transfer fee
* @param closing_fee how high is the closing fee
* @param master_sig signature over the above by the exchange master key * @param master_sig signature over the above by the exchange master key
* @return transaction status code * @return transaction status code
*/ */
@ -4286,6 +4421,7 @@ postgres_insert_wire_fee (void *cls,
struct GNUNET_TIME_Absolute start_date, struct GNUNET_TIME_Absolute start_date,
struct GNUNET_TIME_Absolute end_date, struct GNUNET_TIME_Absolute end_date,
const struct TALER_Amount *wire_fee, const struct TALER_Amount *wire_fee,
const struct TALER_Amount *closing_fee,
const struct TALER_MasterSignatureP *master_sig) const struct TALER_MasterSignatureP *master_sig)
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
@ -4293,10 +4429,12 @@ postgres_insert_wire_fee (void *cls,
TALER_PQ_query_param_absolute_time (&start_date), TALER_PQ_query_param_absolute_time (&start_date),
TALER_PQ_query_param_absolute_time (&end_date), TALER_PQ_query_param_absolute_time (&end_date),
TALER_PQ_query_param_amount (wire_fee), TALER_PQ_query_param_amount (wire_fee),
TALER_PQ_query_param_amount (closing_fee),
GNUNET_PQ_query_param_auto_from_type (master_sig), GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct TALER_Amount wf; struct TALER_Amount wf;
struct TALER_Amount cf;
struct TALER_MasterSignatureP sig; struct TALER_MasterSignatureP sig;
struct GNUNET_TIME_Absolute sd; struct GNUNET_TIME_Absolute sd;
struct GNUNET_TIME_Absolute ed; struct GNUNET_TIME_Absolute ed;
@ -4309,6 +4447,7 @@ postgres_insert_wire_fee (void *cls,
&sd, &sd,
&ed, &ed,
&wf, &wf,
&cf,
&sig); &sig);
if (qs < 0) if (qs < 0)
return qs; return qs;
@ -4327,6 +4466,12 @@ postgres_insert_wire_fee (void *cls,
GNUNET_break (0); GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if (0 != TALER_amount_cmp (closing_fee,
&cf))
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if ( (sd.abs_value_us != start_date.abs_value_us) || if ( (sd.abs_value_us != start_date.abs_value_us) ||
(ed.abs_value_us != end_date.abs_value_us) ) (ed.abs_value_us != end_date.abs_value_us) )
{ {
@ -6236,6 +6381,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits; plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
plugin->insert_deposit = &postgres_insert_deposit; plugin->insert_deposit = &postgres_insert_deposit;
plugin->insert_refund = &postgres_insert_refund; plugin->insert_refund = &postgres_insert_refund;
plugin->select_refunds_by_coin = &postgres_select_refunds_by_coin;
plugin->insert_melt = &postgres_insert_melt; plugin->insert_melt = &postgres_insert_melt;
plugin->get_melt = &postgres_get_melt; plugin->get_melt = &postgres_get_melt;
plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal; plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;

View File

@ -1062,10 +1062,12 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
struct GNUNET_TIME_Absolute start_date; struct GNUNET_TIME_Absolute start_date;
struct GNUNET_TIME_Absolute end_date; struct GNUNET_TIME_Absolute end_date;
struct TALER_Amount wire_fee; struct TALER_Amount wire_fee;
struct TALER_Amount closing_fee;
struct TALER_MasterSignatureP master_sig; struct TALER_MasterSignatureP master_sig;
struct GNUNET_TIME_Absolute sd; struct GNUNET_TIME_Absolute sd;
struct GNUNET_TIME_Absolute ed; struct GNUNET_TIME_Absolute ed;
struct TALER_Amount fee; struct TALER_Amount fee;
struct TALER_Amount fee2;
struct TALER_MasterSignatureP ms; struct TALER_MasterSignatureP ms;
start_date = GNUNET_TIME_absolute_get (); start_date = GNUNET_TIME_absolute_get ();
@ -1075,6 +1077,9 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.424242", TALER_string_to_amount (CURRENCY ":1.424242",
&wire_fee)); &wire_fee));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":2.424242",
&closing_fee));
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&master_sig, &master_sig,
sizeof (master_sig)); sizeof (master_sig));
@ -1085,6 +1090,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
start_date, start_date,
end_date, end_date,
&wire_fee, &wire_fee,
&closing_fee,
&master_sig)) &master_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1097,6 +1103,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
start_date, start_date,
end_date, end_date,
&wire_fee, &wire_fee,
&closing_fee,
&master_sig)) &master_sig))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1112,6 +1119,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
&sd, &sd,
&ed, &ed,
&fee, &fee,
&fee2,
&ms)) &ms))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1125,6 +1133,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
&sd, &sd,
&ed, &ed,
&fee, &fee,
&fee2,
&ms)) &ms))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -1134,6 +1143,8 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
(ed.abs_value_us != end_date.abs_value_us) || (ed.abs_value_us != end_date.abs_value_us) ||
(0 != TALER_amount_cmp (&fee, (0 != TALER_amount_cmp (&fee,
&wire_fee)) || &wire_fee)) ||
(0 != TALER_amount_cmp (&fee2,
&closing_fee)) ||
(0 != memcmp (&ms, (0 != memcmp (&ms,
&master_sig, &master_sig,
sizeof (ms))) ) sizeof (ms))) )
@ -1393,6 +1404,72 @@ wire_missing_cb (void *cls,
} }
/**
* Callback invoked with information about refunds applicable
* to a particular coin.
*
* @param cls closure with the `struct TALER_EXCHANGEDB_Refund *` we expect to get
* @param merchant_pub public key of merchant who authorized refund
* @param merchant_sig signature of merchant authorizing refund
* @param h_contract hash of contract being refunded
* @param rtransaction_id refund transaction ID
* @param amount_with_fee amount being refunded
* @param refund_fee fee the exchange keeps for the refund processing
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
check_refund_cb (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
const struct GNUNET_HashCode *h_contract,
uint64_t rtransaction_id,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *refund_fee)
{
const struct TALER_EXCHANGEDB_Refund *refund = cls;
if (0 != memcmp (merchant_pub,
&refund->merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP)))
{
GNUNET_break (0);
result = 66;
}
if (0 != memcmp (merchant_sig,
&refund->merchant_sig,
sizeof (struct TALER_MerchantSignatureP)))
{
GNUNET_break (0);
result = 66;
}
if (0 != memcmp (h_contract,
&refund->h_contract_terms,
sizeof (struct GNUNET_HashCode)))
{
GNUNET_break (0);
result = 66;
}
if (rtransaction_id != refund->rtransaction_id)
{
GNUNET_break (0);
result = 66;
}
if (0 != TALER_amount_cmp (amount_with_fee,
&refund->refund_amount))
{
GNUNET_break (0);
result = 66;
}
if (0 != TALER_amount_cmp (refund_fee,
&refund->refund_fee))
{
GNUNET_break (0);
result = 66;
}
return GNUNET_OK;
}
/** /**
* Main function that will be run by the scheduler. * Main function that will be run by the scheduler.
* *
@ -1890,14 +1967,20 @@ run (void *cls)
refund.merchant_pub = deposit.merchant_pub; refund.merchant_pub = deposit.merchant_pub;
RND_BLK (&refund.merchant_sig); RND_BLK (&refund.merchant_sig);
refund.h_contract_terms = deposit.h_contract_terms; refund.h_contract_terms = deposit.h_contract_terms;
refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX); refund.rtransaction_id = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
UINT64_MAX);
refund.refund_amount = deposit.amount_with_fee; refund.refund_amount = deposit.amount_with_fee;
refund.refund_fee = fee_refund; refund.refund_fee = fee_refund;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_refund (plugin->cls, plugin->insert_refund (plugin->cls,
session, session,
&refund)); &refund));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->select_refunds_by_coin (plugin->cls,
session,
&refund.coin.coin_pub,
&check_refund_cb,
&refund));
/* test payback / revocation */ /* test payback / revocation */
RND_BLK (&master_sig); RND_BLK (&master_sig);
@ -2127,9 +2210,11 @@ main (int argc,
NULL); NULL);
plugin_name++; plugin_name++;
(void) GNUNET_asprintf (&testname, (void) GNUNET_asprintf (&testname,
"test-exchange-db-%s", plugin_name); "test-exchange-db-%s",
plugin_name);
(void) GNUNET_asprintf (&config_filename, (void) GNUNET_asprintf (&config_filename,
"%s.conf", testname); "%s.conf",
testname);
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,
@ -2140,7 +2225,8 @@ main (int argc,
GNUNET_free (testname); GNUNET_free (testname);
return 2; return 2;
} }
GNUNET_SCHEDULER_run (&run, cfg); GNUNET_SCHEDULER_run (&run,
cfg);
GNUNET_CONFIGURATION_destroy (cfg); GNUNET_CONFIGURATION_destroy (cfg);
GNUNET_free (config_filename); GNUNET_free (config_filename);
GNUNET_free (testname); GNUNET_free (testname);

View File

@ -86,6 +86,9 @@ main (int argc,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:1.0", TALER_string_to_amount ("EUR:1.0",
&af->wire_fee)); &af->wire_fee));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:1.0",
&af->closing_fee));
sign_af (af, sign_af (af,
priv); priv);
n = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees); n = GNUNET_new (struct TALER_EXCHANGEDB_AggregateFees);
@ -94,6 +97,9 @@ main (int argc,
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:0.1", TALER_string_to_amount ("EUR:0.1",
&n->wire_fee)); &n->wire_fee));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:0.1",
&n->closing_fee));
sign_af (n, sign_af (n,
priv); priv);
af->next = n; af->next = n;

View File

@ -1001,6 +1001,7 @@ enum TALER_ErrorCode
TALER_EC_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE = 2114, TALER_EC_PAY_REFUND_DEADLINE_PAST_WIRE_TRANSFER_DEADLINE = 2114,
/** /**
* The request fails to provide coins for the payment. * The request fails to provide coins for the payment.
* This response is provided with HTTP status code * This response is provided with HTTP status code
* MHD_HTTP_BAD_REQUEST. * MHD_HTTP_BAD_REQUEST.
@ -1503,67 +1504,84 @@ enum TALER_ErrorCode
/* *************** Taler BANK/FAKEBANK error codes *************** */ /* *************** Taler BANK/FAKEBANK error codes *************** */
/** /**
* Authentication failed for an unspecified request. * The request cannot be served because the client failed to
* To return when the view name is not available, or * login. To be returned along HTTP 401 Unauthorized.
* no specific error code is defined yet.
*/ */
TALER_EC_BANK_NOT_AUTHORIZED = 5000, TALER_EC_BANK_REJECT_LOGIN_FAILED = 5312,
/** /**
* The bank could not find the bank account specified * The transaction cannot be rejected becasue it does not exist
* in the request. Returned with a status code of MHD_HTTP_NOT_FOUND. * at the bank. To be returned along HTTP 404 Not Found.
*/ */
TALER_EC_BANK_UNKNOWN_ACCOUNT = 5001, TALER_EC_BANK_REJECT_TRANSACTION_NOT_FOUND = 5301,
/** /**
* Authentication failed for the /admin/add/incoming request. * The client does not own the account credited by the transaction
* Returned with a status code of MHD_HTTP_FORBIDDEN. * which is to be rejected, so it has no rights do reject it. To be
* returned along HTTP 403 Forbidden.
*/ */
TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 5100, TALER_EC_BANK_REJECT_NO_RIGHTS = 5313,
/** /**
* The wire transfer cannot be done because the debitor would * The POSTed JSON at /reject was invalid. To be returned along
* reach a unallowed debit. * HTTP 400 Bad Request.
*/ */
TALER_EC_BANK_TRANSFER_DEBIT = 5101, TALER_EC_BANK_REJECT_JSON_INVALID = 5306,
/** /**
* The wire transfer cannot be done because the credit and * A URL parameter for /history was missing. To be returned along
* debit account are the same. * HTTP 400 Bad Request.
*/ */
TALER_EC_BANK_TRANSFER_SAME_ACCOUNT = 5102, TALER_EC_BANK_HISTORY_PARAMETER_MISSING = 5208,
/** /**
* Authentication failed for the /history request. * A URL parameter for /history was malformed. To be returned along
* Returned with a status code of MHD_HTTP_FORBIDDEN. * HTTP 400 Bad Request.
*/ */
TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 5200, TALER_EC_BANK_HISTORY_PARAMETER_MALFORMED = 5209,
/** /**
* The bank library had trouble obtaining a valid * The client failed to login for /history. To be returned along
* HTTP response. * HTTP 401 Unauthorized.
* Returned with a status code of 0.
*/ */
TALER_EC_BANK_HISTORY_HTTP_FAILURE = 5201, TALER_EC_BANK_HISTORY_LOGIN_FAILED = 5212,
/** /**
* The bank could not find the wire transfer that was supposed to * The bank had trouble obtaining a valid HTTP response. To be returned
* be rejected. * along status code 0.
* Returned with a status code of MHD_HTTP_NOT_FOUND.
*/ */
TALER_EC_BANK_REJECT_NOT_FOUND = 5300, TALER_EC_BANK_HISTORY_HTTP_FAILURE = 5213,
/** /**
* Authentication failed for the /reject request. * The debit account for /admin/add/incoming is not known to the
* Returned with a status code of MHD_HTTP_FORBIDDEN. * bank. To be returned along HTTP 404 Not Found.
*/ */
TALER_EC_BANK_REJECT_NOT_AUTHORIZED = 5301, TALER_EC_BANK_ADD_INCOMING_UNKNOWN_ACCOUNT = 5100,
/** /**
* The client wants to reject a transaction where they are * The client specified the same bank account for both the credit
* not the _credit_ party, impossible! * and the debit account. The bank will not accomplish this operation.
* To be returned along HTTP 403 Forbidden.
*/ */
TALER_EC_BANK_REJECT_NO_RIGHTS = 5302, TALER_EC_BANK_ADD_INCOMING_SAME_ACCOUNT = 5102,
/**
* The operation would put the client in a debit situation which is
* forbidden to them. To return along HTTP 403 Forbidden.
*/
TALER_EC_BANK_ADD_INCOMING_UNALLOWED_DEBIT = 5103,
/**
* The client POSTed an invalid JSON. To be returned along HTTP
* 400 Bad Request.
*/
TALER_EC_BANK_ADD_INCOMING_JSON_INVALID = 5106,
/**
* The client failed to login for /admin/add/incoming. To be returned
* along HTTP 401 Unauthorized.
*/
TALER_EC_BANK_ADD_INCOMING_LOGIN_FAILED = 5112,
/** /**
* End of error code range. * End of error code range.

View File

@ -716,6 +716,50 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
void *cb_cls); void *cb_cls);
/**
* Submit a refund request to the exchange and get the exchange's
* response. This API is used by a merchant. Note that
* while we return the response verbatim to the caller for further
* processing, we do already verify that the response is well-formed
* (i.e. that signatures included in the response are all valid). If
* the exchange's reply is not well-formed, we return an HTTP status code
* of zero to @a cb.
*
* The @a exchange must be ready to operate (i.e. have
* finished processing the /keys reply). If this check fails, we do
* NOT initiate the transaction with the exchange and instead return NULL.
*
* @param exchange the exchange handle; the exchange must be ready to operate
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
* this is needed as we may first do a partial refund and later a full refund. If both
* refunds are also over the same amount, we need the @a rtransaction_id to make the disjoint
* refund requests different (as requests are idempotent and otherwise the 2nd refund might not work).
* @param merchant_pub public key of the merchant
* @param merchant_sig signature affirming the refund from the merchant
* @param cb the callback to call when a reply for this request is available
* @param cb_cls closure for the above callback
* @return a handle for this request; NULL if the inputs are invalid (i.e.
* signatures fail to verify). In this case, the callback is not called.
*/
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
TALER_EXCHANGE_RefundResultCallback cb,
void *cb_cls);
/** /**
* Cancel a refund permission request. This function cannot be used * Cancel a refund permission request. This function cannot be used
* on a request handle if a response is already served for it. If * on a request handle if a response is already served for it. If

View File

@ -732,6 +732,29 @@ typedef int
const struct TALER_RefreshCommitmentP *rc); const struct TALER_RefreshCommitmentP *rc);
/**
* Callback invoked with information about refunds applicable
* to a particular coin.
*
* @param cls closure
* @param merchant_pub public key of merchant who authorized refund
* @param merchant_sig signature of merchant authorizing refund
* @param h_contract hash of contract being refunded
* @param rtransaction_id refund transaction ID
* @param amount_with_fee amount being refunded
* @param refund_fee fee the exchange keeps for the refund processing
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef int
(*TALER_EXCHANGEDB_RefundCoinCallback)(void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_MerchantSignatureP *merchant_sig,
const struct GNUNET_HashCode *h_contract,
uint64_t rtransaction_id,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *refund_fee);
/** /**
* Information about a coin that was revealed to the exchange * Information about a coin that was revealed to the exchange
* during /refresh/reveal. * during /refresh/reveal.
@ -1358,6 +1381,23 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_EXCHANGEDB_Session *session, struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Refund *refund); const struct TALER_EXCHANGEDB_Refund *refund);
/**
* Select refunds by @a coin_pub.
*
* @param cls closure of plugin
* @param session database handle to use
* @param coin_pub coin to get refunds for
* @param cb function to call for each refund found
* @param cb_cls closure for @a cb
* @return query result status
*/
enum GNUNET_DB_QueryStatus
(*select_refunds_by_coin)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
TALER_EXCHANGEDB_RefundCoinCallback cb,
void *cb_cls);
/** /**
* Mark a deposit as tiny, thereby declaring that it cannot be * Mark a deposit as tiny, thereby declaring that it cannot be
@ -1658,6 +1698,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param start_date when does the fee go into effect * @param start_date when does the fee go into effect
* @param end_date when does the fee end being valid * @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee * @param wire_fee how high is the wire transfer fee
* @param closing_fee how high is the closing fee
* @param master_sig signature over the above by the exchange master key * @param master_sig signature over the above by the exchange master key
* @return transaction status code * @return transaction status code
*/ */
@ -1668,6 +1709,7 @@ struct TALER_EXCHANGEDB_Plugin
struct GNUNET_TIME_Absolute start_date, struct GNUNET_TIME_Absolute start_date,
struct GNUNET_TIME_Absolute end_date, struct GNUNET_TIME_Absolute end_date,
const struct TALER_Amount *wire_fee, const struct TALER_Amount *wire_fee,
const struct TALER_Amount *closing_fee,
const struct TALER_MasterSignatureP *master_sig); const struct TALER_MasterSignatureP *master_sig);
@ -1681,6 +1723,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param[out] start_date when does the fee go into effect * @param[out] start_date when does the fee go into effect
* @param[out] end_date when does the fee end being valid * @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee * @param[out] wire_fee how high is the wire transfer fee
* @param[out] closing_fee how high is the closing fee
* @param[out] master_sig signature over the above by the exchange master key * @param[out] master_sig signature over the above by the exchange master key
* @return query status of the transaction * @return query status of the transaction
*/ */
@ -1692,6 +1735,7 @@ struct TALER_EXCHANGEDB_Plugin
struct GNUNET_TIME_Absolute *start_date, struct GNUNET_TIME_Absolute *start_date,
struct GNUNET_TIME_Absolute *end_date, struct GNUNET_TIME_Absolute *end_date,
struct TALER_Amount *wire_fee, struct TALER_Amount *wire_fee,
struct TALER_Amount *closing_fee,
struct TALER_MasterSignatureP *master_sig); struct TALER_MasterSignatureP *master_sig);

View File

@ -178,6 +178,12 @@
*/ */
#define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105 #define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105
/**
* Signature where the merchant confirms that the user replayed
* a payment for a browser session.
*/
#define TALER_SIGNATURE_MERCHANT_PAY_SESSION 1106
/*********************/ /*********************/
/* Wallet signatures */ /* Wallet signatures */
@ -1291,6 +1297,31 @@ struct TALER_MerchantRefundConfirmationPS
}; };
/**
* Used by the merchant to confirm to the frontend that
* the user did a payment replay with the current browser session.
*/
struct TALER_MerchantPaySessionSigPS
{
/**
* Set to #TALER_SIGNATURE_MERCHANT_PAY_SESSION.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Hashed order id.
* Hashed without the 0-termination.
*/
struct GNUNET_HashCode h_order_id GNUNET_PACKED;
/**
* Hashed session id.
* Hashed without the 0-termination.
*/
struct GNUNET_HashCode h_session_id GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END GNUNET_NETWORK_STRUCT_END
#endif #endif

View File

@ -207,6 +207,8 @@ void
TALER_amount_hton (struct TALER_AmountNBO *res, TALER_amount_hton (struct TALER_AmountNBO *res,
const struct TALER_Amount *d) const struct TALER_Amount *d)
{ {
GNUNET_assert (GNUNET_YES ==
TALER_amount_is_valid (d));
res->value = GNUNET_htonll (d->value); res->value = GNUNET_htonll (d->value);
res->fraction = htonl (d->fraction); res->fraction = htonl (d->fraction);
memcpy (res->currency, memcpy (res->currency,
@ -230,6 +232,8 @@ TALER_amount_ntoh (struct TALER_Amount *res,
memcpy (res->currency, memcpy (res->currency,
dn->currency, dn->currency,
TALER_CURRENCY_LEN); TALER_CURRENCY_LEN);
GNUNET_assert (GNUNET_YES ==
TALER_amount_is_valid (res));
} }