work on #5077: reserve_pub vs. wtid issue, add reject functionality to wire plugin API (with stub implementations for now)

This commit is contained in:
Christian Grothoff 2017-11-12 15:46:52 +01:00
parent f299130c50
commit 8440de1333
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
7 changed files with 503 additions and 72 deletions

View File

@ -70,7 +70,8 @@ static struct GNUNET_CONTAINER_MultiHashMap *in_map;
/** /**
* Map with information about outgoing wire transfers. * Map with information about outgoing wire transfers.
* Maps hashes of the wire offsets to `struct ReserveOutInfo`s. * Maps hashes of the wire subjects (in binary encoding)
* to `struct ReserveOutInfo`s.
*/ */
static struct GNUNET_CONTAINER_MultiHashMap *out_map; static struct GNUNET_CONTAINER_MultiHashMap *out_map;
@ -658,7 +659,9 @@ complain_out_not_found (void *cls,
"row", (json_int_t) 0, "row", (json_int_t) 0,
"amount_wired", TALER_JSON_from_amount (&roi->details.amount), "amount_wired", TALER_JSON_from_amount (&roi->details.amount),
"amount_justified", TALER_JSON_from_amount (&zero), "amount_justified", TALER_JSON_from_amount (&zero),
"wtid", GNUNET_JSON_from_data_auto (&roi->details.reserve_pub), /* #5077 missnomer */ "wtid", (NULL == roi->details.wtid_s)
? GNUNET_JSON_from_data_auto (&roi->details.wtid)
: json_string (roi->details.wtid_s),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (roi->details.execution_date), "timestamp", GNUNET_STRINGS_absolute_time_to_string (roi->details.execution_date),
"diagnostic", "justification for wire transfer not found")); "diagnostic", "justification for wire transfer not found"));
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
@ -726,6 +729,7 @@ history_debit_cb (void *cls,
const struct TALER_WIRE_TransferDetails *details) const struct TALER_WIRE_TransferDetails *details)
{ {
struct ReserveOutInfo *roi; struct ReserveOutInfo *roi;
struct GNUNET_HashCode rowh;
if (TALER_BANK_DIRECTION_NONE == dir) if (TALER_BANK_DIRECTION_NONE == dir)
{ {
@ -735,13 +739,36 @@ history_debit_cb (void *cls,
check_exchange_wire_out (); check_exchange_wire_out ();
return GNUNET_OK; return GNUNET_OK;
} }
if (NULL != details->wtid_s)
{
char *diagnostic;
GNUNET_CRYPTO_hash (row_off,
row_off_size,
&rowh);
GNUNET_asprintf (&diagnostic,
"malformed wire transfer subject `%s'",
details->wtid_s);
report (report_row_inconsistencies,
json_pack ("{s:s, s:I, s:o, s:o, s:s}",
"table", "bank wire log",
"row", (json_int_t) 0,
"amount", TALER_JSON_from_amount (&details->amount),
"wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
"diagnostic", diagnostic));
/* TODO: report generator currently ignores 'amount' for this
table, maybe use a different table to report this issue! */
/* TODO: add 'amount' to some total amount that was badly wired! */
GNUNET_free (diagnostic);
return GNUNET_SYSERR;
}
roi = GNUNET_new (struct ReserveOutInfo); roi = GNUNET_new (struct ReserveOutInfo);
GNUNET_CRYPTO_hash (&details->reserve_pub, /* FIXME (#5077): missnomer */ GNUNET_CRYPTO_hash (&details->wtid,
sizeof (details->reserve_pub), sizeof (details->wtid),
&roi->subject_hash); &roi->subject_hash);
roi->details.amount = details->amount; roi->details.amount = details->amount;
roi->details.execution_date = details->execution_date; roi->details.execution_date = details->execution_date;
roi->details.reserve_pub = details->reserve_pub; /* FIXME (#5077): missnomer & redundant */ roi->details.wtid = details->wtid;
roi->details.account_details = json_incref ((json_t *) details->account_details); roi->details.account_details = json_incref ((json_t *) details->account_details);
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (out_map, GNUNET_CONTAINER_multihashmap_put (out_map,
@ -749,13 +776,25 @@ history_debit_cb (void *cls,
roi, roi,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{ {
GNUNET_break_op (0); /* duplicate wire offset is not allowed! */ char *diagnostic;
GNUNET_CRYPTO_hash (row_off,
row_off_size,
&rowh);
GNUNET_asprintf (&diagnostic,
"duplicate wire transfer subject `%s'",
TALER_B2S (&roi->subject_hash));
report (report_row_inconsistencies, report (report_row_inconsistencies,
json_pack ("{s:s, s:I, s:o, s:s}", json_pack ("{s:s, s:I, s:o, s:o, s:s}",
"table", "bank wire log", "table", "bank wire log",
"row", (json_int_t) 0, "row", (json_int_t) 0,
"wire_offset_hash", GNUNET_JSON_from_data_auto (&roi->subject_hash), "amount", TALER_JSON_from_amount (&details->amount),
"diagnostic", "duplicate wire offset")); "wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
"diagnostic", diagnostic));
/* TODO: report generator currently ignores 'amount' for this
table, maybe use a different table to report this issue! */
/* TODO: add 'amount' to some total amount that was badly wired! */
GNUNET_free (diagnostic);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
return GNUNET_OK; return GNUNET_OK;
@ -830,7 +869,12 @@ reserve_in_cb (void *cls,
rii->row_off_size = wire_reference_size; 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;
rii->details.reserve_pub = *reserve_pub; /* reserve public key should be the WTID */
GNUNET_assert (sizeof (rii->details.wtid) ==
sizeof (*reserve_pub));
memcpy (&rii->details.wtid,
reserve_pub,
sizeof (*reserve_pub));
rii->details.account_details = json_incref ((json_t *) sender_account_details); rii->details.account_details = json_incref ((json_t *) sender_account_details);
rii->rowid = rowid; rii->rowid = rowid;
if (GNUNET_OK != if (GNUNET_OK !=
@ -875,7 +919,7 @@ complain_in_not_found (void *cls,
"row", (json_int_t) rii->rowid, "row", (json_int_t) rii->rowid,
"amount_expected", TALER_JSON_from_amount (&rii->details.amount), "amount_expected", TALER_JSON_from_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.reserve_pub), /* also reserve_pub, but see #5077 missnomer */ "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date), "timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
"diagnostic", "incoming wire transfer claimed by exchange not found")); "diagnostic", "incoming wire transfer claimed by exchange not found"));
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
@ -966,16 +1010,16 @@ history_credit_cb (void *cls,
"diagnostic", "wire reference size missmatch")); "diagnostic", "wire reference size missmatch"));
return GNUNET_OK; return GNUNET_OK;
} }
if (0 != memcmp (&details->reserve_pub, if (0 != memcmp (&details->wtid,
&rii->details.reserve_pub, &rii->details.wtid,
sizeof (struct TALER_ReservePublicKeyP))) sizeof (struct TALER_WireTransferIdentifierRawP)))
{ {
report (report_reserve_in_inconsistencies, report (report_reserve_in_inconsistencies,
json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}", json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
"row", GNUNET_JSON_from_data (row_off, row_off_size), "row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount), "amount_exchange_expected", TALER_JSON_from_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.reserve_pub), /* #5077 missnomer */ "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date), "timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
"diagnostic", "wire subject does not match")); "diagnostic", "wire subject does not match"));
GNUNET_break (GNUNET_OK == GNUNET_break (GNUNET_OK ==
@ -987,7 +1031,7 @@ history_credit_cb (void *cls,
"row", GNUNET_JSON_from_data (row_off, row_off_size), "row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&zero), "amount_exchange_expected", TALER_JSON_from_amount (&zero),
"amount_wired", TALER_JSON_from_amount (&details->amount), "amount_wired", TALER_JSON_from_amount (&details->amount),
"wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */ "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date), "timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
"diagnostic", "wire subject does not match")); "diagnostic", "wire subject does not match"));
@ -1005,7 +1049,7 @@ history_credit_cb (void *cls,
"row", GNUNET_JSON_from_data (row_off, row_off_size), "row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount), "amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&details->amount), "amount_wired", TALER_JSON_from_amount (&details->amount),
"wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */ "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date), "timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
"diagnostic", "wire amount does not match")); "diagnostic", "wire amount does not match"));
if (0 < TALER_amount_cmp (&details->amount, if (0 < TALER_amount_cmp (&details->amount,
@ -1046,7 +1090,7 @@ history_credit_cb (void *cls,
json_pack ("{s:s, s:o, s:o}", json_pack ("{s:s, s:o, s:o}",
"amount", TALER_JSON_from_amount (&rii->details.amount), "amount", TALER_JSON_from_amount (&rii->details.amount),
"row", GNUNET_JSON_from_data (row_off, row_off_size), "row", GNUNET_JSON_from_data (row_off, row_off_size),
"wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub))); /* FIXME #5077 missnomer */ "wtid", GNUNET_JSON_from_data_auto (&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,

View File

@ -36,6 +36,23 @@
#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;
};
/** /**
* Handle to the plugin. * Handle to the plugin.
*/ */
@ -109,6 +126,11 @@ static struct GNUNET_SCHEDULER_Task *task;
*/ */
static struct TALER_WIRE_HistoryHandle *hh; static struct TALER_WIRE_HistoryHandle *hh;
/**
* Active request to reject a wire transfer.
*/
static struct TALER_WIRE_RejectHandle *rt;
/** /**
* We're being aborted with CTRL-C (or SIGTERM). Shut down. * We're being aborted with CTRL-C (or SIGTERM). Shut down.
@ -129,6 +151,15 @@ shutdown_task (void *cls)
hh); hh);
hh = NULL; hh = NULL;
} }
if (NULL != rt)
{
char *wtid_s;
wtid_s = wire_plugin->reject_transfer_cancel (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;
TALER_WIRE_plugin_unload (wire_plugin); TALER_WIRE_plugin_unload (wire_plugin);
@ -204,6 +235,48 @@ 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)
{
fprintf (stderr,
"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_free (rtc);
if (0 <= qs)
{
GNUNET_free_non_null (start_off);
start_off = last_row_off;
start_off_size = last_row_off_size;
}
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.
@ -224,6 +297,7 @@ history_cb (void *cls,
{ {
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;
if (TALER_BANK_DIRECTION_NONE == dir) if (TALER_BANK_DIRECTION_NONE == dir)
{ {
@ -254,13 +328,45 @@ 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_INFO,
"Wire transfer over %s has invalid subject `%s', sending it back!\n",
TALER_amount2s (&details->amount),
details->wtid_s);
if (last_row_off_size != row_off_size)
{
GNUNET_free_non_null (last_row_off);
last_row_off = GNUNET_malloc (row_off_size);
}
memcpy (last_row_off,
row_off,
row_off_size);
rtc = GNUNET_new (struct RejectContext);
rtc->session = session;
rtc->wtid_s = GNUNET_strdup (details->wtid_s);
rt = wire_plugin->reject_transfer (wire_plugin->cls,
row_off,
row_off_size,
&reject_cb,
rtc);
return GNUNET_SYSERR; /* will continue later... */
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Adding wire transfer over %s with subject `%s'\n", "Adding wire transfer over %s with subject `%s'\n",
TALER_amount2s (&details->amount), TALER_amount2s (&details->amount),
TALER_B2S (&details->reserve_pub)); TALER_B2S (&details->wtid));
/* 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,
&details->reserve_pub, &reserve_pub,
&details->amount, &details->amount,
details->execution_date, details->execution_date,
details->account_details, details->account_details,

View File

@ -63,6 +63,10 @@ enum TALER_ErrorCode
*/ */
TALER_EC_INTERNAL_INVARIANT_FAILURE = 5, TALER_EC_INTERNAL_INVARIANT_FAILURE = 5,
/**
* Operation timed out.
*/
TALER_EC_TIMEOUT = 6,
/* ********** generic error codes ************* */ /* ********** generic error codes ************* */

View File

@ -59,13 +59,17 @@ struct TALER_WIRE_TransferDetails
struct GNUNET_TIME_Absolute execution_date; struct GNUNET_TIME_Absolute execution_date;
/** /**
* Reserve public key that was encoded in the wire transfer subject. * Binary data that was encoded in the wire transfer subject, if
* FIXME (#5077): this is incorrect for *outgoing* wire transfers. * it decoded properly. Otherwise all-zeros and @e wtid_s is set.
* Maybe use `struct TALER_WireTransferIdentifierRawP` here instead?
* OTOH, we might want to make this even more generic in case of
* invalid transfers, so that we can capture those as well!
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_WireTransferIdentifierRawP wtid;
/**
* Wire transfer identifer as a string. Set to NULL if the
* identifier was properly Base32 encoded and this @e wtid could be
* set instead.
*/
char *wtid_s;
/** /**
* The other account that was involved * The other account that was involved
@ -93,6 +97,18 @@ typedef int
const struct TALER_WIRE_TransferDetails *details); const struct TALER_WIRE_TransferDetails *details);
/**
* Callbacks of this type are used to serve the result of asking
* the bank to reject a wire transfer.
*
* @param cls closure
* @param ec status of the operation, #TALER_EC_NONE on success
*/
typedef void
(*TALER_WIRE_RejectTransferCallback) (void *cls,
enum TALER_ErrorCode ec);
/** /**
* Handle returned for cancelling a preparation step. * Handle returned for cancelling a preparation step.
*/ */
@ -308,12 +324,55 @@ struct TALER_WIRE_Plugin
/** /**
* Cancel going over the account's history. * Cancel going over the account's history.
* *
* @param cls plugins' closure
* @param whh operation to cancel * @param whh operation to cancel
*/ */
void void
(*get_history_cancel) (void *cls, (*get_history_cancel) (void *cls,
struct TALER_WIRE_HistoryHandle *whh); struct TALER_WIRE_HistoryHandle *whh);
/**
* 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 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
*/
struct TALER_WIRE_RejectHandle *
(*reject_transfer)(void *cls,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls);
/**
* 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
*/
void *
(*reject_transfer_cancel)(void *cls,
struct TALER_WIRE_RejectHandle *rh);
}; };

View File

@ -791,6 +791,107 @@ sepa_get_history_cancel (void *cls,
} }
/**
* Context for a rejection operation.
*/
struct TALER_WIRE_RejectHandle
{
/**
* Function to call with the result.
*/
TALER_WIRE_RejectTransferCallback rej_cb;
/**
* Closure for @e rej_cb.
*/
void *rej_cb_cls;
/**
* Handle to task for timeout of operation.
*/
struct GNUNET_SCHEDULER_Task *timeout_task;
};
/**
* Rejection operation failed with timeout, notify callback
* and clean up.
*
* @param cls closure with `struct TALER_WIRE_RejectHandle`
*/
static void
timeout_reject (void *cls)
{
struct TALER_WIRE_RejectHandle *rh = cls;
rh->timeout_task = NULL;
rh->rej_cb (rh->rej_cb_cls,
TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
GNUNET_free (rh);
}
/**
* 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 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 *
sepa_reject_transfer (void *cls,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls)
{
struct TALER_WIRE_RejectHandle *rh;
GNUNET_break (0); /* not implemented, just a stub! */
rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
rh->rej_cb = rej_cb;
rh->rej_cb_cls = rej_cb_cls;
rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
rh);
return rh;
}
/**
* 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 *
sepa_reject_transfer_cancel (void *cls,
struct TALER_WIRE_RejectHandle *rh)
{
void *ret = rh->rej_cb_cls;
GNUNET_SCHEDULER_cancel (rh->timeout_task);
GNUNET_free (rh);
return ret;
}
/** /**
* Initialize sepa-wire subsystem. * Initialize sepa-wire subsystem.
* *
@ -832,6 +933,8 @@ libtaler_plugin_wire_sepa_init (void *cls)
plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel; plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
plugin->get_history = &sepa_get_history; plugin->get_history = &sepa_get_history;
plugin->get_history_cancel = &sepa_get_history_cancel; plugin->get_history_cancel = &sepa_get_history_cancel;
plugin->reject_transfer = &sepa_reject_transfer;
plugin->reject_transfer_cancel = &sepa_reject_transfer_cancel;
return plugin; return plugin;
} }

View File

@ -820,52 +820,52 @@ bhist_cb (void *cls,
uint64_t bserial_id = GNUNET_htonll (serial_id); uint64_t bserial_id = GNUNET_htonll (serial_id);
struct TALER_WIRE_TransferDetails wd; struct TALER_WIRE_TransferDetails wd;
if (MHD_HTTP_OK == http_status) switch (http_status) {
{ case MHD_HTTP_OK:
char *subject; {
char *space; char *subject;
char *space;
wd.amount = details->amount; wd.amount = details->amount;
wd.execution_date = details->execution_date; wd.execution_date = details->execution_date;
subject = GNUNET_strdup (details->wire_transfer_subject); subject = GNUNET_strdup (details->wire_transfer_subject);
space = strchr (subject, (int) ' '); space = strchr (subject, (int) ' ');
if (NULL != space) if (NULL != space)
{ {
/* Space separates the actual wire transfer subject from the /* Space separates the actual wire transfer subject from the
exchange base URL (if present, expected only for outgoing exchange base URL (if present, expected only for outgoing
transactions). So we cut the string off at the space. */ transactions). So we cut the string off at the space. */
*space = '\0'; *space = '\0';
} }
/* NOTE: For a real bank, the subject should include a checksum! */ /* NOTE: For a real bank, the subject should include a checksum! */
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (subject, GNUNET_STRINGS_string_to_data (subject,
strlen (subject), strlen (subject),
&wd.reserve_pub, &wd.wtid,
sizeof (wd.reserve_pub))) sizeof (wd.wtid)))
{ {
GNUNET_break (0); /* Ill-formed wire subject, set binary version to all zeros
and pass as a string, this time including the part after
the space. */
memset (&wd.wtid,
0,
sizeof (wd.wtid));
wd.wtid_s = details->wire_transfer_subject;
}
GNUNET_free (subject); GNUNET_free (subject);
/* NOTE: for a "real" bank, we would want to trigger logic to undo the wd.account_details = details->account_details;
wire transfer. However, for the "demo" bank, it should currently
be "impossible" to do wire transfers with invalid subjects, and
equally we thus don't need to undo them (and there is no API to do
that nicely either right now). So we don't handle this case for now. */
return;
}
GNUNET_free (subject);
wd.account_details = details->account_details;
if ( (NULL != whh->hres_cb) && if ( (NULL != whh->hres_cb) &&
(GNUNET_OK != (GNUNET_OK !=
whh->hres_cb (whh->hres_cb_cls, whh->hres_cb (whh->hres_cb_cls,
dir, dir,
&bserial_id, &bserial_id,
sizeof (bserial_id), sizeof (bserial_id),
&wd)) ) &wd)) )
whh->hres_cb = NULL; whh->hres_cb = NULL;
} break;
else }
{ case MHD_HTTP_NO_CONTENT:
if (NULL != whh->hres_cb) if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls, (void) whh->hres_cb (whh->hres_cb_cls,
TALER_BANK_DIRECTION_NONE, TALER_BANK_DIRECTION_NONE,
@ -874,6 +874,20 @@ bhist_cb (void *cls,
NULL); NULL);
whh->hh = NULL; whh->hh = NULL;
GNUNET_free (whh); GNUNET_free (whh);
break;
default:
/* FIXME: consider modifying API to pass more specific error code(s)
back to the application. */
GNUNET_break (0);
if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls,
TALER_BANK_DIRECTION_NONE,
NULL,
0,
NULL);
whh->hh = NULL;
GNUNET_free (whh);
break;
} }
} }
@ -975,6 +989,106 @@ test_get_history_cancel (void *cls,
} }
/**
* Context for a rejection operation.
*/
struct TALER_WIRE_RejectHandle
{
/**
* Function to call with the result.
*/
TALER_WIRE_RejectTransferCallback rej_cb;
/**
* Closure for @e rej_cb.
*/
void *rej_cb_cls;
/**
* Handle to task for timeout of operation.
*/
struct GNUNET_SCHEDULER_Task *timeout_task;
};
/**
* Rejection operation failed with timeout, notify callback
* and clean up.
*
* @param cls closure with `struct TALER_WIRE_RejectHandle`
*/
static void
timeout_reject (void *cls)
{
struct TALER_WIRE_RejectHandle *rh = cls;
rh->timeout_task = NULL;
rh->rej_cb (rh->rej_cb_cls,
TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
GNUNET_free (rh);
}
/**
* 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 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 *
test_reject_transfer (void *cls,
const void *start_off,
size_t start_off_len,
TALER_WIRE_RejectTransferCallback rej_cb,
void *rej_cb_cls)
{
struct TALER_WIRE_RejectHandle *rh;
GNUNET_break (0); /* not implemented, just a stub! */
rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
rh->rej_cb = rej_cb;
rh->rej_cb_cls = rej_cb_cls;
rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
rh);
return rh;
}
/**
* 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 *
test_reject_transfer_cancel (void *cls,
struct TALER_WIRE_RejectHandle *rh)
{
void *ret = rh->rej_cb_cls;
GNUNET_SCHEDULER_cancel (rh->timeout_task);
GNUNET_free (rh);
return ret;
}
/** /**
* Initialize test-wire subsystem. * Initialize test-wire subsystem.
* *
@ -1087,6 +1201,8 @@ libtaler_plugin_wire_test_init (void *cls)
plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel; plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
plugin->get_history = &test_get_history; plugin->get_history = &test_get_history;
plugin->get_history_cancel = &test_get_history_cancel; plugin->get_history_cancel = &test_get_history_cancel;
plugin->reject_transfer = &test_reject_transfer;
plugin->reject_transfer_cancel = &test_reject_transfer_cancel;
return plugin; return plugin;
} }

View File

@ -211,9 +211,8 @@ history_result_cb (void *cls,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (0 != memcmp (&wtid, if (0 != memcmp (&wtid,
&details->reserve_pub, &details->wtid,
GNUNET_MIN (sizeof (struct TALER_ReservePublicKeyP), sizeof (struct TALER_WireTransferIdentifierRawP)))
sizeof (wtid))))
{ {
GNUNET_break (0); GNUNET_break (0);
global_ret = GNUNET_SYSERR; global_ret = GNUNET_SYSERR;