implementing #4929

This commit is contained in:
Christian Grothoff 2017-03-04 16:49:33 +01:00
parent f406f96129
commit 6ab67a3a76
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
15 changed files with 573 additions and 58 deletions

View File

@ -87,12 +87,14 @@ check_track_transfer_response_ok (struct TALER_EXCHANGE_TrackTransferHandle *wdh
struct GNUNET_HashCode h_wire; struct GNUNET_HashCode h_wire;
struct GNUNET_TIME_Absolute exec_time; struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount total_amount; struct TALER_Amount total_amount;
struct TALER_Amount wire_fee;
struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantPublicKeyP merchant_pub;
unsigned int num_details; unsigned int num_details;
struct TALER_ExchangePublicKeyP exchange_pub; struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig; struct TALER_ExchangeSignatureP exchange_sig;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("total", &total_amount), TALER_JSON_spec_amount ("total", &total_amount),
TALER_JSON_spec_amount ("wire_fee", &wire_fee),
GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub), GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
GNUNET_JSON_spec_fixed_auto ("H_wire", &h_wire), GNUNET_JSON_spec_fixed_auto ("H_wire", &h_wire),
GNUNET_JSON_spec_absolute_time ("execution_time", &exec_time), GNUNET_JSON_spec_absolute_time ("execution_time", &exec_time),
@ -158,6 +160,8 @@ check_track_transfer_response_ok (struct TALER_EXCHANGE_TrackTransferHandle *wdh
wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS)); wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
TALER_amount_hton (&wdp.total, TALER_amount_hton (&wdp.total,
&total_amount); &total_amount);
TALER_amount_hton (&wdp.wire_fee,
&wire_fee);
wdp.merchant_pub = merchant_pub; wdp.merchant_pub = merchant_pub;
wdp.h_wire = h_wire; wdp.h_wire = h_wire;
GNUNET_CRYPTO_hash_context_finish (hash_context, GNUNET_CRYPTO_hash_context_finish (hash_context,
@ -186,6 +190,7 @@ check_track_transfer_response_ok (struct TALER_EXCHANGE_TrackTransferHandle *wdh
&h_wire, &h_wire,
exec_time, exec_time,
&total_amount, &total_amount,
&wire_fee,
num_details, num_details,
details); details);
} }
@ -257,6 +262,7 @@ handle_track_transfer_finished (void *cls,
NULL, NULL,
GNUNET_TIME_UNIT_ZERO_ABS, GNUNET_TIME_UNIT_ZERO_ABS,
NULL, NULL,
NULL,
0, NULL); 0, NULL);
TALER_EXCHANGE_track_transfer_cancel (wdh); TALER_EXCHANGE_track_transfer_cancel (wdh);
} }

View File

