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,7 +820,8 @@ 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 *subject;
char *space; char *space;
@ -840,17 +841,16 @@ bhist_cb (void *cls,
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
GNUNET_free (subject); and pass as a string, this time including the part after
/* NOTE: for a "real" bank, we would want to trigger logic to undo the the space. */
wire transfer. However, for the "demo" bank, it should currently memset (&wd.wtid,
be "impossible" to do wire transfers with invalid subjects, and 0,
equally we thus don't need to undo them (and there is no API to do sizeof (wd.wtid));
that nicely either right now). So we don't handle this case for now. */ wd.wtid_s = details->wire_transfer_subject;
return;
} }
GNUNET_free (subject); GNUNET_free (subject);
wd.account_details = details->account_details; wd.account_details = details->account_details;
@ -863,9 +863,9 @@ bhist_cb (void *cls,
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;