major refactoring, eliminating wire-plugins and moving towards new bank API. main code compiles, testcases known to fail, code sure not to fully work yet

This commit is contained in:
Christian Grothoff 2020-01-11 15:19:56 +01:00
parent 554da10133
commit 9443c10d7f
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
66 changed files with 3806 additions and 6559 deletions

View File

@ -558,6 +558,5 @@ AC_CONFIG_FILES([Makefile
src/util/Makefile src/util/Makefile
src/util/taler-config src/util/taler-config
src/wire/Makefile src/wire/Makefile
src/wire-plugins/Makefile
]) ])
AC_OUTPUT AC_OUTPUT

View File

@ -22,7 +22,7 @@ pkgcfg_DATA = \
EXTRA_DIST = \ EXTRA_DIST = \
taler.conf taler.conf
SUBDIRS = include util wire json curl $(PQ_DIR) mhd $(BANK_LIB) wire-plugins exchangedb exchange exchange-tools auditordb auditor SUBDIRS = include util wire json curl $(PQ_DIR) mhd $(BANK_LIB) exchangedb exchange exchange-tools auditordb auditor
if HAVE_LIBCURL if HAVE_LIBCURL
SUBDIRS += lib benchmark SUBDIRS += lib benchmark
else else

View File

@ -73,10 +73,12 @@ taler_wire_auditor_LDADD = \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
$(top_builddir)/src/auditordb/libtalerauditordb.la \ $(top_builddir)/src/auditordb/libtalerauditordb.la \
-ljansson \ -ljansson \
-lgnunetjson \ -lgnunetjson \
-lgnunetcurl \
-lgnunetutil -lgnunetutil
taler_auditor_sign_SOURCES = \ taler_auditor_sign_SOURCES = \

View File

@ -1010,8 +1010,7 @@ struct ReserveContext
* @param reserve_pub public key of the reserve (also the WTID) * @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received * @param credit amount that was received
* @param sender_account_details information about the sender's bank account * @param sender_account_details information about the sender's bank account
* @param wire_reference unique reference identifying the wire transfer (binary blob) * @param wire_reference unique reference identifying the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds * @param execution_date when did we receive the funds
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/ */
@ -1021,8 +1020,7 @@ handle_reserve_in (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit, const struct TALER_Amount *credit,
const char *sender_account_details, const char *sender_account_details,
const void *wire_reference, uint64_t wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date) struct GNUNET_TIME_Absolute execution_date)
{ {
struct ReserveContext *rc = cls; struct ReserveContext *rc = cls;
@ -2031,35 +2029,6 @@ analyze_reserves (void *cls)
paying each merchant what they were due (and on time). */ paying each merchant what they were due (and on time). */
/**
* Information we keep per loaded wire plugin.
*/
struct WirePlugin
{
/**
* Kept in a DLL.
*/
struct WirePlugin *next;
/**
* Kept in a DLL.
*/
struct WirePlugin *prev;
/**
* Name of the wire method.
*/
char *type;
/**
* Handle to the wire plugin.
*/
struct TALER_WIRE_Plugin *plugin;
};
/** /**
* Information about wire fees charged by the exchange. * Information about wire fees charged by the exchange.
*/ */
@ -2105,16 +2074,6 @@ struct WireFeeInfo
struct AggregationContext struct AggregationContext
{ {
/**
* DLL of wire plugins encountered.
*/
struct WirePlugin *wire_head;
/**
* DLL of wire plugins encountered.
*/
struct WirePlugin *wire_tail;
/** /**
* DLL of wire fees charged by the exchange. * DLL of wire fees charged by the exchange.
*/ */
@ -2132,46 +2091,6 @@ struct AggregationContext
}; };
/**
* Find the relevant wire plugin.
*
* @param ac context to search
* @param type type of the wire plugin to load; it
* will be used _as is_ from the dynamic loader.
* @return NULL on error
*/
static struct TALER_WIRE_Plugin *
get_wire_plugin (struct AggregationContext *ac,
const char *type)
{
struct WirePlugin *wp;
struct TALER_WIRE_Plugin *plugin;
for (wp = ac->wire_head; NULL != wp; wp = wp->next)
if (0 == strcmp (type,
wp->type))
return wp->plugin;
/* Wants the exact *plugin name* (!= method) */
plugin = TALER_WIRE_plugin_load (cfg,
type);
if (NULL == plugin)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to locate wire plugin for `%s'\n",
type);
return NULL;
}
wp = GNUNET_new (struct WirePlugin);
wp->type = GNUNET_strdup (type);
wp->plugin = plugin;
GNUNET_CONTAINER_DLL_insert (ac->wire_head,
ac->wire_tail,
wp);
return plugin;
}
/** /**
* Closure for #wire_transfer_information_cb. * Closure for #wire_transfer_information_cb.
*/ */
@ -2884,7 +2803,6 @@ check_wire_out_cb
{ {
struct AggregationContext *ac = cls; struct AggregationContext *ac = cls;
struct WireCheckContext wcc; struct WireCheckContext wcc;
struct TALER_WIRE_Plugin *plugin;
struct TALER_Amount final_amount; struct TALER_Amount final_amount;
struct TALER_Amount exchange_gain; struct TALER_Amount exchange_gain;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -2976,19 +2894,7 @@ check_wire_out_cb
} }
/* Round down to amount supported by wire method */ /* Round down to amount supported by wire method */
plugin = get_wire_plugin GNUNET_break (TALER_WIRE_amount_round (&final_amount));
(ac,
TALER_WIRE_get_plugin_from_method (method));
if (NULL == plugin)
{
GNUNET_break (0);
GNUNET_free (method);
return GNUNET_SYSERR;
}
GNUNET_free (method);
GNUNET_break (GNUNET_SYSERR !=
plugin->amount_round (plugin->cls,
&final_amount));
/* Calculate the exchange's gain as the fees plus rounding differences! */ /* Calculate the exchange's gain as the fees plus rounding differences! */
if (GNUNET_OK != if (GNUNET_OK !=
@ -3071,7 +2977,6 @@ static enum GNUNET_DB_QueryStatus
analyze_aggregations (void *cls) analyze_aggregations (void *cls)
{ {
struct AggregationContext ac; struct AggregationContext ac;
struct WirePlugin *wc;
struct WireFeeInfo *wfi; struct WireFeeInfo *wfi;
enum GNUNET_DB_QueryStatus qsx; enum GNUNET_DB_QueryStatus qsx;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -3125,15 +3030,6 @@ analyze_aggregations (void *cls)
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
ac.qs = qs; ac.qs = qs;
} }
while (NULL != (wc = ac.wire_head))
{
GNUNET_CONTAINER_DLL_remove (ac.wire_head,
ac.wire_tail,
wc);
TALER_WIRE_plugin_unload (wc->plugin);
GNUNET_free (wc->type);
GNUNET_free (wc);
}
while (NULL != (wfi = ac.fee_head)) while (NULL != (wfi = ac.fee_head))
{ {
GNUNET_CONTAINER_DLL_remove (ac.fee_head, GNUNET_CONTAINER_DLL_remove (ac.fee_head,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2017-2019 Taler Systems SA Copyright (C) 2017-2020 Taler Systems SA
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
@ -27,9 +27,11 @@
*/ */
#include "platform.h" #include "platform.h"
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_curl_lib.h>
#include "taler_auditordb_plugin.h" #include "taler_auditordb_plugin.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_bank_service.h"
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
#include "taler_signatures.h" #include "taler_signatures.h"
@ -65,9 +67,14 @@ struct WireAccount
struct WireAccount *prev; struct WireAccount *prev;
/** /**
* Handle to the plugin. * Authentication data for the account.
*/ */
struct TALER_WIRE_Plugin *wire_plugin; struct TALER_BANK_AuthenticationData auth;
/**
* Our bank account number.
*/
struct TALER_Account account;
/** /**
* Name of the section that configures this account. * Name of the section that configures this account.
@ -77,7 +84,12 @@ struct WireAccount
/** /**
* Active wire request for the transaction history. * Active wire request for the transaction history.
*/ */
struct TALER_WIRE_HistoryHandle *hh; struct TALER_BANK_CreditHistoryHandle *chh;
/**
* Active wire request for the transaction history.
*/
struct TALER_BANK_DebitHistoryHandle *dhh;
/** /**
* Progress point for this account. * Progress point for this account.
@ -92,17 +104,12 @@ struct WireAccount
/** /**
* Where we are in the inbound (CREDIT) transaction history. * Where we are in the inbound (CREDIT) transaction history.
*/ */
void *in_wire_off; uint64_t in_wire_off;
/** /**
* Where we are in the inbound (DEBIT) transaction history. * Where we are in the inbound (DEBIT) transaction history.
*/ */
void *out_wire_off; uint64_t out_wire_off;
/**
* Number of bytes in #in_wire_off and #out_wire_off.
*/
size_t wire_off_size;
/** /**
* We should check for inbound transactions to this account. * We should check for inbound transactions to this account.
@ -341,6 +348,16 @@ static struct TALER_Amount total_wire_format_amount;
*/ */
static struct TALER_Amount zero; static struct TALER_Amount zero;
/**
* Handle to the context for interacting with the bank.
*/
static struct GNUNET_CURL_Context *ctx;
/**
* Scheduler context for running the @e ctx.
*/
static struct GNUNET_CURL_RescheduleContext *rc;
/* ***************************** Shutdown **************************** */ /* ***************************** Shutdown **************************** */
@ -356,15 +373,12 @@ struct ReserveInInfo
*/ */
struct GNUNET_HashCode row_off_hash; struct GNUNET_HashCode row_off_hash;
/**
* Number of bytes in @e row_off.
*/
size_t row_off_size;
/** /**
* Expected details about the wire transfer. * Expected details about the wire transfer.
* The member "account_url" is to be allocated
* at the end of this struct!
*/ */
struct TALER_WIRE_TransferDetails details; struct TALER_BANK_CreditDetails details;
/** /**
* RowID in reserves_in table. * RowID in reserves_in table.
@ -389,7 +403,7 @@ struct ReserveOutInfo
/** /**
* Expected details about the wire transfer. * Expected details about the wire transfer.
*/ */
struct TALER_WIRE_TransferDetails details; struct TALER_BANK_DebitDetails details;
}; };
@ -427,8 +441,6 @@ free_rii (void *cls,
GNUNET_CONTAINER_multihashmap_remove (in_map, GNUNET_CONTAINER_multihashmap_remove (in_map,
key, key,
rii)); rii));
GNUNET_free (rii->details.account_url);
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
GNUNET_free (rii); GNUNET_free (rii);
return GNUNET_OK; return GNUNET_OK;
} }
@ -453,8 +465,6 @@ free_roi (void *cls,
GNUNET_CONTAINER_multihashmap_remove (out_map, GNUNET_CONTAINER_multihashmap_remove (out_map,
key, key,
roi)); roi));
GNUNET_free (roi->details.account_url);
GNUNET_free_non_null (roi->details.wtid_s); /* field not used (yet) */
GNUNET_free (roi); GNUNET_free (roi);
return GNUNET_OK; return GNUNET_OK;
} }
@ -495,6 +505,17 @@ do_shutdown (void *cls)
{ {
struct WireAccount *wa; struct WireAccount *wa;
if (NULL != ctx)
{
GNUNET_CURL_fini (ctx);
ctx = NULL;
}
if (NULL != rc)
{
GNUNET_CURL_gnunet_rc_destroy (rc);
rc = NULL;
}
if (NULL != report_row_inconsistencies) if (NULL != report_row_inconsistencies)
{ {
json_t *report; json_t *report;
@ -617,21 +638,22 @@ do_shutdown (void *cls)
} }
while (NULL != (wa = wa_head)) while (NULL != (wa = wa_head))
{ {
if (NULL != wa->hh) if (NULL != wa->dhh)
{ {
struct TALER_WIRE_Plugin *wp = wa->wire_plugin; TALER_BANK_debit_history_cancel (wa->dhh);
wa->dhh = NULL;
wp->get_history_cancel (wp->cls, }
wa->hh); if (NULL != wa->chh)
wa->hh = NULL; {
TALER_BANK_credit_history_cancel (wa->chh);
wa->chh = NULL;
} }
GNUNET_CONTAINER_DLL_remove (wa_head, GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail, wa_tail,
wa); wa);
TALER_WIRE_plugin_unload (wa->wire_plugin); TALER_BANK_auth_free (&wa->auth);
TALER_WIRE_account_free (&wa->account);
GNUNET_free (wa->section_name); GNUNET_free (wa->section_name);
GNUNET_free_non_null (wa->in_wire_off);
GNUNET_free_non_null (wa->out_wire_off);
GNUNET_free (wa); GNUNET_free (wa);
} }
if (NULL != adb) if (NULL != adb)
@ -787,8 +809,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
wa->section_name, wa->section_name,
&wa->pp, &wa->pp,
wa->in_wire_off, wa->in_wire_off,
wa->out_wire_off, wa->out_wire_off);
wa->wire_off_size);
else else
qs = adb->insert_wire_auditor_account_progress (adb->cls, qs = adb->insert_wire_auditor_account_progress (adb->cls,
asession, asession,
@ -796,8 +817,7 @@ commit (enum GNUNET_DB_QueryStatus qs)
wa->section_name, wa->section_name,
&wa->pp, &wa->pp,
wa->in_wire_off, wa->in_wire_off,
wa->out_wire_off, wa->out_wire_off);
wa->wire_off_size);
if (0 >= qs) if (0 >= qs)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -1250,9 +1270,7 @@ complain_out_not_found (void *cls,
"amount_wired", TALER_JSON_from_amount ( "amount_wired", TALER_JSON_from_amount (
&roi->details.amount), &roi->details.amount),
"amount_justified", TALER_JSON_from_amount (&zero), "amount_justified", TALER_JSON_from_amount (&zero),
"wtid", (NULL == roi->details.wtid_s) "wtid", GNUNET_JSON_from_data_auto (&roi->details.wtid),
? GNUNET_JSON_from_data_auto (&roi->details.wtid)
: json_string (roi->details.wtid_s),
"timestamp", json_from_time_abs ( "timestamp", json_from_time_abs (
roi->details.execution_date), roi->details.execution_date),
"diagnostic", "diagnostic",
@ -1317,29 +1335,28 @@ check_exchange_wire_out (struct WireAccount *wa)
/** /**
* This function is called for all transactions that * This function is called for all transactions that
* are credited to the exchange's account (incoming * are debited from the exchange's account (outgoing
* transactions). * transactions).
* *
* @param cls `struct WireAccount` with current wire account to process * @param cls `struct WireAccount` with current wire account to process
* @param http_status_code http status of the request
* @param ec error code in case something went wrong * @param ec error code in case something went wrong
* @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer * @param details details about the wire transfer
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
static int static int
history_debit_cb (void *cls, history_debit_cb (void *cls,
unsigned int http_status_code,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, uint64_t row_off,
const void *row_off, const struct TALER_BANK_DebitDetails *details,
size_t row_off_size, const json_t *json)
const struct TALER_WIRE_TransferDetails *details)
{ {
struct WireAccount *wa = cls; struct WireAccount *wa = cls;
struct ReserveOutInfo *roi; struct ReserveOutInfo *roi;
if (TALER_BANK_DIRECTION_NONE == dir) if (NULL == details)
{ {
if (TALER_EC_NONE != ec) if (TALER_EC_NONE != ec)
{ {
@ -1348,7 +1365,7 @@ history_debit_cb (void *cls,
"Error fetching history: %u!\n", "Error fetching history: %u!\n",
(unsigned int) ec); (unsigned int) ec);
} }
wa->hh = NULL; wa->dhh = NULL;
check_exchange_wire_out (wa); check_exchange_wire_out (wa);
return GNUNET_OK; return GNUNET_OK;
} }
@ -1357,45 +1374,8 @@ history_debit_cb (void *cls,
GNUNET_STRINGS_absolute_time_to_string (details->execution_date), GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
TALER_amount2s (&details->amount), TALER_amount2s (&details->amount),
TALER_B2S (&details->wtid)); TALER_B2S (&details->wtid));
if (NULL != details->wtid_s)
{
char *diagnostic;
GNUNET_asprintf (&diagnostic,
"malformed subject `%s'",
details->wtid_s);
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_wire_format_amount,
&total_wire_format_amount,
&details->amount));
report (report_wire_format_inconsistencies,
json_pack ("{s:o, s:o, s:s}",
"amount", TALER_JSON_from_amount (&details->amount),
"wire_offset", GNUNET_JSON_from_data (row_off,
row_off_size),
"diagnostic", diagnostic));
GNUNET_free (diagnostic);
return GNUNET_OK;
}
/* Update offset */ /* Update offset */
if (NULL == wa->out_wire_off) wa->out_wire_off = row_off;
{
wa->wire_off_size = row_off_size;
wa->out_wire_off = GNUNET_malloc (row_off_size);
}
if (wa->wire_off_size != row_off_size)
{
GNUNET_break (0);
commit (GNUNET_DB_STATUS_HARD_ERROR);
wa->hh = NULL;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
memcpy (wa->out_wire_off,
row_off,
row_off_size);
roi = GNUNET_new (struct ReserveOutInfo); roi = GNUNET_new (struct ReserveOutInfo);
GNUNET_CRYPTO_hash (&details->wtid, GNUNET_CRYPTO_hash (&details->wtid,
sizeof (details->wtid), sizeof (details->wtid),
@ -1420,10 +1400,9 @@ history_debit_cb (void *cls,
&total_wire_format_amount, &total_wire_format_amount,
&details->amount)); &details->amount));
report (report_wire_format_inconsistencies, report (report_wire_format_inconsistencies,
json_pack ("{s:o, s:o, s:s}", json_pack ("{s:o, s:I, s:s}",
"amount", TALER_JSON_from_amount (&details->amount), "amount", TALER_JSON_from_amount (&details->amount),
"wire_offset", GNUNET_JSON_from_data (row_off, "wire_offset", (json_int_t) row_off,
row_off_size),
"diagnostic", diagnostic)); "diagnostic", diagnostic));
GNUNET_free (diagnostic); GNUNET_free (diagnostic);
return GNUNET_OK; return GNUNET_OK;
@ -1443,7 +1422,6 @@ static void
process_debits (void *cls) process_debits (void *cls)
{ {
struct WireAccount *wa = cls; struct WireAccount *wa = cls;
struct TALER_WIRE_Plugin *wp;
/* skip accounts where DEBIT is not enabled */ /* skip accounts where DEBIT is not enabled */
while ( (NULL != wa) && while ( (NULL != wa) &&
@ -1459,17 +1437,16 @@ process_debits (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Checking bank DEBIT records of account `%s'\n", "Checking bank DEBIT records of account `%s'\n",
wa->section_name); wa->section_name);
GNUNET_assert (NULL == wa->hh); GNUNET_assert (NULL == wa->dhh);
wp = wa->wire_plugin; wa->dhh = TALER_BANK_debit_history (ctx,
wa->hh = wp->get_history (wp->cls, wa->account.details.x_taler_bank.
wa->section_name, account_base_url,
TALER_BANK_DIRECTION_DEBIT, &wa->auth,
wa->out_wire_off, wa->out_wire_off,
wa->wire_off_size,
INT64_MAX, INT64_MAX,
&history_debit_cb, &history_debit_cb,
wa); wa);
if (NULL == wa->hh) if (NULL == wa->dhh)
{ {
fprintf (stderr, fprintf (stderr,
"Failed to obtain bank transaction history for `%s'\n", "Failed to obtain bank transaction history for `%s'\n",
@ -1519,24 +1496,25 @@ conclude_credit_history ()
* @param rowid unique serial ID for the entry in our DB * @param rowid unique serial ID for the entry in our DB
* @param reserve_pub public key of the reserve (also the WTID) * @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received * @param credit amount that was received
* @param sender_url payto://-URL of the sender's bank account * @param sender_account_details payto://-URL of the sender's bank account
* @param wire_reference unique identifier for the wire transfer (plugin-specific format) * @param wire_reference unique identifier for the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds * @param execution_date when did we receive the funds
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/ */
static int static int
reserve_in_cb (void *cls, reserve_in_cb (void *cls,
uint64_t rowid, uint64_t rowid,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct
TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit, const struct TALER_Amount *credit,
const char *sender_url, const char *sender_account_details,
const void *wire_reference, uint64_t wire_reference,
size_t wire_reference_size, struct GNUNET_TIME_Absolute
struct GNUNET_TIME_Absolute execution_date) execution_date)
{ {
struct WireAccount *wa = cls; struct WireAccount *wa = cls;
struct ReserveInInfo *rii; struct ReserveInInfo *rii;
size_t slen;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n", "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
@ -1544,21 +1522,20 @@ reserve_in_cb (void *cls,
GNUNET_STRINGS_absolute_time_to_string (execution_date), GNUNET_STRINGS_absolute_time_to_string (execution_date),
TALER_amount2s (credit), TALER_amount2s (credit),
TALER_B2S (reserve_pub)); TALER_B2S (reserve_pub));
rii = GNUNET_new (struct ReserveInInfo); slen = strlen (sender_account_details) + 1;
GNUNET_CRYPTO_hash (wire_reference, rii = GNUNET_malloc (sizeof (struct ReserveInInfo)
wire_reference_size, + slen);
&rii->row_off_hash); rii->rowid = rowid;
rii->row_off_size = wire_reference_size;
rii->details.amount = *credit; rii->details.amount = *credit;
rii->details.execution_date = execution_date; rii->details.execution_date = execution_date;
/* reserve public key should be the WTID */ rii->details.reserve_pub = *reserve_pub;
GNUNET_assert (sizeof (rii->details.wtid) == rii->details.account_url = (const char *) &rii[1];
sizeof (*reserve_pub)); memcpy (&rii[1],
memcpy (&rii->details.wtid, sender_account_details,
reserve_pub, slen);
sizeof (*reserve_pub)); GNUNET_CRYPTO_hash (&wire_reference,
rii->details.account_url = GNUNET_strdup (sender_url); sizeof (uint64_t),
rii->rowid = rowid; &rii->row_off_hash);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (in_map, GNUNET_CONTAINER_multihashmap_put (in_map,
&rii->row_off_hash, &rii->row_off_hash,
@ -1572,8 +1549,6 @@ reserve_in_cb (void *cls,
"wire_offset_hash", GNUNET_JSON_from_data_auto ( "wire_offset_hash", GNUNET_JSON_from_data_auto (
&rii->row_off_hash), &rii->row_off_hash),
"diagnostic", "duplicate wire offset")); "diagnostic", "duplicate wire offset"));
GNUNET_free (rii->details.account_url);
GNUNET_free_non_null (rii->details.wtid_s); /* field not used (yet) */
GNUNET_free (rii); GNUNET_free (rii);
return GNUNET_OK; return GNUNET_OK;
} }
@ -1604,7 +1579,8 @@ complain_in_not_found (void *cls,
"amount_exchange_expected", TALER_JSON_from_amount ( "amount_exchange_expected", TALER_JSON_from_amount (
&rii->details.amount), &rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&zero), "amount_wired", TALER_JSON_from_amount (&zero),
"wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid), "reserve_pub", GNUNET_JSON_from_data_auto (
&rii->details.reserve_pub),
"timestamp", json_from_time_abs ( "timestamp", json_from_time_abs (
rii->details.execution_date), rii->details.execution_date),
"account", wa->section_name, "account", wa->section_name,
@ -1635,25 +1611,24 @@ process_credits (void *cls);
* *
* @param cls `struct WireAccount` we are processing * @param cls `struct WireAccount` we are processing
* @param ec error code in case something went wrong * @param ec error code in case something went wrong
* @param dir direction of the transfer
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer * @param details details about the wire transfer
* @param json raw response
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
static int static int
history_credit_cb (void *cls, history_credit_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, uint64_t row_off,
const void *row_off, const struct TALER_BANK_CreditDetails *details,
size_t row_off_size, const json_t *json)
const struct TALER_WIRE_TransferDetails *details)
{ {
struct WireAccount *wa = cls; struct WireAccount *wa = cls;
struct ReserveInInfo *rii; struct ReserveInInfo *rii;
struct GNUNET_HashCode key; struct GNUNET_HashCode key;
if (TALER_BANK_DIRECTION_NONE == dir) if (NULL == details)
{ {
if (TALER_EC_NONE != ec) if (TALER_EC_NONE != ec)
{ {
@ -1663,7 +1638,7 @@ history_credit_cb (void *cls,
(unsigned int) ec); (unsigned int) ec);
} }
/* end of operation */ /* end of operation */
wa->hh = NULL; wa->chh = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reconciling CREDIT processing of account `%s'\n", "Reconciling CREDIT processing of account `%s'\n",
wa->section_name); wa->section_name);
@ -1678,12 +1653,12 @@ history_credit_cb (void *cls,
return GNUNET_OK; return GNUNET_OK;
} }
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Analyzing bank CREDIT at %s of %s with WTID %s\n", "Analyzing bank CREDIT at %s of %s with Reserve-pub %s\n",
GNUNET_STRINGS_absolute_time_to_string (details->execution_date), GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
TALER_amount2s (&details->amount), TALER_amount2s (&details->amount),
TALER_B2S (&details->wtid)); TALER_B2S (&details->reserve_pub));
GNUNET_CRYPTO_hash (row_off, GNUNET_CRYPTO_hash (&row_off,
row_off_size, sizeof (row_off),
&key); &key);
rii = GNUNET_CONTAINER_multihashmap_get (in_map, rii = GNUNET_CONTAINER_multihashmap_get (in_map,
&key); &key);
@ -1693,55 +1668,26 @@ history_credit_cb (void *cls,
"Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n", "Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n",
GNUNET_STRINGS_absolute_time_to_string ( GNUNET_STRINGS_absolute_time_to_string (
details->execution_date)); details->execution_date));
wa->hh = NULL; wa->chh = NULL;
process_credits (wa->next); process_credits (wa->next);
return GNUNET_SYSERR; /* not an error, just end of processing */ return GNUNET_SYSERR; /* not an error, just end of processing */
} }
/* Update offset */ /* Update offset */
if (NULL == wa->in_wire_off) wa->in_wire_off = row_off;
{
wa->wire_off_size = row_off_size;
wa->in_wire_off = GNUNET_malloc (row_off_size);
}
if (wa->wire_off_size != row_off_size)
{
GNUNET_break (0);
commit (GNUNET_DB_STATUS_HARD_ERROR);
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
memcpy (wa->in_wire_off,
row_off,
row_off_size);
/* compare records with expected data */ /* compare records with expected data */
if (row_off_size != rii->row_off_size) if (0 != GNUNET_memcmp (&details->reserve_pub,
{ &rii->details.reserve_pub))
GNUNET_break (0);
report (report_row_inconsistencies,
json_pack ("{s:s, s:I, s:o, s:o, s:s}",
"table", "reserves_in",
"row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off,
row_off_size),
"wire_offset_hash", GNUNET_JSON_from_data_auto (&key),
"diagnostic", "wire reference size missmatch"));
return GNUNET_OK;
}
if (0 != GNUNET_memcmp (&details->wtid,
&rii->details.wtid))
{ {
report (report_reserve_in_inconsistencies, report (report_reserve_in_inconsistencies,
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}",
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off, "bank_row", (json_int_t) row_off,
row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount ( "amount_exchange_expected", TALER_JSON_from_amount (
&rii->details.amount), &rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&zero), "amount_wired", TALER_JSON_from_amount (&zero),
"wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid), "reserve_pub", GNUNET_JSON_from_data_auto (
&rii->details.reserve_pub),
"timestamp", json_from_time_abs ( "timestamp", json_from_time_abs (
rii->details.execution_date), rii->details.execution_date),
"diagnostic", "wire subject does not match")); "diagnostic", "wire subject does not match"));
@ -1750,15 +1696,15 @@ history_credit_cb (void *cls,
&total_bad_amount_in_minus, &total_bad_amount_in_minus,
&rii->details.amount)); &rii->details.amount));
report (report_reserve_in_inconsistencies, report (report_reserve_in_inconsistencies,
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}",
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off, "bank_row", (json_int_t) row_off,
row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount ( "amount_exchange_expected", TALER_JSON_from_amount (
&zero), &zero),
"amount_wired", TALER_JSON_from_amount ( "amount_wired", TALER_JSON_from_amount (
&details->amount), &details->amount),
"wtid", GNUNET_JSON_from_data_auto (&details->wtid), "reserve_pub", GNUNET_JSON_from_data_auto (
&details->reserve_pub),
"timestamp", json_from_time_abs ( "timestamp", json_from_time_abs (
details->execution_date), details->execution_date),
"diagnostic", "wire subject does not match")); "diagnostic", "wire subject does not match"));
@ -1773,15 +1719,15 @@ history_credit_cb (void *cls,
&details->amount)) &details->amount))
{ {
report (report_reserve_in_inconsistencies, report (report_reserve_in_inconsistencies,
json_pack ("{s:I, s:o, s:o, s:o, s:o, s:o, s:s}", json_pack ("{s:I, s:I, s:o, s:o, s:o, s:o, s:s}",
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off, "bank_row", (json_int_t) row_off,
row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount ( "amount_exchange_expected", TALER_JSON_from_amount (
&rii->details.amount), &rii->details.amount),
"amount_wired", TALER_JSON_from_amount ( "amount_wired", TALER_JSON_from_amount (
&details->amount), &details->amount),
"wtid", GNUNET_JSON_from_data_auto (&details->wtid), "reserve_pub", GNUNET_JSON_from_data_auto (
&details->reserve_pub),
"timestamp", json_from_time_abs ( "timestamp", json_from_time_abs (
details->execution_date), details->execution_date),
"diagnostic", "wire amount does not match")); "diagnostic", "wire amount does not match"));
@ -1820,13 +1766,12 @@ history_credit_cb (void *cls,
rii->details.account_url)) rii->details.account_url))
{ {
report (report_missattribution_in_inconsistencies, report (report_missattribution_in_inconsistencies,
json_pack ("{s:o, s:I, s:o, s:o}", json_pack ("{s:o, s:I, s:I, s:o}",
"amount", TALER_JSON_from_amount (&rii->details.amount), "amount", TALER_JSON_from_amount (&rii->details.amount),
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off, "bank_row", (json_int_t) row_off,
row_off_size), "reserve_pub", GNUNET_JSON_from_data_auto (
"wtid", GNUNET_JSON_from_data_auto ( &rii->details.reserve_pub)));
&rii->details.wtid)));
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_missattribution_in, TALER_amount_add (&total_missattribution_in,
&total_missattribution_in, &total_missattribution_in,
@ -1836,11 +1781,10 @@ history_credit_cb (void *cls,
rii->details.execution_date.abs_value_us) rii->details.execution_date.abs_value_us)
{ {
report (report_row_minor_inconsistencies, report (report_row_minor_inconsistencies,
json_pack ("{s:s, s:I, s:o, s:s}", json_pack ("{s:s, s:I, s:I, s:s}",
"table", "reserves_in", "table", "reserves_in",
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"raw_bank_row", GNUNET_JSON_from_data (row_off, "bank_row", (json_int_t) row_off,
row_off_size),
"diagnostic", "execution date missmatch")); "diagnostic", "execution date missmatch"));
} }
cleanup: cleanup:
@ -1865,7 +1809,6 @@ static void
process_credits (void *cls) process_credits (void *cls)
{ {
struct WireAccount *wa = cls; struct WireAccount *wa = cls;
struct TALER_WIRE_Plugin *wp;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
/* skip accounts where CREDIT is not enabled */ /* skip accounts where CREDIT is not enabled */
@ -1899,16 +1842,15 @@ process_credits (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Starting bank CREDIT history of account `%s'\n", "Starting bank CREDIT history of account `%s'\n",
wa->section_name); wa->section_name);
wp = wa->wire_plugin; wa->chh = TALER_BANK_credit_history (ctx,
wa->hh = wp->get_history (wp->cls, wa->account.details.x_taler_bank.
wa->section_name, account_base_url,
TALER_BANK_DIRECTION_CREDIT, &wa->auth,
wa->in_wire_off, wa->in_wire_off,
wa->wire_off_size,
INT64_MAX, INT64_MAX,
&history_credit_cb, &history_credit_cb,
wa); wa);
if (NULL == wa->hh) if (NULL == wa->chh)
{ {
fprintf (stderr, fprintf (stderr,
"Failed to obtain bank transaction history\n"); "Failed to obtain bank transaction history\n");
@ -2037,8 +1979,7 @@ begin_transaction ()
wa->section_name, wa->section_name,
&wa->pp, &wa->pp,
&wa->in_wire_off, &wa->in_wire_off,
&wa->out_wire_off, &wa->out_wire_off);
&wa->wire_off_size);
if (0 > qsx) if (0 > qsx)
{ {
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx); GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
@ -2096,30 +2037,62 @@ process_account_cb (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai) const struct TALER_EXCHANGEDB_AccountInfo *ai)
{ {
struct WireAccount *wa; struct WireAccount *wa;
struct TALER_WIRE_Plugin *wp;
if ( (GNUNET_NO == ai->debit_enabled) && if ( (GNUNET_NO == ai->debit_enabled) &&
(GNUNET_NO == ai->credit_enabled) ) (GNUNET_NO == ai->credit_enabled) )
return; /* not an active exchange account */ return; /* not an active exchange account */
wp = TALER_WIRE_plugin_load (cfg,
ai->plugin_name);
if (NULL == wp)
{
fprintf (stderr,
"Failed to load wire plugin `%s'\n",
ai->plugin_name);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Found exchange account `%s'\n", "Found exchange account `%s'\n",
ai->section_name); ai->section_name);
wa = GNUNET_new (struct WireAccount); wa = GNUNET_new (struct WireAccount);
wa->wire_plugin = wp;
wa->section_name = GNUNET_strdup (ai->section_name); wa->section_name = GNUNET_strdup (ai->section_name);
wa->watch_debit = ai->debit_enabled; wa->watch_debit = ai->debit_enabled;
wa->watch_credit = ai->credit_enabled; wa->watch_credit = ai->credit_enabled;
if (GNUNET_OK !=
TALER_BANK_auth_parse_cfg (cfg,
ai->section_name,
&wa->auth))
{
GNUNET_break (0);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
fprintf (stderr,
"Failed to access bank account `%s'\n",
wa->section_name);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (GNUNET_OK !=
TALER_BANK_account_parse_cfg (cfg,
wa->section_name,
&wa->account))
{
GNUNET_break (0);
TALER_BANK_auth_free (&wa->auth);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
fprintf (stderr,
"Failed to access bank account `%s'\n",
wa->section_name);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
if (TALER_PAC_X_TALER_BANK != wa->account.type)
{
GNUNET_break (0);
TALER_WIRE_account_free (&wa->account);
TALER_BANK_auth_free (&wa->auth);
GNUNET_free (wa->section_name);
GNUNET_free (wa);
fprintf (stderr,
"Need x-taler-bank account URL in `%s'\n",
wa->section_name);
global_ret = 1;
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_CONTAINER_DLL_insert (wa_head, GNUNET_CONTAINER_DLL_insert (wa_head,
wa_tail, wa_tail,
wa); wa);
@ -2258,6 +2231,14 @@ run (void *cls,
} }
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL); NULL);
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
if (NULL == ctx)
{
GNUNET_break (0);
return;
}
esession = edb->get_session (edb->cls); esession = edb->get_session (edb->cls);
if (NULL == esession) if (NULL == esession)
{ {

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2018 GNUnet e.V. Copyright (C) 2014-2020 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
@ -241,8 +241,8 @@ postgres_create_tables (void *cls)
",account_name TEXT NOT NULL" ",account_name TEXT NOT NULL"
",last_wire_reserve_in_serial_id INT8 NOT NULL DEFAULT 0" ",last_wire_reserve_in_serial_id INT8 NOT NULL DEFAULT 0"
",last_wire_wire_out_serial_id INT8 NOT NULL DEFAULT 0" ",last_wire_wire_out_serial_id INT8 NOT NULL DEFAULT 0"
",wire_in_off BYTEA" ",wire_in_off INT8"
",wire_out_off BYTEA" ",wire_out_off INT8"
")"), ")"),
GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS wire_auditor_progress" GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS wire_auditor_progress"
"(master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE" "(master_pub BYTEA CONSTRAINT master_pub_ref REFERENCES auditor_exchanges(master_pub) ON DELETE CASCADE"
@ -2135,23 +2135,16 @@ postgres_insert_wire_auditor_account_progress (void *cls,
const struct const struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
const void *in_wire_off, uint64_t in_wire_off,
const void *out_wire_off, uint64_t out_wire_off)
size_t wire_off_size)
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_string (account_name),
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
GNUNET_PQ_query_param_fixed_size (in_wire_off, GNUNET_PQ_query_param_uint64 (&in_wire_off),
NULL == in_wire_off GNUNET_PQ_query_param_uint64 (&out_wire_off),
? 0
: wire_off_size),
GNUNET_PQ_query_param_fixed_size (out_wire_off,
NULL == out_wire_off
? 0
: wire_off_size),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
@ -2182,21 +2175,14 @@ postgres_update_wire_auditor_account_progress (void *cls,
const struct const struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
const void *in_wire_off, uint64_t in_wire_off,
const void *out_wire_off, uint64_t out_wire_off)
size_t wire_off_size)
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id), GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
GNUNET_PQ_query_param_fixed_size (in_wire_off, GNUNET_PQ_query_param_uint64 (&in_wire_off),
NULL == in_wire_off GNUNET_PQ_query_param_uint64 (&out_wire_off),
? 0
: wire_off_size),
GNUNET_PQ_query_param_fixed_size (out_wire_off,
NULL == out_wire_off
? 0
: wire_off_size),
GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_string (account_name),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
@ -2231,12 +2217,9 @@ postgres_get_wire_auditor_account_progress (void *cls,
struct struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
void **in_wire_off, uint64_t *in_wire_off,
void **out_wire_off, uint64_t *out_wire_off)
size_t *wire_off_size)
{ {
size_t xsize;
enum GNUNET_DB_QueryStatus qs;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (master_pub), GNUNET_PQ_query_param_auto_from_type (master_pub),
GNUNET_PQ_query_param_string (account_name), GNUNET_PQ_query_param_string (account_name),
@ -2247,30 +2230,17 @@ postgres_get_wire_auditor_account_progress (void *cls,
&pp->last_reserve_in_serial_id), &pp->last_reserve_in_serial_id),
GNUNET_PQ_result_spec_uint64 ("last_wire_wire_out_serial_id", GNUNET_PQ_result_spec_uint64 ("last_wire_wire_out_serial_id",
&pp->last_wire_out_serial_id), &pp->last_wire_out_serial_id),
GNUNET_PQ_result_spec_variable_size ("wire_in_off", GNUNET_PQ_result_spec_uint64 ("wire_in_off",
in_wire_off, in_wire_off),
wire_off_size), GNUNET_PQ_result_spec_uint64 ("wire_out_off",
GNUNET_PQ_result_spec_variable_size ("wire_out_off", out_wire_off),
out_wire_off,
&xsize),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"wire_auditor_account_progress_select", "wire_auditor_account_progress_select",
params, params,
rs); rs);
if (qs <= 0)
{
*wire_off_size = 0;
xsize = 0;
}
if ( (0 != xsize) &&
(0 != *wire_off_size) )
{
GNUNET_assert (xsize == *wire_off_size);
}
return qs;
} }

View File

@ -39,8 +39,9 @@ libtalerbank_la_LDFLAGS = \
libtalerbank_la_SOURCES = \ libtalerbank_la_SOURCES = \
bank_api_admin.c \ bank_api_admin.c \
bank_api_common.c bank_api_common.h \ bank_api_common.c bank_api_common.h \
bank_api_history.c \ bank_api_credit.c \
bank_api_reject.c \ bank_api_debit.c \
bank_api_transaction.c \
bank_api_parse.c bank_api_parse.c
libtalerbank_la_LIBADD = \ libtalerbank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
@ -55,10 +56,10 @@ libtalerfakebank_la_LDFLAGS = \
-version-info 0:0:0 \ -version-info 0:0:0 \
-no-undefined -no-undefined
libtalerfakebank_la_SOURCES = \ libtalerfakebank_la_SOURCES = \
fakebank_history.c \ fakebank.c
fakebank.c fakebank.h
libtalerfakebank_la_LIBADD = \ libtalerfakebank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/mhd/libtalermhd.la \
-lgnunetjson \ -lgnunetjson \
-lgnunetutil \ -lgnunetutil \
-ljansson \ -ljansson \
@ -69,8 +70,8 @@ libtalerbanktesting_la_LDFLAGS = \
-version-info 0:0:0 \ -version-info 0:0:0 \
-no-undefined -no-undefined
libtalerbanktesting_la_SOURCES = \ libtalerbanktesting_la_SOURCES = \
testing_api_cmd_history.c \ testing_api_cmd_history_credit.c \
testing_api_cmd_reject.c \ testing_api_cmd_history_debit.c \
testing_api_helpers.c testing_api_helpers.c
libtalerbanktesting_la_LIBADD = \ libtalerbanktesting_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015, 2016, 2017 Taler Systems SA Copyright (C) 2015--2020 Taler Systems SA
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
@ -166,13 +166,11 @@ handle_admin_add_incoming_finished (void *cls,
* to the operators of the bank. * to the operators of the bank.
* *
* @param ctx curl context for the event loop * @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request) * @param account_base_url URL of the bank (used to execute this request)
* @param auth authentication data to send to the bank * @param auth authentication data to send to the bank
* @param exchange_base_url base URL of the exchange (for tracking) * @param reserve_pub wire transfer subject for the transfer
* @param subject wire transfer subject for the transfer
* @param amount amount that was deposited * @param amount amount that was deposited
* @param debit_account_no account number to withdraw from (53 bits at most) * @param credit_account account to deposit into (payto)
* @param credit_account_no account number to deposit into (53 bits at most)
* @param res_cb the callback to call when the final result for this request is available * @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback * @param res_cb_cls closure for the above callback
* @return NULL * @return NULL
@ -181,13 +179,12 @@ handle_admin_add_incoming_finished (void *cls,
*/ */
struct TALER_BANK_AdminAddIncomingHandle * struct TALER_BANK_AdminAddIncomingHandle *
TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx, TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *account_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url, const struct
const char *subject, TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
uint64_t debit_account_no, const char *credit_account,
uint64_t credit_account_no,
TALER_BANK_AdminAddIncomingResultCallback res_cb, TALER_BANK_AdminAddIncomingResultCallback res_cb,
void *res_cb_cls) void *res_cb_cls)
{ {
@ -195,18 +192,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
json_t *admin_obj; json_t *admin_obj;
CURL *eh; CURL *eh;
if (NULL == exchange_base_url) admin_obj = json_pack ("{s:o, s:o, s:s}",
{ "subject", GNUNET_JSON_from_data_auto (reserve_pub),
GNUNET_break (0);
return NULL;
}
admin_obj = json_pack ("{s:{s:s}, s:s, s:s, s:o, s:I, s:I}",
"auth", "type", "basic",
"exchange_url", exchange_base_url,
"subject", subject,
"amount", TALER_JSON_from_amount (amount), "amount", TALER_JSON_from_amount (amount),
"debit_account", (json_int_t) debit_account_no, "credit_account", credit_account);
"credit_account", (json_int_t) credit_account_no);
if (NULL == admin_obj) if (NULL == admin_obj)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -215,26 +204,32 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle); aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle);
aai->cb = res_cb; aai->cb = res_cb;
aai->cb_cls = res_cb_cls; aai->cb_cls = res_cb_cls;
aai->request_url = TALER_BANK_path_to_url_ (bank_base_url, aai->request_url = TALER_BANK_path_to_url_ (account_base_url,
"/admin/add/incoming"); "/admin/add/incoming");
aai->post_ctx.headers = TALER_BANK_make_auth_header_ (auth); aai->post_ctx.headers = curl_slist_append
GNUNET_assert
(NULL != (aai->post_ctx.headers = curl_slist_append
(aai->post_ctx.headers, (aai->post_ctx.headers,
"Content-Type: application/json"))); "Content-Type: application/json");
eh = curl_easy_init (); eh = curl_easy_init ();
if ( (GNUNET_OK !=
GNUNET_assert (GNUNET_OK == TALER_BANK_setup_auth_ (eh,
TALER_curl_easy_post (&aai->post_ctx, eh, admin_obj)); auth)) ||
(CURLE_OK !=
json_decref (admin_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh, curl_easy_setopt (eh,
CURLOPT_URL, CURLOPT_URL,
aai->request_url)); aai->request_url)) ||
(GNUNET_OK !=
TALER_curl_easy_post (&aai->post_ctx,
eh,
admin_obj)) )
{
GNUNET_break (0);
TALER_BANK_admin_add_incoming_cancel (aai);
curl_easy_cleanup (eh);
json_decref (admin_obj);
return NULL;
}
json_decref (admin_obj);
aai->job = GNUNET_CURL_job_add2 (ctx, aai->job = GNUNET_CURL_job_add2 (ctx,
eh, eh,

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015, 2016, 2017 GNUnet e.V. Copyright (C) 2015-2020 Taler Systems SA
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
@ -24,67 +24,47 @@
/** /**
* Append HTTP key-value pair to curl header list. * Set authentication data in @a easy from @a auth.
* The API currently specifies the use of HTTP basic
* authentication.
* *
* @param hdr list to append to, can be NULL * @param easy curl handle to setup for authentication
* @param key key to append * @param auth authentication data to use
* @param value value to append * @return #GNUNET_OK in success
* @return new list, NULL on error
*/ */
static struct curl_slist * int
append (struct curl_slist *hdr, TALER_BANK_setup_auth_ (CURL *easy,
const char *key, const struct TALER_BANK_AuthenticationData *auth)
const char *value)
{ {
char *str; int ret;
struct curl_slist *ret;
GNUNET_asprintf (&str,
"%s: %s",
key,
value);
ret = curl_slist_append (hdr,
str);
GNUNET_free (str);
if (NULL == ret)
{
GNUNET_break (0);
curl_slist_free_all (hdr);
return NULL;
}
return ret;
}
/**
* Build authentication header from @a auth.
*
* @param auth authentication data to use.
*
* @return NULL on error, otherwise curl headers to use.
*/
struct curl_slist *
TALER_BANK_make_auth_header_
(const struct TALER_BANK_AuthenticationData *auth)
{
struct curl_slist *authh;
ret = GNUNET_OK;
switch (auth->method) switch (auth->method)
{ {
case TALER_BANK_AUTH_NONE: case TALER_BANK_AUTH_NONE:
return NULL; return GNUNET_OK;
case TALER_BANK_AUTH_BASIC: case TALER_BANK_AUTH_BASIC:
authh = append (NULL, {
"X-Taler-Bank-Username", char *up;
auth->details.basic.username);
if (NULL == authh) GNUNET_asprintf (&up,
return NULL; "%s:%s",
authh = append (authh, auth->details.basic.username,
"X-Taler-Bank-Password",
auth->details.basic.password); auth->details.basic.password);
return authh; if ( (CURLE_OK !=
curl_easy_setopt (easy,
CURLOPT_HTTPAUTH,
CURLAUTH_BASIC)) ||
(CURLE_OK !=
curl_easy_setopt (easy,
CURLOPT_USERPWD,
up)) )
ret = GNUNET_SYSERR;
GNUNET_free (up);
break;
} }
return NULL; }
return ret;
} }

View File

@ -39,6 +39,18 @@ struct curl_slist *
TALER_BANK_make_auth_header_ (const struct TALER_BANK_AuthenticationData *auth); TALER_BANK_make_auth_header_ (const struct TALER_BANK_AuthenticationData *auth);
/**
* Set authentication data in @a easy from @a auth.
*
* @param easy curl handle to setup for authentication
* @param auth authentication data to use
* @return #GNUNET_OK in success
*/
int
TALER_BANK_setup_auth_ (CURL *easy,
const struct TALER_BANK_AuthenticationData *auth);
/** /**
* Obtain the URL to use for an API request. * Obtain the URL to use for an API request.
* *

View File

@ -0,0 +1,315 @@
/*
This file is part of TALER
Copyright (C) 2017--2020 Taler Systems SA
TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3,
or (at your option) any later version.
TALER is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not,
see <http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/bank_api_history.c
* @brief Implementation of the /history[-range]
* requests of the bank's HTTP API.
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "bank_api_common.h"
#include <microhttpd.h> /* just for HTTP status codes */
#include "taler_signatures.h"
/**
* @brief A /history Handle
*/
struct TALER_BANK_CreditHistoryHandle
{
/**
* The url for this request.
*/
char *request_url;
/**
* The base URL of the bank.
*/
char *bank_base_url;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* Function to call with the result.
*/
TALER_BANK_CreditResultCallback hcb;
/**
* Closure for @a cb.
*/
void *hcb_cls;
};
/**
* Parse history given in JSON format and invoke the callback on each item.
*
* @param hh handle to the account history request
* @param history JSON array with the history
* @return #GNUNET_OK if history was valid and @a rhistory and @a balance
* were set,
* #GNUNET_SYSERR if there was a protocol violation in @a history
*/
static int
parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
const json_t *history)
{
json_t *history_array;
if (NULL == (history_array = json_object_get (history,
"data")))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (! json_is_array (history_array))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
for (unsigned int i = 0; i<json_array_size (history_array); i++)
{
struct TALER_BANK_CreditDetails td;
uint64_t row_id;
struct GNUNET_JSON_Specification hist_spec[] = {
TALER_JSON_spec_amount ("amount",
&td.amount),
GNUNET_JSON_spec_absolute_time ("date",
&td.execution_date),
GNUNET_JSON_spec_uint64 ("row_id",
&row_id),
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
&td.reserve_pub),
GNUNET_JSON_spec_string ("counterpart",
&td.account_url),
GNUNET_JSON_spec_end ()
};
json_t *transaction = json_array_get (history_array,
i);
if (GNUNET_OK !=
GNUNET_JSON_parse (transaction,
hist_spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
row_id,
&td,
transaction);
GNUNET_JSON_parse_free (hist_spec);
}
return GNUNET_OK;
}
/**
* Function called when we're done processing the
* HTTP /history request.
*
* @param cls the `struct TALER_BANK_CreditHistoryHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
static void
handle_history_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_BANK_CreditHistoryHandle *hh = cls;
enum TALER_ErrorCode ec;
const json_t *j = response;
hh->job = NULL;
switch (response_code)
{
case 0:
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
parse_account_history (hh,
j))
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
}
response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
ec = TALER_EC_NONE;
break;
case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (j);
response_code = 0;
break;
}
hh->hcb (hh->hcb_cls,
response_code,
ec,
0LLU,
NULL,
j);
TALER_BANK_credit_history_cancel (hh);
}
/**
* Request the credit history of the exchange's bank account.
*
* @param ctx curl context for the event loop
* @param bank_base_url URL of the base INCLUDING account number
* @param auth authentication data to use
* @param start_row from which row on do we want to get results,
* use UINT64_MAX for the latest; exclusive
* @param num_results how many results do we want;
* negative numbers to go into the past, positive numbers
* to go into the future starting at @a start_row;
* must not be zero.
* @param hres_cb the callback to call with the transaction
* history
* @param hres_cb_cls closure for the above callback
* @return NULL if the inputs are invalid (i.e. zero value for
* @e num_results). In this case, the callback is not
* called.
*/
struct TALER_BANK_CreditHistoryHandle *
TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
uint64_t start_row,
int64_t num_results,
TALER_BANK_CreditResultCallback hres_cb,
void *hres_cb_cls)
{
char *url;
struct TALER_BANK_CreditHistoryHandle *hh;
CURL *eh;
if (0 == num_results)
{
GNUNET_break (0);
return NULL;
}
if (UINT64_MAX == start_row)
GNUNET_asprintf (&url,
"/history&delta=%lld",
(long long) num_results);
else
GNUNET_asprintf (&url,
"/history&delta=%lld&start=%llu",
(long long) num_results,
start_row);
hh = GNUNET_new (struct TALER_BANK_CreditHistoryHandle);
hh->hcb = hres_cb;
hh->hcb_cls = hres_cb_cls;
hh->bank_base_url = GNUNET_strdup (bank_base_url);
hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
url);
eh = curl_easy_init ();
if ( (GNUNET_OK !=
TALER_BANK_setup_auth_ (eh,
auth)) ||
(CURLE_OK !=
curl_easy_setopt (eh,
CURLOPT_URL,
hh->request_url)) )
{
GNUNET_break (0);
TALER_BANK_credit_history_cancel (hh);
curl_easy_cleanup (eh);
GNUNET_free (url);
return NULL;
}
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
NULL,
&handle_history_finished,
hh);
GNUNET_free (url);
return hh;
}
/**
* Cancel a history request. This function cannot be
* used on a request handle if a response is already
* served for it.
*
* @param hh the history request handle
*/
void
TALER_BANK_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh)
{
if (NULL != hh->job)
{
GNUNET_CURL_job_cancel (hh->job);
hh->job = NULL;
}
GNUNET_free (hh->request_url);
GNUNET_free (hh->bank_base_url);
GNUNET_free (hh);
}
/* end of bank_api_credit.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2017 GNUnet e.V. & Inria Copyright (C) 2017--2020 Taler Systems SA
TALER is free software; you can redistribute it and/or TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -32,7 +32,7 @@
/** /**
* @brief A /history Handle * @brief A /history Handle
*/ */
struct TALER_BANK_HistoryHandle struct TALER_BANK_DebitHistoryHandle
{ {
/** /**
@ -50,15 +50,10 @@ struct TALER_BANK_HistoryHandle
*/ */
struct GNUNET_CURL_Job *job; struct GNUNET_CURL_Job *job;
/**
* HTTP authentication-related headers for the request.
*/
struct curl_slist *authh;
/** /**
* Function to call with the result. * Function to call with the result.
*/ */
TALER_BANK_HistoryResultCallback hcb; TALER_BANK_DebitResultCallback hcb;
/** /**
* Closure for @a cb. * Closure for @a cb.
@ -77,37 +72,39 @@ struct TALER_BANK_HistoryHandle
* #GNUNET_SYSERR if there was a protocol violation in @a history * #GNUNET_SYSERR if there was a protocol violation in @a history
*/ */
static int static int
parse_account_history (struct TALER_BANK_HistoryHandle *hh, parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
const json_t *history) const json_t *history)
{ {
json_t *history_array; json_t *history_array;
char *bank_hostname;
if (NULL == (history_array = json_object_get (history, "data"))) if (NULL == (history_array = json_object_get (history,
"data")))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (! json_is_array (history_array))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
for (unsigned int i = 0; i<json_array_size (history_array); i++) for (unsigned int i = 0; i<json_array_size (history_array); i++)
{ {
struct TALER_BANK_TransferDetails td; struct TALER_BANK_DebitDetails td;
const char *sign;
uint64_t other_account;
uint64_t row_id; uint64_t row_id;
enum TALER_BANK_Direction direction;
struct GNUNET_JSON_Specification hist_spec[] = { struct GNUNET_JSON_Specification hist_spec[] = {
GNUNET_JSON_spec_string ("sign",
&sign),
TALER_JSON_spec_amount ("amount", TALER_JSON_spec_amount ("amount",
&td.amount), &td.amount),
GNUNET_JSON_spec_absolute_time ("date", GNUNET_JSON_spec_absolute_time ("date",
&td.execution_date), &td.execution_date),
GNUNET_JSON_spec_uint64 ("row_id", GNUNET_JSON_spec_uint64 ("row_id",
&row_id), &row_id),
GNUNET_JSON_spec_string ("wt_subject", GNUNET_JSON_spec_fixed_auto ("wtid",
(const char **) &td.wire_transfer_subject), &td.wtid),
GNUNET_JSON_spec_uint64 ("counterpart", GNUNET_JSON_spec_string ("counterpart",
&other_account), &td.account_url),
GNUNET_JSON_spec_string ("exchange_base_url",
&td.exchange_base_url),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
json_t *transaction = json_array_get (history_array, json_t *transaction = json_array_get (history_array,
@ -121,45 +118,12 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (0 == strcasecmp (sign,
"+"))
direction = TALER_BANK_DIRECTION_CREDIT;
else if (0 == strcasecmp (sign,
"-"))
direction = TALER_BANK_DIRECTION_DEBIT;
else if (0 == strcasecmp (sign,
"cancel+"))
direction = TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_CANCEL;
else if (0 == strcasecmp (sign,
"cancel-"))
direction = TALER_BANK_DIRECTION_DEBIT | TALER_BANK_DIRECTION_CANCEL;
else
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (hist_spec);
return GNUNET_SYSERR;
}
/* Note, bank_base_url has _always_ the protocol scheme
* and it proved to be good at this point. */
bank_hostname = strchr (hh->bank_base_url, ':');
GNUNET_assert (NULL != bank_hostname);
bank_hostname += 3;
GNUNET_asprintf (&td.account_url,
('/' == bank_hostname[strlen (bank_hostname) - 1])
? "payto://x-taler-bank/%s%llu"
: "payto://x-taler-bank/%s/%llu",
bank_hostname,
(unsigned long long) other_account);
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
MHD_HTTP_OK, MHD_HTTP_OK,
TALER_EC_NONE, TALER_EC_NONE,
direction,
row_id, row_id,
&td, &td,
transaction); transaction);
GNUNET_free (td.account_url);
GNUNET_JSON_parse_free (hist_spec); GNUNET_JSON_parse_free (hist_spec);
} }
return GNUNET_OK; return GNUNET_OK;
@ -170,7 +134,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
* Function called when we're done processing the * Function called when we're done processing the
* HTTP /history request. * HTTP /history request.
* *
* @param cls the `struct TALER_BANK_HistoryHandle` * @param cls the `struct TALER_BANK_DebitHistoryHandle`
* @param response_code HTTP response code, 0 on error * @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error * @param response parsed JSON result, NULL on error
*/ */
@ -179,7 +143,7 @@ handle_history_finished (void *cls,
long response_code, long response_code,
const void *response) const void *response)
{ {
struct TALER_BANK_HistoryHandle *hh = cls; struct TALER_BANK_DebitHistoryHandle *hh = cls;
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
const json_t *j = response; const json_t *j = response;
@ -243,120 +207,19 @@ handle_history_finished (void *cls,
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
response_code, response_code,
ec, ec,
TALER_BANK_DIRECTION_NONE,
0LLU, 0LLU,
NULL, NULL,
j); j);
TALER_BANK_history_cancel (hh); TALER_BANK_debit_history_cancel (hh);
} }
/** /**
* Backend of both the /history[-range] requests. * Request the debit history of the exchange's bank account.
* *
* @param ctx curl context for the event loop * @param ctx curl context for the event loop
* @param bank_base_url base URL of the bank. * @param bank_base_url URL of the base INCLUDING account number
* @param urlargs path + URL arguments.
* @param auth authentication data to use * @param auth authentication data to use
* @param hres_cb the callback to call with the transaction
* history
* @param hres_cb_cls closure for the above callback
* @return NULL if the inputs are invalid (i.e. zero value for
* @e num_results). In this case, the callback is not
* called.
*/
static struct TALER_BANK_HistoryHandle *
put_history_job (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const char *urlargs,
const struct TALER_BANK_AuthenticationData *auth,
TALER_BANK_HistoryResultCallback hres_cb,
void *hres_cb_cls)
{
struct TALER_BANK_HistoryHandle *hh;
CURL *eh;
hh = GNUNET_new (struct TALER_BANK_HistoryHandle);
hh->hcb = hres_cb;
hh->hcb_cls = hres_cb_cls;
hh->bank_base_url = GNUNET_strdup (bank_base_url);
hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
urlargs);
hh->authh = TALER_BANK_make_auth_header_ (auth);
eh = curl_easy_init ();
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
hh->request_url));
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
hh->authh,
&handle_history_finished,
hh);
return hh;
}
/**
* Convert fixed value 'direction' into string.
*
* @param direction the value to convert.
* @return string representation of @a direction. NULL on error
*/
static const char *
conv_direction (enum TALER_BANK_Direction direction)
{
if (TALER_BANK_DIRECTION_NONE == direction)
{
/* Should just never happen. */
GNUNET_break (0);
return NULL;
}
if (TALER_BANK_DIRECTION_BOTH ==
(TALER_BANK_DIRECTION_BOTH & direction))
return "both";
else if (TALER_BANK_DIRECTION_CREDIT ==
(TALER_BANK_DIRECTION_CREDIT & direction))
return "credit";
else if (TALER_BANK_DIRECTION_DEBIT ==
(TALER_BANK_DIRECTION_BOTH & direction)) /*why use 'both' flag?*/
return "debit";
/* Should just never happen. */
GNUNET_break (0);
return NULL;
}
/**
* Convert fixed value 'direction' into string representation
* of the "cancel" argument.
*
* @param direction the value to convert.
* @return string representation of @a direction
*/
static const char *
conv_cancel (enum TALER_BANK_Direction direction)
{
if (TALER_BANK_DIRECTION_CANCEL ==
(TALER_BANK_DIRECTION_CANCEL & direction))
return "show";
return "omit";
}
/**
* Request the wire transfer history of a bank account.
*
* @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this
* request)
* @param auth authentication data to use
* @param account_number which account number should we query
* @param direction what kinds of wire transfers should be
* returned
* @param ascending if GNUNET_YES, history elements will
* be returned in chronological order.
* @param start_row from which row on do we want to get results, * @param start_row from which row on do we want to get results,
* use UINT64_MAX for the latest; exclusive * use UINT64_MAX for the latest; exclusive
* @param num_results how many results do we want; * @param num_results how many results do we want;
@ -370,20 +233,18 @@ conv_cancel (enum TALER_BANK_Direction direction)
* @e num_results). In this case, the callback is not * @e num_results). In this case, the callback is not
* called. * called.
*/ */
struct TALER_BANK_HistoryHandle * struct TALER_BANK_DebitHistoryHandle *
TALER_BANK_history (struct GNUNET_CURL_Context *ctx, TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number,
enum TALER_BANK_Direction direction,
unsigned int ascending,
uint64_t start_row, uint64_t start_row,
int64_t num_results, int64_t num_results,
TALER_BANK_HistoryResultCallback hres_cb, TALER_BANK_DebitResultCallback hres_cb,
void *hres_cb_cls) void *hres_cb_cls)
{ {
struct TALER_BANK_HistoryHandle *hh;
char *url; char *url;
struct TALER_BANK_DebitHistoryHandle *hh;
CURL *eh;
if (0 == num_results) if (0 == num_results)
{ {
@ -393,28 +254,40 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
if (UINT64_MAX == start_row) if (UINT64_MAX == start_row)
GNUNET_asprintf (&url, GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s&ordering=%s", "/history&delta=%lld",
(unsigned long long) account_number, (long long) num_results);
(long long) num_results,
conv_direction (direction),
conv_cancel (direction),
(GNUNET_YES == ascending) ? "ascending" : "descending");
else else
GNUNET_asprintf (&url, GNUNET_asprintf (&url,
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s&ordering=%s&start=%llu", "/history&delta=%lld&start=%llu",
(unsigned long long) account_number,
(long long) num_results, (long long) num_results,
conv_direction (direction),
conv_cancel (direction),
(GNUNET_YES == ascending) ? "ascending" : "descending",
start_row); start_row);
hh = put_history_job (ctx, hh = GNUNET_new (struct TALER_BANK_DebitHistoryHandle);
bank_base_url, hh->hcb = hres_cb;
url, hh->hcb_cls = hres_cb_cls;
auth, hh->bank_base_url = GNUNET_strdup (bank_base_url);
hres_cb, hh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
hres_cb_cls); url);
eh = curl_easy_init ();
if ( (GNUNET_OK !=
TALER_BANK_setup_auth_ (eh,
auth)) ||
(CURLE_OK !=
curl_easy_setopt (eh,
CURLOPT_URL,
hh->request_url)) )
{
GNUNET_break (0);
TALER_BANK_debit_history_cancel (hh);
curl_easy_cleanup (eh);
GNUNET_free (url);
return NULL;
}
hh->job = GNUNET_CURL_job_add2 (ctx,
eh,
NULL,
&handle_history_finished,
hh);
GNUNET_free (url); GNUNET_free (url);
return hh; return hh;
} }
@ -428,18 +301,17 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
* @param hh the history request handle * @param hh the history request handle
*/ */
void void
TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh) TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh)
{ {
if (NULL != hh->job) if (NULL != hh->job)
{ {
GNUNET_CURL_job_cancel (hh->job); GNUNET_CURL_job_cancel (hh->job);
hh->job = NULL; hh->job = NULL;
} }
curl_slist_free_all (hh->authh);
GNUNET_free (hh->request_url); GNUNET_free (hh->request_url);
GNUNET_free (hh->bank_base_url); GNUNET_free (hh->bank_base_url);
GNUNET_free (hh); GNUNET_free (hh);
} }
/* end of bank_api_history.c */ /* end of bank_api_debit.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2018 Taler Systems SA Copyright (C) 2018-2020 Taler Systems SA
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
@ -23,6 +23,58 @@
#include "taler_bank_service.h" #include "taler_bank_service.h"
/**
* Convenience method for parsing configuration section with bank account data.
*
* @param cfg configuration to parse
* @param section the section with the configuration data
* @param acc[out] set to the account details
* @return #GNUNET_OK on success
*/
int
TALER_BANK_account_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section,
struct TALER_Account *acc)
{
char *account_url;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
section,
"URL",
&account_url))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
section,
"URL");
return GNUNET_SYSERR;
}
if (TALER_EC_NONE !=
TALER_WIRE_payto_to_account (account_url,
acc))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section,
"URL",
"Malformed payto:// URL for x-taler-bank method");
GNUNET_free (account_url);
return GNUNET_SYSERR;
}
if (TALER_PAC_X_TALER_BANK != acc->type)
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
section,
"URL",
"Malformed payto:// URL for x-taler-bank method");
GNUNET_free (account_url);
TALER_WIRE_account_free (acc);
return GNUNET_SYSERR;
}
GNUNET_free (account_url);
return GNUNET_OK;
}
/** /**
* Parse configuration section with bank authentication data. * Parse configuration section with bank authentication data.
* *

View File

@ -1,242 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2015, 2016, 2017 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/bank_api_reject.c
* @brief Implementation of the /reject request of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include "bank_api_common.h"
#include <microhttpd.h> /* just for HTTP status codes */
#include "taler_signatures.h"
/**
* @brief A /reject Handle
*/
struct TALER_BANK_RejectHandle
{
/**
* The url for this request.
*/
char *request_url;
/**
* JSON encoding of the request to POST.
*/
char *json_enc;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* HTTP authentication-related headers for the request.
*/
struct curl_slist *authh;
/**
* Function to call with the result.
*/
TALER_BANK_RejectResultCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
};
/**
* Function called when we're done processing the
* HTTP /reject request.
*
* @param cls the `struct TALER_BANK_RejectHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
static void
handle_reject_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_BANK_RejectHandle *rh = cls;
enum TALER_ErrorCode ec;
const json_t *j = response;
rh->job = NULL;
switch (response_code)
{
case 0:
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (j);
response_code = 0;
break;
}
rh->cb (rh->cb_cls,
response_code,
ec);
TALER_BANK_reject_cancel (rh);
}
/**
* Request rejection of a wire transfer, marking it as cancelled and voiding
* its effects.
*
* @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request)
* @param auth authentication data to use
* @param account_number which account number should we query
* @param rowid transfer to reject
* @param rcb the callback to call with the operation result
* @param rcb_cls closure for @a rcb
* @return NULL
* if the inputs are invalid.
* In this case, the callback is not called.
*/
struct TALER_BANK_RejectHandle *
TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number,
uint64_t rowid,
TALER_BANK_RejectResultCallback rcb,
void *rcb_cls)
{
struct TALER_BANK_RejectHandle *rh;
json_t *reject_obj;
CURL *eh;
reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
"auth", "type", "basic",
"row_id", (json_int_t) rowid,
"account_number", (json_int_t) account_number);
if (NULL == reject_obj)
{
GNUNET_break (0);
return NULL;
}
rh = GNUNET_new (struct TALER_BANK_RejectHandle);
rh->cb = rcb;
rh->cb_cls = rcb_cls;
rh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
"/reject");
rh->authh = TALER_BANK_make_auth_header_ (auth);
/* Append content type header here, can't do it in GNUNET_CURL_job_add
as that would override the CURLOPT_HTTPHEADER instead of appending. */
{
struct curl_slist *ext;
ext = curl_slist_append (rh->authh,
"Content-Type: application/json");
if (NULL == ext)
GNUNET_break (0);
else
rh->authh = ext;
}
eh = curl_easy_init ();
GNUNET_assert (NULL != (rh->json_enc =
json_dumps (reject_obj,
JSON_COMPACT)));
json_decref (reject_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
rh->request_url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDS,
rh->json_enc));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDSIZE,
strlen (rh->json_enc)));
rh->job = GNUNET_CURL_job_add2 (ctx,
eh,
rh->authh,
&handle_reject_finished,
rh);
return rh;
}
/**
* Cancel an reject request. This function cannot be used on a request
* handle if the response was is already served for it.
*
* @param rh the reject request handle
*/
void
TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh)
{
if (NULL != rh->job)
{
GNUNET_CURL_job_cancel (rh->job);
rh->job = NULL;
}
curl_slist_free_all (rh->authh);
GNUNET_free (rh->request_url);
GNUNET_free (rh->json_enc);
GNUNET_free (rh);
}
/* end of bank_api_reject.c */

View File

@ -0,0 +1,368 @@
/*
This file is part of TALER
Copyright (C) 2015--2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/bank_api_transaction.c
* @brief Implementation of the /transaction/ requests of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include "bank_api_common.h"
#include <microhttpd.h> /* just for HTTP status codes */
#include "taler_signatures.h"
#include "taler_curl_lib.h"
#include "taler_bank_service.h"
GNUNET_NETWORK_STRUCT_BEGIN
/**
* Data structure serialized in the prepare stage.
*/
struct WirePackP
{
/**
* Random unique identifier for the request.
*/
struct GNUNET_HashCode request_uid;
/**
* Amount to be transferred.
*/
struct TALER_AmountNBO amount;
/**
* Wire transfer identifier to use.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Length of the payto:// URL of the target account,
* including 0-terminator, in network byte order.
*/
uint32_t account_len GNUNET_PACKED;
/**
* Length of the exchange's base URL,
* including 0-terminator, in network byte order.
*/
uint32_t exchange_url_len GNUNET_PACKED;
};
GNUNET_NETWORK_STRUCT_END
/**
* Prepare for exeuction of a wire transfer.
*
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of this exchange (included in subject
* to facilitate use of tracking API by merchant backend)
* @param wtid wire transfer identifier to use
* @param buf[out] set to transaction data to persist, NULL on error
* @param buf_size[out] set to number of bytes in @a buf, 0 on error
*/
void
TALER_BANK_prepare_wire_transfer (const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct
TALER_WireTransferIdentifierRawP *wtid,
void **buf,
size_t *buf_size)
{
struct WirePackP *wp;
size_t d_len = strlen (destination_account_url) + 1;
size_t u_len = strlen (exchange_base_url) + 1;
char *end;
*buf_size = sizeof (*wp) + d_len + u_len;
wp = GNUNET_malloc (*buf_size);
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
&wp->request_uid);
TALER_amount_hton (&wp->amount,
amount);
wp->wtid = *wtid;
wp->account_len = htonl ((uint32_t) d_len);
wp->exchange_url_len = htonl ((uint32_t) u_len);
end = (char *) &wp[1];
memcpy (end,
destination_account_url,
d_len);
memcpy (end + d_len,
exchange_base_url,
u_len);
*buf = (char *) wp;
}
/**
* @brief An transaction Handle
*/
struct TALER_BANK_WireExecuteHandle
{
/**
* The url for this request.
*/
char *request_url;
/**
* POST context.
*/
struct TEAH_PostContext post_ctx;
/**
* Handle for the request.
*/
struct GNUNET_CURL_Job *job;
/**
* Function to call with the result.
*/
TALER_BANK_ConfirmationCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
};
/**
* Function called when we're done processing the
* HTTP /transaction request.
*
* @param cls the `struct TALER_BANK_WireExecuteHandle`
* @param response_code HTTP response code, 0 on error
* @param response parsed JSON result, NULL on error
*/
static void
handle_transaction_finished (void *cls,
long response_code,
const void *response)
{
struct TALER_BANK_WireExecuteHandle *weh = cls;
uint64_t row_id = UINT64_MAX;
struct GNUNET_TIME_Absolute timestamp;
enum TALER_ErrorCode ec;
const json_t *j = response;
weh->job = NULL;
timestamp = GNUNET_TIME_UNIT_FOREVER_ABS;
switch (response_code)
{
case 0:
ec = TALER_EC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id",
&row_id),
GNUNET_JSON_spec_absolute_time ("timestamp",
&timestamp),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (j,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
response_code = 0;
ec = TALER_EC_INVALID_RESPONSE;
break;
}
ec = TALER_EC_NONE;
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_NOT_ACCEPTABLE:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
ec = TALER_BANK_parse_ec_ (j);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
ec = TALER_BANK_parse_ec_ (j);
response_code = 0;
break;
}
weh->cb (weh->cb_cls,
response_code,
ec,
row_id,
timestamp);
TALER_BANK_execute_wire_transfer_cancel (weh);
}
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
struct TALER_BANK_WireExecuteHandle *
TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct
TALER_BANK_AuthenticationData *auth,
const void *buf,
size_t buf_size,
TALER_BANK_ConfirmationCallback cc,
void *cc_cls)
{
struct TALER_BANK_WireExecuteHandle *weh;
json_t *transaction_obj;
CURL *eh;
const struct WirePackP *wp = buf;
uint32_t d_len;
uint32_t u_len;
const char *destination_account_url;
const char *exchange_base_url;
struct TALER_Amount amount;
if (sizeof (*wp) > buf_size)
{
GNUNET_break (0);
return NULL;
}
d_len = ntohl (wp->account_len);
u_len = ntohl (wp->exchange_url_len);
if (sizeof (*wp) + d_len + u_len != buf_size)
{
GNUNET_break (0);
return NULL;
}
destination_account_url = (const char *) &wp[1];
exchange_base_url = destination_account_url + d_len;
if (NULL == bank_base_url)
{
GNUNET_break (0);
return NULL;
}
TALER_amount_ntoh (&amount,
&wp->amount);
transaction_obj = json_pack ("{s:o, s:o, s:s, s:o, s:o, s:s}",
"request_uid", GNUNET_JSON_from_data_auto (
&wp->request_uid),
"amount", TALER_JSON_from_amount (&amount),
"exchange_url", exchange_base_url,
"wtid", GNUNET_JSON_from_data_auto (&wp->wtid),
"credit_account", destination_account_url);
if (NULL == transaction_obj)
{
GNUNET_break (0);
return NULL;
}
weh = GNUNET_new (struct TALER_BANK_WireExecuteHandle);
weh->cb = cc;
weh->cb_cls = cc_cls;
weh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
"/transaction");
weh->post_ctx.headers = curl_slist_append
(weh->post_ctx.headers,
"Content-Type: application/json");
eh = curl_easy_init ();
if ( (GNUNET_OK !=
TALER_BANK_setup_auth_ (eh,
auth)) ||
(CURLE_OK !=
curl_easy_setopt (eh,
CURLOPT_URL,
weh->request_url)) ||
(GNUNET_OK !=
TALER_curl_easy_post (&weh->post_ctx,
eh,
transaction_obj)) )
{
GNUNET_break (0);
TALER_BANK_execute_wire_transfer_cancel (weh);
curl_easy_cleanup (eh);
json_decref (transaction_obj);
return NULL;
}
json_decref (transaction_obj);
weh->job = GNUNET_CURL_job_add2 (ctx,
eh,
weh->post_ctx.headers,
&handle_transaction_finished,
weh);
return weh;
}
/**
* Cancel a wire transfer. This function cannot be used on a request handle
* if a response is already served for it.
*
* @param weh the wire transfer request handle
*/
void
TALER_BANK_execute_wire_transfer_cancel (struct
TALER_BANK_WireExecuteHandle *weh)
{
if (NULL != weh->job)
{
GNUNET_CURL_job_cancel (weh->job);
weh->job = NULL;
}
TALER_curl_easy_post_finished (&weh->post_ctx);
GNUNET_free (weh->request_url);
GNUNET_free (weh);
}
/* end of bank_api_transaction.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2016, 2017, 2018 Inria and GNUnet e.V. (C) 2016-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -25,7 +25,7 @@
#include "platform.h" #include "platform.h"
#include "taler_fakebank_lib.h" #include "taler_fakebank_lib.h"
#include "taler_bank_service.h" #include "taler_bank_service.h"
#include "fakebank.h" #include "taler_mhd_lib.h"
/** /**
* Maximum POST request size (for /admin/add/incoming) * Maximum POST request size (for /admin/add/incoming)
@ -33,6 +33,65 @@
#define REQUEST_BUFFER_MAX (4 * 1024) #define REQUEST_BUFFER_MAX (4 * 1024)
/**
* Details about a transcation we (as the simulated bank) received.
*/
struct Transaction
{
/**
* We store transactions in a DLL.
*/
struct Transaction *next;
/**
* We store transactions in a DLL.
*/
struct Transaction *prev;
/**
* Amount to be transferred.
*/
struct TALER_Amount amount;
/**
* Account to debit.
*/
char *debit_account;
/**
* Account to credit.
*/
char *credit_account;
/**
* Subject of the transfer.
*/
char *subject;
/**
* Base URL of the exchange.
*/
char *exchange_base_url;
/**
* When did the transaction happen?
*/
struct GNUNET_TIME_Absolute date;
/**
* Number of this transaction.
*/
uint64_t row_id;
/**
* Has this transaction been subjected to #TALER_FAKEBANK_check()
* and should thus no longer be counted in
* #TALER_FAKEBANK_check_empty()?
*/
int checked;
};
/** /**
* Handle for the fake bank. * Handle for the fake bank.
*/ */
@ -63,6 +122,11 @@ struct TALER_FAKEBANK_Handle
*/ */
uint64_t serial_counter; uint64_t serial_counter;
/**
* Our port number.
*/
uint16_t port;
#if EPOLL_SUPPORT #if EPOLL_SUPPORT
/** /**
* Boxed @e mhd_fd. * Boxed @e mhd_fd.
@ -95,8 +159,8 @@ struct TALER_FAKEBANK_Handle
int int
TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h, TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
const struct TALER_Amount *want_amount, const struct TALER_Amount *want_amount,
uint64_t want_debit, const char *want_debit,
uint64_t want_credit, const char *want_credit,
const char *exchange_base_url, const char *exchange_base_url,
char **subject) char **subject)
{ {
@ -151,8 +215,8 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
*/ */
uint64_t uint64_t
TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h, TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t debit_account, const char *debit_account,
uint64_t credit_account, const char *credit_account,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
const char *subject, const char *subject,
const char *exchange_base_url) const char *exchange_base_url)
@ -160,15 +224,15 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
struct Transaction *t; struct Transaction *t;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Making transfer from %llu to %llu over %s and subject %s; for exchange: %s\n", "Making transfer from %s to %s over %s and subject %s; for exchange: %s\n",
(unsigned long long) debit_account, debit_account,
(unsigned long long) credit_account, credit_account,
TALER_amount2s (amount), TALER_amount2s (amount),
subject, subject,
exchange_base_url); exchange_base_url);
t = GNUNET_new (struct Transaction); t = GNUNET_new (struct Transaction);
t->debit_account = debit_account; t->debit_account = GNUNET_strdup (debit_account);
t->credit_account = credit_account; t->credit_account = GNUNET_strdup (credit_account);
t->amount = *amount; t->amount = *amount;
t->exchange_base_url = GNUNET_strdup (exchange_base_url); t->exchange_base_url = GNUNET_strdup (exchange_base_url);
t->row_id = ++h->serial_counter; t->row_id = ++h->serial_counter;
@ -182,31 +246,6 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
} }
/**
* Reject incoming wire transfer to account @a credit_account
* as identified by @a rowid.
*
* @param h fake bank handle
* @param rowid identifies transfer to reject
* @param credit_account account number of owner of credited account
* @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not found
*/
int
TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t rowid,
uint64_t credit_account)
{
for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
if ( (t->row_id == rowid) &&
(t->credit_account == credit_account) )
{
t->rejected = GNUNET_YES;
return GNUNET_YES;
}
return GNUNET_NO;
}
/** /**
* Check that no wire transfers were ordered (or at least none * Check that no wire transfers were ordered (or at least none
* that have not been taken care of via #TALER_FAKEBANK_check()). * that have not been taken care of via #TALER_FAKEBANK_check()).
@ -223,8 +262,7 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
t = h->transactions_head; t = h->transactions_head;
while (NULL != t) while (NULL != t)
{ {
if ( (GNUNET_YES != t->checked) && if (GNUNET_YES != t->checked)
(GNUNET_YES != t->rejected) )
break; break;
t = t->next; t = t->next;
} }
@ -234,16 +272,15 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
"Expected empty transaction set, but I have:\n"); "Expected empty transaction set, but I have:\n");
while (NULL != t) while (NULL != t)
{ {
if ( (GNUNET_YES != t->checked) && if (GNUNET_YES != t->checked)
(GNUNET_YES != t->rejected) )
{ {
char *s; char *s;
s = TALER_amount_to_string (&t->amount); s = TALER_amount_to_string (&t->amount);
fprintf (stderr, fprintf (stderr,
"%llu -> %llu (%s) from %s\n", "%s -> %s (%s) from %s\n",
(unsigned long long) t->debit_account, t->debit_account,
(unsigned long long) t->credit_account, t->credit_account,
s, s,
t->exchange_base_url); t->exchange_base_url);
GNUNET_free (s); GNUNET_free (s);
@ -270,6 +307,8 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
h->transactions_tail, h->transactions_tail,
t); t);
GNUNET_free (t->subject); GNUNET_free (t->subject);
GNUNET_free (t->debit_account);
GNUNET_free (t->credit_account);
GNUNET_free (t->exchange_base_url); GNUNET_free (t->exchange_base_url);
GNUNET_free (t); GNUNET_free (t);
} }
@ -290,62 +329,6 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
} }
/**
* Create and queue a bank error message with the HTTP response
* code @a response_code on connection @a connection.
*
* @param connection where to queue the reply
* @param response_code http status code to use
* @param ec taler error code to use
* @param message human readable error message
* @return MHD status code
*/
static int
create_bank_error (struct MHD_Connection *connection,
unsigned int response_code,
enum TALER_ErrorCode ec,
const char *message)
{
json_t *json;
struct MHD_Response *resp;
void *json_str;
size_t json_len;
int ret;
json = json_pack ("{s:s, s:I}",
"error",
message,
"ec",
(json_int_t) ec);
json_str = json_dumps (json,
JSON_INDENT (2));
json_decref (json);
if (NULL == json_str)
{
GNUNET_break (0);
return MHD_NO;
}
json_len = strlen (json_str);
resp = MHD_create_response_from_buffer (json_len,
json_str,
MHD_RESPMEM_MUST_FREE);
if (NULL == resp)
{
GNUNET_break (0);
free (json_str);
return MHD_NO;
}
(void) MHD_add_response_header (resp,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json");
ret = MHD_queue_response (connection,
response_code,
resp);
MHD_destroy_response (resp);
return ret;
}
/** /**
* Function called whenever MHD is done with a request. If the * Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the * request was a POST, we may have stored a `struct Buffer *` in the
@ -394,8 +377,6 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
{ {
enum GNUNET_JSON_PostResult pr; enum GNUNET_JSON_PostResult pr;
json_t *json; json_t *json;
struct MHD_Response *resp;
int ret;
uint64_t row_id; uint64_t row_id;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX, pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
@ -422,15 +403,14 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
} }
{ {
const char *subject; const char *subject;
uint64_t debit_account; const char *debit_account;
uint64_t credit_account; const char *credit_account;
const char *base_url; const char *base_url;
struct TALER_Amount amount; struct TALER_Amount amount;
char *amount_s;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("subject", &subject), GNUNET_JSON_spec_string ("subject", &subject),
GNUNET_JSON_spec_uint64 ("debit_account", &debit_account), GNUNET_JSON_spec_string ("debit_account", &debit_account),
GNUNET_JSON_spec_uint64 ("credit_account", &credit_account), GNUNET_JSON_spec_string ("credit_account", &credit_account),
TALER_JSON_spec_amount ("amount", &amount), TALER_JSON_spec_amount ("amount", &amount),
GNUNET_JSON_spec_string ("exchange_url", &base_url), GNUNET_JSON_spec_string ("exchange_url", &base_url),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
@ -450,80 +430,49 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
&amount, &amount,
subject, subject,
base_url); base_url);
amount_s = TALER_amount_to_string (&amount);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Receiving incoming wire transfer: %llu->%llu, subject: %s, amount: %s, from %s\n", "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
(unsigned long long) debit_account, debit_account,
(unsigned long long) credit_account, credit_account,
subject, subject,
amount_s, TALER_amount2s (&amount),
base_url); base_url);
GNUNET_free (amount_s);
} }
json_decref (json); json_decref (json);
/* Finally build response object */ /* Finally build response object */
{ return TALER_MHD_reply_json_pack (connection,
void *json_str; MHD_HTTP_OK,
size_t json_len; "{s:I, s:o}",
json = json_pack ("{s:I, s:o}",
"row_id", "row_id",
(json_int_t) row_id, (json_int_t) row_id,
"timestamp", GNUNET_JSON_from_time_abs (GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */ "timestamp", GNUNET_JSON_from_time_abs (
GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */
json_str = json_dumps (json,
JSON_INDENT (2));
json_decref (json);
if (NULL == json_str)
{
GNUNET_break (0);
return MHD_NO;
}
json_len = strlen (json_str);
resp = MHD_create_response_from_buffer (json_len,
json_str,
MHD_RESPMEM_MUST_FREE);
if (NULL == resp)
{
GNUNET_break (0);
free (json_str);
return MHD_NO;
}
(void) MHD_add_response_header (resp,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json");
}
ret = MHD_queue_response (connection,
MHD_HTTP_OK,
resp);
MHD_destroy_response (resp);
return ret;
} }
/** /**
* Handle incoming HTTP request for /reject. * Handle incoming HTTP request for /transaction.
* *
* @param h the fakebank handle * @param h the fakebank handle
* @param connection the connection * @param connection the connection
* @param account account making the transaction
* @param upload_data request data * @param upload_data request data
* @param upload_data_size size of @a upload_data in bytes * @param upload_data_size size of @a upload_data in bytes
* @param con_cls closure for request (a `struct Buffer *`) * @param con_cls closure for request (a `struct Buffer *`)
* @return MHD result code * @return MHD result code
*/ */
static int static int
handle_reject (struct TALER_FAKEBANK_Handle *h, handle_transaction (struct TALER_FAKEBANK_Handle *h,
struct MHD_Connection *connection, struct MHD_Connection *connection,
const char *account,
const char *upload_data, const char *upload_data,
size_t *upload_data_size, size_t *upload_data_size,
void **con_cls) void **con_cls)
{ {
enum GNUNET_JSON_PostResult pr; enum GNUNET_JSON_PostResult pr;
json_t *json; json_t *json;
struct MHD_Response *resp; uint64_t row_id;
int ret;
int found;
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX, pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
connection, connection,
@ -548,13 +497,25 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
break; break;
} }
{ {
uint64_t row_id; struct GNUNET_HashCode uuid;
uint64_t credit_account; struct TALER_WireTransferIdentifierRawP wtid;
const char *credit_account;
const char *base_url;
struct TALER_Amount amount;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id", &row_id), GNUNET_JSON_spec_fixed_auto ("request_uid",
GNUNET_JSON_spec_uint64 ("account_number", &credit_account), &uuid),
TALER_JSON_spec_amount ("amount",
&amount),
GNUNET_JSON_spec_string ("exchange_base_url",
&base_url),
GNUNET_JSON_spec_fixed_auto ("wtid",
&wtid),
GNUNET_JSON_spec_string ("credit_account",
&credit_account),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (json, GNUNET_JSON_parse (json,
spec, spec,
@ -564,31 +525,38 @@ handle_reject (struct TALER_FAKEBANK_Handle *h,
json_decref (json); json_decref (json);
return MHD_NO; return MHD_NO;
} }
found = TALER_FAKEBANK_reject_transfer (h, {
row_id, char *subject;
credit_account);
subject = GNUNET_STRINGS_data_to_string_alloc (&wtid,
sizeof (wtid));
// FIXME: use uuid here!!!
row_id = TALER_FAKEBANK_make_transfer (h,
account,
credit_account,
&amount,
subject,
base_url);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Rejected wire transfer #%llu (to %llu)\n", "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
(unsigned long long) row_id, account,
(unsigned long long) credit_account); credit_account,
subject,
TALER_amount2s (&amount),
base_url);
GNUNET_free (subject);
}
} }
json_decref (json); json_decref (json);
if (GNUNET_OK != found) /* Finally build response object */
return create_bank_error return TALER_MHD_reply_json_pack (connection,
(connection, MHD_HTTP_OK,
MHD_HTTP_NOT_FOUND, "{s:I, s:o}",
TALER_EC_BANK_TRANSACTION_NOT_FOUND, "row_id",
"transaction unknown"); (json_int_t) row_id,
/* finally build regular response */ "timestamp", GNUNET_JSON_from_time_abs (
resp = MHD_create_response_from_buffer (0, GNUNET_TIME_UNIT_ZERO_ABS)); /*dummy tmp */
NULL,
MHD_RESPMEM_PERSISTENT);
ret = MHD_queue_response (connection,
MHD_HTTP_NO_CONTENT,
resp);
MHD_destroy_response (resp);
return ret;
} }
@ -626,75 +594,153 @@ handle_home_page (struct TALER_FAKEBANK_Handle *h,
/** /**
* Handle incoming HTTP request for /history * This is the "base" structure for both the /history and the
* /history-range API calls.
*/
struct HistoryArgs
{
/**
* Bank account number of the requesting client.
*/
uint64_t account_number;
/**
* Index of the starting transaction.
*/
uint64_t start_idx;
/**
* Requested number of results and order
* (positive: ascending, negative: descending)
*/
int64_t delta;
/**
* Timeout for long polling.
*/
struct GNUNET_TIME_Relative lp_timeout;
/**
* #GNUNET_YES if starting point was given.
*/
int have_start;
};
/**
* Parse URL history arguments, of _both_ APIs:
* /history/incoming and /history/outgoing.
*
* @param connection MHD connection.
* @param function_name name of the caller.
* @param ha[out] will contain the parsed values.
* @return GNUNET_OK only if the parsing succeedes.
*/
static int
parse_history_common_args (struct MHD_Connection *connection,
struct HistoryArgs *ha)
{
const char *start;
const char *delta;
const char *long_poll_ms;
unsigned long long lp_timeout;
unsigned long long sval;
long long d;
start = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"start");
ha->have_start = (NULL != start);
delta = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"delta");
long_poll_ms = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"long_poll_ms");
lp_timeout = 0;
if ( (NULL == delta) ||
(1 != sscanf (delta,
"%lld",
&d)) ||
( (NULL != long_poll_ms) &&
(1 != sscanf (long_poll_ms,
"%llu",
&lp_timeout)) ) ||
( (NULL != start) &&
(1 != sscanf (start,
"%llu",
&sval)) ) )
{
/* Fail if one of the above failed. */
/* Invalid request, given that this is fakebank we impolitely
* just kill the connection instead of returning a nice error.
*/
GNUNET_break (0);
return GNUNET_NO;
}
if (NULL == start)
ha->start_idx = (d > 0) ? 0 : UINT64_MAX;
else
ha->start_idx = (uint64_t) sval;
ha->delta = (int64_t) d;
ha->lp_timeout
= GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
lp_timeout);
return GNUNET_OK;
}
/**
* Handle incoming HTTP request for /history/incoming
* *
* @param h the fakebank handle * @param h the fakebank handle
* @param connection the connection * @param connection the connection
* @param con_cls place to store state, not used * @param account which account the request is about
* @return MHD result code * @return MHD result code
*/ */
static int static int
handle_history (struct TALER_FAKEBANK_Handle *h, handle_credit_history (struct TALER_FAKEBANK_Handle *h,
struct MHD_Connection *connection, struct MHD_Connection *connection,
void **con_cls) const char *account)
{ {
struct HistoryArgs ha; struct HistoryArgs ha;
struct HistoryRangeIds hri;
const char *start;
const char *delta;
struct Transaction *pos; struct Transaction *pos;
json_t *history;
(void) con_cls;
if (GNUNET_OK != if (GNUNET_OK !=
TFH_parse_history_common_args (connection, parse_history_common_args (connection,
&ha)) &ha))
{ {
GNUNET_break (0); GNUNET_break (0);
return MHD_NO; return MHD_NO;
} }
start = MHD_lookup_connection_value (connection, if (! ha.have_start)
MHD_GET_ARGUMENT_KIND,
"start");
delta = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"delta");
if ( ((NULL != start) && (1 != sscanf (start,
"%llu",
&hri.start))) ||
(NULL == delta) || (1 != sscanf (delta,
"%lld",
&hri.count)) )
{ {
GNUNET_break (0); pos = (0 > ha.delta)
return MHD_NO; ? h->transactions_tail
} : h->transactions_head;
ha.range = &hri;
if (NULL == start)
{
pos = 0 > hri.count ?
h->transactions_tail : h->transactions_head;
} }
else if (NULL != h->transactions_head) else if (NULL != h->transactions_head)
{ {
for (pos = h->transactions_head; for (pos = h->transactions_head;
NULL != pos; NULL != pos;
pos = pos->next) pos = pos->next)
if (pos->row_id == hri.start) if (pos->row_id == ha.start_idx)
break; break;
if (NULL == pos) if (NULL == pos)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid range specified," "Invalid start specified, transaction %llu not known!\n",
" transaction %llu not known!\n", (unsigned long long) ha.start_idx);
(unsigned long long) hri.start);
return MHD_NO; return MHD_NO;
} }
/* range is exclusive, skip the matching entry */ /* range is exclusive, skip the matching entry */
if (hri.count > 0) if (ha.delta > 0)
pos = pos->next; pos = pos->next;
if (hri.count < 0) if (ha.delta < 0)
pos = pos->prev; pos = pos->prev;
} }
else else
@ -702,15 +748,210 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
/* list is empty */ /* list is empty */
pos = NULL; pos = NULL;
} }
history = json_array ();
while ( (0 != ha.delta) &&
(NULL != pos) )
{
if (0 == strcasecmp (pos->credit_account,
account))
{
json_t *trans;
trans = json_pack
("{s:I, s:o, s:o, s:s, s:s, s:s}",
"row_id", (json_int_t) pos->row_id,
"date", GNUNET_JSON_from_time_abs (pos->date),
"amount", TALER_JSON_from_amount (&pos->amount),
"credit_account", account,
"debit_account", pos->debit_account,
"wtid", pos->subject /* we "know" it is OK */);
GNUNET_assert (0 ==
json_array_append_new (history,
trans));
if (ha.delta > 0)
ha.delta--;
else
ha.delta++;
}
if (ha.delta > 0)
pos = pos->prev;
else
pos = pos->next;
}
return TALER_MHD_reply_json (connection,
history,
MHD_HTTP_OK);
}
/**
* Handle incoming HTTP request for /history/incoming
*
* @param h the fakebank handle
* @param connection the connection
* @param account which account the request is about
* @return MHD result code
*/
static int
handle_debit_history (struct TALER_FAKEBANK_Handle *h,
struct MHD_Connection *connection,
const char *account)
{
struct HistoryArgs ha;
struct Transaction *pos;
json_t *history;
if (GNUNET_OK !=
parse_history_common_args (connection,
&ha))
{
GNUNET_break (0);
return MHD_NO;
}
if (! ha.have_start)
{
pos = (0 > ha.delta)
? h->transactions_tail
: h->transactions_head;
}
else if (NULL != h->transactions_head)
{
for (pos = h->transactions_head;
NULL != pos;
pos = pos->next)
if (pos->row_id == ha.start_idx)
break;
if (NULL == pos)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid start specified, transaction %llu not known!\n",
(unsigned long long) ha.start_idx);
return MHD_NO;
}
/* range is exclusive, skip the matching entry */
if (ha.delta > 0)
pos = pos->next;
if (ha.delta < 0)
pos = pos->prev;
}
else
{
/* list is empty */
pos = NULL;
}
history = json_array ();
while ( (0 != ha.delta) &&
(NULL != pos) )
{
if (0 == strcasecmp (pos->debit_account,
account))
{
json_t *trans;
trans = json_pack
("{s:I, s:o, s:o, s:s, s:s, s:s}",
"row_id", (json_int_t) pos->row_id,
"date", GNUNET_JSON_from_time_abs (pos->date),
"amount", TALER_JSON_from_amount (&pos->amount),
"credit_account", pos->credit_account,
"debit_account", account,
"reserve_pub", pos->subject /* we "know" it is OK */);
GNUNET_assert (0 ==
json_array_append_new (history,
trans));
if (ha.delta > 0)
ha.delta--;
else
ha.delta++;
}
if (ha.delta > 0)
pos = pos->prev;
else
pos = pos->next;
}
return TALER_MHD_reply_json (connection,
history,
MHD_HTTP_OK);
}
/**
* Handle incoming HTTP request.
*
* @param h our handle
* @param connection the connection
* @param url the requested url
* @param method the method (POST, GET, ...)
* @param account which account should process the request
* @param upload_data request data
* @param upload_data_size size of @a upload_data in bytes
* @param con_cls closure for request (a `struct Buffer *`)
* @return MHD result code
*/
static int
serve (struct TALER_FAKEBANK_Handle *h,
struct MHD_Connection *connection,
const char *account,
const char *url,
const char *method,
const char *upload_data,
size_t *upload_data_size,
void **con_cls)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"/history, start row (0 == no transactions exist): %llu\n", "Fakebank, serving: %s\n",
NULL != pos ? pos->row_id : 0LL); url);
return TFH_build_history_response (connection, if ( (0 == strcmp (url,
pos, "/")) &&
&ha, (0 == strcasecmp (method,
&TFH_handle_history_skip, MHD_HTTP_METHOD_GET)) )
&TFH_handle_history_step, return handle_home_page (h,
&TFH_handle_history_advance); connection,
con_cls);
if ( (0 == strcmp (url,
"/admin/add/incoming")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_admin_add_incoming (h,
connection,
upload_data,
upload_data_size,
con_cls);
if ( (0 == strcmp (url,
"/transaction")) &&
(NULL != account) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_transaction (h,
connection,
account,
upload_data,
upload_data_size,
con_cls);
if ( (0 == strcmp (url,
"/history/incoming")) &&
(NULL != account) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_GET)) )
return handle_credit_history (h,
connection,
account);
if ( (0 == strcmp (url,
"/history/outgoing")) &&
(NULL != account) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_GET)) )
return handle_debit_history (h,
connection,
account);
/* Unexpected URL path, just close the connection. */
/* we're rather impolite here, but it's a testcase. */
TALER_LOG_ERROR ("Breaking URL: %s\n",
url);
GNUNET_break_op (0);
return MHD_NO;
} }
@ -738,50 +979,28 @@ handle_mhd_request (void *cls,
void **con_cls) void **con_cls)
{ {
struct TALER_FAKEBANK_Handle *h = cls; struct TALER_FAKEBANK_Handle *h = cls;
char *account = NULL;
char *end;
int ret;
(void) version; (void) version;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, if ( (strlen (url) > 1) &&
"Fakebank, serving: %s\n", (NULL != (end = strchr (url + 1, '/'))) )
url); {
if ( (0 == strcasecmp (url, account = GNUNET_strndup (url + 1,
"/")) && end - url - 1);
(0 == strcasecmp (method, url = end;
MHD_HTTP_METHOD_GET)) ) }
return handle_home_page (h, ret = serve (h,
connection,
con_cls);
if ( (0 == strcasecmp (url,
"/admin/add/incoming")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_admin_add_incoming (h,
connection, connection,
account,
url,
method,
upload_data, upload_data,
upload_data_size, upload_data_size,
con_cls); con_cls);
if ( (0 == strcasecmp (url, GNUNET_free_non_null (account);
"/reject")) && return ret;
(0 == strcasecmp (method,
MHD_HTTP_METHOD_POST)) )
return handle_reject (h,
connection,
upload_data,
upload_data_size,
con_cls);
if ( (0 == strcasecmp (url,
"/history")) &&
(0 == strcasecmp (method,
MHD_HTTP_METHOD_GET)) )
return handle_history (h,
connection,
con_cls);
/* Unexpected URL path, just close the connection. */
/* we're rather impolite here, but it's a testcase. */
TALER_LOG_ERROR ("Breaking URL: %s\n",
url);
GNUNET_break_op (0);
return MHD_NO;
} }
@ -918,6 +1137,7 @@ TALER_FAKEBANK_start (uint16_t port)
struct TALER_FAKEBANK_Handle *h; struct TALER_FAKEBANK_Handle *h;
h = GNUNET_new (struct TALER_FAKEBANK_Handle); h = GNUNET_new (struct TALER_FAKEBANK_Handle);
h->port = port;
h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
#if EPOLL_SUPPORT #if EPOLL_SUPPORT
| MHD_USE_EPOLL_INTERNAL_THREAD | MHD_USE_EPOLL_INTERNAL_THREAD

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2016, 2017, 2018 Inria and GNUnet e.V. (C) 2016-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -19,7 +19,7 @@
/** /**
* @file bank-lib/fakebank.h * @file bank-lib/fakebank.h
* @brief definitions for the "/history[-range]" layer. * @brief definitions for the "/history" layer.
* @author Marcello Stanisci <stanisci.m@gmail.com> * @author Marcello Stanisci <stanisci.m@gmail.com>
*/ */
@ -29,283 +29,4 @@
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_bank_service.h" #include "taler_bank_service.h"
/**
* Details about a transcation we (as the simulated bank) received.
*/
struct Transaction
{
/**
* We store transactions in a DLL.
*/
struct Transaction *next;
/**
* We store transactions in a DLL.
*/
struct Transaction *prev;
/**
* Amount to be transferred.
*/
struct TALER_Amount amount;
/**
* Account to debit.
*/
uint64_t debit_account;
/**
* Account to credit.
*/
uint64_t credit_account;
/**
* Subject of the transfer.
*/
char *subject;
/**
* Base URL of the exchange.
*/
char *exchange_base_url;
/**
* When did the transaction happen?
*/
struct GNUNET_TIME_Absolute date;
/**
* Number of this transaction.
*/
long long unsigned int row_id;
/**
* Flag set if the transfer was rejected.
*/
int rejected;
/**
* Has this transaction been subjected to #TALER_FAKEBANK_check()
* and should thus no longer be counted in
* #TALER_FAKEBANK_check_empty()?
*/
int checked;
};
/******************************************
* Definitions for "/history" start here. *
******************************************/
/**
* Needed to implement ascending/descending ordering
* of /history results.
*/
struct HistoryElement
{
/**
* History JSON element.
*/
json_t *element;
/**
* Previous element.
*/
struct HistoryElement *prev;
/**
* Next element.
*/
struct HistoryElement *next;
};
/**
* Values to implement the "/history-range" range.
*/
struct HistoryRangeDates
{
/**
* Oldest row in the results.
*/
struct GNUNET_TIME_Absolute start;
/**
* Youngest row in the results.
*/
struct GNUNET_TIME_Absolute end;
};
/**
* Values to implement the "/history" range.
*/
struct HistoryRangeIds
{
/**
* (Exclusive) row ID for the result set.
*/
unsigned long long start;
/**
* How many transactions we want in the result set. If
* negative/positive, @a start will be strictly younger/older
* of any element in the result set.
*/
long long count;
};
/**
* This is the "base" structure for both the /history and the
* /history-range API calls.
*/
struct HistoryArgs
{
/**
* Direction asked by the client: CREDIT / DEBIT / BOTH / CANCEL.
*/
enum TALER_BANK_Direction direction;
/**
* Bank account number of the requesting client.
*/
unsigned long long account_number;
/**
* Ordering of the results.
*/
unsigned int ascending;
/**
* Overloaded type that indicates the "range" to be returned
* in the results; this can be either a date range, or a
* starting row id + the count.
*/
void *range;
};
/**
* Type for a function that decides whether or not
* the history-building loop should iterate once again.
* Typically called from inside the 'while' condition.
*
* @param ha history argument.
* @param pos current position.
* @return GNUNET_YES if the iteration shuold go on.
*/
typedef int (*CheckAdvance)(const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Type for a function that steps over the next element
* in the list of all transactions, after the current @a pos
* _got_ included in the result.
*/
typedef struct Transaction * (*Step)(const struct HistoryArgs *ha,
const struct Transaction *pos);
/*
* Type for a function that steps over the next element
* in the list of all transactions, after the current @a pos
* did _not_ get included in the result.
*/
typedef struct Transaction * (*Skip)(const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Actual history response builder.
*
* @param pos first (included) element in the result set.
* @param ha history arguments.
* @param caller_name which function is building the history.
* @return MHD_YES / MHD_NO, after having enqueued the response
* object into MHD.
*/
int
TFH_build_history_response (struct MHD_Connection *connection,
struct Transaction *pos,
struct HistoryArgs *ha,
Skip skip,
Step step,
CheckAdvance advance);
/**
* Parse URL history arguments, of _both_ APIs:
* /history and /history-range.
*
* @param connection MHD connection.
* @param function_name name of the caller.
* @param ha[out] will contain the parsed values.
* @return GNUNET_OK only if the parsing succeedes.
*/
int
TFH_parse_history_common_args (struct MHD_Connection *connection,
struct HistoryArgs *ha);
/**
* Decides whether the history builder will advance or not
* to the next element.
*
* @param ha history args
* @return GNUNET_YES/NO to advance/not-advance.
*/
int
TFH_handle_history_advance (const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Iterates on the "next" element to be processed. To
* be used when the current element does not get inserted in
* the result.
*
* @param ha history arguments.
* @param pos current element being processed.
* @return the next element to be processed.
*/
struct Transaction *
TFH_handle_history_skip (const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Iterates on the "next" element to be processed. To
* be used when the current element _gets_ inserted in the result.
*
* @param ha history arguments.
* @param pos current element being processed.
* @return the next element to be processed.
*/
struct Transaction *
TFH_handle_history_step (const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Decides whether the history builder will advance or not
* to the next element.
*
* @param ha history args
* @return GNUNET_YES/NO to advance/not-advance.
*/
int
TFH_handle_history_range_advance (const struct HistoryArgs *ha,
const struct Transaction *pos);
/**
* Iterates towards the "next" element to be processed. To
* be used when the current element does not get inserted in
* the result.
*
* @param ha history arguments.
* @param pos current element being processed.
* @return the next element to be processed.
*/
struct Transaction *
TFH_handle_history_range_skip (const struct HistoryArgs *ha,
const struct Transaction *pos);
#endif #endif

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2016, 2017, 2018 Inria and GNUnet e.V. (C) 2016-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@ -19,92 +19,15 @@
/** /**
* @file bank-lib/fakebank_history.c * @file bank-lib/fakebank_history.c
* @brief definitions for the "/history[-range]" layer. * @brief definitions for the "/history" layer.
* @author Marcello Stanisci <stanisci.m@gmail.com> * @author Marcello Stanisci <stanisci.m@gmail.com>
*/ */
#include "platform.h" #include "platform.h"
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "fakebank.h" #include "fakebank.h"
/**
* Decides whether the history builder will advance or not
* to the next element.
*
* @param ha history args
* @return GNUNET_YES/NO to advance/not-advance.
*/
int
TFH_handle_history_advance (const struct HistoryArgs *ha,
const struct Transaction *pos)
{
const struct HistoryRangeIds *hri = ha->range;
return (NULL != pos) && (0 != hri->count);
}
/**
* Iterates on the "next" element to be processed. To
* be used when the current element does not get inserted in
* the result.
*
* @param ha history arguments.
* @param pos current element being processed.
* @return the next element to be processed.
*/
struct Transaction *
TFH_handle_history_skip (const struct HistoryArgs *ha,
const struct Transaction *pos)
{
const struct HistoryRangeIds *hri = ha->range;
if (hri->count > 0)
return pos->next;
if (hri->count < 0)
return pos->prev;
return NULL;
}
/**
* Iterates on the "next" element to be processed. To
* be used when the current element _gets_ inserted in the result.
*
* @param ha history arguments.
* @param pos current element being processed.
* @return the next element to be processed.
*/
struct Transaction *
TFH_handle_history_step (const struct HistoryArgs *ha,
const struct Transaction *pos)
{
struct HistoryRangeIds *hri = ha->range;
if (hri->count > 0)
{
hri->count--;
return pos->next;
}
if (hri->count < 0)
{
hri->count++;
return pos->prev;
}
return NULL;
}
/**
* Actual history response builder.
*
* @param pos first (included) element in the result set, NULL if history is empty
* @param ha history arguments.
* @param caller_name which function is building the history.
* @return MHD_YES / MHD_NO, after having enqueued the response
* object into MHD.
*/
int int
TFH_build_history_response (struct MHD_Connection *connection, TFH_build_history_response (struct MHD_Connection *connection,
struct Transaction *pos, struct Transaction *pos,
@ -257,126 +180,3 @@ TFH_build_history_response (struct MHD_Connection *connection,
} }
return ret; return ret;
} }
/**
* Parse URL history arguments, of _both_ APIs:
* /history and /history-range.
*
* @param connection MHD connection.
* @param function_name name of the caller.
* @param ha[out] will contain the parsed values.
* @return GNUNET_OK only if the parsing succeedes.
*/
int
TFH_parse_history_common_args (struct MHD_Connection *connection,
struct HistoryArgs *ha)
{
/**
* @variable
* Just check if given and == "basic", no need to keep around.
*/
const char *auth;
/**
* All those will go into the structure, after parsing.
*/
const char *direction;
const char *cancelled;
const char *ordering;
const char *account_number;
auth = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"auth");
direction = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"direction");
cancelled = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"cancelled");
ordering = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
"ordering");
account_number = MHD_lookup_connection_value
(connection,
MHD_GET_ARGUMENT_KIND,
"account_number");
/* Fail if one of the above failed. */
if ( (NULL == direction) ||
(NULL == cancelled) ||
( (0 != strcasecmp (cancelled,
"OMIT")) &&
(0 != strcasecmp (cancelled,
"SHOW")) ) ||
( (0 != strcasecmp (direction,
"BOTH")) &&
(0 != strcasecmp (direction,
"CREDIT")) &&
(0 != strcasecmp (direction,
"DEBIT")) ) ||
(1 != sscanf (account_number,
"%llu",
&ha->account_number)) ||
( (NULL == auth) || (0 != strcasecmp (auth,
"basic")) ) )
{
/* Invalid request, given that this is fakebank we impolitely
* just kill the connection instead of returning a nice error.
*/
GNUNET_break (0);
return GNUNET_NO;
}
if (0 == strcasecmp (direction,
"CREDIT"))
{
ha->direction = TALER_BANK_DIRECTION_CREDIT;
}
else if (0 == strcasecmp (direction,
"DEBIT"))
{
ha->direction = TALER_BANK_DIRECTION_DEBIT;
}
else if (0 == strcasecmp (direction,
"BOTH"))
{
ha->direction = TALER_BANK_DIRECTION_BOTH;
}
/* Direction is invalid. */
else
{
GNUNET_break (0);
return GNUNET_NO;
}
if (0 == strcasecmp (cancelled,
"OMIT"))
{
/* nothing */
}
else if (0 == strcasecmp (cancelled,
"SHOW"))
{
ha->direction |= TALER_BANK_DIRECTION_CANCEL;
}
/* Cancel-showing policy is invalid. */
else
{
GNUNET_break (0);
return GNUNET_NO;
}
if ((NULL != ordering)
&& (0 == strcmp ("ascending",
ordering)))
ha->ascending = GNUNET_YES;
else
ha->ascending = GNUNET_NO;
return GNUNET_OK;
}

View File

@ -25,9 +25,9 @@
#include "taler_bank_service.h" #include "taler_bank_service.h"
/** /**
* Bank URL. * Account base URL.
*/ */
static char *bank_url; static char *account_base_url;
/** /**
* Amount to transfer. * Amount to transfer.
@ -35,14 +35,9 @@ static char *bank_url;
static struct TALER_Amount amount; static struct TALER_Amount amount;
/** /**
* Debit account number. * Credit account payto://-URI.
*/ */
static unsigned long long debit_account_no; static char *credit_account;
/**
* Credit account number.
*/
static unsigned long long credit_account_no;
/** /**
* Wire transfer subject. * Wire transfer subject.
@ -168,11 +163,23 @@ run (void *cls,
const struct GNUNET_CONFIGURATION_Handle *cfg) const struct GNUNET_CONFIGURATION_Handle *cfg)
{ {
struct TALER_BANK_AuthenticationData auth; struct TALER_BANK_AuthenticationData auth;
struct TALER_ReservePublicKeyP reserve_pub;
(void) cls; (void) cls;
(void) args; (void) args;
(void) cfgfile; (void) cfgfile;
(void) cfg; (void) cfg;
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (subject,
strlen (subject),
&reserve_pub,
sizeof (reserve_pub)))
{
fprintf (stderr,
"Error: wire transfer subject must be a reserve public key\n");
return;
}
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc); &rc);
GNUNET_assert (NULL != ctx); GNUNET_assert (NULL != ctx);
@ -182,13 +189,11 @@ run (void *cls,
auth.details.basic.username = username; auth.details.basic.username = username;
auth.details.basic.password = password; auth.details.basic.password = password;
op = TALER_BANK_admin_add_incoming (ctx, op = TALER_BANK_admin_add_incoming (ctx,
bank_url, account_base_url,
&auth, &auth,
"https://exchange.com/legacy", &reserve_pub,
subject,
&amount, &amount,
debit_account_no, credit_account,
credit_account_no,
&res_cb, &res_cb,
NULL); NULL);
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
@ -219,26 +224,20 @@ main (int argc, char *const *argv)
(GNUNET_GETOPT_option_string ('b', (GNUNET_GETOPT_option_string ('b',
"bank", "bank",
"URL", "URL",
"base URL of the bank", "base URL of the account at the bank",
&bank_url)), &account_base_url)),
GNUNET_GETOPT_option_help ("Deposit funds into a Taler reserve"), GNUNET_GETOPT_option_help ("Deposit funds into a Taler reserve"),
GNUNET_GETOPT_option_mandatory GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_ulong ('C', (GNUNET_GETOPT_option_string ('C',
"credit", "credit",
"ACCOUNT", "ACCOUNT",
"number of the bank account to credit", "payto URL of the bank account to credit",
&credit_account_no)), &credit_account)),
GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_ulong ('D',
"debit",
"ACCOUNT",
"number of the bank account to debit",
&debit_account_no)),
GNUNET_GETOPT_option_mandatory GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('s', (GNUNET_GETOPT_option_string ('s',
"subject", "subject",
"STRING", "STRING",
"specifies the wire transfer subject", "specifies the wire transfer subject (must be a reserve public key)",
&subject)), &subject)),
GNUNET_GETOPT_option_mandatory GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('u', (GNUNET_GETOPT_option_string ('u',

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V. Copyright (C) 2016-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2018 Taler Systems SA Copyright (C) 2018-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as
@ -16,13 +16,11 @@
License along with TALER; see the file COPYING. If not, see License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/> <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file bank-lib/testing_api_cmd_history.c * @file bank-lib/testing_api_cmd_history.c
* @brief command to check the /history API from the bank. * @brief command to check the /history API from the bank.
* @author Marcello Stanisci * @author Marcello Stanisci
*/ */
#include "platform.h" #include "platform.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
@ -40,35 +38,26 @@
struct HistoryState struct HistoryState
{ {
/** /**
* Base URL of the bank offering the "history" operation. * Base URL of the account offering the "history" operation.
*/ */
const char *bank_url; char *account_url;
/** /**
* Account number to ask the history for. * Reference to command defining the
*/ * first row number we want in the result.
uint64_t account_no;
/**
* Which type of records we are interested: in-transfers
* / out-transfers / rejected transfers.
*/
enum TALER_BANK_Direction direction;
/**
* First row number we want in the result.
*/ */
const char *start_row_reference; const char *start_row_reference;
/** /**
* How many rows we want in the result, _at most_. * How many rows we want in the result, _at most_,
* and ascending/descending.
*/ */
unsigned long long num_results; long long num_results;
/** /**
* Handle to a pending "history" operation. * Handle to a pending "history" operation.
*/ */
struct TALER_BANK_HistoryHandle *hh; struct TALER_BANK_CreditHistoryHandle *hh;
/** /**
* Expected number of results (= rows). * Expected number of results (= rows).
@ -81,34 +70,9 @@ struct HistoryState
*/ */
int failed; int failed;
/**
* If GNUNET_YES, this parameter will ask for results in
* chronological order.
*/
unsigned int ascending;
/**********************************
* Following defs are specific to *
* the "/history-range" version. *
**********************************/
/**
* Last row number we want in the result. Only used
* as a trait source when using the /history-range API.
*/
const char *end_row_reference;
/**
* Start date for /history-range.
*/
struct GNUNET_TIME_Absolute start_date;
/**
* End date for /history-range.
*/
struct GNUNET_TIME_Absolute end_date;
}; };
/** /**
* Item in the transaction history, as reconstructed from the * Item in the transaction history, as reconstructed from the
* command history. * command history.
@ -119,7 +83,7 @@ struct History
/** /**
* Wire details. * Wire details.
*/ */
struct TALER_BANK_TransferDetails details; struct TALER_BANK_CreditDetails details;
/** /**
* Serial ID of the wire transfer. * Serial ID of the wire transfer.
@ -127,19 +91,12 @@ struct History
uint64_t row_id; uint64_t row_id;
/** /**
* Direction of the transfer. * URL to free.
*/ */
enum TALER_BANK_Direction direction; char *url;
}; };
/**
* Array mapping bank account numbers to login credentials.
*/
extern struct TALER_BANK_AuthenticationData AUTHS[];
/** /**
* Offer internal data to other commands. * Offer internal data to other commands.
* *
@ -166,51 +123,6 @@ history_traits (void *cls,
} }
/**
* Test if the CMD at offset @a off has been /rejected, and
* is indeed a wire transfer CMD.
*
* @param is interpreter state (where we are right now)
* @param off offset of the command to test for rejection.
*
* @return GNUNET_YES if the command at @a off was cancelled.
*/
static int
test_cancelled (struct TALER_TESTING_Interpreter *is,
unsigned int off)
{
const char *rejected_reference;
const struct TALER_TESTING_Command *current_cmd;
current_cmd = &is->commands[off];
TALER_LOG_INFO ("Is `%s' rejected?\n",
current_cmd->label);
for (int i = 0; i<is->ip; i++)
{
const struct TALER_TESTING_Command *c = &is->commands[i];
/* XXX: Errors reported here are NOT fatal */
/* Rejected wire transfers have a non-NULL reference to a
* reject command to mark them as rejected. So errors
* about "reject traits" not found are NOT fatal here */
if (GNUNET_OK != TALER_TESTING_get_trait_rejected
(c, 0, &rejected_reference))
continue;
TALER_LOG_INFO ("Command `%s' was rejected by `%s'.\n",
current_cmd->label,
c->label);
if (0 == strcmp (rejected_reference,
current_cmd->label))
return GNUNET_YES;
}
return GNUNET_NO;
}
/** /**
* Free history @a h of length @a h_len. * Free history @a h of length @a h_len.
* *
@ -222,10 +134,7 @@ free_history (struct History *h,
uint64_t h_len) uint64_t h_len)
{ {
for (uint64_t off = 0; off<h_len; off++) for (uint64_t off = 0; off<h_len; off++)
{ GNUNET_free (h[off].url);
GNUNET_free (h[off].details.wire_transfer_subject);
GNUNET_free (h[off].details.account_url);
}
GNUNET_free_non_null (h); GNUNET_free_non_null (h);
} }
@ -251,14 +160,12 @@ print_expected (struct History *h,
for (uint64_t i = 0; i<h_len; i++) for (uint64_t i = 0; i<h_len; i++)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"H(%llu): %s%s (serial: %llu, subject: %s," "H(%llu): %s (serial: %llu, subject: %s,"
" counterpart: %s)\n", " counterpart: %s)\n",
(unsigned long long) i, (unsigned long long) i,
(TALER_BANK_DIRECTION_CREDIT == h[i].direction) ?
"+" : "-",
TALER_amount2s (&h[i].details.amount), TALER_amount2s (&h[i].details.amount),
(unsigned long long) h[i].row_id, (unsigned long long) h[i].row_id,
h[i].details.wire_transfer_subject, TALER_B2S (&h[i].details.reserve_pub),
h[i].details.account_url); h[i].details.account_url);
} }
} }
@ -280,20 +187,6 @@ build_history_hit_limit (uint64_t total,
const struct HistoryState *hs, const struct HistoryState *hs,
const struct TALER_TESTING_Command *pos) const struct TALER_TESTING_Command *pos)
{ {
/* "/history-range" case. */
if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
hs->start_date.abs_value_us)
{
const struct GNUNET_TIME_Absolute *timestamp;
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_absolute_time (pos,
0,
&timestamp));
GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
hs->end_date.abs_value_us);
return timestamp->abs_value_us >= hs->end_date.abs_value_us;
}
return total >= hs->num_results; return total >= hs->num_results;
} }
@ -305,13 +198,6 @@ build_history_hit_limit (uint64_t total,
* to be allocated, and the second to actually populate every * to be allocated, and the second to actually populate every
* element. * element.
* *
* This command has a limitation currently: it orders the history
* list with descending elements if and only if the 'delta' was
* given negative; and will order the list with ascending elements
* if and only if the 'delta' was given positive. Therefore,
* for now it is NOT possible to test such a "/history" request:
* "/history?auth=basic&direction=both&delta=10&ordering=descending"
*
* @param is interpreter state (supposedly having the * @param is interpreter state (supposedly having the
* current CMD pointing at a "history" CMD). * current CMD pointing at a "history" CMD).
* @param[out] rh history array to initialize. * @param[out] rh history array to initialize.
@ -350,10 +236,7 @@ build_history (struct TALER_TESTING_Interpreter *is,
(add_incoming_cmd, 0, &row_id_start)); (add_incoming_cmd, 0, &row_id_start));
} }
GNUNET_assert ((0 != hs->num_results) || /* "/history" */ GNUNET_assert (0 != hs->num_results);
(GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != /* "/history-range" */
hs->start_date.abs_value_us));
if (0 == is->ip) if (0 == is->ip)
{ {
TALER_LOG_DEBUG ("Checking history at first CMD..\n"); TALER_LOG_DEBUG ("Checking history at first CMD..\n");
@ -387,8 +270,9 @@ build_history (struct TALER_TESTING_Interpreter *is,
for (unsigned int off = start; off != end + inc; off += inc) for (unsigned int off = start; off != end + inc; off += inc)
{ {
const struct TALER_TESTING_Command *pos = &is->commands[off]; const struct TALER_TESTING_Command *pos = &is->commands[off];
int cancelled;
const uint64_t *row_id; const uint64_t *row_id;
const char *credit_account;
const char *debit_account;
/** /**
* The following command allows us to skip over those CMDs * The following command allows us to skip over those CMDs
@ -411,28 +295,6 @@ build_history (struct TALER_TESTING_Interpreter *is,
} }
} }
/* Seek "/history-range" starting row, _if_ that's the case */
if ((GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
hs->start_date.abs_value_us) && (GNUNET_YES != ok))
{
const struct GNUNET_TIME_Absolute *timestamp;
TALER_TESTING_get_trait_absolute_time (pos,
0,
&timestamp);
TALER_LOG_DEBUG
("Seeking first row, start vs timestamp: %llu vs %llu\n",
(long long unsigned int) hs->start_date.abs_value_us,
(long long unsigned int) timestamp->abs_value_us);
if (hs->start_date.abs_value_us <= timestamp->abs_value_us)
{
total = 0;
ok = GNUNET_YES;
continue;
}
}
/* when 'start' was _not_ given, then ok == GNUNET_YES */ /* when 'start' was _not_ given, then ok == GNUNET_YES */
if (GNUNET_NO == ok) if (GNUNET_NO == ok)
continue; /* skip until we find the marker */ continue; /* skip until we find the marker */
@ -447,37 +309,23 @@ build_history (struct TALER_TESTING_Interpreter *is,
break; break;
} }
cancelled = test_cancelled (is, off);
if ( (GNUNET_YES == cancelled) &&
(0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) )
{
TALER_LOG_INFO ("Ignoring canceled wire"
" transfer from history\n");
continue;
}
const uint64_t *credit_account_no;
const uint64_t *debit_account_no;
GNUNET_assert GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
(pos, &credit_account_no)); (pos, &credit_account));
GNUNET_assert GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
(pos, &debit_account_no)); (pos, &debit_account));
TALER_LOG_INFO ("Potential history element:" TALER_LOG_INFO ("Potential history element:"
" %llu->%llu; my account: %llu\n", " %s->%s; my account: %s\n",
(unsigned long long) *debit_account_no, debit_account,
(unsigned long long) *credit_account_no, credit_account,
(unsigned long long) hs->account_no); hs->account_url);
if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && if (0 == strcasecmp (hs->account_url,
(hs->account_no == *credit_account_no)) || credit_account))
( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
(hs->account_no == *debit_account_no)) )
{ {
TALER_LOG_INFO ("+1 my history\n"); TALER_LOG_INFO ("+1 my history\n");
total++; /* found matching record */ total++; /* found matching record */
@ -508,11 +356,10 @@ build_history (struct TALER_TESTING_Interpreter *is,
for (unsigned int off = start; off != end + inc; off += inc) for (unsigned int off = start; off != end + inc; off += inc)
{ {
const struct TALER_TESTING_Command *pos = &is->commands[off]; const struct TALER_TESTING_Command *pos = &is->commands[off];
int cancelled;
const uint64_t *row_id; const uint64_t *row_id;
char *bank_hostname; char *bank_hostname;
const uint64_t *credit_account_no; const char *credit_account;
const uint64_t *debit_account_no; const char *debit_account;
if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID
(pos, &row_id)) (pos, &row_id))
@ -533,28 +380,6 @@ build_history (struct TALER_TESTING_Interpreter *is,
} }
} }
/* Seek "/history-range" starting row, _if_ that's the case */
if ((GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us !=
hs->start_date.abs_value_us) && (GNUNET_YES != ok))
{
const struct GNUNET_TIME_Absolute *timestamp;
TALER_TESTING_get_trait_absolute_time (pos,
0,
&timestamp);
TALER_LOG_DEBUG
("Seeking first row, start vs timestamp (2): %llu vs %llu\n",
(long long unsigned int) hs->start_date.abs_value_us,
(long long unsigned int) timestamp->abs_value_us);
if (hs->start_date.abs_value_us <= timestamp->abs_value_us)
{
total = 0;
ok = GNUNET_YES;
continue;
}
}
TALER_LOG_INFO ("Found first row (2)\n"); TALER_LOG_INFO ("Found first row (2)\n");
if (GNUNET_NO == ok) if (GNUNET_NO == ok)
@ -574,43 +399,31 @@ build_history (struct TALER_TESTING_Interpreter *is,
GNUNET_assert GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT (GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
(pos, &credit_account_no)); (pos, &credit_account));
GNUNET_assert GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT (GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
(pos, &debit_account_no)); (pos, &debit_account));
TALER_LOG_INFO ("Potential history bit:" TALER_LOG_INFO ("Potential history bit:"
" %llu->%llu; my account: %llu\n", " %s->%s; my account: %s\n",
(unsigned long long) *debit_account_no, debit_account,
(unsigned long long) *credit_account_no, credit_account,
(unsigned long long) hs->account_no); hs->account_url);
/** /**
* Discard transactions where the audited account played * Discard transactions where the audited account played
* _both_ the credit and the debit roles, but _only if_ * _both_ the credit and the debit roles, but _only if_
* the audit goes on both directions.. This needs more * the audit goes on both directions.. This needs more
* explaination! * explaination!
*/if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && */if (0 == strcasecmp (hs->account_url,
(hs->account_no == *credit_account_no)) && credit_account))
( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
(hs->account_no == *debit_account_no)) )
{ {
GNUNET_break (0); GNUNET_break (0);
continue; continue;
} }
cancelled = test_cancelled (is, off); bank_hostname = strchr (hs->account_url, ':');
if ( (GNUNET_YES == cancelled) &&
(0 == (hs->direction & TALER_BANK_DIRECTION_CANCEL)) )
{
TALER_LOG_WARNING ("`%s' was cancelled\n",
TALER_TESTING_interpreter_get_current_label
(is));
continue;
}
bank_hostname = strchr (hs->bank_url, ':');
GNUNET_assert (NULL != bank_hostname); GNUNET_assert (NULL != bank_hostname);
bank_hostname += 3; bank_hostname += 3;
@ -618,68 +431,36 @@ build_history (struct TALER_TESTING_Interpreter *is,
* information. */ * information. */
/* Asked for credit, and account got the credit. */ /* Asked for credit, and account got the credit. */
if ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && if (0 == strcasecmp (hs->account_url,
(hs->account_no == *credit_account_no)) credit_account))
{ {
h[total].direction = TALER_BANK_DIRECTION_CREDIT; h[total].url = GNUNET_strdup (debit_account);
if (GNUNET_YES == cancelled) h[total].details.account_url = h[total].url;
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
GNUNET_asprintf
(&h[total].details.account_url,
('/' == bank_hostname[strlen (bank_hostname) - 1])
? "payto://x-taler-bank/%s%llu"
: "payto://x-taler-bank/%s/%llu",
bank_hostname,
(unsigned long long) *debit_account_no);
}
/* Asked for debit, and account got the debit. */
if ( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
(hs->account_no == *debit_account_no))
{
h[total].direction = TALER_BANK_DIRECTION_DEBIT;
if (GNUNET_YES == cancelled)
h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
GNUNET_asprintf
(&h[total].details.account_url,
('/' == bank_hostname[strlen (bank_hostname) - 1])
? "payto://x-taler-bank/%s%llu"
: "payto://x-taler-bank/%s/%llu",
bank_hostname,
(unsigned long long) *credit_account_no);
} }
/* This block _completes_ the information of the current item, /* This block _completes_ the information of the current item,
* with amount / subject / exchange URL. */ * with amount / subject / exchange URL. */
if ( ( (0 != (hs->direction & TALER_BANK_DIRECTION_CREDIT)) && if (0 == strcasecmp (hs->account_url,
(hs->account_no == *credit_account_no)) || credit_account))
( (0 != (hs->direction & TALER_BANK_DIRECTION_DEBIT)) &&
(hs->account_no == *debit_account_no)) )
{ {
const struct TALER_Amount *amount; const struct TALER_Amount *amount;
const char *subject; const struct TALER_ReservePublicKeyP *reserve_pub;
const char *exchange_url; const char *account_url;
GNUNET_assert GNUNET_assert (GNUNET_OK ==
(GNUNET_OK == TALER_TESTING_get_trait_amount_obj TALER_TESTING_get_trait_amount_obj
(pos, 0, &amount)); (pos, 0, &amount));
GNUNET_assert (GNUNET_OK ==
GNUNET_assert TALER_TESTING_get_trait_reserve_pub
(GNUNET_OK == TALER_TESTING_get_trait_transfer_subject (pos, 0, &reserve_pub));
(pos, 0, &subject)); GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_url
GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_url (pos, 1,
(pos, 0, &exchange_url)); &account_url));
h[total].details.amount = *amount; h[total].details.amount = *amount;
h[total].row_id = *row_id; h[total].row_id = *row_id;
GNUNET_asprintf (&h[total].details.wire_transfer_subject, h[total].details.reserve_pub = *reserve_pub;
"%s %s", h[total].details.account_url = account_url;
subject,
exchange_url);
TALER_LOG_INFO ("+1-bit of my history\n"); TALER_LOG_INFO ("+1-bit of my history\n");
total++; total++;
} }
@ -723,8 +504,7 @@ compute_result_count (struct TALER_TESTING_Interpreter *is)
static int static int
check_result (struct TALER_TESTING_Interpreter *is, check_result (struct TALER_TESTING_Interpreter *is,
unsigned int off, unsigned int off,
enum TALER_BANK_Direction dir, const struct TALER_BANK_CreditDetails *details)
const struct TALER_BANK_TransferDetails *details)
{ {
uint64_t total; uint64_t total;
struct History *h; struct History *h;
@ -737,27 +517,22 @@ check_result (struct TALER_TESTING_Interpreter *is,
" results, but got result #%u to check\n", " results, but got result #%u to check\n",
(unsigned int) total, (unsigned int) total,
off); off);
print_expected (h, total, off); print_expected (h,
total,
off);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (h[off].direction != dir) if ( (0 != GNUNET_memcmp (&h[off].details.reserve_pub,
{ &details->reserve_pub)) ||
GNUNET_break (0);
print_expected (h, total, off);
free_history (h,
total);
return GNUNET_SYSERR;
}
if ( (0 != strcmp (h[off].details.wire_transfer_subject,
details->wire_transfer_subject)) ||
(0 != TALER_amount_cmp (&h[off].details.amount, (0 != TALER_amount_cmp (&h[off].details.amount,
&details->amount)) || &details->amount)) ||
(0 != strcasecmp (h[off].details.account_url, (0 != strcasecmp (h[off].details.account_url,
details->account_url)) ) details->account_url)) )
{ {
GNUNET_break (0); GNUNET_break (0);
print_expected (h, total, off); print_expected (h,
total,
off);
free_history (h, free_history (h,
total); total);
return GNUNET_SYSERR; return GNUNET_SYSERR;
@ -789,22 +564,30 @@ check_result (struct TALER_TESTING_Interpreter *is,
* @param details details about the wire transfer. * @param details details about the wire transfer.
* @param json detailed response from the HTTPD, or NULL if * @param json detailed response from the HTTPD, or NULL if
* reply was not in JSON. * reply was not in JSON.
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
static void static int
history_cb (void *cls, history_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
uint64_t row_id, uint64_t row_id,
const struct TALER_BANK_TransferDetails *details, const struct TALER_BANK_CreditDetails *details,
const json_t *json) const json_t *json)
{ {
struct TALER_TESTING_Interpreter *is = cls; struct TALER_TESTING_Interpreter *is = cls;
struct HistoryState *hs = is->commands[is->ip].cls; struct HistoryState *hs = is->commands[is->ip].cls;
(void) row_id; (void) row_id;
/*NOTE: "204 No Content" is used to signal the end of results.*/ if (MHD_HTTP_OK != http_status)
if (MHD_HTTP_NO_CONTENT == http_status) {
hs->hh = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unwanted response code from /history: %u\n",
http_status);
TALER_TESTING_interpreter_fail (is);
return GNUNET_SYSERR;
}
if (NULL == details)
{ {
hs->hh = NULL; hs->hh = NULL;
if ( (hs->results_obtained != compute_result_count (is)) || if ( (hs->results_obtained != compute_result_count (is)) ||
@ -829,33 +612,20 @@ history_cb (void *cls,
free_history (h, free_history (h,
total); total);
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return GNUNET_SYSERR;
} }
TALER_TESTING_interpreter_next (is); TALER_TESTING_interpreter_next (is);
return; return GNUNET_OK;
}
if (MHD_HTTP_OK != http_status)
{
hs->hh = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unwanted response code from /history[-range]: %u\n",
http_status);
TALER_TESTING_interpreter_fail (is);
return;
} }
/* check current element */ /* check current element */
if (GNUNET_OK != check_result (is, if (GNUNET_OK != check_result (is,
hs->results_obtained, hs->results_obtained,
dir,
details)) details))
{
GNUNET_break (0);
{ {
char *acc; char *acc;
GNUNET_break (0);
acc = json_dumps (json, acc = json_dumps (json,
JSON_COMPACT); JSON_COMPACT);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -864,12 +634,11 @@ history_cb (void *cls,
acc); acc);
if (NULL != acc) if (NULL != acc)
free (acc); free (acc);
}
hs->failed = GNUNET_YES; hs->failed = GNUNET_YES;
return; return GNUNET_SYSERR;
} }
hs->results_obtained++; hs->results_obtained++;
return GNUNET_OK;
} }
@ -886,9 +655,8 @@ history_run (void *cls,
struct TALER_TESTING_Interpreter *is) struct TALER_TESTING_Interpreter *is)
{ {
struct HistoryState *hs = cls; struct HistoryState *hs = cls;
uint64_t row_id = UINT64_MAX; uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX;
const uint64_t *row_id_ptr = &row_id; const uint64_t *row_ptr;
struct TALER_BANK_AuthenticationData *auth;
(void) cmd; (void) cmd;
/* Get row_id from trait. */ /* Get row_id from trait. */
@ -902,23 +670,20 @@ history_run (void *cls,
if (NULL == history_cmd) if (NULL == history_cmd)
TALER_TESTING_FAIL (is); TALER_TESTING_FAIL (is);
if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (history_cmd, if (GNUNET_OK !=
TALER_TESTING_get_trait_uint64 (history_cmd,
0, 0,
&row_id_ptr)) &row_ptr))
TALER_TESTING_FAIL (is); TALER_TESTING_FAIL (is);
row_id = *row_id_ptr; else
row_id = *row_ptr;
TALER_LOG_DEBUG ("row id (from trait) is %llu\n", TALER_LOG_DEBUG ("row id (from trait) is %llu\n",
(unsigned long long) row_id); (unsigned long long) row_id);
} }
auth = &AUTHS[hs->account_no - 1]; hs->hh = TALER_BANK_credit_history (is->ctx,
hs->hh = TALER_BANK_history (is->ctx, hs->account_url,
hs->bank_url, NULL,
auth,
hs->account_no,
hs->direction,
hs->ascending,
row_id, row_id,
hs->num_results, hs->num_results,
&history_cb, &history_cb,
@ -944,8 +709,9 @@ history_cleanup (void *cls,
if (NULL != hs->hh) if (NULL != hs->hh)
{ {
TALER_LOG_WARNING ("/history did not complete\n"); TALER_LOG_WARNING ("/history did not complete\n");
TALER_BANK_history_cancel (hs->hh); TALER_BANK_credit_history_cancel (hs->hh);
} }
GNUNET_free (hs->account_url);
GNUNET_free (hs); GNUNET_free (hs);
} }
@ -954,12 +720,8 @@ history_cleanup (void *cls,
* Make a "history" CMD. * Make a "history" CMD.
* *
* @param label command label. * @param label command label.
* @param bank_url base URL of the bank offering the "history" * @param account_url base URL of the account offering the "history"
* operation. * operation.
* @param account_no bank account number to ask the history for.
* @param direction which direction this operation is interested.
* @param ascending if #GNUNET_YES, the bank will return the rows
* in ascending (= chronological) order.
* @param start_row_reference reference to a command that can * @param start_row_reference reference to a command that can
* offer a row identifier, to be used as the starting row * offer a row identifier, to be used as the starting row
* to accept in the result. * to accept in the result.
@ -967,25 +729,17 @@ history_cleanup (void *cls,
* @return the command. * @return the command.
*/ */
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_history (const char *label, TALER_TESTING_cmd_bank_credits (const char *label,
const char *bank_url, const char *account_url,
uint64_t account_no,
enum TALER_BANK_Direction direction,
unsigned int ascending,
const char *start_row_reference, const char *start_row_reference,
unsigned long long num_results) long long num_results)
{ {
struct HistoryState *hs; struct HistoryState *hs;
hs = GNUNET_new (struct HistoryState); hs = GNUNET_new (struct HistoryState);
hs->bank_url = bank_url; hs->account_url = GNUNET_strdup (account_url);
hs->account_no = account_no;
hs->direction = direction;
hs->start_row_reference = start_row_reference; hs->start_row_reference = start_row_reference;
hs->num_results = num_results; hs->num_results = num_results;
hs->ascending = ascending;
hs->start_date = GNUNET_TIME_UNIT_FOREVER_ABS;
hs->end_date = GNUNET_TIME_UNIT_FOREVER_ABS;
{ {
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
@ -1001,4 +755,4 @@ TALER_TESTING_cmd_bank_history (const char *label,
} }
/* end of testing_api_cmd_history.c */ /* end of testing_api_cmd_credit_history.c */

View File

@ -0,0 +1,758 @@
/*
This file is part of TALER
Copyright (C) 2018-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3, or
(at your option) any later version.
TALER is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/testing_api_cmd_history.c
* @brief command to check the /history API from the bank.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "taler_exchange_service.h"
#include "taler_testing_lib.h"
#include "taler_testing_bank_lib.h"
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
#include "taler_fakebank_lib.h"
/**
* State for a "history" CMD.
*/
struct HistoryState
{
/**
* Base URL of the account offering the "history" operation.
*/
const char *account_url;
/**
* Reference to command defining the
* first row number we want in the result.
*/
const char *start_row_reference;
/**
* How many rows we want in the result, _at most_,
* and ascending/descending.
*/
long long num_results;
/**
* Handle to a pending "history" operation.
*/
struct TALER_BANK_DebitHistoryHandle *hh;
/**
* Expected number of results (= rows).
*/
uint64_t results_obtained;
/**
* Set to GNUNET_YES if the callback detects something
* unexpected.
*/
int failed;
};
/**
* Item in the transaction history, as reconstructed from the
* command history.
*/
struct History
{
/**
* Wire details.
*/
struct TALER_BANK_DebitDetails details;
/**
* Serial ID of the wire transfer.
*/
uint64_t row_id;
/**
* URL to free.
*/
char *url;
};
/**
* Offer internal data to other commands.
*
* @param cls closure.
* @param ret[out] set to the wanted data.
* @param trait name of the trait.
* @param index index number of the traits to be returned.
*
* @return #GNUNET_OK on success
*/
static int
history_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
{
(void) cls;
(void) ret;
(void) trait;
(void) index;
/* Must define this function because some callbacks
* look for certain traits on _all_ the commands. */
return GNUNET_SYSERR;
}
/**
* Free history @a h of length @a h_len.
*
* @param h history array to free.
* @param h_len number of entries in @a h.
*/
static void
free_history (struct History *h,
uint64_t h_len)
{
for (uint64_t off = 0; off<h_len; off++)
GNUNET_free (h[off].url);
GNUNET_free_non_null (h);
}
/**
* Log which history we expected. Called when an error occurs.
*
* @param h what we expected.
* @param h_len number of entries in @a h.
* @param off position of the missmatch.
*/
static void
print_expected (struct History *h,
uint64_t h_len,
unsigned int off)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Transaction history missmatch at position %u/%llu\n",
off,
(unsigned long long) h_len);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Expected history:\n");
for (uint64_t i = 0; i<h_len; i++)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"H(%llu): %s (serial: %llu, subject: %s,"
" counterpart: %s)\n",
(unsigned long long) i,
TALER_amount2s (&h[i].details.amount),
(unsigned long long) h[i].row_id,
TALER_B2S (&h[i].details.wtid),
h[i].details.account_url);
}
}
/**
* Tell if the current item is beyond the allowed limit.
*
* @param total current number of items in the built history list.
* Note, this is the list we build locally and compare with
* what the server returned.
* @param hs the history CMD state.
* @param pos current item to be evaluated or not (if the list
* has already enough elements).
* @return GNUNET_OK / GNUNET_NO.
*/
static int
build_history_hit_limit (uint64_t total,
const struct HistoryState *hs,
const struct TALER_TESTING_Command *pos)
{
return total >= hs->num_results;
}
/**
* This function constructs the list of history elements that
* interest the account number of the caller. It has two main
* loops: the first to figure out how many history elements have
* to be allocated, and the second to actually populate every
* element.
*
* @param is interpreter state (supposedly having the
* current CMD pointing at a "history" CMD).
* @param[out] rh history array to initialize.
*
* @return number of entries in @a rh.
*/
static uint64_t
build_history (struct TALER_TESTING_Interpreter *is,
struct History **rh)
{
struct HistoryState *hs = is->commands[is->ip].cls;
uint64_t total;
struct History *h;
const struct TALER_TESTING_Command *add_incoming_cmd;
int inc;
unsigned int start;
unsigned int end;
/**
* @var turns GNUNET_YES whenever either no 'start' value was
* given for the history query, or the given value is found
* in the list of all the CMDs.
*/int ok;
const uint64_t *row_id_start = NULL;
if (NULL != hs->start_row_reference)
{
TALER_LOG_INFO
("`%s': start row given via reference `%s'\n",
TALER_TESTING_interpreter_get_current_label (is),
hs->start_row_reference);
add_incoming_cmd = TALER_TESTING_interpreter_lookup_command
(is, hs->start_row_reference);
GNUNET_assert (NULL != add_incoming_cmd);
GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_uint64
(add_incoming_cmd, 0, &row_id_start));
}
GNUNET_assert (0 != hs->num_results);
if (0 == is->ip)
{
TALER_LOG_DEBUG ("Checking history at first CMD..\n");
*rh = NULL;
return 0;
}
/* AKA 'delta'. */
if (hs->num_results > 0)
{
inc = 1; /* _inc_rement */
start = 0;
end = is->ip - 1;
}
else
{
inc = -1;
start = is->ip - 1;
end = 0;
}
total = 0;
ok = GNUNET_NO;
if (NULL == row_id_start)
ok = GNUNET_YES;
/* This loop counts how many commands _later than "start"_ belong
* to the history of the caller. This is stored in the @var total
* variable. */
for (unsigned int off = start; off != end + inc; off += inc)
{
const struct TALER_TESTING_Command *pos = &is->commands[off];
const uint64_t *row_id;
const char *debit_account;
const char *credit_account;
/**
* The following command allows us to skip over those CMDs
* that do not offer a "row_id" trait. Such skipped CMDs are
* not interesting for building a history.
*/if (GNUNET_OK != TALER_TESTING_get_trait_uint64 (pos,
0,
&row_id))
continue;
/* Seek "/history" starting row. */
if (NULL != row_id_start)
{
if (*row_id_start == *row_id)
{
/* Doesn't count, start is excluded from output. */
total = 0;
ok = GNUNET_YES;
continue;
}
}
/* when 'start' was _not_ given, then ok == GNUNET_YES */
if (GNUNET_NO == ok)
continue; /* skip until we find the marker */
TALER_LOG_DEBUG ("Found first row\n");
if (build_history_hit_limit (total,
hs,
pos))
{
TALER_LOG_DEBUG ("Hit history limit\n");
break;
}
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
(pos, &debit_account));
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
(pos, &credit_account));
TALER_LOG_INFO ("Potential history element:"
" %s->%s; my account: %s\n",
debit_account,
credit_account,
hs->account_url);
if (0 == strcasecmp (hs->account_url,
debit_account))
{
TALER_LOG_INFO ("+1 my history\n");
total++; /* found matching record */
}
}
GNUNET_assert (GNUNET_YES == ok);
if (0 == total)
{
TALER_LOG_DEBUG ("Checking history at first CMD.. (2)\n");
*rh = NULL;
return 0;
}
GNUNET_assert (total < UINT_MAX);
h = GNUNET_new_array ((unsigned int) total,
struct History);
total = 0;
ok = GNUNET_NO;
if (NULL == row_id_start)
ok = GNUNET_YES;
/**
* This loop _only_ populates the array of history elements.
*/
for (unsigned int off = start; off != end + inc; off += inc)
{
const struct TALER_TESTING_Command *pos = &is->commands[off];
const uint64_t *row_id;
char *bank_hostname;
const char *credit_account;
const char *debit_account;
if (GNUNET_OK != TALER_TESTING_GET_TRAIT_ROW_ID
(pos, &row_id))
continue;
if (NULL != row_id_start)
{
if (*row_id_start == *row_id)
{
/**
* Warning: this zeroing is superfluous, as
* total doesn't get incremented if 'start'
* was given and couldn't be found.
*/total = 0;
ok = GNUNET_YES;
continue;
}
}
TALER_LOG_INFO ("Found first row (2)\n");
if (GNUNET_NO == ok)
{
TALER_LOG_INFO ("Skip on `%s'\n",
pos->label);
continue; /* skip until we find the marker */
}
if (build_history_hit_limit (total,
hs,
pos))
{
TALER_LOG_INFO ("Hit history limit (2)\n");
break;
}
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT
(pos, &debit_account));
GNUNET_assert
(GNUNET_OK == TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT
(pos, &credit_account));
TALER_LOG_INFO ("Potential history bit:"
" %s->%s; my account: %s\n",
debit_account,
credit_account,
hs->account_url);
/**
* Discard transactions where the audited account played
* _both_ the debit and the debit roles, but _only if_
* the audit goes on both directions.. This needs more
* explaination!
*/if (0 == strcasecmp (hs->account_url,
debit_account))
{
GNUNET_break (0);
continue;
}
bank_hostname = strchr (hs->account_url, ':');
GNUNET_assert (NULL != bank_hostname);
bank_hostname += 3;
/* Next two blocks only put the 'direction' and 'banking'
* information. */
/* Asked for debit, and account got the debit. */
if (0 == strcasecmp (hs->account_url,
debit_account))
{
h[total].url = GNUNET_strdup (credit_account);
h[total].details.account_url = h[total].url;
}
/* This block _completes_ the information of the current item,
* with amount / subject / exchange URL. */
if (0 == strcasecmp (hs->account_url,
debit_account))
{
const struct TALER_Amount *amount;
const struct TALER_WireTransferIdentifierRawP *wtid;
const char *account_url;
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_amount_obj
(pos, 0, &amount));
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_wtid
(pos, 0, &wtid));
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_url
(pos, 1,
&account_url));
h[total].details.amount = *amount;
h[total].row_id = *row_id;
h[total].details.wtid = *wtid;
h[total].details.account_url = account_url;
TALER_LOG_INFO ("+1-bit of my history\n");
total++;
}
}
*rh = h;
return total;
}
/**
* Compute how many results we expect to be returned for
* the current command at @a is.
*
* @param is the interpreter state to inspect.
* @return number of results expected.
*/
static uint64_t
compute_result_count (struct TALER_TESTING_Interpreter *is)
{
uint64_t total;
struct History *h;
total = build_history (is, &h);
free_history (h, total);
return total;
}
/**
* Check that the "/history" response matches the
* CMD whose offset in the list of CMDs is @a off.
*
* @param is the interpreter state.
* @param off the offset (of the CMD list) where the command
* to check is.
* @param dir the expected direction of the transaction.
* @param details the expected transaction details.
*
* @return #GNUNET_OK if the transaction is what we expect.
*/
static int
check_result (struct TALER_TESTING_Interpreter *is,
unsigned int off,
const struct TALER_BANK_DebitDetails *details)
{
uint64_t total;
struct History *h;
total = build_history (is, &h);
if (off >= total)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Test says history has at most %u"
" results, but got result #%u to check\n",
(unsigned int) total,
off);
print_expected (h,
total,
off);
return GNUNET_SYSERR;
}
if ( (0 != GNUNET_memcmp (&h[off].details.wtid,
&details->wtid)) ||
(0 != TALER_amount_cmp (&h[off].details.amount,
&details->amount)) ||
(0 != strcasecmp (h[off].details.account_url,
details->account_url)) )
{
GNUNET_break (0);
print_expected (h,
total,
off);
free_history (h,
total);
return GNUNET_SYSERR;
}
free_history (h,
total);
return GNUNET_OK;
}
/**
* This callback will (1) check that the HTTP response code
* is acceptable and (2) that the history is consistent. The
* consistency is checked by going through all the past CMDs,
* reconstructing then the expected history as of those, and
* finally check it against what the bank returned.
*
* @param cls closure.
* @param http_status HTTP response code, #MHD_HTTP_OK (200)
* for successful status request 0 if the bank's reply is
* bogus (fails to follow the protocol),
* #MHD_HTTP_NO_CONTENT if there are no more results; on
* success the last callback is always of this status
* (even if `abs(num_results)` were already returned).
* @param ec taler status code.
* @param dir direction of the transfer.
* @param row_id monotonically increasing counter corresponding to
* the transaction.
* @param details details about the wire transfer.
* @param json detailed response from the HTTPD, or NULL if
* reply was not in JSON.
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/
static int
history_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t row_id,
const struct TALER_BANK_DebitDetails *details,
const json_t *json)
{
struct TALER_TESTING_Interpreter *is = cls;
struct HistoryState *hs = is->commands[is->ip].cls;
(void) row_id;
if (MHD_HTTP_OK != http_status)
{
hs->hh = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unwanted response code from /history: %u\n",
http_status);
TALER_TESTING_interpreter_fail (is);
return GNUNET_SYSERR;
}
if (NULL == details)
{
hs->hh = NULL;
if ( (hs->results_obtained != compute_result_count (is)) ||
(GNUNET_YES == hs->failed) )
{
uint64_t total;
struct History *h;
GNUNET_break (0);
total = build_history (is, &h);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Expected history of length %llu, got %llu;"
" HTTP status code: %u/%d, failed: %d\n",
(unsigned long long) total,
(unsigned long long) hs->results_obtained,
http_status,
(int) ec,
hs->failed);
print_expected (h,
total,
UINT_MAX);
free_history (h,
total);
TALER_TESTING_interpreter_fail (is);
return GNUNET_SYSERR;
}
TALER_TESTING_interpreter_next (is);
return GNUNET_OK;
}
/* check current element */
if (GNUNET_OK != check_result (is,
hs->results_obtained,
details))
{
char *acc;
GNUNET_break (0);
acc = json_dumps (json,
JSON_COMPACT);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Result %u was `%s'\n",
(unsigned int) hs->results_obtained++,
acc);
if (NULL != acc)
free (acc);
hs->failed = GNUNET_YES;
return GNUNET_SYSERR;
}
hs->results_obtained++;
return GNUNET_OK;
}
/**
* Run the command.
*
* @param cls closure.
* @param cmd the command to execute.
* @param is the interpreter state.
*/
static void
history_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct HistoryState *hs = cls;
uint64_t row_id = (hs->num_results > 0) ? 0 : UINT64_MAX;
const uint64_t *row_ptr;
(void) cmd;
/* Get row_id from trait. */
if (NULL != hs->start_row_reference)
{
const struct TALER_TESTING_Command *history_cmd;
history_cmd = TALER_TESTING_interpreter_lookup_command
(is, hs->start_row_reference);
if (NULL == history_cmd)
TALER_TESTING_FAIL (is);
if (GNUNET_OK !=
TALER_TESTING_get_trait_uint64 (history_cmd,
0,
&row_ptr))
TALER_TESTING_FAIL (is);
else
row_id = *row_ptr;
TALER_LOG_DEBUG ("row id (from trait) is %llu\n",
(unsigned long long) row_id);
}
hs->hh = TALER_BANK_debit_history (is->ctx,
hs->account_url,
NULL,
row_id,
hs->num_results,
&history_cb,
is);
GNUNET_assert (NULL != hs->hh);
}
/**
* Free the state from a "history" CMD, and possibly cancel
* a pending operation thereof.
*
* @param cls closure.
* @param cmd the command which is being cleaned up.
*/
static void
history_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct HistoryState *hs = cls;
(void) cmd;
if (NULL != hs->hh)
{
TALER_LOG_WARNING ("/history did not complete\n");
TALER_BANK_debit_history_cancel (hs->hh);
}
GNUNET_free (hs);
}
/**
* Make a "history" CMD.
*
* @param label command label.
* @param account_url base URL of the account offering the "history"
* operation.
* @param start_row_reference reference to a command that can
* offer a row identifier, to be used as the starting row
* to accept in the result.
* @param num_results how many rows we want in the result.
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_debits (const char *label,
const char *account_url,
const char *start_row_reference,
long long num_results)
{
struct HistoryState *hs;
hs = GNUNET_new (struct HistoryState);
hs->account_url = account_url;
hs->start_row_reference = start_row_reference;
hs->num_results = num_results;
{
struct TALER_TESTING_Command cmd = {
.label = label,
.cls = hs,
.run = &history_run,
.cleanup = &history_cleanup,
.traits = &history_traits
};
return cmd;
}
}
/* end of testing_api_cmd_history_debit.c */

View File

@ -1,223 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3, or
(at your option) any later version.
TALER is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/testing_api_cmd_reject.c
* @brief command to check the /reject API from the bank.
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "taler_exchange_service.h"
#include "taler_testing_lib.h"
#include "taler_fakebank_lib.h"
#include "taler_bank_service.h"
#include "taler_fakebank_lib.h"
/**
* State for a "reject" CMD.
*/
struct RejectState
{
/**
* Handle of a ongoing "reject" operation.
*/
struct TALER_BANK_RejectHandle *rh;
/**
* Reference to any command that can offer a wire
* transfer "row id" and its credit account so as
* to give input data to the "reject" operation.
*/
const char *deposit_reference;
/**
* Base URL of the bank implementing the "reject"
* operation.
*/
const char *bank_url;
};
/**
* Check that the response code from the "reject" opetation
* is acceptable, namely it equals "204 No Content".
*
* @param cls closure.
* @param http_status HTTP response code.
* @param ec taler-specific error code.
*/
static void
reject_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec)
{
struct TALER_TESTING_Interpreter *is = cls;
struct RejectState *rs = is->commands[is->ip].cls;
rs->rh = NULL;
if (MHD_HTTP_NO_CONTENT != http_status)
{
GNUNET_break (0);
fprintf (stderr,
"Unexpected response code %u/%d\n",
http_status,
(int) ec);
TALER_TESTING_interpreter_fail (is);
return;
}
TALER_TESTING_interpreter_next (is);
}
/**
* Cleanup the state of a "reject" CMD, and possibly
* cancel a pending operation thereof.
*
* @param cls closure.
* @param cmd the command.
*/
static void
reject_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
struct RejectState *rs = cls;
(void) cmd;
if (NULL != rs->rh)
{
TALER_LOG_WARNING ("/reject did not complete\n");
TALER_BANK_reject_cancel (rs->rh);
}
GNUNET_free (rs);
}
/**
* Run the command.
*
* @param cls closure.
* @param cmd the command to execute.
* @param is the interpreter state.
*/
static void
reject_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RejectState *rs = cls;
const struct TALER_TESTING_Command *deposit_cmd;
const uint64_t *credit_account;
const uint64_t *row_id;
extern struct TALER_BANK_AuthenticationData AUTHS[];
(void) cmd;
deposit_cmd
= TALER_TESTING_interpreter_lookup_command (is,
rs->deposit_reference);
if (NULL == deposit_cmd)
TALER_TESTING_FAIL (is);
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT (deposit_cmd,
&credit_account));
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_GET_TRAIT_ROW_ID (deposit_cmd,
&row_id));
TALER_LOG_INFO ("Account %llu rejects deposit\n",
(unsigned long long) *credit_account);
rs->rh = TALER_BANK_reject (is->ctx,
rs->bank_url,
&AUTHS[*credit_account - 1],
*credit_account,
*row_id,
&reject_cb,
is);
GNUNET_assert (NULL != rs->rh);
}
/**
* Offer internal data from a "reject" CMD to other commands.
*
* @param cls closure.
* @param ret[out] result.
* @param trait name of the trait.
* @param index index number of the trait to return.
*
* @return #GNUNET_OK on success.
*/
static int
reject_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
{
struct RejectState *rs = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_rejected (0, rs->deposit_reference),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
/**
* Create a "reject" CMD.
*
* @param label command label.
* @param bank_url base URL of the bank implementing the
* "reject" operation.
* @param deposit_reference reference to a command that will
* provide a "row id" and credit (bank) account to craft
* the "reject" request.
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_reject (const char *label,
const char *bank_url,
const char *deposit_reference)
{
struct RejectState *rs;
rs = GNUNET_new (struct RejectState);
rs->bank_url = bank_url;
rs->deposit_reference = deposit_reference;
{
struct TALER_TESTING_Command cmd = {
.cls = rs,
.run = &reject_run,
.cleanup = &reject_cleanup,
.label = label,
.traits = &reject_traits
};
return cmd;
}
}
/* end of testing_api_cmd_reject.c */

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2014-2019 Taler Systems SA (C) 2014-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as under the terms of the GNU Affero General Public License as
@ -16,15 +16,13 @@
along with TALER; see the file COPYING. If not, along with TALER; see the file COPYING. If not,
see <http://www.gnu.org/licenses/> see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file merchant/backend/taler-merchant-httpd.c * @file benchmark/taler-exchange-benchmark.c
* @brief HTTP serving layer intended to perform crypto-work and * @brief HTTP serving layer intended to perform crypto-work and
* communication with the exchange * communication with the exchange
* @author Marcello Stanisci * @author Marcello Stanisci
* @author Christian Grothoff * @author Christian Grothoff
*/ */
#include "platform.h" #include "platform.h"
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h> #include <microhttpd.h>
@ -58,23 +56,14 @@ enum BenchmarkError
*/ */
#define UNITY_SIZE 6 #define UNITY_SIZE 6
/**
* Account number of the merchant. Fakebank likes any number,
* the only requirement is that this number then matches the
* number given when building payto URLs at deposit time.
*/
#define TALER_TESTING_USER_ACCOUNT_NUMBER 3
#define FIRST_INSTRUCTION -1 #define FIRST_INSTRUCTION -1
#define CMD_TRANSFER_TO_EXCHANGE(label, amount) \ #define CMD_TRANSFER_TO_EXCHANGE(label, amount) \
TALER_TESTING_cmd_fakebank_transfer_retry \ TALER_TESTING_cmd_fakebank_transfer_retry \
(TALER_TESTING_cmd_fakebank_transfer (label, amount, \ (TALER_TESTING_cmd_fakebank_transfer (label, amount, \
exchange_bank_account.details. \ user_bank_account.details. \
x_taler_bank.bank_base_url, \ x_taler_bank.account_base_url, \
TALER_TESTING_USER_ACCOUNT_NUMBER, \ exchange_payto_url, \
exchange_bank_account.details. \
x_taler_bank.no, \
"dummy_user", \ "dummy_user", \
"dummy_password", \ "dummy_password", \
"http://example.com/")) "http://example.com/"))
@ -107,6 +96,11 @@ enum BenchmarkMode
*/ */
static struct TALER_Account exchange_bank_account; static struct TALER_Account exchange_bank_account;
/**
* Hold information about a user at the bank.
*/
static struct TALER_Account user_bank_account;
/** /**
* Time snapshot taken right before executing the CMDs. * Time snapshot taken right before executing the CMDs.
*/ */
@ -168,6 +162,11 @@ static enum BenchmarkMode mode;
*/ */
static char *cfg_filename; static char *cfg_filename;
/**
* payto://-URL of the exchange's bank account.
*/
static char *exchange_payto_url;
/** /**
* Currency used. * Currency used.
*/ */
@ -405,12 +404,12 @@ stop_fakebank (void *cls)
static void static void
launch_fakebank (void *cls) launch_fakebank (void *cls)
{ {
const char *bank_base_url = cls; const char *hostname = cls;
const char *port; const char *port;
long pnum; long pnum;
struct TALER_FAKEBANK_Handle *fakebank; struct TALER_FAKEBANK_Handle *fakebank;
port = strrchr (bank_base_url, port = strrchr (hostname,
(unsigned char) ':'); (unsigned char) ':');
if (NULL == port) if (NULL == port)
pnum = 80; pnum = 80;
@ -419,7 +418,7 @@ launch_fakebank (void *cls)
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Starting Fakebank on port %u (%s)\n", "Starting Fakebank on port %u (%s)\n",
(unsigned int) pnum, (unsigned int) pnum,
bank_base_url); hostname);
fakebank = TALER_FAKEBANK_start ((uint16_t) pnum); fakebank = TALER_FAKEBANK_start ((uint16_t) pnum);
if (NULL == fakebank) if (NULL == fakebank)
{ {
@ -466,8 +465,7 @@ parallel_benchmark (TALER_TESTING_Main main_cb,
NULL == loglev ? "INFO" : loglev, NULL == loglev ? "INFO" : loglev,
logfile); logfile);
GNUNET_SCHEDULER_run (&launch_fakebank, GNUNET_SCHEDULER_run (&launch_fakebank,
exchange_bank_account.details.x_taler_bank. exchange_bank_account.details.x_taler_bank.hostname);
bank_base_url);
exit (0); exit (0);
} }
if (-1 == fakebank) if (-1 == fakebank)
@ -824,10 +822,36 @@ main (int argc,
return BAD_CLI_ARG; return BAD_CLI_ARG;
} }
{
char *user_payto_url;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string
(cfg,
"benchmark",
"user-url",
&user_payto_url))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"benchmark",
"user-url");
return BAD_CONFIG_FILE;
}
if (TALER_EC_NONE !=
TALER_WIRE_payto_to_account (user_payto_url,
&user_bank_account))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_ ("Malformed payto:// URL `%s' in configuration\n"),
user_payto_url);
GNUNET_free (user_payto_url);
return BAD_CONFIG_FILE;
}
GNUNET_free (user_payto_url);
}
{ {
const char *bank_details_section; const char *bank_details_section;
char *exchange_payto_url;
GNUNET_CONFIGURATION_iterate_sections GNUNET_CONFIGURATION_iterate_sections
(cfg, (cfg,
@ -873,7 +897,6 @@ main (int argc,
GNUNET_free (exchange_payto_url); GNUNET_free (exchange_payto_url);
return BAD_CONFIG_FILE; return BAD_CONFIG_FILE;
} }
GNUNET_free (exchange_payto_url);
} }
if ( (MODE_EXCHANGE == mode) || (MODE_BOTH == mode) ) if ( (MODE_EXCHANGE == mode) || (MODE_BOTH == mode) )
{ {

View File

@ -23,7 +23,9 @@ taler_wire_SOURCES = \
taler-wire.c taler-wire.c
taler_wire_LDADD = \ taler_wire_LDADD = \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
-lgnunetcurl \
-lgnunetutil -lgnunetutil
taler_exchange_keyup_SOURCES = \ taler_exchange_keyup_SOURCES = \
@ -32,6 +34,7 @@ taler_exchange_keyup_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/pq/libtalerpq.la \ $(top_builddir)/src/pq/libtalerpq.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-lgnunetutil $(XLIB) -lgnunetutil $(XLIB)
@ -44,6 +47,7 @@ taler_exchange_wire_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
-lgnunetjson \ -lgnunetjson \

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA Copyright (C) 2014-2020 Taler Systems SA
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
@ -923,17 +923,17 @@ exchange_keys_update_denomkeys ()
* Sign @a af with @a priv * Sign @a af with @a priv
* *
* @param[in,out] af fee structure to sign * @param[in,out] af fee structure to sign
* @param wireplugin name of the plugin for which we sign * @param method name of the wire method for which we sign
* @param priv private key to use for signing * @param priv private key to use for signing
*/ */
static void static void
sign_af (struct TALER_EXCHANGEDB_AggregateFees *af, sign_af (struct TALER_EXCHANGEDB_AggregateFees *af,
const char *wireplugin, const char *method,
const struct GNUNET_CRYPTO_EddsaPrivateKey *priv) const struct GNUNET_CRYPTO_EddsaPrivateKey *priv)
{ {
struct TALER_MasterWireFeePS wf; struct TALER_MasterWireFeePS wf;
TALER_EXCHANGEDB_fees_2_wf (wireplugin, TALER_EXCHANGEDB_fees_2_wf (method,
af, af,
&wf); &wf);
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
@ -1101,28 +1101,15 @@ create_wire_fee_by_account (void *cls,
const struct TALER_EXCHANGEDB_AccountInfo *ai) const struct TALER_EXCHANGEDB_AccountInfo *ai)
{ {
int *ret = cls; int *ret = cls;
struct TALER_WIRE_Plugin *plugin;
if (GNUNET_NO == ai->credit_enabled) if (GNUNET_NO == ai->credit_enabled)
return; return;
plugin = TALER_WIRE_plugin_load (kcfg,
ai->plugin_name);
if (NULL == plugin)
{
fprintf (stderr,
"Failed to load wire plugin `%s' configured for account `%s'\n",
ai->plugin_name,
ai->section_name);
*ret = GNUNET_SYSERR;
return;
}
/* We may call this function repeatedly for the same method /* We may call this function repeatedly for the same method
if there are multiple accounts with plugins using the if there are multiple accounts with plugins using the
same method, but except for some minor performance loss, same method, but except for some minor performance loss,
this is harmless. */ this is harmless. */
create_wire_fee_for_method (ret, create_wire_fee_for_method (ret,
plugin->method); ai->method);
TALER_WIRE_plugin_unload (plugin);
} }

View File

@ -22,11 +22,10 @@
* @author Marcello Stanisci * @author Marcello Stanisci
* @author Christian Grothoff * @author Christian Grothoff
*/ */
#include <platform.h> #include <platform.h>
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_util.h" #include "taler_util.h"
#include "taler_wire_lib.h" #include "taler_bank_service.h"
/** /**
* If set to #GNUNET_YES, then we'll ask the bank for a list * If set to #GNUNET_YES, then we'll ask the bank for a list
@ -36,16 +35,11 @@
static int history; static int history;
/** /**
* If set to GNUNET_YES, then we'll ask the bank to execute a * If set to #GNUNET_YES, then we'll ask the bank to execute a
* wire transfer. * wire transfer.
*/ */
static int transfer; static int transfer;
/**
* Name of the wire plugin to use with the bank.
*/
static char *plugin_name;
/** /**
* Global return code. * Global return code.
*/ */
@ -59,11 +53,9 @@ static unsigned int global_ret = 1;
static char *amount; static char *amount;
/** /**
* Base32 encoding of a transaction ID. When asking the * Starting row.
* bank for a transaction history, all the results will
* have a transaction ID settled *after* this one.
*/ */
static char *since_when; static unsigned long long start_row;
/** /**
* Which config section has the credentials to access the bank. * Which config section has the credentials to access the bank.
@ -77,19 +69,29 @@ static char *account_section;
static char *destination_account_url; static char *destination_account_url;
/** /**
* Handle for the wire transfer preparation task. * Handle for executing the wire transfer.
*/ */
static struct TALER_WIRE_PrepareHandle *ph; static struct TALER_BANK_WireExecuteHandle *eh;
/**
* Wire plugin handle.
*/
static struct TALER_WIRE_Plugin *plugin_handle;
/** /**
* Handle to ongoing history operation. * Handle to ongoing history operation.
*/ */
static struct TALER_WIRE_HistoryHandle *hh; static struct TALER_BANK_CreditHistoryHandle *hh;
/**
* For authentication.
*/
static struct TALER_BANK_AuthenticationData auth;
/**
* Handle to the context for interacting with the bank.
*/
static struct GNUNET_CURL_Context *ctx;
/**
* Scheduler context for running the @e ctx.
*/
static struct GNUNET_CURL_RescheduleContext *rc;
/** /**
@ -108,16 +110,16 @@ static struct TALER_WIRE_HistoryHandle *hh;
*/ */
static int static int
history_cb (void *cls, history_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, uint64_t serial_id,
const void *row_off, const struct TALER_BANK_CreditDetails *details,
size_t row_off_size, const json_t *json)
const struct TALER_WIRE_TransferDetails *details)
{ {
char *row_off_enc;
(void) cls; (void) cls;
if (TALER_BANK_DIRECTION_NONE == dir) (void) ec;
(void) http_status;
if (NULL == details)
{ {
fprintf (stdout, fprintf (stdout,
"End of transactions list.\n"); "End of transactions list.\n");
@ -126,15 +128,9 @@ history_cb (void *cls,
return GNUNET_NO; return GNUNET_NO;
} }
row_off_enc = GNUNET_STRINGS_data_to_string_alloc (row_off,
row_off_size);
/* Give more details on screen (??) */
fprintf (stdout, fprintf (stdout,
"%s\n", "%llu\n",
row_off_enc); (unsigned long long) serial_id);
GNUNET_free (row_off_enc);
return GNUNET_OK; return GNUNET_OK;
} }
@ -142,71 +138,36 @@ history_cb (void *cls,
/** /**
* Callback that processes the outcome of a wire transfer * Callback that processes the outcome of a wire transfer
* execution. * execution.
*
* @param cls closure
* @param response_code HTTP status code
* @param ec taler error code
* @param row_id unique ID of the wire transfer in the bank's records
* @param timestamp when did the transaction go into effect
*/ */
static void static void
confirmation_cb (void *cls, confirmation_cb (void *cls,
int success, unsigned int response_code,
const void *row_id, enum TALER_ErrorCode ec,
size_t row_id_size, uint64_t row_id,
const char *emsg) struct GNUNET_TIME_Absolute timestamp)
{ {
if (GNUNET_YES != success) if (MHD_HTTP_OK != response_code)
{ {
fprintf (stderr, fprintf (stderr,
"The wire transfer didn't execute correctly.\n"); "The wire transfer didn't execute correctly (%d).\n",
GNUNET_assert (NULL != emsg); ec);
fprintf (stderr,
"%s",
emsg);
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return; return;
} }
fprintf (stdout, fprintf (stdout,
"Wire transfer executed successfully.\n"); "Wire transfer executed successfully.\n");
global_ret = 0; global_ret = 0;
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
} }
/**
* Takes prepared blob and executes the wire-transfer.
*
* @param cls NULL.
* @param buf prepared wire transfer data.
* @param buf_size size of the prepared wire transfer data.
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size)
{
struct TALER_WIRE_ExecuteHandle *eh;
if (NULL == (eh = plugin_handle->execute_wire_transfer
(plugin_handle->cls,
buf,
buf_size,
confirmation_cb,
NULL)))
{
fprintf (stderr,
"Could not execute the wire transfer\n");
plugin_handle->prepare_wire_transfer_cancel
(plugin_handle->cls,
ph);
plugin_handle->execute_wire_transfer_cancel
(plugin_handle->cls,
eh);
GNUNET_SCHEDULER_shutdown ();
}
}
/** /**
* Ask the bank to execute a wire transfer. * Ask the bank to execute a wire transfer.
*/ */
@ -215,6 +176,8 @@ execute_wire_transfer ()
{ {
struct TALER_Amount a; struct TALER_Amount a;
struct TALER_WireTransferIdentifierRawP wtid; struct TALER_WireTransferIdentifierRawP wtid;
void *buf;
size_t buf_size;
if (NULL == amount) if (NULL == amount)
{ {
@ -223,7 +186,6 @@ execute_wire_transfer ()
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return; return;
} }
if (GNUNET_OK != TALER_string_to_amount (amount, if (GNUNET_OK != TALER_string_to_amount (amount,
&a)) &a))
{ {
@ -240,19 +202,25 @@ execute_wire_transfer ()
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return; return;
} }
if (NULL == (ph = plugin_handle->prepare_wire_transfer TALER_BANK_prepare_wire_transfer (destination_account_url,
(plugin_handle->cls,
account_section,
destination_account_url,
&a, &a,
"http://exchange.example.com/", "http://exchange.example.com/",
&wtid, /* Any value will do. */ &wtid,
prepare_cb, &buf,
NULL))) &buf_size);
eh = TALER_BANK_execute_wire_transfer (ctx,
destination_account_url,
&auth,
buf,
buf_size,
&confirmation_cb,
NULL);
if (NULL == eh)
{ {
fprintf (stderr, fprintf (stderr,
"Could not prepare the wire transfer\n"); "Could not execute the wire transfer\n");
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return;
} }
} }
@ -264,30 +232,14 @@ execute_wire_transfer ()
static void static void
execute_history () execute_history ()
{ {
size_t bin_len = 0; hh = TALER_BANK_credit_history (ctx,
void *since_when_bin = NULL; destination_account_url,
&auth,
if (NULL != since_when) start_row,
{
bin_len = (strlen (since_when) * 5) / 8;
since_when_bin = GNUNET_malloc (bin_len);
GNUNET_assert
(GNUNET_OK == GNUNET_STRINGS_string_to_data
(since_when,
strlen (since_when),
since_when_bin,
bin_len));
}
if (NULL == (hh = plugin_handle->get_history (plugin_handle->cls,
account_section,
TALER_BANK_DIRECTION_BOTH,
since_when_bin,
bin_len,
-10, -10,
&history_cb, &history_cb,
NULL))) NULL);
if (NULL == hh)
{ {
fprintf (stderr, fprintf (stderr,
"Could not request the transaction history.\n"); "Could not request the transaction history.\n");
@ -305,19 +257,27 @@ execute_history ()
static void static void
do_shutdown (void *cls) do_shutdown (void *cls)
{ {
if (NULL != ctx)
{
GNUNET_CURL_fini (ctx);
ctx = NULL;
}
if (NULL != rc)
{
GNUNET_CURL_gnunet_rc_destroy (rc);
rc = NULL;
}
if (NULL != hh) if (NULL != hh)
{ {
plugin_handle->get_history_cancel (plugin_handle->cls, TALER_BANK_credit_history_cancel (hh);
hh);
hh = NULL; hh = NULL;
} }
if (NULL != ph) if (NULL != eh)
{ {
plugin_handle->prepare_wire_transfer_cancel (plugin_handle->cls, TALER_BANK_execute_wire_transfer_cancel (eh);
ph); eh = NULL;
ph = NULL;
} }
TALER_WIRE_plugin_unload (plugin_handle); TALER_BANK_auth_free (&auth);
} }
@ -342,27 +302,18 @@ run (void *cls,
"The option: -s ACCOUNT-SECTION, is mandatory.\n"); "The option: -s ACCOUNT-SECTION, is mandatory.\n");
return; return;
} }
if (GNUNET_OK !=
if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string TALER_BANK_auth_parse_cfg (cfg,
(cfg,
account_section, account_section,
"plugin", &auth))
&plugin_name))
{ {
fprintf (stderr, fprintf (stderr,
"Could not find the 'plugin' value under %s\n", "Authentication information not found in configuration section `%s'\n",
account_section); account_section);
GNUNET_SCHEDULER_shutdown ();
return; return;
} }
plugin_handle = TALER_WIRE_plugin_load (cfg,
plugin_name);
if (NULL == plugin_handle)
{
fprintf (stderr,
"Could not load the wire plugin\n");
return;
}
if (GNUNET_YES == history) if (GNUNET_YES == history)
execute_history (); execute_history ();
@ -372,6 +323,14 @@ run (void *cls,
fprintf (stderr, fprintf (stderr,
"Please give either --history/-H or --transfer/t\n"); "Please give either --history/-H or --transfer/t\n");
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
if (NULL == ctx)
{
GNUNET_break (0);
return;
}
GNUNET_SCHEDULER_add_shutdown (&do_shutdown, GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL); NULL);
} }
@ -400,7 +359,7 @@ main (int argc,
"transfer", "transfer",
"Execute a wire transfer.", "Execute a wire transfer.",
&transfer), &transfer),
GNUNET_GETOPT_option_string ('w', GNUNET_GETOPT_option_ulong ('w',
"since-when", "since-when",
"SW", "SW",
"When asking the bank for" "When asking the bank for"
@ -410,12 +369,13 @@ main (int argc,
" after SW. If not given, then" " after SW. If not given, then"
" the 10 youngest transactions" " the 10 youngest transactions"
" are returned.", " are returned.",
&since_when), &start_row),
GNUNET_GETOPT_option_string ('s', GNUNET_GETOPT_option_mandatory
(GNUNET_GETOPT_option_string ('s',
"section", "section",
"ACCOUNT-SECTION", "ACCOUNT-SECTION",
"Which config section has the credentials to access the bank. Mandatory.\n", "Which config section has the credentials to access the bank. Mandatory.\n",
&account_section), &account_section)),
GNUNET_GETOPT_option_string ('a', GNUNET_GETOPT_option_string ('a',
"amount", "amount",
"AMOUNT", "AMOUNT",

View File

@ -29,9 +29,11 @@ taler_exchange_aggregator_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-ljansson \ -ljansson \
-lgnunetcurl \
-lgnunetutil -lgnunetutil
taler_exchange_wirewatch_SOURCES = \ taler_exchange_wirewatch_SOURCES = \
@ -40,9 +42,11 @@ taler_exchange_wirewatch_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \ $(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \ $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-ljansson \ -ljansson \
-lgnunetcurl \
-lgnunetutil -lgnunetutil
taler_exchange_httpd_SOURCES = \ taler_exchange_httpd_SOURCES = \
@ -66,6 +70,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h
taler_exchange_httpd_LDADD = \ taler_exchange_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \ $(LIBGCRYPT_LIBS) \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/wire/libtalerwire.la \ $(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/mhd/libtalermhd.la \ $(top_builddir)/src/mhd/libtalermhd.la \
$(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/json/libtalerjson.la \

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016-2018 Taler Systems SA Copyright (C) 2016-2020 Taler Systems SA
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
@ -26,6 +26,7 @@
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_bank_service.h"
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
@ -45,9 +46,14 @@ struct WireAccount
struct WireAccount *prev; struct WireAccount *prev;
/** /**
* Handle to the plugin. * Account information.
*/ */
struct TALER_WIRE_Plugin *wire_plugin; struct TALER_Account account;
/**
* Authentication data.
*/
struct TALER_BANK_AuthenticationData auth;
/** /**
* Wire transfer fee structure. * Wire transfer fee structure.
@ -59,6 +65,11 @@ struct WireAccount
*/ */
char *section_name; char *section_name;
/**
* Name of the wire method underlying the account.
*/
char *method;
}; };
@ -77,7 +88,7 @@ struct WirePrepareData
/** /**
* Wire execution handle. * Wire execution handle.
*/ */
struct TALER_WIRE_ExecuteHandle *eh; struct TALER_BANK_WireExecuteHandle *eh;
/** /**
* Wire plugin used for this preparation. * Wire plugin used for this preparation.
@ -187,10 +198,6 @@ struct AggregationUnit
*/ */
struct CloseTransferContext struct CloseTransferContext
{ {
/**
* Handle for preparing the wire transfer.
*/
struct TALER_WIRE_PrepareHandle *ph;
/** /**
* Our database session. * Our database session.
@ -262,6 +269,16 @@ static struct WirePrepareData *wpd;
*/ */
static struct AggregationUnit *au; static struct AggregationUnit *au;
/**
* Handle to the context for interacting with the bank.
*/
static struct GNUNET_CURL_Context *ctx;
/**
* Scheduler context for running the @e ctx.
*/
static struct GNUNET_CURL_RescheduleContext *rc;
/** /**
* Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR
* on serious errors. * on serious errors.
@ -339,7 +356,7 @@ update_fees (struct WireAccount *wa,
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
/* Let's try to load it from disk... */ /* Let's try to load it from disk... */
wa->af = TALER_EXCHANGEDB_fees_read (cfg, wa->af = TALER_EXCHANGEDB_fees_read (cfg,
wa->wire_plugin->method); wa->method);
advance_fees (wa, advance_fees (wa,
now); now);
for (struct TALER_EXCHANGEDB_AggregateFees *p = wa->af; for (struct TALER_EXCHANGEDB_AggregateFees *p = wa->af;
@ -348,7 +365,7 @@ update_fees (struct WireAccount *wa,
{ {
qs = db_plugin->insert_wire_fee (db_plugin->cls, qs = db_plugin->insert_wire_fee (db_plugin->cls,
session, session,
wa->wire_plugin->method, wa->method,
p->start_date, p->start_date,
p->end_date, p->end_date,
&p->wire_fee, &p->wire_fee,
@ -365,7 +382,7 @@ update_fees (struct WireAccount *wa,
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find current wire transfer fees for `%s'\n", "Failed to find current wire transfer fees for `%s'\n",
wa->wire_plugin->method); wa->method);
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
} }
@ -381,7 +398,7 @@ find_account_by_method (const char *method)
{ {
for (struct WireAccount *wa = wa_head; NULL != wa; wa = wa->next) for (struct WireAccount *wa = wa_head; NULL != wa; wa = wa->next)
if (0 == strcmp (method, if (0 == strcmp (method,
wa->wire_plugin->method)) wa->method))
return wa; return wa;
return NULL; return NULL;
} }
@ -431,13 +448,40 @@ add_account_cb (void *cls,
if (GNUNET_YES != ai->debit_enabled) if (GNUNET_YES != ai->debit_enabled)
return; /* not enabled for us, skip */ return; /* not enabled for us, skip */
wa = GNUNET_new (struct WireAccount); wa = GNUNET_new (struct WireAccount);
wa->wire_plugin = TALER_WIRE_plugin_load (cfg, if (GNUNET_OK !=
ai->plugin_name); GNUNET_CONFIGURATION_get_value_string (cfg,
if (NULL == wa->wire_plugin) ai->section_name,
"METHOD",
&wa->method))
{ {
fprintf (stderr, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"Failed to load wire plugin for `%s'\n", ai->section_name,
ai->plugin_name); "METHOD");
GNUNET_free (wa);
return;
}
if (GNUNET_OK !=
TALER_BANK_auth_parse_cfg (cfg,
ai->section_name,
&wa->auth))
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Failed to load account `%s'\n",
ai->section_name);
GNUNET_free (wa->method);
GNUNET_free (wa);
return;
}
if (GNUNET_OK !=
TALER_BANK_account_parse_cfg (cfg,
ai->section_name,
&wa->account))
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Failed to load account `%s'\n",
ai->section_name);
TALER_BANK_auth_free (&wa->auth);
GNUNET_free (wa->method);
GNUNET_free (wa); GNUNET_free (wa);
return; return;
} }
@ -476,6 +520,16 @@ static void
shutdown_task (void *cls) shutdown_task (void *cls)
{ {
(void) cls; (void) cls;
if (NULL != ctx)
{
GNUNET_CURL_fini (ctx);
ctx = NULL;
}
if (NULL != rc)
{
GNUNET_CURL_gnunet_rc_destroy (rc);
rc = NULL;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Running shutdown\n"); "Running shutdown\n");
if (NULL != task) if (NULL != task)
@ -487,9 +541,7 @@ shutdown_task (void *cls)
{ {
if (NULL != wpd->eh) if (NULL != wpd->eh)
{ {
wpd->wa->wire_plugin->execute_wire_transfer_cancel ( TALER_BANK_execute_wire_transfer_cancel (wpd->eh);
wpd->wa->wire_plugin->cls,
wpd->eh);
wpd->eh = NULL; wpd->eh = NULL;
} }
db_plugin->rollback (db_plugin->cls, db_plugin->rollback (db_plugin->cls,
@ -499,23 +551,12 @@ shutdown_task (void *cls)
} }
if (NULL != au) if (NULL != au)
{ {
if (NULL != au->ph)
{
au->wa->wire_plugin->prepare_wire_transfer_cancel (
au->wa->wire_plugin->cls,
au->ph);
au->ph = NULL;
}
db_plugin->rollback (db_plugin->cls, db_plugin->rollback (db_plugin->cls,
au->session); au->session);
cleanup_au (); cleanup_au ();
} }
if (NULL != ctc) if (NULL != ctc)
{ {
ctc->wa->wire_plugin->prepare_wire_transfer_cancel (
ctc->wa->wire_plugin->cls,
ctc->ph);
ctc->ph = NULL;
db_plugin->rollback (db_plugin->cls, db_plugin->rollback (db_plugin->cls,
ctc->session); ctc->session);
GNUNET_free (ctc->method); GNUNET_free (ctc->method);
@ -532,9 +573,11 @@ shutdown_task (void *cls)
GNUNET_CONTAINER_DLL_remove (wa_head, GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail, wa_tail,
wa); wa);
TALER_WIRE_plugin_unload (wa->wire_plugin); TALER_WIRE_account_free (&wa->account);
TALER_BANK_auth_free (&wa->auth);
TALER_EXCHANGEDB_fees_free (wa->af); TALER_EXCHANGEDB_fees_free (wa->af);
GNUNET_free (wa->section_name); GNUNET_free (wa->section_name);
GNUNET_free (wa->method);
GNUNET_free (wa); GNUNET_free (wa);
} }
} }
@ -921,20 +964,6 @@ aggregate_cb (void *cls,
} }
/**
* Function to be called with the prepared transfer data
* when running an aggregation on a merchant.
*
* @param cls closure with the `struct AggregationUnit`
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size);
/** /**
* Main work function that finds and triggers transfers for reserves * Main work function that finds and triggers transfers for reserves
* closures. * closures.
@ -988,83 +1017,6 @@ commit_or_warn (struct TALER_EXCHANGEDB_Session *session)
} }
/**
* Function to be called with the prepared transfer data
* when closing a reserve.
*
* @param cls closure with a `struct CloseTransferContext`
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_close_cb (void *cls,
const char *buf,
size_t buf_size)
{
enum GNUNET_DB_QueryStatus qs;
GNUNET_assert (cls == ctc);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Prepared for reserve closing\n");
ctc->ph = NULL;
if (NULL == buf)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
ctc->session);
/* start again */
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
return;
}
/* Commit our intention to execute the wire transfer! */
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
ctc->session,
ctc->method,
buf,
buf_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
db_plugin->rollback (db_plugin->cls,
ctc->session);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return;
}
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
db_plugin->rollback (db_plugin->cls,
ctc->session);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return;
}
/* finally commit */
(void) commit_or_warn (ctc->session);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve closure committed, running transfer\n");
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
}
/** /**
* Closure for #expired_reserve_cb(). * Closure for #expired_reserve_cb().
*/ */
@ -1113,6 +1065,8 @@ expired_reserve_cb (void *cls,
int ret; int ret;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct WireAccount *wa; struct WireAccount *wa;
void *buf;
size_t buf_size;
/* NOTE: potential optimization: use custom SQL API to not /* NOTE: potential optimization: use custom SQL API to not
fetch this: */ fetch this: */
@ -1121,7 +1075,7 @@ expired_reserve_cb (void *cls,
now = GNUNET_TIME_absolute_get (); now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now); (void) GNUNET_TIME_round_abs (&now);
/* lookup wire plugin */ /* lookup account we should use */
wa = find_account_by_url (account_details); wa = find_account_by_url (account_details);
if (NULL == wa) if (NULL == wa)
{ {
@ -1161,6 +1115,18 @@ expired_reserve_cb (void *cls,
TALER_amount_get_zero (left->currency, TALER_amount_get_zero (left->currency,
&amount_without_fee)); &amount_without_fee));
} }
/* round down to enable transfer */
if (GNUNET_SYSERR ==
TALER_WIRE_amount_round (&amount_without_fee))
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR;
}
if ( (0 == amount_without_fee.value) &&
(0 == amount_without_fee.fraction) )
ret = GNUNET_NO;
/* NOTE: sizeof (*reserve_pub) == sizeof (wtid) right now, but to /* NOTE: sizeof (*reserve_pub) == sizeof (wtid) right now, but to
be future-compatible, we use the memset + min construction */ be future-compatible, we use the memset + min construction */
@ -1171,7 +1137,7 @@ expired_reserve_cb (void *cls,
reserve_pub, reserve_pub,
GNUNET_MIN (sizeof (wtid), GNUNET_MIN (sizeof (wtid),
sizeof (*reserve_pub))); sizeof (*reserve_pub)));
if (GNUNET_SYSERR != ret)
qs = db_plugin->insert_reserve_closed (db_plugin->cls, qs = db_plugin->insert_reserve_closed (db_plugin->cls,
session, session,
reserve_pub, reserve_pub,
@ -1180,52 +1146,14 @@ expired_reserve_cb (void *cls,
&wtid, &wtid,
left, left,
closing_fee); closing_fee);
else
ret = GNUNET_DB_STATUS_HARD_ERROR;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Closing reserve %s over %s (%d, %d)\n", "Closing reserve %s over %s (%d, %d)\n",
TALER_B2S (reserve_pub), TALER_B2S (reserve_pub),
TALER_amount2s (left), TALER_amount2s (left),
ret, ret,
qs); qs);
if ( (GNUNET_OK == ret) &&
(GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) )
{
/* success, perform wire transfer */
if (GNUNET_SYSERR ==
wa->wire_plugin->amount_round (wa->wire_plugin->cls,
&amount_without_fee))
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR;
}
ctc = GNUNET_new (struct CloseTransferContext);
ctc->wa = wa;
ctc->session = session;
ctc->method = TALER_WIRE_payto_get_method (account_details);
ctc->ph
= wa->wire_plugin->prepare_wire_transfer (wa->wire_plugin->cls,
wa->section_name,
account_details,
&amount_without_fee,
exchange_base_url,
&wtid,
&prepare_close_cb,
ctc);
if (NULL == ctc->ph)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return GNUNET_DB_STATUS_HARD_ERROR;
}
erc->async_cont = GNUNET_YES;
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
/* Check for hard failure */ /* Check for hard failure */
if ( (GNUNET_SYSERR == ret) || if ( (GNUNET_SYSERR == ret) ||
(GNUNET_DB_STATUS_HARD_ERROR == qs) ) (GNUNET_DB_STATUS_HARD_ERROR == qs) )
@ -1235,12 +1163,61 @@ expired_reserve_cb (void *cls,
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if ( (GNUNET_OK != ret) ||
(GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) )
{
/* Reserve balance was almost zero OR soft error */ /* Reserve balance was almost zero OR soft error */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Reserve was virtually empty, moving on\n"); "Reserve was virtually empty, moving on\n");
(void) commit_or_warn (ctc->session);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return qs; return qs;
} }
/* success, perform wire transfer */
ctc = GNUNET_new (struct CloseTransferContext);
ctc->wa = wa;
ctc->session = session;
ctc->method = TALER_WIRE_payto_get_method (account_details);
TALER_BANK_prepare_wire_transfer (account_details,
&amount_without_fee,
exchange_base_url,
&wtid,
&buf,
&buf_size);
/* Commit our intention to execute the wire transfer! */
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
ctc->session,
ctc->method,
buf,
buf_size);
GNUNET_free (buf);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
GNUNET_break (0);
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
{
/* start again */
GNUNET_free (ctc->method);
GNUNET_free (ctc);
ctc = NULL;
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
erc->async_cont = GNUNET_YES;
task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL);
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
/** /**
* Main work function that finds and triggers transfers for reserves * Main work function that finds and triggers transfers for reserves
@ -1344,6 +1321,8 @@ run_aggregation (void *cls)
struct TALER_EXCHANGEDB_Session *session; struct TALER_EXCHANGEDB_Session *session;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
const struct GNUNET_SCHEDULER_TaskContext *tc; const struct GNUNET_SCHEDULER_TaskContext *tc;
void *buf;
size_t buf_size;
(void) cls; (void) cls;
task = NULL; task = NULL;
@ -1470,8 +1449,7 @@ run_aggregation (void *cls)
&au->total_amount, &au->total_amount,
&au->wire_fee)) || &au->wire_fee)) ||
(GNUNET_SYSERR == (GNUNET_SYSERR ==
au->wa->wire_plugin->amount_round (au->wa->wire_plugin->cls, TALER_WIRE_amount_round (&au->final_amount)) ||
&au->final_amount)) ||
( (0 == au->final_amount.value) && ( (0 == au->final_amount.value) &&
(0 == au->final_amount.fraction) ) ) (0 == au->final_amount.fraction) ) )
{ {
@ -1555,70 +1533,26 @@ run_aggregation (void *cls)
char *url; char *url;
url = TALER_JSON_wire_to_payto (au->wire); url = TALER_JSON_wire_to_payto (au->wire);
au->ph = au->wa->wire_plugin->prepare_wire_transfer ( TALER_BANK_prepare_wire_transfer (url,
au->wa->wire_plugin->cls,
au->wa->section_name,
url,
&au->final_amount, &au->final_amount,
exchange_base_url, exchange_base_url,
&au->wtid, &au->wtid,
&prepare_cb, &buf,
au); &buf_size);
GNUNET_free (url); GNUNET_free (url);
} }
if (NULL == au->ph)
{
/* something went very wrong, likely bad configuration,
abort */
db_plugin->rollback (db_plugin->cls,
session);
cleanup_au ();
GNUNET_SCHEDULER_shutdown ();
return;
}
/* otherwise we continue with #prepare_cb(), see below */
}
/**
* Function to be called with the prepared transfer data.
*
* @param cls NULL
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size)
{
struct TALER_EXCHANGEDB_Session *session = au->session;
enum GNUNET_DB_QueryStatus qs;
(void) cls;
GNUNET_free_non_null (au->additional_rows); GNUNET_free_non_null (au->additional_rows);
au->additional_rows = NULL; au->additional_rows = NULL;
if (NULL == buf)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
NULL);
cleanup_au ();
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Storing %u bytes of wire prepare data\n", "Storing %u bytes of wire prepare data\n",
(unsigned int) buf_size); (unsigned int) buf_size);
/* Commit our intention to execute the wire transfer! */ /* Commit our intention to execute the wire transfer! */
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls, qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
session, session,
au->wa->wire_plugin->method, au->wa->method,
buf, buf,
buf_size); buf_size);
GNUNET_free (buf);
/* Commit the WTID data to 'wire_out' to finally satisfy aggregation /* Commit the WTID data to 'wire_out' to finally satisfy aggregation
table constraints */ table constraints */
if (qs >= 0) if (qs >= 0)
@ -1691,29 +1625,30 @@ prepare_cb (void *cls,
* Function called with the result from the execute step. * Function called with the result from the execute step.
* *
* @param cls NULL * @param cls NULL
* @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure * @param http_status_code #MHD_HTTP_OK on success
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error * @param ec taler error code
* @param emsg NULL on success, otherwise an error message * @param row_id unique ID of the wire transfer in the bank's records
* @param wire_timestamp when did the transfer happen
*/ */
static void static void
wire_confirm_cb (void *cls, wire_confirm_cb (void *cls,
int success, unsigned int http_status_code,
const void *row_id, enum TALER_ErrorCode ec,
size_t row_id_size, uint64_t row_id,
const char *emsg) struct GNUNET_TIME_Absolute wire_timestamp)
{ {
struct TALER_EXCHANGEDB_Session *session = wpd->session; struct TALER_EXCHANGEDB_Session *session = wpd->session;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
(void) cls; (void) cls;
(void) row_id; (void) row_id;
(void) row_id_size;
wpd->eh = NULL; wpd->eh = NULL;
if (GNUNET_SYSERR == success) if (MHD_HTTP_OK != http_status_code)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire transaction failed: %s\n", "Wire transaction failed: %u/%d\n",
emsg); http_status_code,
ec);
db_plugin->rollback (db_plugin->cls, db_plugin->rollback (db_plugin->cls,
session); session);
global_ret = GNUNET_SYSERR; global_ret = GNUNET_SYSERR;
@ -1792,6 +1727,8 @@ wire_prepare_cb (void *cls,
const char *buf, const char *buf,
size_t buf_size) size_t buf_size)
{ {
struct WireAccount *wa;
(void) cls; (void) cls;
wpd->row_id = rowid; wpd->row_id = rowid;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -1811,8 +1748,11 @@ wire_prepare_cb (void *cls,
wpd = NULL; wpd = NULL;
return; return;
} }
wpd->eh = wpd->wa->wire_plugin->execute_wire_transfer ( wa = wpd->wa;
wpd->wa->wire_plugin->cls, wpd->eh = TALER_BANK_execute_wire_transfer (ctx,
wa->account.details.x_taler_bank.
account_base_url,
&wa->auth,
buf, buf,
buf_size, buf_size,
&wire_confirm_cb, &wire_confirm_cb,
@ -1927,6 +1867,7 @@ run (void *cls,
(void) cls; (void) cls;
(void) args; (void) args;
(void) cfgfile; (void) cfgfile;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (c, GNUNET_CONFIGURATION_get_value_string (c,
"exchange", "exchange",
@ -1947,6 +1888,15 @@ run (void *cls,
global_ret = 1; global_ret = 1;
return; return;
} }
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
if (NULL == ctx)
{
GNUNET_break (0);
return;
}
task = GNUNET_SCHEDULER_add_now (&run_transfers, task = GNUNET_SCHEDULER_add_now (&run_transfers,
NULL); NULL);
GNUNET_SCHEDULER_add_shutdown (&shutdown_task, GNUNET_SCHEDULER_add_shutdown (&shutdown_task,

View File

@ -400,7 +400,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
json_t *json; json_t *json;
int res; int res;
json_t *wire; json_t *wire;
char *emsg;
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
unsigned int hc; unsigned int hc;
struct TALER_EXCHANGEDB_Deposit deposit; struct TALER_EXCHANGEDB_Deposit deposit;
@ -460,18 +459,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
"refund_deadline"); "refund_deadline");
} }
if (TALER_EC_NONE !=
(ec = TEH_json_validate_wireformat (wire,
&emsg)))
{
GNUNET_JSON_parse_free (spec);
res = TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
ec,
emsg);
GNUNET_free (emsg);
return res;
}
if (GNUNET_OK != if (GNUNET_OK !=
check_timestamp_current (deposit.timestamp)) check_timestamp_current (deposit.timestamp))
{ {

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016, 2017, 2018 Taler Systems SA Copyright (C) 2016-2020 Taler Systems SA
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
@ -29,40 +29,6 @@
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
/**
* Information we keep for each plugin.
*/
struct Plugin
{
/**
* We keep plugins in a DLL.
*/
struct Plugin *next;
/**
* We keep plugins in a DLL.
*/
struct Plugin *prev;
/**
* Pointer to the plugin.
*/
struct TALER_WIRE_Plugin *plugin;
};
/**
* Head of DLL of wire plugins.
*/
static struct Plugin *wire_head;
/**
* Tail of DLL of wire plugins.
*/
static struct Plugin *wire_tail;
/** /**
* Array of wire methods supported by this exchange. * Array of wire methods supported by this exchange.
*/ */
@ -191,9 +157,8 @@ load_account (void *cls,
else else
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire fees not specified for `%s', ignoring plugin %s\n", "Wire fees not specified for `%s'\n",
method, method);
ai->plugin_name);
*ret = GNUNET_SYSERR; *ret = GNUNET_SYSERR;
} }
GNUNET_free (method); GNUNET_free (method);
@ -201,35 +166,15 @@ load_account (void *cls,
if (GNUNET_YES == ai->debit_enabled) if (GNUNET_YES == ai->debit_enabled)
{ {
struct Plugin *p;
p = GNUNET_new (struct Plugin);
p->plugin = TALER_WIRE_plugin_load (cfg,
ai->plugin_name);
if (NULL == p->plugin)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load plugin %s\n",
ai->plugin_name);
GNUNET_free (p);
*ret = GNUNET_SYSERR;
return;
}
if (GNUNET_OK != if (GNUNET_OK !=
load_fee (p->plugin->method)) load_fee (ai->method))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Disabling plugin `%s' as wire transfer fees for `%s' are not given correctly\n", "Wire transfer fees for `%s' are not given correctly\n",
ai->plugin_name, ai->method);
p->plugin->method);
TALER_WIRE_plugin_unload (p->plugin);
GNUNET_free (p);
*ret = GNUNET_SYSERR; *ret = GNUNET_SYSERR;
return; return;
} }
GNUNET_CONTAINER_DLL_insert (wire_head,
wire_tail,
p);
} }
} }
@ -251,12 +196,6 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
TALER_EXCHANGEDB_find_accounts (cfg, TALER_EXCHANGEDB_find_accounts (cfg,
&load_account, &load_account,
&ret); &ret);
if (NULL == wire_head)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to find properly configured wire transfer method\n");
ret = GNUNET_SYSERR;
}
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
TEH_VALIDATION_done (); TEH_VALIDATION_done ();
return ret; return ret;
@ -269,16 +208,6 @@ TEH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
void void
TEH_VALIDATION_done () TEH_VALIDATION_done ()
{ {
struct Plugin *p;
while (NULL != (p = wire_head))
{
GNUNET_CONTAINER_DLL_remove (wire_head,
wire_tail,
p);
TALER_WIRE_plugin_unload (p->plugin);
GNUNET_free (p);
}
json_decref (wire_fee_object); json_decref (wire_fee_object);
wire_fee_object = NULL; wire_fee_object = NULL;
json_decref (wire_accounts_array); json_decref (wire_accounts_array);
@ -286,65 +215,6 @@ TEH_VALIDATION_done ()
} }
/**
* Check if the given wire format JSON object is correctly formatted as
* a wire address.
*
* @param wire the JSON wire format object
* @param[out] emsg set to error message if we return an error code
* @return #TALER_EC_NONE if correctly formatted; otherwise error code
*/
enum TALER_ErrorCode
TEH_json_validate_wireformat (const json_t *wire,
char **emsg)
{
const char *payto_url;
json_error_t error;
char *method;
*emsg = NULL;
if (0 != json_unpack_ex ((json_t *) wire,
&error, 0,
"{s:s}",
"url", &payto_url))
{
GNUNET_asprintf (emsg,
"No `url' specified in the wire details\n");
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_MISSING;
}
method = TALER_WIRE_payto_get_method (payto_url);
if (NULL == method)
{
GNUNET_asprintf (emsg,
"Malformed payto URL `%s'\n",
payto_url);
return TALER_EC_PAYTO_MALFORMED;
}
for (struct Plugin *p = wire_head; NULL != p; p = p->next)
{
if (0 == strcasecmp (p->plugin->method,
method))
{
enum TALER_ErrorCode ec;
GNUNET_free (method);
ec = p->plugin->wire_validate (p->plugin->cls,
payto_url);
if (TALER_EC_NONE != ec)
GNUNET_asprintf (emsg,
"Payto URL `%s' rejected by plugin\n",
payto_url);
return ec;
}
}
GNUNET_asprintf (emsg,
"Wire format type `%s' is not supported by this exchange\n",
method);
GNUNET_free (method);
return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_TYPE_UNSUPPORTED;
}
/** /**
* Obtain JSON response for /wire * Obtain JSON response for /wire
* *

View File

@ -27,6 +27,7 @@
#include "taler_exchangedb_lib.h" #include "taler_exchangedb_lib.h"
#include "taler_exchangedb_plugin.h" #include "taler_exchangedb_plugin.h"
#include "taler_json_lib.h" #include "taler_json_lib.h"
#include "taler_bank_service.h"
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
/** /**
@ -36,23 +37,6 @@
#define DELAY GNUNET_TIME_UNIT_SECONDS #define DELAY GNUNET_TIME_UNIT_SECONDS
/**
* Closure for #reject_cb().
*/
struct RejectContext
{
/**
* Wire transfer subject that was illformed.
*/
char *wtid_s;
/**
* Database session that encountered the problem.
*/
struct TALER_EXCHANGEDB_Session *session;
};
/** /**
* Information we keep for each supported account. * Information we keep for each supported account.
*/ */
@ -68,16 +52,21 @@ struct WireAccount
*/ */
struct WireAccount *prev; struct WireAccount *prev;
/**
* Handle to the plugin.
*/
struct TALER_WIRE_Plugin *wire_plugin;
/** /**
* Name of the section that configures this account. * Name of the section that configures this account.
*/ */
char *section_name; char *section_name;
/**
* Account information.
*/
struct TALER_Account account;
/**
* Authentication data.
*/
struct TALER_BANK_AuthenticationData auth;
/** /**
* Are we running from scratch and should re-process all transactions * Are we running from scratch and should re-process all transactions
* for this account? * for this account?
@ -107,6 +96,16 @@ static struct WireAccount *wa_tail;
*/ */
static struct WireAccount *wa_pos; static struct WireAccount *wa_pos;
/**
* Handle to the context for interacting with the bank.
*/
static struct GNUNET_CURL_Context *ctx;
/**
* Scheduler context for running the @e ctx.
*/
static struct GNUNET_CURL_RescheduleContext *rc;
/** /**
* Which currency is used by this exchange? * Which currency is used by this exchange?
*/ */
@ -132,23 +131,13 @@ static int global_ret;
* Encoded offset in the wire transfer list from where * Encoded offset in the wire transfer list from where
* to start the next query with the bank. * to start the next query with the bank.
*/ */
static void *last_row_off; static uint64_t last_row_off;
/**
* Number of bytes in #last_row_off.
*/
static size_t last_row_off_size;
/** /**
* Latest row offset seen in this transaction, becomes * Latest row offset seen in this transaction, becomes
* the new #last_row_off upon commit. * the new #last_row_off upon commit.
*/ */
static void *latest_row_off; static uint64_t latest_row_off;
/**
* Number of bytes in #latest_row_off.
*/
static size_t latest_row_off_size;
/** /**
* Should we delay the next request to the wire plugin a bit? * Should we delay the next request to the wire plugin a bit?
@ -183,12 +172,7 @@ static struct GNUNET_SCHEDULER_Task *task;
/** /**
* Active request for history. * Active request for history.
*/ */
static struct TALER_WIRE_HistoryHandle *hh; static struct TALER_BANK_CreditHistoryHandle *hh;
/**
* Active request to reject a wire transfer.
*/
static struct TALER_WIRE_RejectHandle *rt;
/** /**
@ -202,6 +186,16 @@ shutdown_task (void *cls)
struct WireAccount *wa; struct WireAccount *wa;
(void) cls; (void) cls;
if (NULL != ctx)
{
GNUNET_CURL_fini (ctx);
ctx = NULL;
}
if (NULL != rc)
{
GNUNET_CURL_gnunet_rc_destroy (rc);
rc = NULL;
}
if (NULL != task) if (NULL != task)
{ {
GNUNET_SCHEDULER_cancel (task); GNUNET_SCHEDULER_cancel (task);
@ -209,20 +203,9 @@ shutdown_task (void *cls)
} }
if (NULL != hh) if (NULL != hh)
{ {
wa_pos->wire_plugin->get_history_cancel (wa_pos->wire_plugin->cls, TALER_BANK_credit_history_cancel (hh);
hh);
hh = NULL; hh = NULL;
} }
if (NULL != rt)
{
char *wtid_s;
wtid_s = wa_pos->wire_plugin->reject_transfer_cancel (
wa_pos->wire_plugin->cls,
rt);
rt = NULL;
GNUNET_free (wtid_s);
}
TALER_EXCHANGEDB_plugin_unload (db_plugin); TALER_EXCHANGEDB_plugin_unload (db_plugin);
db_plugin = NULL; db_plugin = NULL;
while (NULL != (wa = wa_head)) while (NULL != (wa = wa_head))
@ -230,14 +213,13 @@ shutdown_task (void *cls)
GNUNET_CONTAINER_DLL_remove (wa_head, GNUNET_CONTAINER_DLL_remove (wa_head,
wa_tail, wa_tail,
wa); wa);
TALER_WIRE_plugin_unload (wa->wire_plugin); TALER_WIRE_account_free (&wa->account);
TALER_BANK_auth_free (&wa->auth);
GNUNET_free (wa->section_name); GNUNET_free (wa->section_name);
GNUNET_free (wa); GNUNET_free (wa);
} }
wa_pos = NULL; wa_pos = NULL;
GNUNET_free_non_null (last_row_off); last_row_off = 0;
last_row_off = NULL;
last_row_off_size = 0;
} }
@ -259,13 +241,26 @@ add_account_cb (void *cls,
return; /* not enabled for us, skip */ return; /* not enabled for us, skip */
wa = GNUNET_new (struct WireAccount); wa = GNUNET_new (struct WireAccount);
wa->reset_mode = reset_mode; wa->reset_mode = reset_mode;
wa->wire_plugin = TALER_WIRE_plugin_load (cfg, if (GNUNET_OK !=
ai->plugin_name); TALER_BANK_auth_parse_cfg (cfg,
if (NULL == wa->wire_plugin) ai->section_name,
&wa->auth))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Failed to load wire plugin for `%s'\n", "Failed to load account `%s'\n",
ai->plugin_name); ai->section_name);
GNUNET_free (wa);
return;
}
if (GNUNET_OK !=
TALER_BANK_account_parse_cfg (cfg,
ai->section_name,
&wa->account))
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"Failed to load account `%s'\n",
ai->section_name);
TALER_BANK_auth_free (&wa->auth);
GNUNET_free (wa); GNUNET_free (wa);
return; return;
} }
@ -335,71 +330,29 @@ static void
find_transfers (void *cls); find_transfers (void *cls);
/**
* Function called upon completion of the rejection of a wire transfer.
*
* @param cls closure with the `struct RejectContext`
* @param ec error code for the operation
*/
static void
reject_cb (void *cls,
enum TALER_ErrorCode ec)
{
struct RejectContext *rtc = cls;
enum GNUNET_DB_QueryStatus qs;
rt = NULL;
if (TALER_EC_NONE != ec)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to wire back transfer `%s': %d\n",
rtc->wtid_s,
ec);
GNUNET_free (rtc->wtid_s);
db_plugin->rollback (db_plugin->cls,
rtc->session);
GNUNET_free (rtc);
GNUNET_SCHEDULER_shutdown ();
return;
}
GNUNET_free (rtc->wtid_s);
qs = db_plugin->commit (db_plugin->cls,
rtc->session);
GNUNET_break (0 <= qs);
GNUNET_free (rtc);
task = GNUNET_SCHEDULER_add_now (&find_transfers,
NULL);
}
/** /**
* Callbacks of this type are used to serve the result of asking * Callbacks of this type are used to serve the result of asking
* the bank for the transaction history. * the bank for the transaction history.
* *
* @param cls closure with the `struct TALER_EXCHANGEDB_Session *` * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
* @param ec taler error code * @param ec taler error code
* @param dir direction of the transfer * @param serial_id identification of the position at which we are querying
* @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer * @param details details about the wire transfer
* @param json raw JSON response
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
static int static int
history_cb (void *cls, history_cb (void *cls,
unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir, uint64_t serial_id,
const void *row_off, const struct TALER_BANK_CreditDetails *details,
size_t row_off_size, const json_t *json)
const struct TALER_WIRE_TransferDetails *details)
{ {
struct TALER_EXCHANGEDB_Session *session = cls; struct TALER_EXCHANGEDB_Session *session = cls;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct TALER_ReservePublicKeyP reserve_pub;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, if (NULL == details)
"Got history callback, direction %u!\n",
(unsigned int) dir);
if (TALER_BANK_DIRECTION_NONE == dir)
{ {
hh = NULL; hh = NULL;
if (TALER_EC_NONE != ec) if (TALER_EC_NONE != ec)
@ -428,11 +381,8 @@ history_cb (void *cls,
if (0 < qs) if (0 < qs)
{ {
/* transaction success, update #last_row_off */ /* transaction success, update #last_row_off */
GNUNET_free_non_null (last_row_off);
last_row_off = latest_row_off; last_row_off = latest_row_off;
last_row_off_size = latest_row_off_size; latest_row_off = 0;
latest_row_off = NULL;
latest_row_off_size = 0;
/* if successful at limit, try increasing transaction batch size (AIMD) */ /* if successful at limit, try increasing transaction batch size (AIMD) */
if (current_batch_size == batch_size) if (current_batch_size == batch_size)
@ -462,49 +412,10 @@ history_cb (void *cls,
NULL); NULL);
return GNUNET_OK; /* will be ignored anyway */ return GNUNET_OK; /* will be ignored anyway */
} }
if (NULL != details->wtid_s)
{
struct RejectContext *rtc;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Wire transfer over %s has invalid subject `%s', sending it back!\n",
TALER_amount2s (&details->amount),
details->wtid_s);
GNUNET_break (0 != row_off_size);
if (latest_row_off_size != row_off_size)
{
GNUNET_free_non_null (latest_row_off);
latest_row_off = GNUNET_malloc (row_off_size);
latest_row_off_size = row_off_size;
}
memcpy (latest_row_off,
row_off,
row_off_size);
rtc = GNUNET_new (struct RejectContext);
rtc->session = session;
rtc->wtid_s = GNUNET_strdup (details->wtid_s);
rt = wa_pos->wire_plugin->reject_transfer (wa_pos->wire_plugin->cls,
wa_pos->section_name,
row_off,
row_off_size,
&reject_cb,
rtc);
if (NULL == rt)
{
GNUNET_break (0);
db_plugin->rollback (db_plugin->cls,
session);
GNUNET_assert (NULL == task);
task = GNUNET_SCHEDULER_add_now (&find_transfers,
NULL);
}
return GNUNET_SYSERR; /* will continue later... */
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Adding wire transfer over %s with (hashed) subject `%s'\n", "Adding wire transfer over %s with (hashed) subject `%s'\n",
TALER_amount2s (&details->amount), TALER_amount2s (&details->amount),
TALER_B2S (&details->wtid)); TALER_B2S (&details->reserve_pub));
/** /**
* Debug block. * Debug block.
@ -515,8 +426,8 @@ history_cb (void *cls,
char wtid_s[PUBSIZE]; char wtid_s[PUBSIZE];
GNUNET_break GNUNET_break
(NULL != GNUNET_STRINGS_data_to_string (&details->wtid, (NULL != GNUNET_STRINGS_data_to_string (&details->reserve_pub,
sizeof (details->wtid), sizeof (details->reserve_pub),
&wtid_s[0], &wtid_s[0],
PUBSIZE)); PUBSIZE));
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@ -525,20 +436,14 @@ history_cb (void *cls,
} }
current_batch_size++; current_batch_size++;
/* Wire transfer identifier == reserve public key */
GNUNET_assert (sizeof (reserve_pub) == sizeof (details->wtid));
memcpy (&reserve_pub,
&details->wtid,
sizeof (reserve_pub));
qs = db_plugin->reserves_in_insert (db_plugin->cls, qs = db_plugin->reserves_in_insert (db_plugin->cls,
session, session,
&reserve_pub, &details->reserve_pub,
&details->amount, &details->amount,
details->execution_date, details->execution_date,
details->account_url, details->account_url,
wa_pos->section_name, wa_pos->section_name,
row_off, serial_id);
row_off_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -560,15 +465,7 @@ history_cb (void *cls,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (latest_row_off_size != row_off_size) latest_row_off = serial_id;
{
GNUNET_free_non_null (latest_row_off);
latest_row_off = GNUNET_malloc (row_off_size);
latest_row_off_size = row_off_size;
}
memcpy (latest_row_off,
row_off,
row_off_size);
return GNUNET_OK; return GNUNET_OK;
} }
@ -615,8 +512,7 @@ find_transfers (void *cls)
qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls, qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
session, session,
wa_pos->section_name, wa_pos->section_name,
&last_row_off, &last_row_off);
&last_row_off_size);
if (GNUNET_DB_STATUS_HARD_ERROR == qs) if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -638,17 +534,14 @@ find_transfers (void *cls)
} }
} }
wa_pos->reset_mode = GNUNET_NO; wa_pos->reset_mode = GNUNET_NO;
GNUNET_assert ( (NULL == last_row_off) ||
( (NULL != last_row_off) &&
(0 != last_row_off_size) ) );
delay = GNUNET_YES; delay = GNUNET_YES;
current_batch_size = 0; current_batch_size = 0;
hh = wa_pos->wire_plugin->get_history (wa_pos->wire_plugin->cls, hh = TALER_BANK_credit_history (ctx,
wa_pos->section_name, wa_pos->account.details.x_taler_bank.
TALER_BANK_DIRECTION_CREDIT, account_base_url,
&wa_pos->auth,
last_row_off, last_row_off,
last_row_off_size,
batch_size, batch_size,
&history_cb, &history_cb,
session); session);
@ -695,6 +588,14 @@ run (void *cls,
NULL); NULL);
GNUNET_SCHEDULER_add_shutdown (&shutdown_task, GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
cls); cls);
ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
&rc);
rc = GNUNET_CURL_gnunet_rc_create (ctx);
if (NULL == ctx)
{
GNUNET_break (0);
return;
}
} }

View File

@ -58,7 +58,7 @@ check_for_account (void *cls,
const char *section) const char *section)
{ {
struct FindAccountContext *ctx = cls; struct FindAccountContext *ctx = cls;
char *plugin_name; char *method;
char *payto_url; char *payto_url;
char *wire_response_filename; char *wire_response_filename;
struct TALER_EXCHANGEDB_AccountInfo ai; struct TALER_EXCHANGEDB_AccountInfo ai;
@ -81,12 +81,12 @@ check_for_account (void *cls,
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ctx->cfg, GNUNET_CONFIGURATION_get_value_string (ctx->cfg,
section, section,
"PLUGIN", "METHOD",
&plugin_name)) &method))
{ {
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
section, section,
"PLUGIN"); "METHOD");
GNUNET_free (payto_url); GNUNET_free (payto_url);
return; return;
} }
@ -97,7 +97,7 @@ check_for_account (void *cls,
&wire_response_filename)) &wire_response_filename))
wire_response_filename = NULL; wire_response_filename = NULL;
ai.section_name = section; ai.section_name = section;
ai.plugin_name = plugin_name; ai.method = method;
ai.payto_url = payto_url; ai.payto_url = payto_url;
ai.wire_response_filename = wire_response_filename; ai.wire_response_filename = wire_response_filename;
@ -112,7 +112,7 @@ check_for_account (void *cls,
ctx->cb (ctx->cb_cls, ctx->cb (ctx->cb_cls,
&ai); &ai);
GNUNET_free (payto_url); GNUNET_free (payto_url);
GNUNET_free (plugin_name); GNUNET_free (method);
GNUNET_free_non_null (wire_response_filename); GNUNET_free_non_null (wire_response_filename);
} }

View File

@ -144,12 +144,12 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg,
/** /**
* Convert @a af to @a wf. * Convert @a af to @a wf.
* *
* @param wireplugin name of the wire plugin the fees are for * @param method name of the wire method the fees are for
* @param[in,out] af aggregate fees, host format (updated to round time) * @param[in,out] af aggregate fees, host format (updated to round time)
* @param[out] wf aggregate fees, disk / signature format * @param[out] wf aggregate fees, disk / signature format
*/ */
void void
TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin, TALER_EXCHANGEDB_fees_2_wf (const char *method,
struct TALER_EXCHANGEDB_AggregateFees *af, struct TALER_EXCHANGEDB_AggregateFees *af,
struct TALER_MasterWireFeePS *wf) struct TALER_MasterWireFeePS *wf)
{ {
@ -157,8 +157,8 @@ TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin,
(void) GNUNET_TIME_round_abs (&af->end_date); (void) GNUNET_TIME_round_abs (&af->end_date);
wf->purpose.size = htonl (sizeof (*wf)); wf->purpose.size = htonl (sizeof (*wf));
wf->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES); wf->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_WIRE_FEES);
GNUNET_CRYPTO_hash (wireplugin, GNUNET_CRYPTO_hash (method,
strlen (wireplugin) + 1, strlen (method) + 1,
&wf->h_wire_method); &wf->h_wire_method);
wf->start_date = GNUNET_TIME_absolute_hton (af->start_date); wf->start_date = GNUNET_TIME_absolute_hton (af->start_date);
wf->end_date = GNUNET_TIME_absolute_hton (af->end_date); wf->end_date = GNUNET_TIME_absolute_hton (af->end_date);

View File

@ -253,7 +253,7 @@ postgres_create_tables (void *cls)
GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_in" GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_in"
"(reserve_in_serial_id BIGSERIAL UNIQUE" "(reserve_in_serial_id BIGSERIAL UNIQUE"
",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE" ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON DELETE CASCADE"
",wire_reference BYTEA NOT NULL" ",wire_reference INT8 NOT NULL"
",credit_val INT8 NOT NULL" ",credit_val INT8 NOT NULL"
",credit_frac INT4 NOT NULL" ",credit_frac INT4 NOT NULL"
",sender_account_details TEXT NOT NULL" ",sender_account_details TEXT NOT NULL"
@ -2158,8 +2158,7 @@ reserves_update (void *cls,
* @param sender_account_details account information for the sender (payto://-URL) * @param sender_account_details account information for the sender (payto://-URL)
* @param exchange_account_section name of the section in the configuration for the exchange's * @param exchange_account_section name of the section in the configuration for the exchange's
* account into which the deposit was made * account into which the deposit was made
* @param wire_reference unique reference identifying the wire transfer (binary blob) * @param wire_ref unique reference identifying the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @return transaction status code * @return transaction status code
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
@ -2170,8 +2169,7 @@ postgres_reserves_in_insert (void *cls,
struct GNUNET_TIME_Absolute execution_time, struct GNUNET_TIME_Absolute execution_time,
const char *sender_account_details, const char *sender_account_details,
const char *exchange_account_section, const char *exchange_account_section,
const void *wire_reference, uint64_t wire_ref)
size_t wire_reference_size)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
enum GNUNET_DB_QueryStatus reserve_exists; enum GNUNET_DB_QueryStatus reserve_exists;
@ -2252,8 +2250,7 @@ postgres_reserves_in_insert (void *cls,
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve.pub), GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
GNUNET_PQ_query_param_fixed_size (wire_reference, GNUNET_PQ_query_param_uint64 (&wire_ref),
wire_reference_size),
TALER_PQ_query_param_amount (balance), TALER_PQ_query_param_amount (balance),
GNUNET_PQ_query_param_string (exchange_account_section), GNUNET_PQ_query_param_string (exchange_account_section),
GNUNET_PQ_query_param_string (sender_account_details), GNUNET_PQ_query_param_string (sender_account_details),
@ -2311,8 +2308,7 @@ postgres_reserves_in_insert (void *cls,
* @param session the database session handle * @param session the database session handle
* @param exchange_account_name name of the section in the exchange's configuration * @param exchange_account_name name of the section in the exchange's configuration
* for the account that we are tracking here * for the account that we are tracking here
* @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob) * @param[out] wire_ref set to unique reference identifying the wire transfer
* @param[out] wire_reference_size set to number of bytes in @a wire_reference
* @return transaction status code * @return transaction status code
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
@ -2320,17 +2316,15 @@ postgres_get_latest_reserve_in_reference (void *cls,
struct TALER_EXCHANGEDB_Session * struct TALER_EXCHANGEDB_Session *
session, session,
const char *exchange_account_name, const char *exchange_account_name,
void **wire_reference, uint64_t *wire_reference)
size_t *wire_reference_size)
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_string (exchange_account_name), GNUNET_PQ_query_param_string (exchange_account_name),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_variable_size ("wire_reference", GNUNET_PQ_result_spec_uint64 ("wire_reference",
wire_reference, wire_reference),
wire_reference_size),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
@ -6192,14 +6186,12 @@ reserves_in_serial_helper_cb (void *cls,
char *sender_account_details; char *sender_account_details;
struct GNUNET_TIME_Absolute execution_date; struct GNUNET_TIME_Absolute execution_date;
uint64_t rowid; uint64_t rowid;
void *wire_reference; uint64_t wire_reference;
size_t wire_reference_size;
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_auto_from_type ("reserve_pub", GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
&reserve_pub), &reserve_pub),
GNUNET_PQ_result_spec_variable_size ("wire_reference", GNUNET_PQ_result_spec_uint64 ("wire_reference",
&wire_reference, &wire_reference),
&wire_reference_size),
TALER_PQ_RESULT_SPEC_AMOUNT ("credit", TALER_PQ_RESULT_SPEC_AMOUNT ("credit",
&credit), &credit),
TALER_PQ_result_spec_absolute_time ("execution_date", TALER_PQ_result_spec_absolute_time ("execution_date",
@ -6227,7 +6219,6 @@ reserves_in_serial_helper_cb (void *cls,
&credit, &credit,
sender_account_details, sender_account_details,
wire_reference, wire_reference,
wire_reference_size,
execution_date); execution_date);
GNUNET_PQ_cleanup_result (rs); GNUNET_PQ_cleanup_result (rs);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)

View File

@ -944,8 +944,7 @@ audit_refund_cb (void *cls,
* @param reserve_pub public key of the reserve (also the WTID) * @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received * @param credit amount that was received
* @param sender_account_details information about the sender's bank account * @param sender_account_details information about the sender's bank account
* @param wire_reference unique reference identifying the wire transfer (binary blob) * @param wire_reference unique reference identifying the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds * @param execution_date when did we receive the funds
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/ */
@ -955,8 +954,7 @@ audit_reserve_in_cb (void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit, const struct TALER_Amount *credit,
const char *sender_account_details, const char *sender_account_details,
const void *wire_reference, uint64_t wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute execution_date) struct GNUNET_TIME_Absolute execution_date)
{ {
auditor_row_cnt++; auditor_row_cnt++;
@ -1507,8 +1505,7 @@ run (void *cls)
const char *sndr = "payto://x-taler-bank/localhost:8080/1"; const char *sndr = "payto://x-taler-bank/localhost:8080/1";
unsigned int matched; unsigned int matched;
unsigned int cnt; unsigned int cnt;
void *rr; uint64_t rr;
size_t rr_size;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Absolute now;
@ -1578,8 +1575,7 @@ run (void *cls)
plugin->get_latest_reserve_in_reference (plugin->cls, plugin->get_latest_reserve_in_reference (plugin->cls,
session, session,
"account-1", "account-1",
&rr, &rr));
&rr_size));
now = GNUNET_TIME_absolute_get (); now = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&now); (void) GNUNET_TIME_round_abs (&now);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
@ -1590,17 +1586,13 @@ run (void *cls)
now, now,
sndr, sndr,
"account-1", "account-1",
"TEST",
4)); 4));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls, plugin->get_latest_reserve_in_reference (plugin->cls,
session, session,
"account-1", "account-1",
&rr, &rr));
&rr_size)); FAILIF (4 != rr);
FAILIF (4 != rr_size);
FAILIF (0 != memcmp ("TEST", rr, 4));
GNUNET_free (rr);
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
check_reserve (session, check_reserve (session,
&reserve_pub, &reserve_pub,
@ -1617,24 +1609,18 @@ run (void *cls)
now, now,
sndr, sndr,
"account-1", "account-1",
"TEST2",
5)); 5));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls, plugin->get_latest_reserve_in_reference (plugin->cls,
session, session,
"account-1", "account-1",
&rr, &rr));
&rr_size));
GNUNET_free (rr);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->get_latest_reserve_in_reference (plugin->cls, plugin->get_latest_reserve_in_reference (plugin->cls,
session, session,
"account-1", "account-1",
&rr, &rr));
&rr_size)); FAILIF (5 != rr);
FAILIF (5 != rr_size);
FAILIF (0 != memcmp ("TEST2", rr, 5));
GNUNET_free (rr);
FAILIF (GNUNET_OK != FAILIF (GNUNET_OK !=
check_reserve (session, check_reserve (session,
&reserve_pub, &reserve_pub,

View File

@ -31,8 +31,6 @@ talerinclude_HEADERS = \
taler_mhd_lib.h \ taler_mhd_lib.h \
taler_pq_lib.h \ taler_pq_lib.h \
taler_signatures.h \ taler_signatures.h \
taler_wire_lib.h \
taler_wire_plugin.h \
taler_testing_bank_lib.h taler_testing_bank_lib.h
endif endif

View File

@ -870,7 +870,6 @@ struct TALER_AUDITORDB_Plugin
* @param pp where is the auditor in processing * @param pp where is the auditor in processing
* @param in_wire_off how far are we in the incoming wire transaction history * @param in_wire_off how far are we in the incoming wire transaction history
* @param out_wire_off how far are we in the outgoing wire transaction history * @param out_wire_off how far are we in the outgoing wire transaction history
* @param wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take?
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -883,9 +882,8 @@ struct TALER_AUDITORDB_Plugin
const struct const struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
const void *in_wire_off, uint64_t in_wire_off,
const void *out_wire_off, uint64_t out_wire_off);
size_t wire_off_size);
/** /**
@ -899,7 +897,6 @@ struct TALER_AUDITORDB_Plugin
* @param pp where is the auditor in processing * @param pp where is the auditor in processing
* @param in_wire_off how far are we in the incoming wire transaction history * @param in_wire_off how far are we in the incoming wire transaction history
* @param out_wire_off how far are we in the outgoing wire transaction history * @param out_wire_off how far are we in the outgoing wire transaction history
* @param wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take?
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -912,9 +909,8 @@ struct TALER_AUDITORDB_Plugin
const struct const struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
const void *in_wire_off, uint64_t in_wire_off,
const void *out_wire_off, uint64_t out_wire_off);
size_t wire_off_size);
/** /**
@ -927,7 +923,6 @@ struct TALER_AUDITORDB_Plugin
* @param[out] pp where is the auditor in processing * @param[out] pp where is the auditor in processing
* @param[out] in_wire_off how far are we in the incoming wire transaction history * @param[out] in_wire_off how far are we in the incoming wire transaction history
* @param[out] out_wire_off how far are we in the outgoing wire transaction history * @param[out] out_wire_off how far are we in the outgoing wire transaction history
* @param[out] wire_off_size how many bytes do @a in_wire_off and @a out_wire_off take?
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -939,9 +934,8 @@ struct TALER_AUDITORDB_Plugin
struct struct
TALER_AUDITORDB_WireAccountProgressPoint TALER_AUDITORDB_WireAccountProgressPoint
*pp, *pp,
void **in_wire_off, uint64_t *in_wire_off,
void **out_wire_off, uint64_t *out_wire_off);
size_t *wire_off_size);
/** /**

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2015, 2016, 2017 Taler Systems SA Copyright (C) 2015-2020 Taler Systems SA
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
@ -25,6 +25,7 @@
#include <jansson.h> #include <jansson.h>
#include <gnunet/gnunet_curl_lib.h> #include <gnunet/gnunet_curl_lib.h>
#include "taler_util.h" #include "taler_util.h"
#include "taler_wire_lib.h"
#include "taler_error_codes.h" #include "taler_error_codes.h"
@ -122,13 +123,11 @@ typedef void
* to the operators of the bank. * to the operators of the bank.
* *
* @param ctx curl context for the event loop * @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request) * @param account_base_url URL of the account (used to execute this request)
* @param auth authentication data to use * @param auth authentication data to use
* @param exchange_base_url base URL of the exchange (for tracking) * @param reserve_pub wire transfer subject for the transfer
* @param subject wire transfer subject for the transfer
* @param amount amount that was deposited * @param amount amount that was deposited
* @param debit_account_no account number to withdraw from (53 bits at most) * @param credit_account account to deposit into
* @param credit_account_no account number to deposit into (53 bits at most)
* @param res_cb the callback to call when the final result for this request is available * @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback * @param res_cb_cls closure for the above callback
* @return NULL * @return NULL
@ -137,13 +136,12 @@ typedef void
*/ */
struct TALER_BANK_AdminAddIncomingHandle * struct TALER_BANK_AdminAddIncomingHandle *
TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx, TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *account_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
const char *exchange_base_url, const struct
const char *subject, TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
uint64_t debit_account_no, const char *credit_account,
uint64_t credit_account_no,
TALER_BANK_AdminAddIncomingResultCallback res_cb, TALER_BANK_AdminAddIncomingResultCallback res_cb,
void *res_cb_cls); void *res_cb_cls);
@ -159,53 +157,103 @@ TALER_BANK_admin_add_incoming_cancel (struct
TALER_BANK_AdminAddIncomingHandle *aai); TALER_BANK_AdminAddIncomingHandle *aai);
/** /* ********************* /taler/transfer *********************** */
* Which types of transactions should be (or is being) returned?
*/
enum TALER_BANK_Direction
{
/** /**
* Base case, used to indicate errors or end of list. * Prepare for exeuction of a wire transfer.
*
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of this exchange (included in subject
* to facilitate use of tracking API by merchant backend)
* @param wtid wire transfer identifier to use
* @param buf[out] set to transaction data to persist, NULL on error
* @param buf_size[out] set to number of bytes in @a buf, 0 on error
*/ */
TALER_BANK_DIRECTION_NONE = 0, void
TALER_BANK_prepare_wire_transfer (const char *destination_account_url,
/** const struct TALER_Amount *amount,
* Transactions where the bank account receives money. const char *exchange_base_url,
*/ const struct
TALER_BANK_DIRECTION_CREDIT = 1, TALER_WireTransferIdentifierRawP *wtid,
void **buf,
/** size_t *buf_size);
* Transactions where the bank account looses money.
*/
TALER_BANK_DIRECTION_DEBIT = 2,
/**
* Return both types of transactions.
*/
TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT
| TALER_BANK_DIRECTION_DEBIT),
/**
* Bit mask that is applied to view transactions that have been
* cancelled. The bit is set for cancelled transactions that are
* returned from /history, and must also be set in order for
* cancelled transactions to show up in the /history.
*/
TALER_BANK_DIRECTION_CANCEL = 4
};
/** /**
* Handle for querying the bank's transaction history. * Handle for active wire transfer.
*/ */
struct TALER_BANK_HistoryHandle; struct TALER_BANK_WireExecuteHandle;
/** /**
* Details about a wire transfer. * Function called with the result from the execute step.
*
* @param cls closure
* @param response_code HTTP status code
* @param ec taler error code
* @param row_id unique ID of the wire transfer in the bank's records
* @param timestamp when did the transaction go into effect
*/ */
struct TALER_BANK_TransferDetails typedef void
(*TALER_BANK_ConfirmationCallback)(void *cls,
unsigned int response_code,
enum TALER_ErrorCode ec,
uint64_t row_id,
struct GNUNET_TIME_Absolute timestamp);
/**
* Execute a wire transfer.
*
* @param ctx context for HTTP interaction
* @param bank_base_url URL of the base INCLUDING account number
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
struct TALER_BANK_WireExecuteHandle *
TALER_BANK_execute_wire_transfer (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url,
const struct
TALER_BANK_AuthenticationData *auth,
const void *buf,
size_t buf_size,
TALER_BANK_ConfirmationCallback cc,
void *cc_cls);
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param eh execution to cancel
*/
void
TALER_BANK_execute_wire_transfer_cancel (struct
TALER_BANK_WireExecuteHandle *eh);
/* ********************* /taler/credits *********************** */
/**
* Handle for querying the bank for transactions
* made to the exchange.
*/
struct TALER_BANK_CreditHistoryHandle;
/**
* Details about a wire transfer to the exchange.
*/
struct TALER_BANK_CreditDetails
{ {
/** /**
* Amount that was transferred * Amount that was transferred
@ -218,21 +266,22 @@ struct TALER_BANK_TransferDetails
struct GNUNET_TIME_Absolute execution_date; struct GNUNET_TIME_Absolute execution_date;
/** /**
* Wire transfer subject. Usually a reserve public key * Reserve public key encoded in the wire
* followed by the base URL of the exchange. * transfer subject.
*/ */
char *wire_transfer_subject; struct TALER_ReservePublicKeyP reserve_pub;
/** /**
* payto://-URL of the other account that was involved * payto://-URL of the source account that
* send the funds.
*/ */
char *account_url; const char *account_url;
}; };
/** /**
* Callbacks of this type are used to serve the result of asking * Callbacks of this type are used to serve the result of asking
* the bank for the transaction history. * the bank for the credit transaction history.
* *
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
@ -241,31 +290,27 @@ struct TALER_BANK_TransferDetails
* last callback is always of this status (even if `abs(num_results)` were * last callback is always of this status (even if `abs(num_results)` were
* already returned). * already returned).
* @param ec detailed error code * @param ec detailed error code
* @param dir direction of the transfer
* @param serial_id monotonically increasing counter corresponding to the transaction * @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer * @param details details about the wire transfer
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON * @param json detailed response from the HTTPD, or NULL if reply was not in JSON
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
typedef void typedef int
(*TALER_BANK_HistoryResultCallback) (void *cls, (*TALER_BANK_CreditResultCallback) (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
uint64_t serial_id, uint64_t serial_id,
const struct const struct
TALER_BANK_TransferDetails *details, TALER_BANK_CreditDetails *details,
const json_t *json); const json_t *json);
/** /**
* Request the wire transfer history of a bank account. * Request the wire credit history of an exchange's bank account.
* *
* @param ctx curl context for the event loop * @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request) * @param account_base_url URL of the base INCLUDING account number
* @param auth authentication data to use * @param auth authentication data to use
* @param account_number which account number should we query
* @param direction what kinds of wire transfers should be returned
* @param ascending if GNUNET_YES, history elements will be returned in chronological order.
* @param start_row from which row on do we want to get results, use UINT64_MAX for the latest; exclusive * @param start_row from which row on do we want to get results, use UINT64_MAX for the latest; exclusive
* @param num_results how many results do we want; negative numbers to go into the past, * @param num_results how many results do we want; negative numbers to go into the past,
* positive numbers to go into the future starting at @a start_row; * positive numbers to go into the future starting at @a start_row;
@ -276,16 +321,13 @@ typedef void
* if the inputs are invalid (i.e. zero value for @e num_results). * if the inputs are invalid (i.e. zero value for @e num_results).
* In this case, the callback is not called. * In this case, the callback is not called.
*/ */
struct TALER_BANK_HistoryHandle * struct TALER_BANK_CreditHistoryHandle *
TALER_BANK_history (struct GNUNET_CURL_Context *ctx, TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *account_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number,
enum TALER_BANK_Direction direction,
unsigned int ascending,
uint64_t start_row, uint64_t start_row,
int64_t num_results, int64_t num_results,
TALER_BANK_HistoryResultCallback hres_cb, TALER_BANK_CreditResultCallback hres_cb,
void *hres_cb_cls); void *hres_cb_cls);
@ -297,64 +339,127 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
* @param hh the history request handle * @param hh the history request handle
*/ */
void void
TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh); TALER_BANK_credit_history_cancel (struct TALER_BANK_CreditHistoryHandle *hh);
/* ********************* /taler/debits *********************** */
/** /**
* Handle for #TALER_BANK_reject() operation. * Handle for querying the bank for transactions
* made from the exchange to merchants.
*/ */
struct TALER_BANK_RejectHandle; struct TALER_BANK_DebitHistoryHandle;
/**
* Details about a wire transfer made by the exchange
* to a merchant.
*/
struct TALER_BANK_DebitDetails
{
/**
* Amount that was transferred
*/
struct TALER_Amount amount;
/**
* Time of the the transfer
*/
struct GNUNET_TIME_Absolute execution_date;
/**
* Wire transfer identifier used by the exchange.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Exchange's base URL as given in the wire transfer.
*/
const char *exchange_base_url;
/**
* payto://-URL of the source account that
* send the funds.
*/
const char *account_url;
};
/** /**
* Callbacks of this type are used to serve the result of asking * Callbacks of this type are used to serve the result of asking
* the bank to reject an incoming wire transfer. * the bank for the debit transaction history.
* *
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for successful status request; * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* #MHD_HTTP_NOT_FOUND if the rowid is unknown;
* 0 if the bank's reply is bogus (fails to follow the protocol), * 0 if the bank's reply is bogus (fails to follow the protocol),
* #MHD_HTTP_NO_CONTENT if there are no more results; on success the
* last callback is always of this status (even if `abs(num_results)` were
* already returned).
* @param ec detailed error code * @param ec detailed error code
* @param serial_id monotonically increasing counter corresponding to the transaction
* @param details details about the wire transfer
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
typedef void typedef int
(*TALER_BANK_RejectResultCallback) (void *cls, (*TALER_BANK_DebitResultCallback) (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec); enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct
TALER_BANK_DebitDetails *details,
const json_t *json);
/** /**
* Request rejection of a wire transfer, marking it as cancelled and voiding * Request the wire credit history of an exchange's bank account.
* its effects.
* *
* @param ctx curl context for the event loop * @param ctx curl context for the event loop
* @param bank_base_url URL of the bank (used to execute this request) * @param bank_base_url URL of the base INCLUDING account number
* @param auth authentication data to use * @param auth authentication data to use
* @param account_number which account number should we query * @param account_number which account number should we query
* @param rowid transfer to reject * @param start_row from which row on do we want to get results, use UINT64_MAX for the latest; exclusive
* @param rcb the callback to call with the operation result * @param num_results how many results do we want; negative numbers to go into the past,
* @param rcb_cls closure for @a rcb * positive numbers to go into the future starting at @a start_row;
* must not be zero.
* @param hres_cb the callback to call with the transaction history
* @param hres_cb_cls closure for the above callback
* @return NULL * @return NULL
* if the inputs are invalid. * if the inputs are invalid (i.e. zero value for @e num_results).
* In this case, the callback is not called. * In this case, the callback is not called.
*/ */
struct TALER_BANK_RejectHandle * struct TALER_BANK_DebitHistoryHandle *
TALER_BANK_reject (struct GNUNET_CURL_Context *ctx, TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
const char *bank_base_url, const char *bank_base_url,
const struct TALER_BANK_AuthenticationData *auth, const struct TALER_BANK_AuthenticationData *auth,
uint64_t account_number, uint64_t start_row,
uint64_t rowid, int64_t num_results,
TALER_BANK_RejectResultCallback rcb, TALER_BANK_DebitResultCallback hres_cb,
void *rcb_cls); void *hres_cb_cls);
/** /**
* Cancel an reject request. This function cannot be used on a request * Cancel an history request. This function cannot be used on a request
* handle if the response was is already served for it. * handle if the last response (anything with a status code other than
* 200) is already served for it.
* *
* @param rh the reject request handle * @param hh the history request handle
*/ */
void void
TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh); TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh);
/**
* Convenience method for parsing configuration section with bank account data.
*
* @param cfg configuration to parse
* @param section the section with the configuration data
* @param acc[out] set to the account details
* @return #GNUNET_OK on success
*/
int
TALER_BANK_account_parse_cfg (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section,
struct TALER_Account *acc);
/** /**

View File

@ -425,12 +425,12 @@ TALER_EXCHANGEDB_fees_read (const struct GNUNET_CONFIGURATION_Handle *cfg,
/** /**
* Convert @a af to @a wf. * Convert @a af to @a wf.
* *
* @param wireplugin name of the wire plugin the fees are for * @param wiremethod name of the wire method the fees are for
* @param[in,out] af aggregate fees, host format (updated to round time) * @param[in,out] af aggregate fees, host format (updated to round time)
* @param[out] wf aggregate fees, disk / signature format * @param[out] wf aggregate fees, disk / signature format
*/ */
void void
TALER_EXCHANGEDB_fees_2_wf (const char *wireplugin, TALER_EXCHANGEDB_fees_2_wf (const char *wiremethod,
struct TALER_EXCHANGEDB_AggregateFees *af, struct TALER_EXCHANGEDB_AggregateFees *af,
struct TALER_MasterWireFeePS *wf); struct TALER_MasterWireFeePS *wf);
@ -470,10 +470,9 @@ struct TALER_EXCHANGEDB_AccountInfo
const char *section_name; const char *section_name;
/** /**
* Name of the wire plugin that should be used to access * Name of the wire method used by this account.
* the account.
*/ */
const char *plugin_name; const char *method;
/** /**
* payto://-URL of the account. * payto://-URL of the account.

View File

@ -960,8 +960,7 @@ typedef int
* @param reserve_pub public key of the reserve (also the WTID) * @param reserve_pub public key of the reserve (also the WTID)
* @param credit amount that was received * @param credit amount that was received
* @param sender_account_details information about the sender's bank account, in payto://-format * @param sender_account_details information about the sender's bank account, in payto://-format
* @param wire_reference unique identifier for the wire transfer (plugin-specific format) * @param wire_reference unique identifier for the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @param execution_date when did we receive the funds * @param execution_date when did we receive the funds
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/ */
@ -972,8 +971,7 @@ typedef int
TALER_ReservePublicKeyP *reserve_pub, TALER_ReservePublicKeyP *reserve_pub,
const struct TALER_Amount *credit, const struct TALER_Amount *credit,
const char *sender_account_details, const char *sender_account_details,
const void *wire_reference, uint64_t wire_reference,
size_t wire_reference_size,
struct GNUNET_TIME_Absolute struct GNUNET_TIME_Absolute
execution_date); execution_date);
@ -1500,8 +1498,7 @@ struct TALER_EXCHANGEDB_Plugin
* @param balance the amount that has to be added to the reserve * @param balance the amount that has to be added to the reserve
* @param execution_time when was the amount added * @param execution_time when was the amount added
* @param sender_account_details information about the sender's bank account, in payto://-format * @param sender_account_details information about the sender's bank account, in payto://-format
* @param wire_reference unique reference identifying the wire transfer (binary blob) * @param wire_reference unique reference identifying the wire transfer
* @param wire_reference_size number of bytes in @a wire_reference
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -1512,8 +1509,7 @@ struct TALER_EXCHANGEDB_Plugin
struct GNUNET_TIME_Absolute execution_time, struct GNUNET_TIME_Absolute execution_time,
const char *sender_account_details, const char *sender_account_details,
const char *exchange_account_name, const char *exchange_account_name,
const void *wire_reference, uint64_t wire_reference);
size_t wire_reference_size);
/** /**
@ -1521,16 +1517,14 @@ struct TALER_EXCHANGEDB_Plugin
* *
* @param cls the @e cls of this struct with the plugin-specific state * @param cls the @e cls of this struct with the plugin-specific state
* @param db the database connection handle * @param db the database connection handle
* @param[out] wire_reference set to unique reference identifying the wire transfer (binary blob) * @param[out] wire_ref set to unique reference identifying the wire transfer
* @param[out] wire_reference_size set to number of bytes in @a wire_reference
* @return transaction status code * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*get_latest_reserve_in_reference)(void *cls, (*get_latest_reserve_in_reference)(void *cls,
struct TALER_EXCHANGEDB_Session *db, struct TALER_EXCHANGEDB_Session *db,
const char *exchange_account_name, const char *exchange_account_name,
void **wire_reference, uint64_t *wire_ref);
size_t *wire_reference_size);
/** /**

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-2020 Taler Systems SA
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
@ -74,8 +74,8 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h);
*/ */
uint64_t uint64_t
TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h, TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t debit_account, const char *debit_account,
uint64_t credit_account, const char *credit_account,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
const char *subject, const char *subject,
const char *exchange_base_url); const char *exchange_base_url);
@ -101,27 +101,12 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
int int
TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h, TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
const struct TALER_Amount *want_amount, const struct TALER_Amount *want_amount,
uint64_t want_debit, const char *want_debit,
uint64_t want_credit, const char *want_credit,
const char *exchange_base_url, const char *exchange_base_url,
char **subject); char **subject);
/**
* Reject incoming wire transfer to account @a credit_account
* as identified by @a rowid.
*
* @param h fake bank handle
* @param rowid identifies transfer to reject
* @param credit_account account number of owner of credited account
* @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not found
*/
int
TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
uint64_t rowid,
uint64_t credit_account);
/** /**
* Stop running the fake bank. * Stop running the fake bank.
* *

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014 GNUnet e.V. Copyright (C) 2014-2020 Taler Systems SA
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

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
(C) 2018 Taler Systems SA (C) 2018-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as
@ -103,15 +103,31 @@ TALER_TESTING_has_in_name (const char *prog,
/* ************** Specific interpreter commands ************ */ /* ************** Specific interpreter commands ************ */
/** /**
* Make a "history" CMD. * Make a credit "history" CMD.
* *
* @param label command label. * @param label command label.
* @param bank_url base URL of the bank offering the "history" * @param account_url base URL of the account offering the "history"
* operation.
* @param start_row_reference reference to a command that can
* offer a row identifier, to be used as the starting row
* to accept in the result.
* @param num_results how many rows we want in the result,
* and ascending/descending call
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_credits (const char *label,
const char *account_url,
const char *start_row_reference,
long long num_results);
/**
* Make a debit "history" CMD.
*
* @param label command label.
* @param account_url base URL of the account offering the "history"
* operation. * operation.
* @param account_no bank account number to ask the history for.
* @param direction which direction this operation is interested
* @param ascending if #GNUNET_YES, it ask the bank to return results
* in chronological order.
* @param start_row_reference reference to a command that can * @param start_row_reference reference to a command that can
* offer a row identifier, to be used as the starting row * offer a row identifier, to be used as the starting row
* to accept in the result. * to accept in the result.
@ -119,30 +135,10 @@ TALER_TESTING_has_in_name (const char *prog,
* @return the command. * @return the command.
*/ */
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_history (const char *label, TALER_TESTING_cmd_bank_debits (const char *label,
const char *bank_url, const char *account_url,
uint64_t account_no,
enum TALER_BANK_Direction direction,
unsigned int ascending,
const char *start_row_reference, const char *start_row_reference,
unsigned long long num_results); long long num_results);
/**
* Create a "reject" CMD.
*
* @param label command label.
* @param bank_url base URL of the bank implementing the
* "reject" operation.
* @param deposit_reference reference to a command that will
* provide a "row id" and credit (bank) account to craft
* the "reject" request.
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_bank_reject (const char *label,
const char *bank_url,
const char *deposit_reference);
#endif #endif

View File

@ -48,18 +48,6 @@
} while (0) } while (0)
#define TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT(cmd,out) \
TALER_TESTING_get_trait_uint64 (cmd, 0, out)
#define TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT(data) \
TALER_TESTING_make_trait_uint64 (0, data)
#define TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT(cmd,out) \
TALER_TESTING_get_trait_uint64 (cmd, 1, out)
#define TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT(data) \
TALER_TESTING_make_trait_uint64 (1, data)
#define TALER_TESTING_GET_TRAIT_ROW_ID(cmd,out) \ #define TALER_TESTING_GET_TRAIT_ROW_ID(cmd,out) \
TALER_TESTING_get_trait_uint64 (cmd, 3, out) TALER_TESTING_get_trait_uint64 (cmd, 3, out)
@ -67,6 +55,19 @@
TALER_TESTING_make_trait_uint64 (3, data) TALER_TESTING_make_trait_uint64 (3, data)
#define TALER_TESTING_GET_TRAIT_CREDIT_ACCOUNT(cmd,out) \
TALER_TESTING_get_trait_string (cmd, 4, out)
#define TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT(data) \
TALER_TESTING_make_trait_string (4, data)
#define TALER_TESTING_GET_TRAIT_DEBIT_ACCOUNT(cmd,out) \
TALER_TESTING_get_trait_string (cmd, 5, out)
#define TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT(data) \
TALER_TESTING_make_trait_string (5, data)
/** /**
* Allocate and return a piece of wire-details. Combines * Allocate and return a piece of wire-details. Combines
* the @a account_no and the @a bank_url to a * the @a account_no and the @a bank_url to a
@ -715,13 +716,9 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb,
* *
* @param label command label. * @param label command label.
* @param amount amount to transfer. * @param amount amount to transfer.
* @param bank_url base URL of the bank that implements this * @param account_base_url base URL of the account that implements this
* wire transer. For simplicity, both credit and debit * wire transer (which account gives money).
* bank account exist at the same bank. * @param payto_credit_account which account receives money.
* @param debit_account_no which account (expressed as a number)
* gives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
* @param auth_username username identifying the @a * @param auth_username username identifying the @a
* debit_account_no at the bank. * debit_account_no at the bank.
* @param auth_password password for @a auth_username. * @param auth_password password for @a auth_username.
@ -734,50 +731,13 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb,
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer (const char *label, TALER_TESTING_cmd_fakebank_transfer (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *payto_credit_account,
uint64_t credit_account_no,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *exchange_url); const char *exchange_url);
/**
* Create "fakebank transfer" CMD, letting the caller specifying
* the subject line.
*
* @param label command label.
* @param amount amount to transfer.
* @param bank_url base URL of the bank that implements this
* wire transer. For simplicity, both credit and debit
* bank account exist at the same bank.
* @param debit_account_no which account (expressed as a number)
* gives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
*
* @param auth_username username identifying the @a
* debit_account_no at the bank.
* @param auth_password password for @a auth_username.
* @param subject wire transfer's subject line.
* @param exchange_url which exchange is involved in this transfer.
* This data is used for tracking purposes (FIXME: explain
* _how_).
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *subject,
const char *exchange_url);
/** /**
* Create "fakebank transfer" CMD, letting the caller specify * Create "fakebank transfer" CMD, letting the caller specify
* a reference to a command that can offer a reserve private key. * a reference to a command that can offer a reserve private key.
@ -786,11 +746,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
* *
* @param label command label. * @param label command label.
* @param amount the amount to transfer. * @param amount the amount to transfer.
* @param bank_url base URL of the bank running the transfer. * @param account_base_url base URL of the account that implements this
* @param debit_account_no which account (expressed as a number) * wire transer (which account gives money).
* gives money. * @param payto_credit_account which account receives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
* @param auth_username username identifying the @a * @param auth_username username identifying the @a
* debit_account_no at the bank. * debit_account_no at the bank.
* @param auth_password password for @a auth_username. * @param auth_password password for @a auth_username.
@ -804,9 +762,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_subject (const char *label,
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label, TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *payto_credit_account,
uint64_t credit_account_no,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *ref, const char *ref,
@ -822,14 +780,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
* *
* @param label command label. * @param label command label.
* @param amount amount to transfer. * @param amount amount to transfer.
* @param bank_url base URL of the bank that implements this * @param account_base_url base URL of the account that implements this
* wire transer. For simplicity, both credit and debit * wire transer (which account gives money).
* bank account exist at the same bank. * @param payto_credit_account which account receives money.
* @param debit_account_no which account (expressed as a number)
* gives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
*
* @param auth_username username identifying the @a * @param auth_username username identifying the @a
* debit_account_no at the bank. * debit_account_no at the bank.
* @param auth_password password for @a auth_username. * @param auth_password password for @a auth_username.
@ -847,9 +800,9 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref (const char *label,
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_instance (const char *label, TALER_TESTING_cmd_fakebank_transfer_with_instance (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *
uint64_t credit_account_no, payto_credit_account,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *instance, const char *instance,
@ -1268,8 +1221,8 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_check_bank_transfer (const char *label, TALER_TESTING_cmd_check_bank_transfer (const char *label,
const char *exchange_base_url, const char *exchange_base_url,
const char *amount, const char *amount,
uint64_t debit_account, const char *debit_account,
uint64_t credit_account); const char *credit_account);
/** /**
@ -1617,7 +1570,6 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
* *
* @return the trait. * @return the trait.
*/ */
struct TALER_TESTING_Trait struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_priv (unsigned int index, TALER_TESTING_make_trait_reserve_priv (unsigned int index,
const struct const struct
@ -1640,6 +1592,34 @@ TALER_TESTING_get_trait_reserve_priv (const struct TALER_TESTING_Command *cmd,
TALER_ReservePrivateKeyP **reserve_priv); TALER_ReservePrivateKeyP **reserve_priv);
/**
* Offer a reserve public key.
*
* @param index reserve pubs's index number.
* @param reserve_priv reserve public key to offer.
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_pub (unsigned int index,
const struct
TALER_ReservePublicKeyP *reserve_pub);
/**
* Obtain a reserve public key from a @a cmd.
*
* @param cmd command to extract the reserve pub from.
* @param index reserve pub's index number.
* @param reserve_pub[out] set to the reserve pub.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_reserve_pub (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const struct
TALER_ReservePublicKeyP **reserve_pub);
/** /**
* Make a trait for a exchange signature. * Make a trait for a exchange signature.
* *
@ -2129,34 +2109,34 @@ TALER_TESTING_make_trait_peer_key_pub (unsigned int index,
/** /**
* Obtain a transfer subject from @a cmd. * Obtain a string from @a cmd.
* *
* @param cmd command to extract the subject from. * @param cmd command to extract the subject from.
* @param index index number associated with the transfer * @param index index number associated with the transfer
* subject to offer. * subject to offer.
* @param transfer_subject[out] where to write the offered * @param s[out] where to write the offered
* transfer subject. * string.
* *
* @return #GNUNET_OK on success. * @return #GNUNET_OK on success.
*/ */
int int
TALER_TESTING_get_trait_transfer_subject (const struct TALER_TESTING_get_trait_string (const struct
TALER_TESTING_Command *cmd, TALER_TESTING_Command *cmd,
unsigned int index, unsigned int index,
const char **transfer_subject); const char **s);
/** /**
* Offer transfer subject. * Offer string subject.
* *
* @param index index number associated with the transfer * @param index index number associated with the transfer
* subject being offered. * subject being offered.
* @param transfer_subject transfer subject to offer. * @param s string to offer.
* @return the trait. * @return the trait.
*/ */
struct TALER_TESTING_Trait struct TALER_TESTING_Trait
TALER_TESTING_make_trait_transfer_subject (unsigned int index, TALER_TESTING_make_trait_string (unsigned int index,
const char *transfer_subject); const char *s);
/** /**
@ -2223,7 +2203,6 @@ TALER_TESTING_get_trait_amount (const struct TALER_TESTING_Command *cmd,
* @param index which url is to be picked, * @param index which url is to be picked,
* in case multiple are offered. * in case multiple are offered.
* @param url the url to offer. * @param url the url to offer.
*
* @return the trait. * @return the trait.
*/ */
struct TALER_TESTING_Trait struct TALER_TESTING_Trait

View File

@ -22,7 +22,7 @@
#define TALER_WIRE_H #define TALER_WIRE_H
#include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_util_lib.h>
#include "taler_wire_plugin.h"
/** /**
* Different account types supported by payto://. * Different account types supported by payto://.
@ -46,6 +46,7 @@ enum TALER_PaytoAccountType
TALER_PAC_IBAN TALER_PAC_IBAN
}; };
/** /**
* Information about an account extracted from a payto://-URL. * Information about an account extracted from a payto://-URL.
*/ */
@ -71,19 +72,15 @@ struct TALER_Account
{ {
/** /**
* Hostname of the bank (possibly including port). * Bank account base URL.
*/
char *account_base_url;
/**
* Only the hostname of the bank.
*/ */
char *hostname; char *hostname;
/**
* Bank account number.
*/
unsigned long long no;
/**
* Base URL of the bank hosting the account above.
*/
char *bank_base_url;
} x_taler_bank; } x_taler_bank;
/** /**
@ -113,6 +110,18 @@ void
TALER_WIRE_account_free (struct TALER_Account *acc); TALER_WIRE_account_free (struct TALER_Account *acc);
/**
* Round the amount to something that can be
* transferred on the wire.
*
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
int
TALER_WIRE_amount_round (struct TALER_Amount *amount);
/** /**
* Parse @a payto_url and store the result in @a acc * Parse @a payto_url and store the result in @a acc
* *
@ -135,36 +144,4 @@ char *
TALER_WIRE_payto_get_method (const char *payto_url); TALER_WIRE_payto_get_method (const char *payto_url);
/**
* Get the plugin name from the payment method.
*
* @param method the method implemented by the plugin (for
* simplicity, we assume 1 method is implemented by 1 plugin).
* @return the plugin name, NULL if not found.
*/
const char *
TALER_WIRE_get_plugin_from_method (const char *method);
/**
* Load a WIRE plugin.
*
* @param cfg configuration to use
* @param plugin_name name of the plugin to load
* @return #GNUNET_OK on success
*/
struct TALER_WIRE_Plugin *
TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *plugin_name);
/**
* Unload a WIRE plugin.
*
* @param plugin the plugin to unload
*/
void
TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin);
#endif #endif

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2016, 2017 GNUnet e.V. & Inria Copyright (C) 2016-2020 Taler Systems SA
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
@ -40,23 +40,10 @@ typedef void
size_t buf_size); size_t buf_size);
/**
* Callback to process a merchant registration outcome.
*
* @param cls closure
* @param status GNUNET_OK if the registration succeeded,
* GNUNET_NO otherwise.
*/
typedef void
(*TALER_WIRE_MerchantRegisterCallback) (void *cls,
unsigned int status);
/** /**
* Details about a valid wire transfer to the exchange. * Details about a valid wire transfer to the exchange.
* It is the plugin's responsibility to filter and undo
* invalid transfers.
*/ */
struct TALER_WIRE_TransferDetails struct TALER_WIRE_CreditDetails
{ {
/** /**
* Amount that was transferred * Amount that was transferred
@ -69,22 +56,44 @@ struct TALER_WIRE_TransferDetails
struct GNUNET_TIME_Absolute execution_date; struct GNUNET_TIME_Absolute execution_date;
/** /**
* Binary data that was encoded in the wire transfer subject, if * Binary data that was encoded in the wire transfer subject.
* it decoded properly. Otherwise all-zeros and @e wtid_s is set. */
struct TALER_ReservePublicKeyP reserve_pub;
/**
* payto://-URL of the source's account (used
* when the reserve is closed or for debugging).
*/
const char *source_account_url;
};
/**
* Details about a valid wire transfer made by the
* exchange's aggregator to a merchant.
*/
struct TALER_WIRE_DebitDetails
{
/**
* Amount that was transferred
*/
struct TALER_Amount amount;
/**
* Time of the the transfer
*/
struct GNUNET_TIME_Absolute execution_date;
/**
* Binary data that was encoded in the wire transfer subject.
*/ */
struct TALER_WireTransferIdentifierRawP wtid; struct TALER_WireTransferIdentifierRawP wtid;
/** /**
* Wire transfer identifer as a string. Set to NULL if the * payto://-URL of the target account which received
* identifier was properly Base32 encoded and this @e wtid could be * the funds.
* set instead.
*/ */
char *wtid_s; const char *target_account_url;
/**
* payto://-URL of the other account that was involved
*/
char *account_url;
}; };
@ -96,33 +105,40 @@ struct TALER_WIRE_TransferDetails
* *
* @param cls closure * @param cls closure
* @param ec taler error code * @param ec taler error code
* @param dir direction of the transfer, #TALER_BANK_DIRECTION_NONE when
* the iteration is complete
* @param row_off identification of the position at which we are querying * @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off * @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer * @param details details about the wire transfer
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
typedef int typedef int
(*TALER_WIRE_HistoryResultCallback) (void *cls, (*TALER_WIRE_CreditResultCallback) (void *cls,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
const void *row_off, const void *row_off,
size_t row_off_size, size_t row_off_size,
const struct const struct
TALER_WIRE_TransferDetails *details); TALER_WIRE_CreditDetails *details);
/** /**
* Callbacks of this type are used to serve the result of asking * Callbacks of this type are used to serve the result of asking
* the bank to reject a wire transfer. * the bank for the transaction history. NOTE: this function will
* NOT get the list of history elements, but rather get (iteratively)
* called for each (parsed) history element.
* *
* @param cls closure * @param cls closure
* @param ec status of the operation, #TALER_EC_NONE on success * @param ec taler error code
* @param row_off identification of the position at which we are querying
* @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
typedef void typedef int
(*TALER_WIRE_RejectTransferCallback) (void *cls, (*TALER_WIRE_DebitResultCallback) (void *cls,
enum TALER_ErrorCode ec); enum TALER_ErrorCode ec,
const void *row_off,
size_t row_off_size,
const struct
TALER_WIRE_DebitDetails *details);
/** /**
@ -136,9 +152,14 @@ struct TALER_WIRE_PrepareHandle;
struct TALER_WIRE_ExecuteHandle; struct TALER_WIRE_ExecuteHandle;
/** /**
* Handle returned for querying the transaction history. * Handle returned for querying the credit transaction history.
*/ */
struct TALER_WIRE_HistoryHandle; struct TALER_WIRE_CreditHistoryHandle;
/**
* Handle returned for querying the debit transaction history.
*/
struct TALER_WIRE_DebitHistoryHandle;
/** /**
@ -146,7 +167,8 @@ struct TALER_WIRE_HistoryHandle;
* *
* @param cls closure * @param cls closure
* @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure * @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error * @param row_id unique ID of the wire transfer in the bank's records; NULL on error
* @param row_id_size number of bytes in @e row_id
* @param emsg NULL on success, otherwise an error message * @param emsg NULL on success, otherwise an error message
*/ */
typedef void typedef void
@ -156,6 +178,7 @@ typedef void
size_t row_id_size, size_t row_id_size,
const char *emsg); const char *emsg);
/** /**
* @brief The plugin API, returned from the plugin's "init" function. * @brief The plugin API, returned from the plugin's "init" function.
* The argument given to "init" is simply a configuration handle. * The argument given to "init" is simply a configuration handle.
@ -176,11 +199,10 @@ struct TALER_WIRE_Plugin
/** /**
* Which wire method (payto://METHOD/") is supported by this plugin? * Which wire method (payto://METHOD/") is supported by this plugin?
* For example, "iban" or "x-taler-bank". * For example, "x-taler-bank" or "iban".
*/ */
const char *method; const char *method;
/** /**
* Round amount DOWN to the amount that can be transferred via the wire * Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of * method. For example, Taler may support 0.000001 EUR as a unit of
@ -210,80 +232,7 @@ struct TALER_WIRE_Plugin
/** /**
* Prepare for exeuction of a wire transfer. * Query credits made to exchange account. We use the variable-size
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param origin_account_section configuration section specifying the origin
* account of the exchange to use
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of this exchange (included in subject
* to facilitate use of tracking API by merchant backend)
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
* @return NULL on failure
*/
struct TALER_WIRE_PrepareHandle *
(*prepare_wire_transfer) (void *cls,
const char *origin_account_section,
const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls);
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
void
(*prepare_wire_transfer_cancel) (void *cls,
struct TALER_WIRE_PrepareHandle *pth);
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
struct TALER_WIRE_ExecuteHandle *
(*execute_wire_transfer) (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls);
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
void
(*execute_wire_transfer_cancel) (void *cls,
struct TALER_WIRE_ExecuteHandle *eh);
/**
* Query transfer history of an account. We use the variable-size
* @a start_off to indicate which transfers we are interested in as * @a start_off to indicate which transfers we are interested in as
* different banking systems may have different ways to identify * different banking systems may have different ways to identify
* transfers. The @a start_off value must thus match the value of * transfers. The @a start_off value must thus match the value of
@ -295,7 +244,6 @@ struct TALER_WIRE_Plugin
* @param cls the @e cls of this struct with the plugin-specific state * @param cls the @e cls of this struct with the plugin-specific state
* @param account_section specifies the configuration section which * @param account_section specifies the configuration section which
* identifies the account for which we should get the history * identifies the account for which we should get the history
* @param direction what kinds of wire transfers should be returned
* @param start_off from which row on do we want to get results, use NULL for the latest; exclusive * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off * @param start_off_len number of bytes in @a start_off
* @param num_results how many results do we want; negative numbers to go into the past, * @param num_results how many results do we want; negative numbers to go into the past,
@ -304,70 +252,65 @@ struct TALER_WIRE_Plugin
* @param hres_cb the callback to call with the transaction history * @param hres_cb the callback to call with the transaction history
* @param hres_cb_cls closure for the above callback * @param hres_cb_cls closure for the above callback
*/ */
struct TALER_WIRE_HistoryHandle * struct TALER_WIRE_CreditHistoryHandle *
(*get_history) (void *cls, (*get_credits) (void *cls,
const char *account_section, const char *account_section,
enum TALER_BANK_Direction direction,
const void *start_off, const void *start_off,
size_t start_off_len, size_t start_off_len,
int64_t num_results, int64_t num_results,
TALER_WIRE_HistoryResultCallback hres_cb, TALER_WIRE_CreditResultCallback hres_cb,
void *hres_cb_cls); void *hres_cb_cls);
/** /**
* Cancel going over the account's history. * Cancel going over the account's history.
* *
* @param cls plugins' closure * @param cls plugins' closure
* @param whh operation to cancel * @param chh operation to cancel
*/ */
void void
(*get_history_cancel) (void *cls, (*get_credits_cancel) (void *cls,
struct TALER_WIRE_HistoryHandle *whh); struct TALER_WIRE_CreditHistoryHandle *chh);
/** /**
* Reject an incoming wire transfer that was obtained from the * Query debits (transfers to merchants) made by an exchange. We use the
* history. This function can be used to transfer funds back to * variable-size @a start_off to indicate which transfers we are interested
* the sender if the WTID was malformed (i.e. due to a typo). * in as different banking systems may have different ways to identify
* transfers. The @a start_off value must thus match the value of a
* `row_off` argument previously given to the @a hres_cb. Use NULL to query
* transfers from the beginning of time (with positive @a num_results) or
* from the latest committed transfers (with negative @a num_results).
* *
* Calling `reject_transfer` twice on the same wire transfer should * @param cls the @e cls of this struct with the plugin-specific state
* be idempotent, i.e. not cause the funds to be wired back twice.
* Furthermore, the transfer should henceforth be removed from the
* results returned by @e get_history.
*
* @param cls plugin's closure
* @param account_section specifies the configuration section which * @param account_section specifies the configuration section which
* identifies the account to use to reject the transfer * identifies the account for which we should get the history
* @param start_off offset of the wire transfer in plugin-specific format * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off * @param start_off_len number of bytes in @a start_off
* @param rej_cb function to call with the result of the operation * @param num_results how many results do we want; negative numbers to go into the past,
* @param rej_cb_cls closure for @a rej_cb * positive numbers to go into the future starting at @a start_row;
* @return handle to cancel the operation * must not be zero.
* @param hres_cb the callback to call with the transaction history
* @param hres_cb_cls closure for the above callback
*/ */
struct TALER_WIRE_RejectHandle * struct TALER_WIRE_DebitHistoryHandle *
(*reject_transfer)(void *cls, (*get_debits) (void *cls,
const char *account_section, const char *account_section,
const void *start_off, const void *start_off,
size_t start_off_len, size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb, int64_t num_results,
void *rej_cb_cls); TALER_WIRE_DebitResultCallback hres_cb,
void *hres_cb_cls);
/** /**
* Cancel ongoing reject operation. Note that the rejection may still * Cancel going over the account's history.
* proceed. Basically, if this function is called, the rejection may
* have happened or not. This function is usually used during shutdown
* or system upgrades. At a later point, the application must call
* @e reject_transfer again for this wire transfer, unless the
* @e get_history shows that the wire transfer no longer exists.
* *
* @param cls plugins' closure * @param cls plugins' closure
* @param rh operation to cancel * @param dhh operation to cancel
* @return closure of the callback of the operation
*/ */
void * void
(*reject_transfer_cancel)(void *cls, (*get_debits_cancel) (void *cls,
struct TALER_WIRE_RejectHandle *rh); struct TALER_WIRE_DebitHistoryHandle *dhh);
}; };

View File

@ -103,6 +103,7 @@ libtalertesting_la_SOURCES = \
testing_api_trait_exchange_sig.c \ testing_api_trait_exchange_sig.c \
testing_api_trait_json.c \ testing_api_trait_json.c \
testing_api_trait_process.c \ testing_api_trait_process.c \
testing_api_trait_reserve_pub.c \
testing_api_trait_reserve_priv.c \ testing_api_trait_reserve_priv.c \
testing_api_trait_number.c \ testing_api_trait_number.c \
testing_api_trait_fresh_coin.c \ testing_api_trait_fresh_coin.c \

View File

@ -52,12 +52,12 @@ struct BankCheckState
/** /**
* Expected debit bank account. * Expected debit bank account.
*/ */
uint64_t debit_account; const char *debit_account;
/** /**
* Expected credit bank account. * Expected credit bank account.
*/ */
uint64_t credit_account; const char *credit_account;
/** /**
* Wire transfer subject (set by fakebank-lib). * Wire transfer subject (set by fakebank-lib).
@ -95,18 +95,16 @@ check_bank_transfer_run (void *cls,
struct TALER_TESTING_Interpreter *is) struct TALER_TESTING_Interpreter *is)
{ {
struct BankCheckState *bcs = cls; struct BankCheckState *bcs = cls;
struct TALER_Amount amount; struct TALER_Amount amount;
const uint64_t *debit_account; const char *debit_account;
const uint64_t *credit_account; const char *credit_account;
const char *exchange_base_url; const char *exchange_base_url;
if (NULL == bcs->deposit_reference) if (NULL == bcs->deposit_reference)
{ {
TALER_LOG_INFO ("Deposit reference NOT given\n"); TALER_LOG_INFO ("Deposit reference NOT given\n");
debit_account = &bcs->debit_account; debit_account = bcs->debit_account;
credit_account = &bcs->credit_account; credit_account = bcs->credit_account;
exchange_base_url = bcs->exchange_base_url; exchange_base_url = bcs->exchange_base_url;
if (GNUNET_OK != if (GNUNET_OK !=
@ -154,14 +152,13 @@ check_bank_transfer_run (void *cls,
GNUNET_assert GNUNET_assert
(GNUNET_OK == TALER_TESTING_get_trait_url (GNUNET_OK == TALER_TESTING_get_trait_url
(deposit_cmd, 0, &exchange_base_url)); // check 0 works! (deposit_cmd, 0, &exchange_base_url)); // check 0 works!
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_FAKEBANK_check (is->fakebank, TALER_FAKEBANK_check (is->fakebank,
&amount, &amount,
*debit_account, debit_account,
*credit_account, credit_account,
exchange_base_url, exchange_base_url,
&bcs->subject)) &bcs->subject))
{ {
@ -217,9 +214,8 @@ check_bank_transfer_traits (void *cls,
wtid_ptr = NULL; wtid_ptr = NULL;
else else
wtid_ptr = &bcs->wtid; wtid_ptr = &bcs->wtid;
{
struct TALER_TESTING_Trait traits[] = { struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_transfer_subject (0, bcs->subject),
TALER_TESTING_make_trait_wtid (0, wtid_ptr), TALER_TESTING_make_trait_wtid (0, wtid_ptr),
TALER_TESTING_make_trait_url (0, bcs->exchange_base_url), TALER_TESTING_make_trait_url (0, bcs->exchange_base_url),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
@ -230,6 +226,7 @@ check_bank_transfer_traits (void *cls,
trait, trait,
index); index);
} }
}
/** /**
@ -250,8 +247,8 @@ TALER_TESTING_cmd_check_bank_transfer
(const char *label, (const char *label,
const char *exchange_base_url, const char *exchange_base_url,
const char *amount, const char *amount,
uint64_t debit_account, const char *debit_account,
uint64_t credit_account) const char *credit_account)
{ {
struct BankCheckState *bcs; struct BankCheckState *bcs;
@ -260,9 +257,8 @@ TALER_TESTING_cmd_check_bank_transfer
bcs->amount = amount; bcs->amount = amount;
bcs->debit_account = debit_account; bcs->debit_account = debit_account;
bcs->credit_account = credit_account; bcs->credit_account = credit_account;
bcs->deposit_reference = NULL; bcs->deposit_reference = NULL;
{
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
.label = label, .label = label,
.cls = bcs, .cls = bcs,
@ -273,6 +269,7 @@ TALER_TESTING_cmd_check_bank_transfer
return cmd; return cmd;
} }
}
/** /**

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2018 Taler Systems SA Copyright (C) 2018-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by under the terms of the GNU General Public License as published by
@ -51,24 +51,14 @@ struct FakebankTransferState
struct TALER_Amount amount; struct TALER_Amount amount;
/** /**
* Wire transfer subject. * Base URL of the debit account.
*/ */
const char *subject; const char *debit_url;
/** /**
* Base URL of the bank serving the request. * Money receiver account URL.
*/ */
const char *bank_url; const char *payto_credit_account;
/**
* Money sender account number.
*/
uint64_t debit_account_no;
/**
* Money receiver account number.
*/
uint64_t credit_account_no;
/** /**
* Username to use for authentication. * Username to use for authentication.
@ -86,6 +76,11 @@ struct FakebankTransferState
*/ */
struct TALER_ReservePrivateKeyP reserve_priv; struct TALER_ReservePrivateKeyP reserve_priv;
/**
* Reserve public key matching @e reserve_priv.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/** /**
* Handle to the pending request at the fakebank. * Handle to the pending request at the fakebank.
*/ */
@ -188,16 +183,15 @@ do_retry (void *cls)
* @param ec taler-specific error code, #TALER_EC_NONE on success * @param ec taler-specific error code, #TALER_EC_NONE on success
* @param serial_id unique ID of the wire transfer * @param serial_id unique ID of the wire transfer
* @param timestamp time stamp of the transaction made. * @param timestamp time stamp of the transaction made.
* @param full_response full response from the exchange (for * @param json raw response
* logging, in case of errors)
*/ */
static void static void
add_incoming_cb (void *cls, confirmation_cb (void *cls,
unsigned int http_status, unsigned int http_status,
enum TALER_ErrorCode ec, enum TALER_ErrorCode ec,
uint64_t serial_id, uint64_t serial_id,
struct GNUNET_TIME_Absolute timestamp, struct GNUNET_TIME_Absolute timestamp,
const json_t *full_response) const json_t *json)
{ {
struct FakebankTransferState *fts = cls; struct FakebankTransferState *fts = cls;
struct TALER_TESTING_Interpreter *is = fts->is; struct TALER_TESTING_Interpreter *is = fts->is;
@ -256,16 +250,8 @@ fakebank_transfer_run (void *cls,
struct TALER_TESTING_Interpreter *is) struct TALER_TESTING_Interpreter *is)
{ {
struct FakebankTransferState *fts = cls; struct FakebankTransferState *fts = cls;
char *subject;
struct TALER_BANK_AuthenticationData auth; struct TALER_BANK_AuthenticationData auth;
struct TALER_ReservePublicKeyP reserve_pub;
if (NULL != fts->subject)
{
subject = GNUNET_strdup (fts->subject);
}
else
{
/* Use reserve public key as subject */ /* Use reserve public key as subject */
if (NULL != fts->reserve_reference) if (NULL != fts->reserve_reference)
{ {
@ -358,28 +344,21 @@ fakebank_transfer_run (void *cls,
GNUNET_free (priv); GNUNET_free (priv);
} }
} }
GNUNET_CRYPTO_eddsa_key_get_public GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
(&fts->reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub); &fts->reserve_pub.eddsa_pub);
subject = GNUNET_STRINGS_data_to_string_alloc
(&reserve_pub, sizeof (reserve_pub));
}
auth.method = TALER_BANK_AUTH_BASIC; auth.method = TALER_BANK_AUTH_BASIC;
auth.details.basic.username = (char *) fts->auth_username; auth.details.basic.username = (char *) fts->auth_username;
auth.details.basic.password = (char *) fts->auth_password; auth.details.basic.password = (char *) fts->auth_password;
fts->is = is; fts->is = is;
fts->aih = TALER_BANK_admin_add_incoming fts->aih = TALER_BANK_admin_add_incoming
(TALER_TESTING_interpreter_get_context (is), (TALER_TESTING_interpreter_get_context (is),
fts->bank_url, fts->debit_url,
&auth, &auth,
fts->exchange_url, &fts->reserve_pub,
subject,
&fts->amount, &fts->amount,
fts->debit_account_no, fts->payto_credit_account,
fts->credit_account_no, &confirmation_cb,
&add_incoming_cb,
fts); fts);
GNUNET_free (subject);
if (NULL == fts->aih) if (NULL == fts->aih)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -408,6 +387,7 @@ fakebank_transfer_cleanup (void *cls,
"Command %s did not complete\n", "Command %s did not complete\n",
cmd->label); cmd->label);
TALER_BANK_admin_add_incoming_cancel (fts->aih); TALER_BANK_admin_add_incoming_cancel (fts->aih);
fts->aih = NULL;
} }
if (NULL != fts->retry_task) if (NULL != fts->retry_task)
{ {
@ -435,32 +415,21 @@ fakebank_transfer_traits (void *cls,
unsigned int index) unsigned int index)
{ {
struct FakebankTransferState *fts = cls; struct FakebankTransferState *fts = cls;
#define MANDATORY 7 struct TALER_TESTING_Trait traits[] = {
struct TALER_TESTING_Trait traits[MANDATORY + 1] = {
TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT
(&fts->debit_account_no),
TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT
(&fts->credit_account_no),
TALER_TESTING_make_trait_url (0, fts->exchange_url), TALER_TESTING_make_trait_url (0, fts->exchange_url),
TALER_TESTING_make_trait_url (1, fts->debit_url),
TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id), TALER_TESTING_MAKE_TRAIT_ROW_ID (&fts->serial_id),
TALER_TESTING_MAKE_TRAIT_CREDIT_ACCOUNT (fts->payto_credit_account),
TALER_TESTING_MAKE_TRAIT_DEBIT_ACCOUNT (fts->debit_url),
TALER_TESTING_make_trait_amount_obj (0, &fts->amount), TALER_TESTING_make_trait_amount_obj (0, &fts->amount),
TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp) TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp),
TALER_TESTING_make_trait_reserve_priv (0,
&fts->reserve_priv),
TALER_TESTING_make_trait_reserve_pub (0,
&fts->reserve_pub),
TALER_TESTING_trait_end ()
}; };
/**
* The user gave explicit subject,
* there must be NO reserve priv. */
if (NULL != fts->subject)
traits[MANDATORY - 1] =
TALER_TESTING_make_trait_transfer_subject (0,
fts->subject);
/* A reserve priv must exist if no subject was given. */
else
traits[MANDATORY - 1] = TALER_TESTING_make_trait_reserve_priv
(0, &fts->reserve_priv),
traits[MANDATORY] = TALER_TESTING_trait_end ();
return TALER_TESTING_get_trait (traits, return TALER_TESTING_get_trait (traits,
ret, ret,
trait, trait,
@ -475,27 +444,21 @@ fakebank_transfer_traits (void *cls,
* *
* @param label command label. * @param label command label.
* @param amount amount to transfer. * @param amount amount to transfer.
* @param bank_url base URL of the bank that implements this * @param account_base_url base URL of the account that implements this
* wire transer. For simplicity, both credit and debit * wire transer (which account gives money).
* bank account exist at the same bank. * @param payto_credit_account which account receives money.
* @param debit_account_no which account (expressed as a number)
* gives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
* @param auth_username username identifying the @a * @param auth_username username identifying the @a
* debit_account_no at the bank. * debit_account_no at the bank.
* @param auth_password password for @a auth_username. * @param auth_password password for @a auth_username.
* @param exchange_url which exchange is involved in this transfer. * @param exchange_url which exchange is involved in this transfer.
*
* @return the command. * @return the command.
*/ */
struct TALER_TESTING_Command struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer TALER_TESTING_cmd_fakebank_transfer
(const char *label, (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *payto_credit_account,
uint64_t credit_account_no,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *exchange_url) const char *exchange_url)
@ -503,9 +466,8 @@ TALER_TESTING_cmd_fakebank_transfer
struct FakebankTransferState *fts; struct FakebankTransferState *fts;
fts = GNUNET_new (struct FakebankTransferState); fts = GNUNET_new (struct FakebankTransferState);
fts->bank_url = bank_url; fts->debit_url = account_base_url;
fts->credit_account_no = credit_account_no; fts->payto_credit_account = payto_credit_account;
fts->debit_account_no = debit_account_no;
fts->auth_username = auth_username; fts->auth_username = auth_username;
fts->auth_password = auth_password; fts->auth_password = auth_password;
fts->exchange_url = exchange_url; fts->exchange_url = exchange_url;
@ -520,6 +482,7 @@ TALER_TESTING_cmd_fakebank_transfer
GNUNET_assert (0); GNUNET_assert (0);
} }
{
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
.cls = fts, .cls = fts,
.label = label, .label = label,
@ -530,77 +493,6 @@ TALER_TESTING_cmd_fakebank_transfer
return cmd; return cmd;
} }
/**
* Create "fakebank transfer" CMD, letting the caller specifying
* the subject line.
*
* @param label command label.
* @param amount amount to transfer.
* @param bank_url base URL of the bank that implements this
* wire transer. For simplicity, both credit and debit
* bank account exist at the same bank.
* @param debit_account_no which account (expressed as a number)
* gives money.
* @param credit_account_no which account (expressed as a number)
* receives money.
*
* @param auth_username username identifying the @a
* debit_account_no at the bank.
* @param auth_password password for @a auth_username.
* @param subject wire transfer's subject line.
* @param exchange_url which exchange is involved in this transfer.
*
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_subject
(const char *label,
const char *amount,
const char *bank_url,
uint64_t debit_account_no,
uint64_t credit_account_no,
const char *auth_username,
const char *auth_password,
const char *subject,
const char *exchange_url)
{
struct FakebankTransferState *fts;
fts = GNUNET_new (struct FakebankTransferState);
TALER_LOG_DEBUG ("%s:FTS@%p\n",
label,
fts);
fts->bank_url = bank_url;
fts->credit_account_no = credit_account_no;
fts->debit_account_no = debit_account_no;
fts->auth_username = auth_username;
fts->auth_password = auth_password;
fts->subject = subject;
fts->exchange_url = exchange_url;
if (GNUNET_OK !=
TALER_string_to_amount (amount,
&fts->amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %s\n",
amount,
label);
GNUNET_assert (0);
}
struct TALER_TESTING_Command cmd = {
.cls = fts,
.label = label,
.run = &fakebank_transfer_run,
.cleanup = &fakebank_transfer_cleanup,
.traits = &fakebank_transfer_traits
};
return cmd;
} }
@ -631,9 +523,8 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_ref TALER_TESTING_cmd_fakebank_transfer_with_ref
(const char *label, (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *payto_credit_account,
uint64_t credit_account_no,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *ref, const char *ref,
@ -642,9 +533,8 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref
struct FakebankTransferState *fts; struct FakebankTransferState *fts;
fts = GNUNET_new (struct FakebankTransferState); fts = GNUNET_new (struct FakebankTransferState);
fts->bank_url = bank_url; fts->debit_url = account_base_url;
fts->credit_account_no = credit_account_no; fts->payto_credit_account = payto_credit_account;
fts->debit_account_no = debit_account_no;
fts->auth_username = auth_username; fts->auth_username = auth_username;
fts->auth_password = auth_password; fts->auth_password = auth_password;
fts->reserve_reference = ref; fts->reserve_reference = ref;
@ -659,7 +549,7 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref
label); label);
GNUNET_assert (0); GNUNET_assert (0);
} }
{
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
.cls = fts, .cls = fts,
.label = label, .label = label,
@ -670,6 +560,7 @@ TALER_TESTING_cmd_fakebank_transfer_with_ref
return cmd; return cmd;
} }
}
/** /**
@ -705,9 +596,8 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_fakebank_transfer_with_instance TALER_TESTING_cmd_fakebank_transfer_with_instance
(const char *label, (const char *label,
const char *amount, const char *amount,
const char *bank_url, const char *account_base_url,
uint64_t debit_account_no, const char *payto_credit_account,
uint64_t credit_account_no,
const char *auth_username, const char *auth_username,
const char *auth_password, const char *auth_password,
const char *instance, const char *instance,
@ -717,9 +607,8 @@ TALER_TESTING_cmd_fakebank_transfer_with_instance
struct FakebankTransferState *fts; struct FakebankTransferState *fts;
fts = GNUNET_new (struct FakebankTransferState); fts = GNUNET_new (struct FakebankTransferState);
fts->bank_url = bank_url; fts->debit_url = account_base_url;
fts->credit_account_no = credit_account_no; fts->payto_credit_account = payto_credit_account;
fts->debit_account_no = debit_account_no;
fts->auth_username = auth_username; fts->auth_username = auth_username;
fts->auth_password = auth_password; fts->auth_password = auth_password;
fts->instance = instance; fts->instance = instance;
@ -735,7 +624,7 @@ TALER_TESTING_cmd_fakebank_transfer_with_instance
label); label);
GNUNET_assert (0); GNUNET_assert (0);
} }
{
struct TALER_TESTING_Command cmd = { struct TALER_TESTING_Command cmd = {
.cls = fts, .cls = fts,
.label = label, .label = label,
@ -746,6 +635,7 @@ TALER_TESTING_cmd_fakebank_transfer_with_instance
return cmd; return cmd;
} }
}
/** /**

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA Copyright (C) 2014-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as
@ -146,6 +146,7 @@ status_run (void *cls,
const struct TALER_TESTING_Command *create_reserve; const struct TALER_TESTING_Command *create_reserve;
const struct TALER_ReservePrivateKeyP *reserve_priv; const struct TALER_ReservePrivateKeyP *reserve_priv;
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
const struct TALER_ReservePublicKeyP *reserve_pubp;
ss->is = is; ss->is = is;
GNUNET_assert (NULL != ss->reserve_reference); GNUNET_assert (NULL != ss->reserve_reference);
@ -163,44 +164,31 @@ status_run (void *cls,
/* NOTE: the following line might generate a ERROR log /* NOTE: the following line might generate a ERROR log
* statements, but it can be ignored. */ * statements, but it can be ignored. */
if (GNUNET_OK == TALER_TESTING_get_trait_reserve_priv if (GNUNET_OK ==
(create_reserve, TALER_TESTING_get_trait_reserve_priv (create_reserve,
0, 0,
&reserve_priv)) &reserve_priv))
{ {
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&reserve_pub.eddsa_pub); &reserve_pub.eddsa_pub);
reserve_pubp = &reserve_pub;
} }
else else
{ {
const char *transfer_subject;
if (GNUNET_OK != TALER_TESTING_get_trait_transfer_subject
(create_reserve,
0,
&transfer_subject))
{
GNUNET_break (0);
TALER_LOG_ERROR ("The reserve has neither a priv nor a subject line.\n");
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (transfer_subject, TALER_TESTING_get_trait_reserve_pub (create_reserve,
strlen (transfer_subject), 0,
&reserve_pub.eddsa_pub, &reserve_pubp))
sizeof (struct TALER_ReservePublicKeyP)))
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_LOG_ERROR ("Transfer subject is not a public key.\n"); TALER_LOG_ERROR ("The reserve has neither a priv nor a pub.\n");
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
return; return;
} }
} }
ss->rsh = TALER_EXCHANGE_reserve_status (is->exchange, ss->rsh = TALER_EXCHANGE_reserve_status (is->exchange,
&reserve_pub, reserve_pubp,
&reserve_status_cb, &reserve_status_cb,
ss); ss);
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2014-2018 Taler Systems SA Copyright (C) 2014-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as it under the terms of the GNU General Public License as
@ -190,15 +190,11 @@ deposit_wtid_cb
if (NULL != tts->bank_transfer_reference) if (NULL != tts->bank_transfer_reference)
{ {
const struct TALER_TESTING_Command *bank_transfer_cmd; const struct TALER_TESTING_Command *bank_transfer_cmd;
char *ws; const struct TALER_WireTransferIdentifierRawP *wtid_want;
/* _this_ wire transfer subject line. */ /* _this_ wire transfer subject line. */
ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
sizeof (*wtid));
bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command bank_transfer_cmd = TALER_TESTING_interpreter_lookup_command
(is, tts->bank_transfer_reference); (is, tts->bank_transfer_reference);
if (NULL == bank_transfer_cmd) if (NULL == bank_transfer_cmd)
{ {
GNUNET_break (0); GNUNET_break (0);
@ -206,12 +202,9 @@ deposit_wtid_cb
return; return;
} }
/* expected wire transfer subject line. */
const char *transfer_subject;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_transfer_subject TALER_TESTING_get_trait_wtid
(bank_transfer_cmd, 0, &transfer_subject)) (bank_transfer_cmd, 0, &wtid_want))
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (is); TALER_TESTING_interpreter_fail (is);
@ -219,15 +212,13 @@ deposit_wtid_cb
} }
/* Compare that expected and gotten subjects match. */ /* Compare that expected and gotten subjects match. */
if (0 != strcmp (ws, transfer_subject)) if (0 != GNUNET_memcmp (wtid,
wtid_want))
{ {
GNUNET_break (0); GNUNET_break (0);
GNUNET_free (ws);
TALER_TESTING_interpreter_fail (tts->is); TALER_TESTING_interpreter_fail (tts->is);
return; return;
} }
GNUNET_free (ws);
} }
break; break;
case MHD_HTTP_ACCEPTED: case MHD_HTTP_ACCEPTED:

View File

@ -0,0 +1,77 @@
/*
This file is part of TALER
Copyright (C) 2018-2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 3, or
(at your option) any later version.
TALER is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public
License along with TALER; see the file COPYING. If not, see
<http://www.gnu.org/licenses/>
*/
/**
* @file exchange-lib/testing_api_trait_reserve_pub.c
* @brief implements reserve public key trait
* @author Christian Grothoff
* @author Marcello Stanisci
*/
#include "platform.h"
#include "taler_json_lib.h"
#include <gnunet/gnunet_curl_lib.h>
#include "exchange_api_handle.h"
#include "taler_signatures.h"
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY \
"reserve-public-key"
/**
* Obtain a reserve public key from a @a cmd.
*
* @param cmd command to extract the reserve pub from.
* @param index reserve pub's index number.
* @param reserve_pub[out] set to the reserve pub.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_reserve_pub
(const struct TALER_TESTING_Command *cmd,
unsigned int index,
const struct TALER_ReservePublicKeyP **reserve_pub)
{
return cmd->traits (cmd->cls,
(const void **) reserve_pub,
TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY,
index);
}
/**
* Offer a reserve public key.
*
* @param index reserve pub's index number.
* @param reserve_pub reserve public key to offer.
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_reserve_pub
(unsigned int index,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_RESERVE_PUBLIC_KEY,
.ptr = (const void *) reserve_pub
};
return ret;
}
/* end of testing_api_trait_reserve_pub.c */

View File

@ -30,7 +30,7 @@
#include "taler_testing_lib.h" #include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms" #define TALER_TESTING_TRAIT_CONTRACT_TERMS "contract-terms"
#define TALER_TESTING_TRAIT_TRANSFER_SUBJECT "transfer-subject" #define TALER_TESTING_TRAIT_STRING "string"
#define TALER_TESTING_TRAIT_AMOUNT "amount" #define TALER_TESTING_TRAIT_AMOUNT "amount"
#define TALER_TESTING_TRAIT_URL "url" #define TALER_TESTING_TRAIT_URL "url"
#define TALER_TESTING_TRAIT_ORDER_ID "order-id" #define TALER_TESTING_TRAIT_ORDER_ID "order-id"
@ -80,46 +80,45 @@ TALER_TESTING_make_trait_contract_terms
/** /**
* Obtain a transfer subject from @a cmd. * Obtain a string from @a cmd.
* *
* @param cmd command to extract the subject from. * @param cmd command to extract the subject from.
* @param index index number associated with the transfer * @param index index number associated with the transfer
* subject to offer. * subject to offer.
* @param transfer_subject[out] where to write the offered * @param s[out] where to write the offered
* transfer subject. * string
* @return #GNUNET_OK on success. * @return #GNUNET_OK on success.
*/ */
int int
TALER_TESTING_get_trait_transfer_subject TALER_TESTING_get_trait_string
(const struct TALER_TESTING_Command *cmd, (const struct TALER_TESTING_Command *cmd,
unsigned int index, unsigned int index,
const char **transfer_subject) const char **s)
{ {
return cmd->traits (cmd->cls, return cmd->traits (cmd->cls,
(const void **) transfer_subject, (const void **) s,
TALER_TESTING_TRAIT_TRANSFER_SUBJECT, TALER_TESTING_TRAIT_STRING,
index); index);
} }
/** /**
* Offer transfer subject. * Offer string.
* *
* @param index index number associated with the transfer * @param index index number associated with the transfer
* subject being offered. * subject being offered.
* @param transfer_subject transfer subject to offer. * @param s transfer subject to offer.
*
* @return the trait. * @return the trait.
*/ */
struct TALER_TESTING_Trait struct TALER_TESTING_Trait
TALER_TESTING_make_trait_transfer_subject TALER_TESTING_make_trait_string
(unsigned int index, (unsigned int index,
const char *transfer_subject) const char *s)
{ {
struct TALER_TESTING_Trait ret = { struct TALER_TESTING_Trait ret = {
.index = index, .index = index,
.trait_name = TALER_TESTING_TRAIT_TRANSFER_SUBJECT, .trait_name = TALER_TESTING_TRAIT_STRING,
.ptr = (const void *) transfer_subject .ptr = (const void *) s
}; };
return ret; return ret;
} }

View File

@ -1,81 +0,0 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
pkgcfgdir = $(prefix)/share/taler/config.d/
EXTRA_DIST = \
test_wire_plugin.conf \
test_wire_plugin_transactions_taler-bank.conf
plugindir = $(libdir)/taler
if HAVE_LIBCURL
plugin_LTLIBRARIES = \
libtaler_plugin_wire_taler_bank.la
else
if HAVE_LIBGNURL
plugin_LTLIBRARIES = \
libtaler_plugin_wire_taler_bank.la
endif
endif
noinst_LTLIBRARIES = \
libtaler_plugin_wire_template.la
libtaler_plugin_wire_taler_bank_la_SOURCES = \
plugin_wire_taler-bank.c
libtaler_plugin_wire_taler_bank_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_taler_bank_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetcurl \
-lgnunetutil $(XLIB)
libtaler_plugin_wire_template_la_SOURCES = \
plugin_wire_template.c
libtaler_plugin_wire_template_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_template_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil $(XLIB)
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
TESTS = \
test_wire_plugin \
test_wire_plugin_transactions_taler_bank
check_PROGRAMS= $(TESTS)
test_wire_plugin_SOURCES = \
test_wire_plugin.c
test_wire_plugin_LDADD = \
-lgnunetutil \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la
test_wire_plugin_transactions_taler_bank_SOURCES = \
test_wire_plugin_transactions_taler-bank.c
test_wire_plugin_transactions_taler_bank_LDADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/bank-lib/libtalerfakebank.la \
$(top_builddir)/src/util/libtalerutil.la

File diff suppressed because it is too large Load Diff

View File

@ -1,384 +0,0 @@
/*
This file is part of TALER
Copyright (C) 2016, 2018 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file plugin_wire_template.c
* @brief template for wire plugins; replace "template" with real plugin name!
* @author Florian Dold
* @author Christian Grothoff
* @author Sree Harsha Totakura
*/
#include "platform.h"
#include "taler_wire_plugin.h"
/**
* Type of the "cls" argument given to each of the functions in
* our API.
*/
struct TemplateClosure
{
/**
* Which currency do we support?
*/
char *currency;
/**
* Which configuration do we use to lookup accounts?
*/
struct GNUNET_CONFIGURATION_Handle *cfg;
};
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
* payment, but SEPA only supports 0.01 EUR. This function would
* round 0.125 EUR to 0.12 EUR in this case.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
static int
template_amount_round (void *cls,
struct TALER_Amount *amount)
{
struct TemplateClosure *tc = cls;
if (0 != strcasecmp (amount->currency,
tc->currency))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_break (0); // not implemented
return GNUNET_SYSERR;
}
/**
* Check if the given payto:// URL is correctly formatted for this plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_url the payto:// URL
* @return #TALER_EC_NONE if correctly formatted
*/
static enum TALER_ErrorCode
template_wire_validate (void *cls,
const char *account_url)
{
(void) cls;
(void) account_url;
GNUNET_break (0);
return TALER_EC_NOT_IMPLEMENTED;
}
/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param origin_account_section configuration section specifying the origin
* account of the exchange to use
* @param destination_account_url payto:// URL identifying where to send the money
* @param amount amount to transfer, already rounded
* @param exchange_base_url base URL of the exchange (for tracking)
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
* @return NULL on failure
*/
static struct TALER_WIRE_PrepareHandle *
template_prepare_wire_transfer (void *cls,
const char *origin_account_section,
const char *destination_account_url,
const struct TALER_Amount *amount,
const char *exchange_base_url,
const struct
TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls)
{
(void) cls;
(void) origin_account_section;
(void) destination_account_url;
(void) amount;
(void) exchange_base_url;
(void) wtid;
(void) ptc;
(void) ptc_cls;
GNUNET_break (0);
return NULL;
}
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
static void
template_prepare_wire_transfer_cancel (void *cls,
struct TALER_WIRE_PrepareHandle *pth)
{
(void) cls;
(void) pth;
GNUNET_break (0);
}
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
static struct TALER_WIRE_ExecuteHandle *
template_execute_wire_transfer (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls)
{
(void) cls;
(void) buf;
(void) buf_size;
(void) cc;
(void) cc_cls;
GNUNET_break (0);
return NULL;
}
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
static void
template_execute_wire_transfer_cancel (void *cls,
struct TALER_WIRE_ExecuteHandle *eh)
{
(void) cls;
(void) eh;
GNUNET_break (0);
}
/**
* Query transfer history of an account. We use the variable-size
* @a start_off to indicate which transfers we are interested in as
* different banking systems may have different ways to identify
* transfers. The @a start_off value must thus match the value of
* a `row_off` argument previously given to the @a hres_cb. Use
* NULL to query transfers from the beginning of time (with
* positive @a num_results) or from the latest committed transfers
* (with negative @a num_results).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param account_section specifies the configuration section which
* identifies the account for which we should get the history
* @param direction what kinds of wire transfers should be returned
* @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
* @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`.
* @param num_results how many results do we want; negative numbers to go into the past,
* positive numbers to go into the future starting at @a start_row;
* must not be zero.
* @param hres_cb the callback to call with the transaction history
* @param hres_cb_cls closure for the above callback
*/
static struct TALER_WIRE_HistoryHandle *
template_get_history (void *cls,
const char *account_section,
enum TALER_BANK_Direction direction,
const void *start_off,
size_t start_off_len,
int64_t num_results,
TALER_WIRE_HistoryResultCallback hres_cb,
void *hres_cb_cls)
{
(void) cls;
(void) account_section;
(void) direction;
(void) start_off;
(void) start_off_len;
(void) num_results;
(void) hres_cb;
(void) hres_cb_cls;
GNUNET_break (0);
return NULL;
}
/**
* Cancel going over the account's history.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param whh operation to cancel
*/
static void
template_get_history_cancel (void *cls,
struct TALER_WIRE_HistoryHandle *whh)
{
(void) cls;
(void) whh;
GNUNET_break (0);
}
/**
* Reject an incoming wire transfer that was obtained from the
* history. This function can be used to transfer funds back to
* the sender if the WTID was malformed (i.e. due to a typo).
*
* Calling `reject_transfer` twice on the same wire transfer should
* be idempotent, i.e. not cause the funds to be wired back twice.
* Furthermore, the transfer should henceforth be removed from the
* results returned by @e get_history.
*
* @param cls plugin's closure
* @param account_section specifies the configuration section which
* identifies the account to use to reject the transfer
* @param start_off offset of the wire transfer in plugin-specific format
* @param start_off_len number of bytes in @a start_off
* @param rej_cb function to call with the result of the operation
* @param rej_cb_cls closure for @a rej_cb
* @return handle to cancel the operation
*/
static struct TALER_WIRE_RejectHandle *
template_reject_transfer (void *cls,
const char *account_section,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls)
{
(void) cls;
(void) account_section;
(void) start_off;
(void) start_off_len;
(void) rej_cb;
(void) rej_cb_cls;
GNUNET_break (0);
return NULL;
}
/**
* Cancel ongoing reject operation. Note that the rejection may still
* proceed. Basically, if this function is called, the rejection may
* have happened or not. This function is usually used during shutdown
* or system upgrades. At a later point, the application must call
* @e reject_transfer again for this wire transfer, unless the
* @e get_history shows that the wire transfer no longer exists.
*
* @param cls plugins' closure
* @param rh operation to cancel
* @return closure of the callback of the operation
*/
static void *
template_reject_transfer_cancel (void *cls,
struct TALER_WIRE_RejectHandle *rh)
{
(void) cls;
(void) rh;
GNUNET_break (0);
return NULL;
}
/**
* Initialize template-wire subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
*/
void *
libtaler_plugin_wire_template_init (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct TemplateClosure *tc;
struct TALER_WIRE_Plugin *plugin;
tc = GNUNET_new (struct TemplateClosure);
tc->cfg = cfg;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"taler",
"CURRENCY",
&tc->currency))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"taler",
"CURRENCY");
GNUNET_free (tc);
return NULL;
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->method = "FIXME-REPLACE-BY-METHOD";
plugin->amount_round = &template_amount_round;
plugin->wire_validate = &template_wire_validate;
plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &template_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel;
plugin->get_history = &template_get_history;
plugin->get_history_cancel = &template_get_history_cancel;
plugin->reject_transfer = &template_reject_transfer;
plugin->reject_transfer_cancel = &template_reject_transfer_cancel;
return plugin;
}
/**
* Shutdown Template wire subsystem.
*
* @param cls a `struct TALER_WIRE_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_wire_template_done (void *cls)
{
struct TALER_WIRE_Plugin *plugin = cls;
struct TemplateClosure *tc = plugin->cls;
GNUNET_free (tc->currency);
GNUNET_free (tc);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_wire_template.c */

View File

@ -1,189 +0,0 @@
/*
This file is part of TALER
(C) 2015, 2016, 2017 GNUnet e.V. and Inria
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file wire/test_wire_plugin.c
* @brief Tests for wire plugins
* @author Christian Grothoff
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
#include "taler_wire_plugin.h"
#include <gnunet/gnunet_json_lib.h>
/**
* Definitions for a test with a plugin.
*/
struct TestBlock
{
/**
* Name of the plugin to test.
*/
const char *plugin_name;
/**
* Amount to give to the rounding function.
*/
const char *round_in;
/**
* Expected result from rounding.
*/
const char *round_out;
/**
* Currency to give to the plugin.
*/
const char *currency;
};
/**
* List of plugins and (unsigned) JSON account definitions
* to use for the tests.
*/
static struct TestBlock tests[] = {
#if HAVE_LIBCURL
{
.plugin_name = "taler_bank",
.round_in = "KUDOS:0.123456",
.round_out = "KUDOS:0.12",
.currency = "KUDOS"
},
#endif
{
NULL, NULL, NULL, NULL
}
};
/**
* Our configuration.
*/
static struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Run the test.
*
* @param test details of the test
* @param plugin plugin to test
* @return #GNUNET_OK on success
*/
static int
run_test (const struct TestBlock *test,
struct TALER_WIRE_Plugin *plugin)
{
struct GNUNET_HashCode salt;
struct TALER_Amount in;
struct TALER_Amount expect;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&salt,
sizeof (salt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (test->round_in,
&in));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (test->round_out,
&expect));
if (GNUNET_OK !=
plugin->amount_round (plugin->cls,
&in))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (0 != TALER_amount_cmp (&in, &expect))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_NO !=
plugin->amount_round (plugin->cls,
&in))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
memset (&in, 0, sizeof (in));
GNUNET_log_skip (GNUNET_ERROR_TYPE_ERROR, 1);
if (GNUNET_SYSERR !=
plugin->amount_round (plugin->cls,
&in))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
int
main (int argc,
const char *const argv[])
{
int ret;
struct TALER_WIRE_Plugin *plugin;
const struct TestBlock *test;
GNUNET_log_setup ("test-wire-plugin",
"WARNING",
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_load (cfg,
"test_wire_plugin.conf"));
ret = GNUNET_OK;
for (unsigned int i = 0; NULL != (test = &tests[i])->plugin_name; i++)
{
GNUNET_CONFIGURATION_set_value_string (cfg,
"taler",
"CURRENCY",
test->currency);
plugin = TALER_WIRE_plugin_load (cfg,
test->plugin_name);
if (NULL == plugin)
{
TALER_LOG_ERROR ("Could not load plugin `%s'\n",
test->plugin_name);
return 77;
}
ret = run_test (test, plugin);
TALER_WIRE_plugin_unload (plugin);
if (GNUNET_OK != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"%s FAILED\n",
test->plugin_name);
break;
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
"%s PASS\n",
test->plugin_name);
}
}
GNUNET_CONFIGURATION_destroy (cfg);
if (GNUNET_OK != ret)
return 1;
return 0;
}

View File

@ -1,25 +0,0 @@
# This file is in the public domain.
#
[account-taler-bank]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_JSON = test_wire_plugin_test.json
# Our bank account URL
URL = payto://x-taler-bank/2
# Which wire plugin should we used to access the account?
PLUGIN = taler_bank
[account-sepa]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
WIRE_JSON = test_wire_plugin_sepa.json
# Which wire plugin should we used to access the account?
PLUGIN = ebics
[taler]
CURRENCY = "EUR"

View File

@ -1,365 +0,0 @@
/*
This file is part of TALER
(C) 2015-2018 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file wire/test_wire_plugin_transactions_taler-bank.c
* @brief Tests performing actual transactions with the taler-bank wire plugin against FAKEBANK
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
#include "taler_wire_plugin.h"
#include "taler_fakebank_lib.h"
#include <gnunet/gnunet_json_lib.h>
/**
* When does the test timeout? Right now, we expect this to be very
* fast.
*/
#define TIMEOUT GNUNET_TIME_UNIT_SECONDS
/**
* Destination account to use.
*/
static const char *dest_account = "payto://x-taler-bank/localhost:8088/42";
/**
* Origin account, section in the configuration file.
*/
static const char *my_account = "account-test";
/**
* Our configuration.
*/
static struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Set to #GNUNET_SYSERR if the test failed.
*/
static int global_ret;
/**
* The 'test' plugin that we are using for the test.
*/
static struct TALER_WIRE_Plugin *plugin;
/**
* Active preparation handle, or NULL if not active.
*/
static struct TALER_WIRE_PrepareHandle *ph;
/**
* Active execution handle, or NULL if not active.
*/
static struct TALER_WIRE_ExecuteHandle *eh;
/**
* Handle to the bank.
*/
static struct TALER_FAKEBANK_Handle *fb;
/**
* Handle to the history request.
*/
static struct TALER_WIRE_HistoryHandle *hh;
/**
* Handle to the history-range request (the "legacy" bank API).
*/
static struct TALER_WIRE_HistoryHandle *hhr;
/**
* Handle for the timeout task.
*/
static struct GNUNET_SCHEDULER_Task *tt;
/**
* Which serial ID do we expect to get from /history?
*/
static uint64_t serial_target;
/**
* Wire transfer identifier we are using.
*/
static struct TALER_WireTransferIdentifierRawP wtid;
/**
* Function called on shutdown (regular, error or CTRL-C).
*
* @param cls NULL
*/
static void
do_shutdown (void *cls)
{
(void) cls;
TALER_FAKEBANK_stop (fb);
fb = NULL;
if (NULL != eh)
{
plugin->execute_wire_transfer_cancel (plugin->cls,
eh);
eh = NULL;
}
if (NULL != ph)
{
plugin->prepare_wire_transfer_cancel (plugin->cls,
ph);
ph = NULL;
}
if (NULL != hh)
{
plugin->get_history_cancel (plugin->cls,
hh);
hh = NULL;
}
if (NULL != hhr)
{
plugin->get_history_cancel (plugin->cls,
hhr);
hhr = NULL;
}
if (NULL != tt)
{
GNUNET_SCHEDULER_cancel (tt);
tt = NULL;
}
TALER_WIRE_plugin_unload (plugin);
}
/**
* Function called on timeout.
*
* @param cls NULL
*/
static void
timeout_cb (void *cls)
{
tt = NULL;
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
}
/**
* Callbacks of this type are used to serve the result of asking
* the bank for the transaction history.
*
* @param cls closure
* @param ec taler status code
* @param dir direction of the transfer
* @param row_off identification of the position at
* which we are querying
* @param row_off_size number of bytes in @a row_off
* @param details details about the wire transfer
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to
* abort iteration
*/
static int
history_result_cb
(void *cls,
enum TALER_ErrorCode ec,
enum TALER_BANK_Direction dir,
const void *row_off,
size_t row_off_size,
const struct TALER_WIRE_TransferDetails *details)
{
uint64_t *serialp;
uint64_t serialh;
struct TALER_Amount amount;
if ( (TALER_BANK_DIRECTION_NONE == dir) &&
(GNUNET_OK == global_ret) )
{
GNUNET_SCHEDULER_shutdown ();
hh = NULL;
return GNUNET_OK;
}
if (sizeof (uint64_t) != row_off_size)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
serialp = (uint64_t *) row_off;
serialh = GNUNET_ntohll (*serialp);
if (serialh != serial_target)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("KUDOS:5.01",
&amount));
if (0 != TALER_amount_cmp (&amount,
&details->amount))
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
if (0 != GNUNET_memcmp (&wtid,
&details->wtid))
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return GNUNET_SYSERR;
}
global_ret = GNUNET_OK;
return GNUNET_OK;
}
/**
* Function called with the result from the execute step.
*
* @param cls closure
* @param success #GNUNET_OK on success,
* #GNUNET_SYSERR on failure
* @param row_id ID of the fresh transaction,
* in _network_ byte order.
* @param emsg NULL on success, otherwise an error message
*/
static void
confirmation_cb (void *cls,
int success,
const void *row_id,
size_t row_id_size,
const char *emsg)
{
uint64_t tmp;
eh = NULL;
if (GNUNET_OK != success)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return;
}
memcpy (&tmp,
row_id,
row_id_size);
serial_target = GNUNET_ntohll (tmp);
hh = plugin->get_history (plugin->cls,
my_account,
TALER_BANK_DIRECTION_BOTH,
NULL,
0,
5,
&history_result_cb,
NULL);
}
/**
* Callback with prepared transaction.
*
* @param cls closure
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size)
{
ph = NULL;
if (NULL == buf)
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
return;
}
plugin->execute_wire_transfer (plugin->cls,
buf,
buf_size,
&confirmation_cb,
NULL);
}
/**
* Run the test.
*
* @param cls NULL
*/
static void
run (void *cls)
{
struct TALER_Amount amount;
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
NULL);
tt = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
&timeout_cb,
NULL);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&wtid,
sizeof (wtid));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("KUDOS:5.01",
&amount));
fb = TALER_FAKEBANK_start (8088);
ph = plugin->prepare_wire_transfer (plugin->cls,
my_account,
dest_account,
&amount,
"https://exchange.net/",
&wtid,
&prepare_cb,
NULL);
}
int
main (int argc,
const char *const argv[])
{
GNUNET_log_setup ("test-wire-plugin-transactions-test",
"WARNING",
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_assert (GNUNET_OK ==
GNUNET_CONFIGURATION_load (cfg,
"test_wire_plugin_transactions_taler-bank.conf"));
global_ret = GNUNET_OK;
plugin = TALER_WIRE_plugin_load (cfg,
"taler_bank");
GNUNET_assert (NULL != plugin);
GNUNET_SCHEDULER_run (&run,
NULL);
GNUNET_CONFIGURATION_destroy (cfg);
if (GNUNET_OK != global_ret)
return 1;
return 0;
}
/* end of test_wire_plugin_transactions_taler-bank.c */

View File

@ -1,12 +0,0 @@
# This file is in the public domain.
#
[account-test]
# This is the response we give out for the /wire request. It provides
# wallets with the bank information for transfers to the exchange.
TALER_BANK_AUTH_METHOD = NONE
URL = payto://x-taler-bank/localhost:8088/2
[taler]
CURRENCY = "KUDOS"

View File

@ -13,7 +13,6 @@ lib_LTLIBRARIES = \
libtalerwire_la_SOURCES = \ libtalerwire_la_SOURCES = \
payto.c \ payto.c \
wire.c \
wire_helper.c wire_helper.c
libtalerwire_la_LIBADD = \ libtalerwire_la_LIBADD = \
-lgnunetutil \ -lgnunetutil \

View File

@ -20,6 +20,7 @@
*/ */
#include "platform.h" #include "platform.h"
#include "taler_util.h" #include "taler_util.h"
#include "taler_bank_service.h"
#include "taler_wire_lib.h" #include "taler_wire_lib.h"
/** /**
@ -43,8 +44,8 @@ TALER_WIRE_account_free (struct TALER_Account *acc)
case TALER_PAC_X_TALER_BANK: case TALER_PAC_X_TALER_BANK:
GNUNET_free (acc->details.x_taler_bank.hostname); GNUNET_free (acc->details.x_taler_bank.hostname);
acc->details.x_taler_bank.hostname = NULL; acc->details.x_taler_bank.hostname = NULL;
GNUNET_free (acc->details.x_taler_bank.bank_base_url); GNUNET_free (acc->details.x_taler_bank.account_base_url);
acc->details.x_taler_bank.bank_base_url = NULL; acc->details.x_taler_bank.account_base_url = NULL;
break; break;
case TALER_PAC_IBAN: case TALER_PAC_IBAN:
GNUNET_free (acc->details.iban.number); GNUNET_free (acc->details.iban.number);
@ -410,7 +411,8 @@ parse_payto_x_taler_bank (const char *account_url,
const char *hostname; const char *hostname;
const char *account; const char *account;
const char *q; const char *q;
unsigned long long no; unsigned int port;
char *p;
#define PREFIX "payto://x-taler-bank/" #define PREFIX "payto://x-taler-bank/"
if (0 != strncasecmp (account_url, if (0 != strncasecmp (account_url,
@ -422,47 +424,22 @@ parse_payto_x_taler_bank (const char *account_url,
(unsigned char) '/'))) (unsigned char) '/')))
return TALER_EC_PAYTO_MALFORMED; return TALER_EC_PAYTO_MALFORMED;
account++; account++;
if (NULL != (q = strchr (account, if (NULL == r_account)
(unsigned char) '?'))) return TALER_EC_NONE;
{ q = strchr (account,
char *s; (unsigned char) '?');
if (0 == q)
s = GNUNET_strndup (account, q = account + strlen (account);
q - account);
if (1 != sscanf (s,
"%llu",
&no))
{
GNUNET_free (s);
return TALER_EC_PAYTO_MALFORMED;
}
GNUNET_free (s);
}
else if (1 != sscanf (account,
"%llu",
&no))
{
return TALER_EC_PAYTO_MALFORMED;
}
if (no > MAX_ACCOUNT_NO)
return TALER_EC_PAYTO_MALFORMED;
if (NULL != r_account)
{
long long unsigned port;
char *p;
r_account->details.x_taler_bank.hostname r_account->details.x_taler_bank.hostname
= GNUNET_strndup (hostname, = GNUNET_strndup (hostname,
account - hostname); account - hostname);
r_account->details.x_taler_bank.no = no;
port = 443; /* if non given, equals 443. */ port = 443; /* if non given, equals 443. */
if (NULL != (p = strchr (r_account->details.x_taler_bank.hostname, if (NULL != (p = strchr (r_account->details.x_taler_bank.hostname,
(unsigned char) ':'))) (unsigned char) ':')))
{ {
p++; p++;
if (1 != sscanf (p, if (1 != sscanf (p,
"%llu", "%u",
&port)) &port))
{ {
GNUNET_break (0); GNUNET_break (0);
@ -476,20 +453,23 @@ parse_payto_x_taler_bank (const char *account_url,
{ {
GNUNET_assert GNUNET_assert
(GNUNET_SYSERR != GNUNET_asprintf (GNUNET_SYSERR != GNUNET_asprintf
(&r_account->details.x_taler_bank.bank_base_url, (&r_account->details.x_taler_bank.account_base_url,
"http://%s", "http://%s/%.*s",
r_account->details.x_taler_bank.hostname)); r_account->details.x_taler_bank.hostname,
(int) (q - account),
account));
} }
else else
{ {
GNUNET_assert GNUNET_assert
(GNUNET_SYSERR != GNUNET_asprintf (GNUNET_SYSERR != GNUNET_asprintf
(&r_account->details.x_taler_bank.bank_base_url, (&r_account->details.x_taler_bank.account_base_url,
"https://%s", "https://%s/%.*s",
r_account->details.x_taler_bank.hostname)); r_account->details.x_taler_bank.hostname,
(int) (q - account),
account));
} }
r_account->type = TALER_PAC_X_TALER_BANK; r_account->type = TALER_PAC_X_TALER_BANK;
}
return TALER_EC_NONE; return TALER_EC_NONE;
} }

View File

@ -1,149 +0,0 @@
/*
This file is part of TALER
(C) 2015, 2016, 2017, 2018 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file wire/wire.c
* @brief Functions for loading wire plugins
* @author Christian Grothoff <christian@grothoff.org>
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
/**
* A wire plugin that we have loaded.
*/
struct WirePlugin
{
/**
* We keep these in a DLL.
*/
struct WirePlugin *next;
/**
* We keep these in a DLL.
*/
struct WirePlugin *prev;
/**
* Type of this wire plugin.
*/
char *type;
/**
* Wire plugin
*/
struct TALER_WIRE_Plugin *plugin;
/**
* Reference counter for the plugin.
*/
unsigned int rc;
};
/**
* Head of the DLL of loaded wire plugins.
*/
static struct WirePlugin *wp_head;
/**
* Tail of the DLL of loaded wire plugins.
*/
static struct WirePlugin *wp_tail;
/**
* Load a WIRE plugin.
*
* @param cfg configuration to use
* @param plugin_name name of the plugin to load
* @return the plugin object pointer, or NULL upon errors.
*/
struct TALER_WIRE_Plugin *
TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *plugin_name)
{
char *lib_name;
struct TALER_WIRE_Plugin *plugin;
struct WirePlugin *wp;
for (wp = wp_head; NULL != wp; wp = wp->next)
if (0 == strcasecmp (plugin_name,
wp->type))
{
wp->rc++;
return wp->plugin;
}
(void) GNUNET_asprintf (&lib_name,
"libtaler_plugin_wire_%s",
plugin_name);
plugin = GNUNET_PLUGIN_load (lib_name,
(void *) cfg);
if (NULL != plugin)
plugin->library_name = lib_name;
else
GNUNET_free (lib_name);
if (NULL == plugin)
return NULL;
wp = GNUNET_new (struct WirePlugin);
wp->plugin = plugin;
wp->type = GNUNET_strdup (plugin_name);
GNUNET_CONTAINER_DLL_insert (wp_head,
wp_tail,
wp);
wp->rc = 1;
return plugin;
}
/**
* Unload a WIRE plugin.
*
* @param plugin the plugin to unload
*/
void
TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin)
{
struct WirePlugin *wp;
char *lib_name;
if (NULL == plugin)
return;
for (wp = wp_head; NULL != wp; wp = wp->next)
{
if (plugin == wp->plugin)
{
wp->rc--;
if (0 < wp->rc)
return;
GNUNET_CONTAINER_DLL_remove (wp_head,
wp_tail,
wp);
GNUNET_free (wp->type);
GNUNET_free (wp);
break;
}
}
lib_name = plugin->library_name;
GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
plugin));
GNUNET_free (lib_name);
}
/* end of wire.c */

View File

@ -20,6 +20,7 @@
/** /**
* @file wire/wire_helper.c * @file wire/wire_helper.c
* @brief Helper functions for dealing with wire formats * @brief Helper functions for dealing with wire formats
* @author Christian Grothoff <christian@grothoff.org> * @author Christian Grothoff <christian@grothoff.org>
*/ */
#include "platform.h" #include "platform.h"
@ -32,23 +33,6 @@
#define PAYTO "payto://" #define PAYTO "payto://"
/**
* Maps wire methods to plugin names.
*/
struct ConversionTable
{
/**
* Wire method (e.g. 'iban', 'x-taler-bank', ..)
*/
const char *method;
/**
* Plugin name, e.g. 'taler_bank', ..
*/
const char *plugin_name;
};
/** /**
* Obtain the payment method from a @a payto_url * Obtain the payment method from a @a payto_url
* *
@ -76,35 +60,23 @@ TALER_WIRE_payto_get_method (const char *payto_url)
/** /**
* Get the plugin name from the payment method. * Round the amount to something that can be
* transferred on the wire.
* *
* FIXME: this is ugly, would be better to have * @param[in,out] amount amount to round down
* a way to iterate over all plugins and interrogate * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* them as to what wire method(s) they support! * #GNUNET_SYSERR if the amount or currency was invalid
*
* @param method the method implemented by the plugin (for
* simplicity, we assume 1 method is implemented by 1 plugin).
* @return the plugin name, NULL if not found.
*/ */
const char * int
TALER_WIRE_get_plugin_from_method (const char *method) TALER_WIRE_amount_round (struct TALER_Amount *amount)
{ {
static const struct ConversionTable ct[] = { uint32_t delta;
{"x-taler-bank", "taler_bank"},
{"iban", "taler_bank"},
{NULL, NULL}
};
for (unsigned int i = 0; delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
NULL != ct[i].method; if (0 == delta)
i++) return GNUNET_NO;
{ amount->fraction -= delta;
if (0 == strcmp (method, return GNUNET_OK;
ct[i].method))
return ct[i].plugin_name;
}
return NULL;
} }