@ -540,6 +540,12 @@ struct Command
*/ */
const char *total_amount_expected; const char *total_amount_expected;
/**
* What is the expected wire fee? Only used if
* @e expected_response_code was #MHD_HTTP_OK.
*/
const char *wire_fee_expected;
/* TODO: may want to add list of deposits we expected /* TODO: may want to add list of deposits we expected
to see aggregated here in the future. */ to see aggregated here in the future. */
@ -1417,6 +1423,7 @@ wire_cb (void *cls,
* @param execution_time time when the exchange claims to have performed the wire transfer * @param execution_time time when the exchange claims to have performed the wire transfer
* @param total_amount total amount of the wire transfer, or NULL if the exchange could * @param total_amount total amount of the wire transfer, or NULL if the exchange could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param wire_fee wire fee that was charged by the exchange
* @param details_length length of the @a details array * @param details_length length of the @a details array
* @param details array with details about the combined transactions * @param details array with details about the combined transactions
*/ */
@ -1429,6 +1436,7 @@ wire_deposits_cb (void *cls,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute execution_time, struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *total_amount, const struct TALER_Amount *total_amount,
const struct TALER_Amount *wire_fee,
unsigned int details_length, unsigned int details_length,
const struct TALER_TrackTransferDetails *details) const struct TALER_TrackTransferDetails *details)
{ {
@ -1469,6 +1477,24 @@ wire_deposits_cb (void *cls,
fail (is); fail (is);
return; return;
} }
if (GNUNET_OK !=
TALER_string_to_amount (cmd->details.wire_deposits.wire_fee_expected,
&expected_amount))
{
GNUNET_break (0);
fail (is);
return;
}
if (0 != TALER_amount_cmp (wire_fee,
&expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fee missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
ref = find_command (is, ref = find_command (is,
cmd->details.wire_deposits.wtid_ref); cmd->details.wire_deposits.wtid_ref);
GNUNET_assert (NULL != ref); GNUNET_assert (NULL != ref);
@ -2923,28 +2949,28 @@ run (void *cls)
{ .oc = OC_CHECK_BANK_TRANSFER, { .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-499c", .label = "check_bank_transfer-499c",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/", .details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:4.99", .details.check_bank_transfer.amount = "EUR:4.98",
.details.check_bank_transfer.account_debit = 2, .details.check_bank_transfer.account_debit = 2,
.details.check_bank_transfer.account_credit = 42 .details.check_bank_transfer.account_credit = 42
}, },
{ .oc = OC_CHECK_BANK_TRANSFER, { .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-99c1", .label = "check_bank_transfer-99c1",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/", .details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:0.99", .details.check_bank_transfer.amount = "EUR:0.98",
.details.check_bank_transfer.account_debit = 2, .details.check_bank_transfer.account_debit = 2,
.details.check_bank_transfer.account_credit = 42 .details.check_bank_transfer.account_credit = 42
}, },
{ .oc = OC_CHECK_BANK_TRANSFER, { .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-99c2", .label = "check_bank_transfer-99c2",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/", .details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:0.99", .details.check_bank_transfer.amount = "EUR:0.98",
.details.check_bank_transfer.account_debit = 2, .details.check_bank_transfer.account_debit = 2,
.details.check_bank_transfer.account_credit = 42 .details.check_bank_transfer.account_credit = 42
}, },
{ .oc = OC_CHECK_BANK_TRANSFER, { .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-9c", .label = "check_bank_transfer-9c",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/", .details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:0.09", .details.check_bank_transfer.amount = "EUR:0.08",
.details.check_bank_transfer.account_debit = 2, .details.check_bank_transfer.account_debit = 2,
.details.check_bank_transfer.account_credit = 43 .details.check_bank_transfer.account_credit = 43
}, },
@ -2959,16 +2985,18 @@ run (void *cls)
.details.deposit_wtid.bank_transfer_ref = "check_bank_transfer-499c" }, .details.deposit_wtid.bank_transfer_ref = "check_bank_transfer-499c" },
{ .oc = OC_WIRE_DEPOSITS, { .oc = OC_WIRE_DEPOSITS,
.label = "wire-deposits-sucess-bank", .label = "wire-deposits-success-bank",
.expected_response_code = MHD_HTTP_OK, .expected_response_code = MHD_HTTP_OK,
.details.wire_deposits.wtid_ref = "check_bank_transfer-99c1", .details.wire_deposits.wtid_ref = "check_bank_transfer-99c1",
.details.wire_deposits.total_amount_expected = "EUR:0.99" }, .details.wire_deposits.total_amount_expected = "EUR:0.98",
.details.wire_deposits.wire_fee_expected = "EUR:0.01" },
{ .oc = OC_WIRE_DEPOSITS, { .oc = OC_WIRE_DEPOSITS,
.label = "wire-deposits-sucess-wtid", .label = "wire-deposits-success-wtid",
.expected_response_code = MHD_HTTP_OK, .expected_response_code = MHD_HTTP_OK,
.details.wire_deposits.wtid_ref = "deposit-wtid-ok", .details.wire_deposits.wtid_ref = "deposit-wtid-ok",
.details.wire_deposits.total_amount_expected = "EUR:4.99" }, .details.wire_deposits.total_amount_expected = "EUR:4.98",
.details.wire_deposits.wire_fee_expected = "EUR:0.01" },
/* ************** End of tracking API testing************* */ /* ************** End of tracking API testing************* */
@ -3030,7 +3058,7 @@ run (void *cls)
{ .oc = OC_CHECK_BANK_TRANSFER, { .oc = OC_CHECK_BANK_TRANSFER,
.label = "check_bank_transfer-pre-refund", .label = "check_bank_transfer-pre-refund",
.details.check_bank_transfer.exchange_base_url = "https://exchange.com/", .details.check_bank_transfer.exchange_base_url = "https://exchange.com/",
.details.check_bank_transfer.amount = "EUR:4.98", .details.check_bank_transfer.amount = "EUR:4.97",
.details.check_bank_transfer.account_debit = 2, .details.check_bank_transfer.account_debit = 2,
.details.check_bank_transfer.account_credit = 42 .details.check_bank_transfer.account_credit = 42
}, },

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016 GNUnet e.V. Copyright (C) 2016, 2017 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software terms of the GNU Affero General Public License as published by the Free Software
@ -53,6 +53,12 @@ struct WirePlugin
* Name of the plugin. * Name of the plugin.
*/ */
char *type; char *type;
/**
* Wire transfer fee structure.
*/
struct TALER_EXCHANGEDB_AggregateFees *af;
}; };
@ -102,6 +108,11 @@ struct AggregationUnit
*/ */
struct TALER_Amount total_amount; struct TALER_Amount total_amount;
/**
* Wire fee we charge for @e wp at @e execution_time.
*/
struct TALER_Amount wire_fee;
/** /**
* Hash of @e wire. * Hash of @e wire.
*/ */
@ -259,6 +270,83 @@ extract_type (const json_t *wire)
} }
/**
* Advance the "af" pointer in @a wp to point to the
* currently valid record.
*
* @param wp wire transfer fee data structure to update
* @param now timestamp to update fees to
*/
static void
advance_fees (struct WirePlugin *wp,
struct GNUNET_TIME_Absolute now)
{
struct TALER_EXCHANGEDB_AggregateFees *af;
/* First, try to see if we have current fee information in memory */
af = wp->af;
while ( (NULL != af) &&
(af->end_date.abs_value_us < now.abs_value_us) )
{
struct TALER_EXCHANGEDB_AggregateFees *n = af->next;
GNUNET_free (af);
af = n;
}
wp->af = af;
}
/**
* Update wire transfer fee data structure in @a wp.
*
* @param wp wire transfer fee data structure to update
* @param now timestamp to update fees to
* @param session DB session to use
* @return #GNUNET_OK on success, #GNUNET_SYSERR if we
* lack current fee information (and need to exit)
*/
static int
update_fees (struct WirePlugin *wp,
struct GNUNET_TIME_Absolute now,
struct TALER_EXCHANGEDB_Session *session)
{
advance_fees (wp,
now);
if (NULL != wp->af)
return GNUNET_OK;
/* Let's try to load it from disk... */
wp->af = TALER_EXCHANGEDB_fees_read (cfg,
wp->type);
advance_fees (wp,
now);
for (struct TALER_EXCHANGEDB_AggregateFees *p = wp->af;
NULL != p;
p = p->next)
{
if (GNUNET_SYSERR ==
db_plugin->insert_wire_fee (db_plugin->cls,
session,
wp->type,
p->start_date,
p->end_date,
&p->wire_fee,
&p->master_sig))
{
TALER_EXCHANGEDB_fees_free (wp->af);
wp->af = NULL;
return GNUNET_SYSERR;
}
}
if (NULL != wp->af)
return GNUNET_OK;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find current wire transfer fees for `%s'\n",
wp->type);
return GNUNET_SYSERR;
}
/** /**
* Find the wire plugin for the given wire address. * Find the wire plugin for the given wire address.
* *
@ -345,6 +433,7 @@ shutdown_task (void *cls)
wp_tail, wp_tail,
wp); wp);
TALER_WIRE_plugin_unload (wp->wire_plugin); TALER_WIRE_plugin_unload (wp->wire_plugin);
TALER_EXCHANGEDB_fees_free (wp->af);
GNUNET_free (wp->type); GNUNET_free (wp->type);
GNUNET_free (wp); GNUNET_free (wp);
} }
@ -433,9 +522,8 @@ deposit_cb (void *cls,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
au->row_id = row_id; au->row_id = row_id;
GNUNET_assert (NULL == au->wire);
au->wire = json_incref ((json_t *) wire); au->wire = json_incref ((json_t *) wire);
au->execution_time = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&au->execution_time);
TALER_JSON_hash (au->wire, TALER_JSON_hash (au->wire,
&au->h_wire); &au->h_wire);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
@ -444,6 +532,21 @@ deposit_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting aggregation under H(WTID)=%s\n", "Starting aggregation under H(WTID)=%s\n",
TALER_B2S (&au->wtid)); TALER_B2S (&au->wtid));
au->wp = find_plugin (extract_type (au->wire));
if (NULL == au->wp)
return GNUNET_SYSERR;
/* make sure we have current fees */
au->execution_time = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&au->execution_time);
if (GNUNET_OK !=
update_fees (au->wp,
au->execution_time,
au->session))
return GNUNET_SYSERR;
au->wire_fee = au->wp->af->wire_fee;
if (GNUNET_OK != if (GNUNET_OK !=
db_plugin->insert_aggregation_tracking (db_plugin->cls, db_plugin->insert_aggregation_tracking (db_plugin->cls,
au->session, au->session,
@ -585,7 +688,7 @@ run_aggregation (void *cls)
unsigned int i; unsigned int i;
int ret; int ret;
const struct GNUNET_SCHEDULER_TaskContext *tc; const struct GNUNET_SCHEDULER_TaskContext *tc;
struct WirePlugin *wp; struct TALER_Amount final_amount;
task = NULL; task = NULL;
tc = GNUNET_SCHEDULER_get_task_context (); tc = GNUNET_SCHEDULER_get_task_context ();
@ -650,18 +753,6 @@ run_aggregation (void *cls)
return; return;
} }
wp = find_plugin (extract_type (au->wire));
if (NULL == wp)
{
json_decref (au->wire);
GNUNET_free (au);
au = NULL;
db_plugin->rollback (db_plugin->cls,
session);
GNUNET_SCHEDULER_shutdown ();
return;
}
/* Now try to find other deposits to aggregate */ /* Now try to find other deposits to aggregate */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Found ready deposit for %s, aggregating\n", "Found ready deposit for %s, aggregating\n",
@ -689,13 +780,18 @@ run_aggregation (void *cls)
return; return;
} }
/* Round to the unit supported by the wire transfer method */ /* Subtract wire transfer fee and round to the unit supported by the
GNUNET_assert (GNUNET_SYSERR != wire transfer method; Check if after rounding down, we still have
wp->wire_plugin->amount_round (wp->wire_plugin->cls, an amount to transfer, and if not mark as 'tiny'. */
&au->total_amount)); if ( (GNUNET_OK !=
/* Check if after rounding down, we still have an amount to transfer */ TALER_amount_subtract (&final_amount,
if ( (0 == au->total_amount.value) && &au->total_amount,
(0 == au->total_amount.fraction) ) &au->wire_fee)) ||
(GNUNET_SYSERR ==
au->wp->wire_plugin->amount_round (au->wp->wire_plugin->cls,
&final_amount)) ||
( (0 == final_amount.value) &&
(0 == final_amount.fraction) ) )
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Aggregate value too low for transfer\n"); "Aggregate value too low for transfer\n");
@ -755,21 +851,20 @@ run_aggregation (void *cls)
{ {
char *amount_s; char *amount_s;
amount_s = TALER_amount_to_string (&au->total_amount); amount_s = TALER_amount_to_string (&final_amount);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Preparing wire transfer of %s to %s\n", "Preparing wire transfer of %s to %s\n",
amount_s, amount_s,
TALER_B2S (&au->merchant_pub)); TALER_B2S (&au->merchant_pub));
GNUNET_free (amount_s); GNUNET_free (amount_s);
} }
au->wp = wp; au->ph = au->wp->wire_plugin->prepare_wire_transfer (au->wp->wire_plugin->cls,
au->ph = wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls, au->wire,
au->wire, &final_amount,
&au->total_amount, exchange_base_url,
exchange_base_url, &au->wtid,
&au->wtid, &prepare_cb,
&prepare_cb, au);
au);
if (NULL == au->ph) if (NULL == au->ph)
{ {
GNUNET_break (0); /* why? how to best recover? */ GNUNET_break (0); /* why? how to best recover? */

View File

@ -1864,6 +1864,11 @@ struct WtidTransactionContext
*/ */
struct TALER_MerchantPublicKeyP merchant_pub; struct TALER_MerchantPublicKeyP merchant_pub;
/**
* Which method was used to wire the funds?
*/
char *wire_method;
/** /**
* Hash of the wire details of the merchant (identical for all * Hash of the wire details of the merchant (identical for all
* deposits), only valid if @e is_valid is #GNUNET_YES. * deposits), only valid if @e is_valid is #GNUNET_YES.
@ -1918,6 +1923,7 @@ struct WtidTransactionContext
static void static void
handle_transaction_data (void *cls, handle_transaction_data (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_proposal_data, const struct GNUNET_HashCode *h_proposal_data,
@ -1936,6 +1942,7 @@ handle_transaction_data (void *cls,
ctx->merchant_pub = *merchant_pub; ctx->merchant_pub = *merchant_pub;
ctx->h_wire = *h_wire; ctx->h_wire = *h_wire;
ctx->exec_time = exec_time; ctx->exec_time = exec_time;
ctx->wire_method = GNUNET_strdup (wire_method);
ctx->is_valid = GNUNET_YES; ctx->is_valid = GNUNET_YES;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_amount_subtract (&ctx->total, TALER_amount_subtract (&ctx->total,
@ -1952,6 +1959,8 @@ handle_transaction_data (void *cls,
if ( (0 != memcmp (&ctx->merchant_pub, if ( (0 != memcmp (&ctx->merchant_pub,
merchant_pub, merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP))) || sizeof (struct TALER_MerchantPublicKeyP))) ||
(0 != strcmp (wire_method,
ctx->wire_method)) ||
(0 != memcmp (&ctx->h_wire, (0 != memcmp (&ctx->h_wire,
h_wire, h_wire,
sizeof (struct GNUNET_HashCode))) ) sizeof (struct GNUNET_HashCode))) )
@ -2006,6 +2015,10 @@ TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
struct WtidTransactionContext ctx; struct WtidTransactionContext ctx;
struct TALER_EXCHANGEDB_Session *session; struct TALER_EXCHANGEDB_Session *session;
struct TEH_TrackTransferDetail *wdd; struct TEH_TrackTransferDetail *wdd;
struct GNUNET_TIME_Absolute wire_fee_start_date;
struct GNUNET_TIME_Absolute wire_fee_end_date;
struct TALER_Amount wire_fee;
struct TALER_MasterSignatureP wire_fee_master_sig;
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
{ {
@ -2016,6 +2029,7 @@ TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
ctx.is_valid = GNUNET_NO; ctx.is_valid = GNUNET_NO;
ctx.wdd_head = NULL; ctx.wdd_head = NULL;
ctx.wdd_tail = NULL; ctx.wdd_tail = NULL;
ctx.wire_method = NULL;
ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls, ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
session, session,
wtid, wtid,
@ -2042,10 +2056,36 @@ TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
"wtid"); "wtid");
goto cleanup; goto cleanup;
} }
if (GNUNET_OK !=
TEH_plugin->get_wire_fee (TEH_plugin->cls,
session,
ctx.wire_method,
ctx.exec_time,
&wire_fee_start_date,
&wire_fee_end_date,
&wire_fee,
&wire_fee_master_sig))
{
GNUNET_break (0);
ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
goto cleanup;
}
if (GNUNET_OK !=
TALER_amount_subtract (&ctx.total,
&ctx.total,
&wire_fee))
{
GNUNET_break (0);
ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
goto cleanup;
}
ret = TEH_RESPONSE_reply_track_transfer_details (connection, ret = TEH_RESPONSE_reply_track_transfer_details (connection,
&ctx.total, &ctx.total,
&ctx.merchant_pub, &ctx.merchant_pub,
&ctx.h_wire, &ctx.h_wire,
&wire_fee,
ctx.exec_time, ctx.exec_time,
ctx.wdd_head); ctx.wdd_head);
cleanup: cleanup:
@ -2056,6 +2096,7 @@ TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
wdd); wdd);
GNUNET_free (wdd); GNUNET_free (wdd);
} }
GNUNET_free_non_null (ctx.wire_method);
return ret; return ret;
} }

View File

@ -1214,6 +1214,7 @@ TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection,
* @param total total amount that was transferred * @param total total amount that was transferred
* @param merchant_pub public key of the merchant * @param merchant_pub public key of the merchant
* @param h_wire destination account * @param h_wire destination account
* @param wire_fee wire fee that was charged
* @param exec_time execution time of the wire transfer * @param exec_time execution time of the wire transfer
* @param wdd_head linked list with details about the combined deposits * @param wdd_head linked list with details about the combined deposits
* @return MHD result code * @return MHD result code
@ -1223,6 +1224,7 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
const struct TALER_Amount *total, const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *wire_fee,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct TEH_TrackTransferDetail *wdd_head) const struct TEH_TrackTransferDetail *wdd_head)
{ {
@ -1261,6 +1263,8 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS)); wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
TALER_amount_hton (&wdp.total, TALER_amount_hton (&wdp.total,
total); total);
TALER_amount_hton (&wdp.wire_fee,
wire_fee);
wdp.merchant_pub = *merchant_pub; wdp.merchant_pub = *merchant_pub;
wdp.h_wire = *h_wire; wdp.h_wire = *h_wire;
GNUNET_CRYPTO_hash_context_finish (hash_context, GNUNET_CRYPTO_hash_context_finish (hash_context,
@ -1270,8 +1274,9 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
&sig); &sig);
return TEH_RESPONSE_reply_json_pack (connection, return TEH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK, MHD_HTTP_OK,
"{s:o, s:o, s:o, s:o, s:o, s:o, s:o}", "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
"total", TALER_JSON_from_amount (total), "total", TALER_JSON_from_amount (total),
"wire_fee", TALER_JSON_from_amount (wire_fee),
"merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub), "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub),
"H_wire", GNUNET_JSON_from_data_auto (h_wire), "H_wire", GNUNET_JSON_from_data_auto (h_wire),
"execution_time", GNUNET_JSON_from_time_abs (exec_time), "execution_time", GNUNET_JSON_from_time_abs (exec_time),

View File

@ -399,6 +399,7 @@ struct TEH_TrackTransferDetail
* @param total total amount that was transferred * @param total total amount that was transferred
* @param merchant_pub public key of the merchant * @param merchant_pub public key of the merchant
* @param h_wire destination account * @param h_wire destination account
* @param wire_fee wire fee that was charged
* @param exec_time execution time of the wire transfer * @param exec_time execution time of the wire transfer
* @param wdd_head linked list with details about the combined deposits * @param wdd_head linked list with details about the combined deposits
* @return MHD result code * @return MHD result code
@ -408,6 +409,7 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
const struct TALER_Amount *total, const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *wire_fee,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct TEH_TrackTransferDetail *wdd_head); const struct TEH_TrackTransferDetail *wdd_head);

View File

@ -29,6 +29,20 @@ DB_CONN_STR = postgres:///talercheck
# Enable 'test' for testing of the actual coin operations. # Enable 'test' for testing of the actual coin operations.
ENABLE = YES ENABLE = YES
# Fees for the forseeable future...
# If you see this after 2017, update to match the next 10 years...
WIRE-FEE-2017 = EUR:0.01
WIRE-FEE-2018 = EUR:0.01
WIRE-FEE-2019 = EUR:0.01
WIRE-FEE-2020 = EUR:0.01
WIRE-FEE-2021 = EUR:0.01
WIRE-FEE-2022 = EUR:0.01
WIRE-FEE-2023 = EUR:0.01
WIRE-FEE-2024 = EUR:0.01
WIRE-FEE-2025 = EUR:0.01
WIRE-FEE-2026 = EUR:0.01
[exchange-wire-outgoing-test] [exchange-wire-outgoing-test]
# What is the main website of the bank? # What is the main website of the bank?
BANK_URI = "http://localhost:8082/" BANK_URI = "http://localhost:8082/"

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2016 Inria and GNUnet e.V. (C) 2016, 2017 Inria and GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software terms of the GNU General Public License as published by the Free Software
@ -621,7 +621,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.9" .details.expect_transaction.amount = "EUR:0.89"
}, },
{ {
@ -668,7 +668,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:1.8" .details.expect_transaction.amount = "EUR:1.79"
}, },
{ {
@ -714,7 +714,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.9" .details.expect_transaction.amount = "EUR:0.89"
}, },
{ {
.opcode = OPCODE_EXPECT_TRANSACTION, .opcode = OPCODE_EXPECT_TRANSACTION,
@ -722,7 +722,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.9" .details.expect_transaction.amount = "EUR:0.89"
}, },
{ {
.opcode = OPCODE_EXPECT_TRANSACTION, .opcode = OPCODE_EXPECT_TRANSACTION,
@ -730,7 +730,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 5, .details.expect_transaction.credit_account = 5,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.9" .details.expect_transaction.amount = "EUR:0.89"
}, },
{ {
.opcode = OPCODE_EXPECT_TRANSACTIONS_EMPTY, .opcode = OPCODE_EXPECT_TRANSACTIONS_EMPTY,
@ -779,7 +779,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.2" .details.expect_transaction.amount = "EUR:0.19"
}, },
/* test picking all deposits at earliest deadline */ /* test picking all deposits at earliest deadline */
@ -824,7 +824,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.2" .details.expect_transaction.amount = "EUR:0.19"
}, },
/* Test NEVER running 'tiny' unless they make up minimum unit */ /* Test NEVER running 'tiny' unless they make up minimum unit */
@ -894,7 +894,7 @@ run_test ()
.details.deposit.merchant_name = "bob", .details.deposit.merchant_name = "bob",
.details.deposit.merchant_account = 4, .details.deposit.merchant_account = 4,
.details.deposit.wire_deadline = { 1000LL * 1000 * 0 }, /* 0s */ .details.deposit.wire_deadline = { 1000LL * 1000 * 0 }, /* 0s */
.details.deposit.amount_with_fee = "EUR:0.102", .details.deposit.amount_with_fee = "EUR:0.112",
.details.deposit.deposit_fee = "EUR:0.1" .details.deposit.deposit_fee = "EUR:0.1"
}, },
{ {
@ -934,7 +934,7 @@ run_test ()
.details.deposit.merchant_name = "bob", .details.deposit.merchant_name = "bob",
.details.deposit.merchant_account = 4, .details.deposit.merchant_account = 4,
.details.deposit.wire_deadline = { 1000LL * 1000 * 0 }, /* 0s */ .details.deposit.wire_deadline = { 1000LL * 1000 * 0 }, /* 0s */
.details.deposit.amount_with_fee = "EUR:0.109", .details.deposit.amount_with_fee = "EUR:0.119",
.details.deposit.deposit_fee = "EUR:0.1" .details.deposit.deposit_fee = "EUR:0.1"
}, },
{ {
@ -969,7 +969,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.02" .details.expect_transaction.amount = "EUR:0.01"
}, },
/* Test that aggregation would happen fully if wire deadline is long */ /* Test that aggregation would happen fully if wire deadline is long */
@ -1027,7 +1027,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.04" .details.expect_transaction.amount = "EUR:0.03"
}, },
@ -1087,7 +1087,7 @@ run_test ()
.details.expect_transaction.debit_account = 3, .details.expect_transaction.debit_account = 3,
.details.expect_transaction.credit_account = 4, .details.expect_transaction.credit_account = 4,
.details.expect_transaction.exchange_base_url = "https://exchange.taler.net/", .details.expect_transaction.exchange_base_url = "https://exchange.taler.net/",
.details.expect_transaction.amount = "EUR:0.02" .details.expect_transaction.amount = "EUR:0.01"
}, },
/* Everything tested, terminate with success */ /* Everything tested, terminate with success */
@ -1203,6 +1203,7 @@ main (int argc,
{ {
const char *plugin_name; const char *plugin_name;
char *testname; char *testname;
struct GNUNET_OS_Process *proc;
struct GNUNET_CONFIGURATION_Handle *cfg; struct GNUNET_CONFIGURATION_Handle *cfg;
struct GNUNET_SIGNAL_Context *shc_chld; struct GNUNET_SIGNAL_Context *shc_chld;
@ -1225,6 +1226,21 @@ main (int argc,
GNUNET_log_setup ("test_taler_exchange_aggregator", GNUNET_log_setup ("test_taler_exchange_aggregator",
"WARNING", "WARNING",
NULL); NULL);
proc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-exchange-keyup",
"taler-exchange-keyup",
"-c", config_filename,
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to run `taler-exchange-keyup`, is your PATH correct?\n");
return 77;
}
GNUNET_OS_process_wait (proc);
GNUNET_OS_process_destroy (proc);
cfg = GNUNET_CONFIGURATION_create (); cfg = GNUNET_CONFIGURATION_create ();
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_parse (cfg, GNUNET_CONFIGURATION_parse (cfg,

View File

@ -11,4 +11,4 @@ AUDITOR_BASE_DIR = ${TALER_DATA_HOME}/auditors/
# the merchant per wire transfer. The directory is expected to # the merchant per wire transfer. The directory is expected to
# contain files "$METHOD.fee" with the cost structure, where # contain files "$METHOD.fee" with the cost structure, where
# $METHOD corresponds to a wire transfer method. # $METHOD corresponds to a wire transfer method.
WIREFEE_BASE_DIR = ${TALER_DATA_HOME}/wirefees/ WIREFEE_BASE_DIR = ${TALER_DATA_HOME}/exchange/wirefees/

View File

@ -215,6 +215,8 @@ postgres_drop_tables (void *cls)
"DROP TABLE IF EXISTS prewire;"); "DROP TABLE IF EXISTS prewire;");
SQLEXEC_ (conn, SQLEXEC_ (conn,
"DROP TABLE IF EXISTS aggregation_tracking;"); "DROP TABLE IF EXISTS aggregation_tracking;");
SQLEXEC_ (conn,
"DROP TABLE IF EXISTS wire_fee;");
SQLEXEC_ (conn, SQLEXEC_ (conn,
"DROP TABLE IF EXISTS deposits;"); "DROP TABLE IF EXISTS deposits;");
SQLEXEC_ (conn, SQLEXEC_ (conn,
@ -472,6 +474,23 @@ postgres_create_tables (void *cls)
SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index " SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
"ON aggregation_tracking(wtid_raw)"); "ON aggregation_tracking(wtid_raw)");
/* Table for the wire fees. */
SQLEXEC("CREATE TABLE IF NOT EXISTS wire_fee "
"(wire_method VARCHAR NOT NULL"
",start_date INT8 NOT NULL"
",end_date INT8 NOT NULL"
",wire_fee_val INT8 NOT NULL"
",wire_fee_frac INT4 NOT NULL"
",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
",PRIMARY KEY (wire_method, start_date)" /* this combo must be unique */
")");
/* Index for lookup_transactions statement on wtid */
SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
"ON aggregation_tracking(wtid_raw)");
/* This table contains the pre-commit data for /* This table contains the pre-commit data for
wire transfers the exchange is about to execute. */ wire transfers the exchange is about to execute. */
SQLEXEC("CREATE TABLE IF NOT EXISTS prewire " SQLEXEC("CREATE TABLE IF NOT EXISTS prewire "
@ -1193,6 +1212,7 @@ postgres_prepare (PGconn *db_conn)
PREPARE ("lookup_transactions", PREPARE ("lookup_transactions",
"SELECT" "SELECT"
" deposits.h_proposal_data" " deposits.h_proposal_data"
",deposits.wire"
",deposits.h_wire" ",deposits.h_wire"
",deposits.coin_pub" ",deposits.coin_pub"
",deposits.merchant_pub" ",deposits.merchant_pub"
@ -1241,6 +1261,35 @@ postgres_prepare (PGconn *db_conn)
"($1, $2, $3)", "($1, $2, $3)",
3, NULL); 3, NULL);
/* Used in #postgres_get_wire_fee() */
PREPARE ("get_wire_fee",
"SELECT "
" start_date"
",end_date"
",wire_fee_val"
",wire_fee_frac"
",wire_fee_curr"
",master_sig"
" FROM wire_fee"
" WHERE wire_method=$1"
" AND start_date <= $2"
" AND end_date > $2",
2, NULL);
/* Used in #postgres_insert_wire_fee */
PREPARE ("insert_wire_fee",
"INSERT INTO wire_fee "
"(wire_method"
",start_date"
",end_date"
",wire_fee_val"
",wire_fee_frac"
",wire_fee_curr"
",master_sig"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7)",
7, NULL);
/* Used in #postgres_wire_prepare_data_insert() to store /* Used in #postgres_wire_prepare_data_insert() to store
wire transfer information before actually committing it with the bank */ wire transfer information before actually committing it with the bank */
@ -3980,15 +4029,19 @@ postgres_lookup_wire_transfer (void *cls,
struct GNUNET_TIME_Absolute exec_time; struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount amount_with_fee; struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee; struct TALER_Amount deposit_fee;
json_t *wire;
json_t *t;
const char *wire_method;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("h_proposal_data", &h_proposal_data), GNUNET_PQ_result_spec_auto_from_type ("h_proposal_data", &h_proposal_data),
TALER_PQ_result_spec_json ("wire", &wire),
GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire), GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub), GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub), GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
GNUNET_PQ_result_spec_absolute_time ("execution_time", &exec_time), GNUNET_PQ_result_spec_absolute_time ("execution_time", &exec_time),
TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee), TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee), TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_PQ_extract_result (result, GNUNET_PQ_extract_result (result,
@ -3999,8 +4052,23 @@ postgres_lookup_wire_transfer (void *cls,
PQclear (result); PQclear (result);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
t = json_object_get (wire, "type");
if (NULL == t)
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
wire_method = json_string_value (t);
if (NULL == wire_method)
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
cb (cb_cls, cb (cb_cls,
&merchant_pub, &merchant_pub,
wire_method,
&h_wire, &h_wire,
exec_time, exec_time,
&h_proposal_data, &h_proposal_data,
@ -4210,6 +4278,170 @@ postgres_insert_aggregation_tracking (void *cls,
} }
/**
* Obtain wire fee from database.
*
* @param cls closure
* @param session database connection
* @param type type of wire transfer the fee applies for
* @param date for which date do we want the fee?
* @param[out] start_date when does the fee go into effect
* @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
* #GNUNET_SYSERR on failure
*/
static int
postgres_get_wire_fee (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
struct GNUNET_TIME_Absolute date,
struct GNUNET_TIME_Absolute *start_date,
struct GNUNET_TIME_Absolute *end_date,
struct TALER_Amount *wire_fee,
struct TALER_MasterSignatureP *master_sig)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (type),
GNUNET_PQ_query_param_absolute_time (&date),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_absolute_time ("start_date", start_date),
GNUNET_PQ_result_spec_absolute_time ("end_date", end_date),
TALER_PQ_result_spec_amount ("wire_fee", wire_fee),
GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
GNUNET_PQ_result_spec_end
};
PGresult *result;
int nrows;
result = GNUNET_PQ_exec_prepared (session->conn,
"get_wire_fee",
params);
if (PGRES_TUPLES_OK !=
PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
nrows = PQntuples (result);
if (0 == nrows)
{
/* no matches found */
PQclear (result);
return GNUNET_NO;
}
if (1 != nrows)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_PQ_extract_result (result,
rs,
0))
{
PQclear (result);
GNUNET_break (0);
return GNUNET_SYSERR;
}
PQclear (result);
return GNUNET_OK;
}
/**
* Insert wire transfer fee into database.
*
* @param cls closure
* @param session database connection
* @param type type of wire transfer this fee applies for
* @param start_date when does the fee go into effect
* @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee
* @param master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if the record exists,
* #GNUNET_SYSERR on failure
*/
static int
postgres_insert_wire_fee (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
struct GNUNET_TIME_Absolute start_date,
struct GNUNET_TIME_Absolute end_date,
const struct TALER_Amount *wire_fee,
const struct TALER_MasterSignatureP *master_sig)
{
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (type),
GNUNET_PQ_query_param_absolute_time (&start_date),
GNUNET_PQ_query_param_absolute_time (&end_date),
TALER_PQ_query_param_amount (wire_fee),
GNUNET_PQ_query_param_auto_from_type (master_sig),
GNUNET_PQ_query_param_end
};
PGresult *result;
struct TALER_Amount wf;
struct TALER_MasterSignatureP sig;
struct GNUNET_TIME_Absolute sd;
struct GNUNET_TIME_Absolute ed;
if (GNUNET_OK ==
postgres_get_wire_fee (cls,
session,
type,
start_date,
&sd,
&ed,
&wf,
&sig))
{
if (0 != memcmp (&sig,
master_sig,
sizeof (sig)))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (0 != TALER_amount_cmp (wire_fee,
&wf))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if ( (sd.abs_value_us != start_date.abs_value_us) ||
(ed.abs_value_us != end_date.abs_value_us) )
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* equal record already exists */
return GNUNET_NO;
}
result = GNUNET_PQ_exec_prepared (session->conn,
"insert_wire_fee",
params);
if (PGRES_COMMAND_OK != PQresultStatus (result))
{
BREAK_DB_ERR (result);
PQclear (result);
return GNUNET_SYSERR;
}
if (0 != strcmp ("1", PQcmdTuples (result)))
{
GNUNET_break (0);
PQclear (result);
return GNUNET_SYSERR;
}
PQclear (result);
return GNUNET_OK;
}
/** /**
* Function called to insert wire transfer commit data into the DB. * Function called to insert wire transfer commit data into the DB.
* *
@ -5090,6 +5322,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer; plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid; plugin->wire_lookup_deposit_wtid = &postgres_wire_lookup_deposit_wtid;
plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking; plugin->insert_aggregation_tracking = &postgres_insert_aggregation_tracking;
plugin->insert_wire_fee = &postgres_insert_wire_fee;
plugin->get_wire_fee = &postgres_get_wire_fee;
plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert; plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished; plugin->wire_prepare_data_mark_finished = &postgres_wire_prepare_data_mark_finished;
plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get; plugin->wire_prepare_data_get = &postgres_wire_prepare_data_get;

View File

@ -783,6 +783,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
static void static void
cb_wt_never (void *cls, cb_wt_never (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_proposal_data, const struct GNUNET_HashCode *h_proposal_data,
@ -825,6 +826,7 @@ static struct TALER_WireTransferIdentifierRawP wtid_wt;
static void static void
cb_wt_check (void *cls, cb_wt_check (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_proposal_data, const struct GNUNET_HashCode *h_proposal_data,
@ -836,6 +838,8 @@ cb_wt_check (void *cls,
GNUNET_assert (0 == memcmp (merchant_pub, GNUNET_assert (0 == memcmp (merchant_pub,
&merchant_pub_wt, &merchant_pub_wt,
sizeof (struct TALER_MerchantPublicKeyP))); sizeof (struct TALER_MerchantPublicKeyP)));
GNUNET_assert (0 == strcmp (wire_method,
"SEPA"));
GNUNET_assert (0 == memcmp (h_wire, GNUNET_assert (0 == memcmp (h_wire,
&h_wire_wt, &h_wire_wt,
sizeof (struct GNUNET_HashCode))); sizeof (struct GNUNET_HashCode)));

View File

@ -779,6 +779,19 @@ enum TALER_ErrorCode
*/ */
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND = 1702, TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND = 1702,
/**
* The exchange did not find information about the wire transfer
* fees it charged. This response is
* provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
*/
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND = 1703,
/**
* The exchange found a wire fee that was above the total transfer
* value (and thus could not have been charged). This response is
* provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
*/
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT = 1704,
/** /**
* The exchange found internally inconsistent fee data when * The exchange found internally inconsistent fee data when
@ -1145,7 +1158,7 @@ enum TALER_ErrorCode
/** /**
* The backend encountered an error while trying to store the * The backend encountered an error while trying to store the
* h_proposal_data into the database. * h_proposal_data into the database.
* The response is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR. * The response is provided with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
*/ */
TALER_EC_PROPOSAL_STORE_DB_ERROR = 2501, TALER_EC_PROPOSAL_STORE_DB_ERROR = 2501,

View File

@ -1130,6 +1130,7 @@ struct TALER_EXCHANGE_TrackTransferHandle;
* @param execution_time time when the exchange claims to have performed the wire transfer * @param execution_time time when the exchange claims to have performed the wire transfer
* @param total_amount total amount of the wire transfer, or NULL if the exchange could * @param total_amount total amount of the wire transfer, or NULL if the exchange could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK) * not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param wire_fee wire fee that was charged by the exchange
* @param details_length length of the @a details array * @param details_length length of the @a details array
* @param details array with details about the combined transactions * @param details array with details about the combined transactions
*/ */
@ -1142,6 +1143,7 @@ typedef void
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute execution_time, struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *total_amount, const struct TALER_Amount *total_amount,
const struct TALER_Amount *wire_fee,
unsigned int details_length, unsigned int details_length,
const struct TALER_TrackTransferDetails *details); const struct TALER_TrackTransferDetails *details);

View File

@ -762,6 +762,7 @@ typedef void
* *
* @param cls closure * @param cls closure
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls) * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param wire_method which wire plugin was used for the transfer?
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls) * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls) * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
* @param h_proposal_data which proposal was this payment about * @param h_proposal_data which proposal was this payment about
@ -772,6 +773,7 @@ typedef void
typedef void typedef void
(*TALER_EXCHANGEDB_WireTransferDataCallback)(void *cls, (*TALER_EXCHANGEDB_WireTransferDataCallback)(void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub, const struct TALER_MerchantPublicKeyP *merchant_pub,
const char *wire_method,
const struct GNUNET_HashCode *h_wire, const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute exec_time, struct GNUNET_TIME_Absolute exec_time,
const struct GNUNET_HashCode *h_proposal_data, const struct GNUNET_HashCode *h_proposal_data,
@ -1520,6 +1522,54 @@ struct TALER_EXCHANGEDB_Plugin
struct GNUNET_TIME_Absolute execution_time); struct GNUNET_TIME_Absolute execution_time);
/**
* Insert wire transfer fee into database.
*
* @param cls closure
* @param session database connection
* @param wire_method which wire method is the fee about?
* @param start_date when does the fee go into effect
* @param end_date when does the fee end being valid
* @param wire_fee how high is the wire transfer fee
* @param master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if the record exists,
* #GNUNET_SYSERR on failure
*/
int
(*insert_wire_fee)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *wire_method,
struct GNUNET_TIME_Absolute start_date,
struct GNUNET_TIME_Absolute end_date,
const struct TALER_Amount *wire_fee,
const struct TALER_MasterSignatureP *master_sig);
/**
* Obtain wire fee from database.
*
* @param cls closure
* @param session database connection
* @param type type of wire transfer the fee applies for
* @param date for which date do we want the fee?
* @param[out] start_date when does the fee go into effect
* @param[out] end_date when does the fee end being valid
* @param[out] wire_fee how high is the wire transfer fee
* @param[out] master_sig signature over the above by the exchange master key
* @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
* #GNUNET_SYSERR on failure
*/
int
(*get_wire_fee) (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const char *type,
struct GNUNET_TIME_Absolute date,
struct GNUNET_TIME_Absolute *start_date,
struct GNUNET_TIME_Absolute *end_date,
struct TALER_Amount *wire_fee,
struct TALER_MasterSignatureP *master_sig);
/** /**
* Function called to insert wire transfer commit data into the DB. * Function called to insert wire transfer commit data into the DB.
* *

View File

@ -1003,6 +1003,11 @@ struct TALER_WireDepositDataPS
*/ */
struct TALER_AmountNBO total; struct TALER_AmountNBO total;
/**
* Wire fee that was charged.
*/
struct TALER_AmountNBO wire_fee;
/** /**
* Public key of the merchant (for all aggregated transactions). * Public key of the merchant (for all aggregated transactions).
*/ */