bank API refactoring for #7276 (incomplete)

This commit is contained in:
Christian Grothoff 2022-11-17 13:28:15 +01:00
parent 8e0f06c86b
commit 741831e87b
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
8 changed files with 541 additions and 488 deletions

View File

@ -77,6 +77,11 @@ static enum GNUNET_GenericReturnValue
parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh, parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
const json_t *history) const json_t *history)
{ {
struct TALER_BANK_CreditHistoryResponse chr = {
.http_status = MHD_HTTP_OK,
.ec = TALER_EC_NONE,
.response = history
};
json_t *history_array; json_t *history_array;
if (NULL == (history_array = json_object_get (history, if (NULL == (history_array = json_object_get (history,
@ -90,23 +95,26 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
{
size_t len = json_array_size (history_array);
struct TALER_BANK_CreditDetails cd[len];
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_CreditDetails td; struct TALER_BANK_CreditDetails *td = &cd[i];
uint64_t row_id;
struct GNUNET_JSON_Specification hist_spec[] = { struct GNUNET_JSON_Specification hist_spec[] = {
TALER_JSON_spec_amount_any ("amount", TALER_JSON_spec_amount_any ("amount",
&td.amount), &td->amount),
GNUNET_JSON_spec_timestamp ("date", GNUNET_JSON_spec_timestamp ("date",
&td.execution_date), &td->execution_date),
GNUNET_JSON_spec_uint64 ("row_id", GNUNET_JSON_spec_uint64 ("row_id",
&row_id), &td->serial_id),
GNUNET_JSON_spec_fixed_auto ("reserve_pub", GNUNET_JSON_spec_fixed_auto ("reserve_pub",
&td.reserve_pub), &td->reserve_pub),
GNUNET_JSON_spec_string ("debit_account", GNUNET_JSON_spec_string ("debit_account",
&td.debit_account_uri), &td->debit_account_uri),
GNUNET_JSON_spec_string ("credit_account", GNUNET_JSON_spec_string ("credit_account",
&td.credit_account_uri), &td->credit_account_uri),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
json_t *transaction = json_array_get (history_array, json_t *transaction = json_array_get (history_array,
@ -115,24 +123,17 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (transaction, GNUNET_JSON_parse (transaction,
hist_spec, hist_spec,
NULL, NULL)) NULL,
NULL))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK !=
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
row_id,
&td,
transaction))
{
hh->hcb = NULL;
GNUNET_JSON_parse_free (hist_spec);
return GNUNET_OK;
} }
GNUNET_JSON_parse_free (hist_spec); chr.details.success.details_length = len;
chr.details.success.details = cd;
hh->hcb (hh->hcb_cls,
&chr);
} }
return GNUNET_OK; return GNUNET_OK;
} }
@ -152,72 +153,67 @@ handle_credit_history_finished (void *cls,
const void *response) const void *response)
{ {
struct TALER_BANK_CreditHistoryHandle *hh = cls; struct TALER_BANK_CreditHistoryHandle *hh = cls;
const json_t *j = response; struct TALER_BANK_CreditHistoryResponse chr = {
enum TALER_ErrorCode ec; .http_status = MHD_HTTP_OK,
.response = response
};
hh->job = NULL; hh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
ec = TALER_EC_GENERIC_INVALID_RESPONSE; chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
if (GNUNET_OK != if (GNUNET_OK !=
parse_account_history (hh, parse_account_history (hh,
j)) chr.response))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
json_dumpf (j, json_dumpf (chr.response,
stderr, stderr,
JSON_INDENT (2)); JSON_INDENT (2));
response_code = 0; chr.http_status = 0;
ec = TALER_EC_GENERIC_INVALID_RESPONSE; chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
} }
response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */ TALER_BANK_credit_history_cancel (hh);
ec = TALER_EC_NONE; return;
break;
case MHD_HTTP_NO_CONTENT: case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy /* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0); GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j); chr.ec = TALER_JSON_get_error_code (chr.response);
break; break;
case MHD_HTTP_UNAUTHORIZED: case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the HTTP Authentication /* Nothing really to verify, bank says the HTTP Authentication
failed. May happen if HTTP authentication is used and the failed. May happen if HTTP authentication is used and the
user supplied a wrong username/password combination. */ user supplied a wrong username/password combination. */
ec = TALER_JSON_get_error_code (j); chr.ec = TALER_JSON_get_error_code (chr.response);
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify: the bank is either unaware /* Nothing really to verify: the bank is either unaware
of the endpoint (not a bank), or of the account. of the endpoint (not a bank), or of the account.
We should pass the JSON (?) reply to the application */ We should pass the JSON (?) reply to the application */
ec = TALER_JSON_get_error_code (j); chr.ec = TALER_JSON_get_error_code (chr.response);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
ec = TALER_JSON_get_error_code (j); chr.ec = TALER_JSON_get_error_code (chr.response);
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n", "Unexpected response code %u\n",
(unsigned int) response_code); (unsigned int) response_code);
ec = TALER_JSON_get_error_code (j); chr.ec = TALER_JSON_get_error_code (chr.response);
break; break;
} }
if (NULL != hh->hcb)
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
response_code, &chr);
ec,
0LLU,
NULL,
j);
TALER_BANK_credit_history_cancel (hh); TALER_BANK_credit_history_cancel (hh);
} }

View File

@ -1,6 +1,6 @@
/* /*
This file is part of TALER This file is part of TALER
Copyright (C) 2017--2021 Taler Systems SA Copyright (C) 2017--2022 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
@ -77,6 +77,11 @@ static enum GNUNET_GenericReturnValue
parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh, parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
const json_t *history) const json_t *history)
{ {
struct TALER_BANK_DebitHistoryResponse dhr = {
.http_status = MHD_HTTP_OK,
.ec = TALER_EC_NONE,
.response = history
};
json_t *history_array; json_t *history_array;
if (NULL == (history_array = json_object_get (history, if (NULL == (history_array = json_object_get (history,
@ -90,25 +95,28 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
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++)
{ {
struct TALER_BANK_DebitDetails td; size_t len = json_array_size (history_array);
uint64_t row_id; struct TALER_BANK_DebitDetails dd[len];
for (unsigned int i = 0; i<len; i++)
{
struct TALER_BANK_DebitDetails *td = &dd[i];
struct GNUNET_JSON_Specification hist_spec[] = { struct GNUNET_JSON_Specification hist_spec[] = {
TALER_JSON_spec_amount_any ("amount", TALER_JSON_spec_amount_any ("amount",
&td.amount), &td->amount),
GNUNET_JSON_spec_timestamp ("date", GNUNET_JSON_spec_timestamp ("date",
&td.execution_date), &td->execution_date),
GNUNET_JSON_spec_uint64 ("row_id", GNUNET_JSON_spec_uint64 ("row_id",
&row_id), &td->serial_id),
GNUNET_JSON_spec_fixed_auto ("wtid", GNUNET_JSON_spec_fixed_auto ("wtid",
&td.wtid), &td->wtid),
GNUNET_JSON_spec_string ("credit_account", GNUNET_JSON_spec_string ("credit_account",
&td.credit_account_uri), &td->credit_account_uri),
GNUNET_JSON_spec_string ("debit_account", GNUNET_JSON_spec_string ("debit_account",
&td.debit_account_uri), &td->debit_account_uri),
GNUNET_JSON_spec_string ("exchange_base_url", GNUNET_JSON_spec_string ("exchange_base_url",
&td.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,
@ -117,24 +125,17 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (transaction, GNUNET_JSON_parse (transaction,
hist_spec, hist_spec,
NULL, NULL)) NULL,
NULL))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
if (GNUNET_OK !=
hh->hcb (hh->hcb_cls,
MHD_HTTP_OK,
TALER_EC_NONE,
row_id,
&td,
transaction))
{
hh->hcb = NULL;
GNUNET_JSON_parse_free (hist_spec);
return GNUNET_OK;
} }
GNUNET_JSON_parse_free (hist_spec); dhr.details.success.details_length = len;
dhr.details.success.details = dd;
hh->hcb (hh->hcb_cls,
&dhr);
} }
return GNUNET_OK; return GNUNET_OK;
} }
@ -154,69 +155,64 @@ handle_debit_history_finished (void *cls,
const void *response) const void *response)
{ {
struct TALER_BANK_DebitHistoryHandle *hh = cls; struct TALER_BANK_DebitHistoryHandle *hh = cls;
const json_t *j = response; struct TALER_BANK_DebitHistoryResponse dhr = {
enum TALER_ErrorCode ec; .http_status = MHD_HTTP_OK,
.response = response
};
hh->job = NULL; hh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
ec = TALER_EC_GENERIC_INVALID_RESPONSE; dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
if (GNUNET_OK != if (GNUNET_OK !=
parse_account_history (hh, parse_account_history (hh,
j)) dhr.response))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
response_code = 0; dhr.http_status = 0;
ec = TALER_EC_GENERIC_INVALID_RESPONSE; dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
} }
response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */ TALER_BANK_debit_history_cancel (hh);
ec = TALER_EC_NONE; return;
break;
case MHD_HTTP_NO_CONTENT: case MHD_HTTP_NO_CONTENT:
ec = TALER_EC_NONE;
break; break;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy /* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0); GNUNET_break_op (0);
ec = TALER_JSON_get_error_code (j); dhr.ec = TALER_JSON_get_error_code (dhr.response);
break; break;
case MHD_HTTP_UNAUTHORIZED: case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the HTTP Authentication /* Nothing really to verify, bank says the HTTP Authentication
failed. May happen if HTTP authentication is used and the failed. May happen if HTTP authentication is used and the
user supplied a wrong username/password combination. */ user supplied a wrong username/password combination. */
ec = TALER_JSON_get_error_code (j); dhr.ec = TALER_JSON_get_error_code (dhr.response);
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify: the bank is either unaware /* Nothing really to verify: the bank is either unaware
of the endpoint (not a bank), or of the account. of the endpoint (not a bank), or of the account.
We should pass the JSON (?) reply to the application */ We should pass the JSON (?) reply to the application */
ec = TALER_JSON_get_error_code (j); dhr.ec = TALER_JSON_get_error_code (dhr.response);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
ec = TALER_JSON_get_error_code (j); dhr.ec = TALER_JSON_get_error_code (dhr.response);
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n", "Unexpected response code %u\n",
(unsigned int) response_code); (unsigned int) response_code);
ec = TALER_JSON_get_error_code (j); dhr.ec = TALER_JSON_get_error_code (dhr.response);
break; break;
} }
if (NULL != hh->hcb)
hh->hcb (hh->hcb_cls, hh->hcb (hh->hcb_cls,
response_code, &dhr);
ec,
0LLU,
NULL,
j);
TALER_BANK_debit_history_cancel (hh); TALER_BANK_debit_history_cancel (hh);
} }

View File

@ -152,83 +152,72 @@ do_shutdown (void *cls)
/** /**
* Callback used to process ONE entry in the transaction * Callback used to process the transaction
* history returned by the bank. * history returned by the bank.
* *
* @param cls closure * @param cls closure
* @param http_status HTTP status code from server * @param reply response we got from the bank
* @param ec taler error code
* @param serial_id identification of the position at
* which we are returning data
* @param details details about the wire transfer
* @param json original full response from server
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to
* abort iteration
*/ */
static enum GNUNET_GenericReturnValue static void
credit_history_cb (void *cls, credit_history_cb (void *cls,
unsigned int http_status, const struct TALER_BANK_CreditHistoryResponse *reply)
enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct TALER_BANK_CreditDetails *details,
const json_t *json)
{ {
(void) cls; (void) cls;
chh = NULL; chh = NULL;
if (MHD_HTTP_OK != http_status) switch (reply->http_status)
{
if ( (MHD_HTTP_NO_CONTENT != http_status) ||
(TALER_EC_NONE != ec) )
{
if (0 == http_status)
{ {
case 0:
fprintf (stderr, fprintf (stderr,
"Failed to obtain HTTP reply from `%s'\n", "Failed to obtain HTTP reply from `%s'\n",
auth.wire_gateway_url); auth.wire_gateway_url);
}
else
{
fprintf (stderr,
"Failed to obtain credit history from `%s': HTTP status %u (%s)\n",
auth.wire_gateway_url,
http_status,
TALER_ErrorCode_get_hint (ec));
}
if (NULL != json)
json_dumpf (json,
stderr,
JSON_INDENT (2));
global_ret = 2; global_ret = 2;
GNUNET_SCHEDULER_shutdown (); break;
return GNUNET_NO; case MHD_HTTP_NO_CONTENT:
}
fprintf (stdout, fprintf (stdout,
"End of transactions list.\n"); "No transactions.\n");
global_ret = 0; global_ret = 0;
GNUNET_SCHEDULER_shutdown (); break;
return GNUNET_NO; case MHD_HTTP_OK:
} for (unsigned int i = 0; i<reply->details.success.details_length; i++)
{
const struct TALER_BANK_CreditDetails *cd =
&reply->details.success.details[i];
/* If credit/debit accounts were specified, use as a filter */ /* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) && if ( (NULL != credit_account) &&
(0 != strcasecmp (credit_account, (0 != strcasecmp (credit_account,
details->credit_account_uri) ) ) cd->credit_account_uri) ) )
return GNUNET_OK; continue;
if ( (NULL != debit_account) && if ( (NULL != debit_account) &&
(0 != strcasecmp (debit_account, (0 != strcasecmp (debit_account,
details->debit_account_uri) ) ) cd->debit_account_uri) ) )
return GNUNET_OK; continue;
fprintf (stdout, fprintf (stdout,
"%llu: %s->%s (%s) over %s at %s\n", "%llu: %s->%s (%s) over %s at %s\n",
(unsigned long long) serial_id, (unsigned long long) cd->serial_id,
details->debit_account_uri, cd->debit_account_uri,
details->credit_account_uri, cd->credit_account_uri,
TALER_B2S (&details->reserve_pub), TALER_B2S (&cd->reserve_pub),
TALER_amount2s (&details->amount), TALER_amount2s (&cd->amount),
GNUNET_TIME_timestamp2s (details->execution_date)); GNUNET_TIME_timestamp2s (cd->execution_date));
return GNUNET_OK; }
global_ret = 0;
break;
default:
fprintf (stderr,
"Failed to obtain credit history from `%s': HTTP status %u (%s)\n",
auth.wire_gateway_url,
reply->http_status,
TALER_ErrorCode_get_hint (reply->ec));
if (NULL != reply->response)
json_dumpf (reply->response,
stderr,
JSON_INDENT (2));
global_ret = 2;
break;
}
GNUNET_SCHEDULER_shutdown ();
} }
@ -264,84 +253,71 @@ execute_credit_history (void)
/** /**
* Function with the debit debit transaction history. * Function with the debit transaction history.
* *
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param reply response details
* 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 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
*/ */
static enum GNUNET_GenericReturnValue static void
debit_history_cb (void *cls, debit_history_cb (void *cls,
unsigned int http_status, const struct TALER_BANK_DebitHistoryResponse *reply)
enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct TALER_BANK_DebitDetails *details,
const json_t *json)
{ {
(void) cls; (void) cls;
dhh = NULL; dhh = NULL;
if (MHD_HTTP_OK != http_status) switch (reply->http_status)
{
if ( (MHD_HTTP_NO_CONTENT != http_status) ||
(TALER_EC_NONE != ec) )
{
if (0 == http_status)
{ {
case 0:
fprintf (stderr, fprintf (stderr,
"Failed to obtain HTTP reply from `%s'\n", "Failed to obtain HTTP reply from `%s'\n",
auth.wire_gateway_url); auth.wire_gateway_url);
}
else
{
fprintf (stderr,
"Failed to obtain debit history from `%s': HTTP status %u (%s)\n",
auth.wire_gateway_url,
http_status,
TALER_ErrorCode_get_hint (ec));
}
if (NULL != json)
json_dumpf (json,
stderr,
JSON_INDENT (2));
global_ret = 2; global_ret = 2;
GNUNET_SCHEDULER_shutdown (); break;
return GNUNET_NO; case MHD_HTTP_NO_CONTENT:
}
fprintf (stdout, fprintf (stdout,
"End of transactions list.\n"); "No transactions.\n");
global_ret = 0; global_ret = 0;
GNUNET_SCHEDULER_shutdown (); break;
return GNUNET_NO; case MHD_HTTP_OK:
} for (unsigned int i = 0; i<reply->details.success.details_length; i++)
{
const struct TALER_BANK_DebitDetails *dd =
&reply->details.success.details[i];
/* If credit/debit accounts were specified, use as a filter */ /* If credit/debit accounts were specified, use as a filter */
if ( (NULL != credit_account) && if ( (NULL != credit_account) &&
(0 != strcasecmp (credit_account, (0 != strcasecmp (credit_account,
details->credit_account_uri) ) ) dd->credit_account_uri) ) )
return GNUNET_OK; continue;
if ( (NULL != debit_account) && if ( (NULL != debit_account) &&
(0 != strcasecmp (debit_account, (0 != strcasecmp (debit_account,
details->debit_account_uri) ) ) dd->debit_account_uri) ) )
return GNUNET_OK; continue;
fprintf (stdout, fprintf (stdout,
"%llu: %s->%s (%s) over %s at %s\n", "%llu: %s->%s (%s) over %s at %s\n",
(unsigned long long) serial_id, (unsigned long long) dd->serial_id,
details->debit_account_uri, dd->debit_account_uri,
details->credit_account_uri, dd->credit_account_uri,
TALER_B2S (&details->wtid), TALER_B2S (&dd->wtid),
TALER_amount2s (&details->amount), TALER_amount2s (&dd->amount),
GNUNET_TIME_timestamp2s (details->execution_date)); GNUNET_TIME_timestamp2s (dd->execution_date));
return GNUNET_OK; }
global_ret = 0;
break;
default:
fprintf (stderr,
"Failed to obtain debit history from `%s': HTTP status %u (%s)\n",
auth.wire_gateway_url,
reply->http_status,
TALER_ErrorCode_get_hint (reply->ec));
if (NULL != reply->response)
json_dumpf (reply->response,
stderr,
JSON_INDENT (2));
global_ret = 2;
break;
}
GNUNET_SCHEDULER_shutdown ();
} }

View File

@ -290,7 +290,6 @@ batch_deposit_transaction (void *cls,
mhd_ret); mhd_ret);
if (qs < 0) if (qs < 0)
return qs; return qs;
qs = TEH_plugin->do_deposit ( qs = TEH_plugin->do_deposit (
TEH_plugin->cls, TEH_plugin->cls,
deposit, deposit,

View File

@ -74,8 +74,7 @@ reply_deposit_success (
struct TALER_ExchangeSignatureP sig; struct TALER_ExchangeSignatureP sig;
enum TALER_ErrorCode ec; enum TALER_ErrorCode ec;
if (TALER_EC_NONE != ec = TALER_exchange_online_deposit_confirmation_sign (
(ec = TALER_exchange_online_deposit_confirmation_sign (
&TEH_keys_exchange_sign_, &TEH_keys_exchange_sign_,
h_contract_terms, h_contract_terms,
h_wire, h_wire,
@ -87,7 +86,8 @@ reply_deposit_success (
coin_pub, coin_pub,
merchant, merchant,
&pub, &pub,
&sig))) &sig);
if (TALER_EC_NONE != ec)
{ {
return TALER_MHD_reply_with_ec (connection, return TALER_MHD_reply_with_ec (connection,
ec, ec,
@ -187,8 +187,6 @@ deposit_transaction (void *cls,
mhd_ret); mhd_ret);
if (qs < 0) if (qs < 0)
return qs; return qs;
/* If the deposit has a policy associated to it, persist it. This will /* If the deposit has a policy associated to it, persist it. This will
* insert or update the record. */ * insert or update the record. */
if (dc->has_policy) if (dc->has_policy)
@ -203,8 +201,6 @@ deposit_transaction (void *cls,
if (qs < 0) if (qs < 0)
return qs; return qs;
} }
qs = TEH_plugin->do_deposit ( qs = TEH_plugin->do_deposit (
TEH_plugin->cls, TEH_plugin->cls,
dc->deposit, dc->deposit,

View File

@ -606,123 +606,64 @@ do_commit (struct WireAccount *wa)
/** /**
* Callbacks of this type are used to serve the result of asking * We got incoming transaction details from the bank. Add them
* the bank for the transaction history. * to the database.
* *
* @param cls closure with the `struct WioreAccount *` we are processing * @param wa wire account we are handling
* @param http_status HTTP status code from the server * @param details array of transaction details
* @param ec taler error code * @param details_length length of the @a details array
* @param serial_id identification of the position at which we are querying * @return true on success
* @param details details about the wire transfer
* @param json raw JSON response
* @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
*/ */
static enum GNUNET_GenericReturnValue static bool
history_cb (void *cls, process_reply (struct WireAccount *wa,
unsigned int http_status,
enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct TALER_BANK_CreditDetails *details, const struct TALER_BANK_CreditDetails *details,
const json_t *json) unsigned int details_length)
{ {
struct WireAccount *wa = cls; uint64_t lroff = wa->latest_row_off;
enum GNUNET_DB_QueryStatus qs;
(void) json; /* check serial IDs for range constraints */
GNUNET_assert (NULL == task); for (unsigned int i = 0; i<details_length; i++)
if (NULL == details)
{ {
wa->hh = NULL; const struct TALER_BANK_CreditDetails *cd = &details[i];
if ( (! ( (MHD_HTTP_NOT_FOUND == http_status) &&
(ignore_account_404) ) ) &&
( (MHD_HTTP_NO_CONTENT != http_status) &&
( (TALER_EC_NONE != ec) ||
(MHD_HTTP_OK != http_status) ) ) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Error fetching history: %s (%u)\n",
TALER_ErrorCode_get_hint (ec),
http_status);
if (! (exit_on_error || test_mode) )
{
account_completed (wa);
return GNUNET_OK;
}
GNUNET_SCHEDULER_shutdown ();
return GNUNET_OK;
}
if (wa->started_transaction)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"End of list. Committing progress on %s of (%llu,%llu]!\n",
wa->job_name,
(unsigned long long) wa->batch_start,
(unsigned long long) wa->latest_row_off);
do_commit (wa);
return GNUNET_OK; /* will be ignored anyway */
}
/* We did not even start a transaction. */
if ( (wa->delay) &&
(test_mode) &&
(NULL == wa->next) )
{
/* We exit on idle */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Shutdown due to test mode!\n");
GNUNET_SCHEDULER_shutdown ();
return GNUNET_OK;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"No transactions in history response, moving on.\n");
account_completed (wa);
return GNUNET_OK; /* will be ignored anyway */
}
/* We did get 'details' from the bank. Do sanity checks before inserting. */ if (cd->serial_id < lroff)
if (serial_id < wa->latest_row_off)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Serial ID %llu not monotonic (got %llu before). Failing!\n", "Serial ID %llu not monotonic (got %llu before). Failing!\n",
(unsigned long long) serial_id, (unsigned long long) cd->serial_id,
(unsigned long long) wa->latest_row_off); (unsigned long long) lroff);
db_plugin->rollback (db_plugin->cls);
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
wa->hh = NULL; wa->hh = NULL;
return GNUNET_SYSERR; return false;
} }
/* If we got 'limit' transactions back from the bank, if (cd->serial_id >= wa->max_row_off)
we should not introduce any delay before the next {
call. */ /* We got 'limit' transactions back from the bank, so we should not
if (serial_id >= wa->max_row_off) introduce any delay before the next call. */
wa->delay = false; wa->delay = false;
if (serial_id > wa->shard_end) }
if (cd->serial_id > wa->shard_end)
{ {
/* we are *past* the current shard (likely because the serial_id of the /* we are *past* the current shard (likely because the serial_id of the
shard_end happens to not exist in the DB). So commit and stop this shard_end happens to not exist in the DB). So commit and stop this
iteration! */ iteration! */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Serial ID %llu past shard end at %llu, ending iteration early!\n", "Serial ID %llu past shard end at %llu, ending iteration early!\n",
(unsigned long long) serial_id, (unsigned long long) cd->serial_id,
(unsigned long long) wa->shard_end); (unsigned long long) wa->shard_end);
wa->latest_row_off = serial_id - 1; /* excluding serial_id! */ details_length = i;
wa->hh = NULL; wa->delay = false;
if (wa->started_transaction) break;
{
GNUNET_assert (NULL == task);
do_commit (wa);
} }
else lroff = cd->serial_id;
{
GNUNET_assert (NULL == task);
if (check_shard_done (wa))
account_completed (wa);
else
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
wa);
} }
return GNUNET_SYSERR; if (0 == details_length)
}
if (! wa->started_transaction)
{ {
/* Server should have used 204, not 200! */
GNUNET_break_op (0);
return true;
}
if (GNUNET_OK != if (GNUNET_OK !=
db_plugin->start_read_committed (db_plugin->cls, db_plugin->start_read_committed (db_plugin->cls,
"wirewatch check for incoming wire transfers")) "wirewatch check for incoming wire transfers"))
@ -732,26 +673,27 @@ history_cb (void *cls,
global_ret = EXIT_FAILURE; global_ret = EXIT_FAILURE;
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
wa->hh = NULL; wa->hh = NULL;
return GNUNET_SYSERR; return false;
} }
wa->started_transaction = true; wa->started_transaction = true;
}
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, for (unsigned int i = 0; i<details_length; i++)
"Adding wire transfer over %s with (hashed) subject `%s'\n", {
TALER_amount2s (&details->amount), const struct TALER_BANK_CreditDetails *cd = &details[i];
TALER_B2S (&details->reserve_pub)); enum GNUNET_DB_QueryStatus qs;
/* FIXME #7276: Consider using Postgres multi-valued insert here, /* FIXME #7276: Consider using Postgres multi-valued insert here,
for up to 15x speed-up according to for up to 15x speed-up according to
https://dba.stackexchange.com/questions/224989/multi-row-insert-vs-transactional-single-row-inserts#225006 https://dba.stackexchange.com/questions/224989/multi-row-insert-vs-transactional-single-row-inserts#225006
(Note: this may require changing both the (Note: this may require changing both the
plugin API as well as modifying how this function is called.) */ plugin API as well as modifying how this function is called.) */
qs = db_plugin->reserves_in_insert (db_plugin->cls, qs = db_plugin->reserves_in_insert (db_plugin->cls,
&details->reserve_pub, &cd->reserve_pub,
&details->amount, &cd->amount,
details->execution_date, cd->execution_date,
details->debit_account_uri, cd->debit_account_uri,
wa->ai->section_name, wa->ai->section_name,
serial_id); cd->serial_id);
switch (qs) switch (qs)
{ {
case GNUNET_DB_STATUS_HARD_ERROR: case GNUNET_DB_STATUS_HARD_ERROR:
@ -760,13 +702,13 @@ history_cb (void *cls,
wa->started_transaction = false; wa->started_transaction = false;
GNUNET_SCHEDULER_shutdown (); GNUNET_SCHEDULER_shutdown ();
wa->hh = NULL; wa->hh = NULL;
return GNUNET_SYSERR; return false;
case GNUNET_DB_STATUS_SOFT_ERROR: case GNUNET_DB_STATUS_SOFT_ERROR:
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got DB soft error for reserves_in_insert. Rolling back.\n"); "Got DB soft error for reserves_in_insert. Rolling back.\n");
handle_soft_error (wa); handle_soft_error (wa);
wa->hh = NULL; wa->hh = NULL;
return GNUNET_SYSERR; return true;
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
/* Either wirewatch was freshly started after the system was /* Either wirewatch was freshly started after the system was
shutdown and we're going over an incomplete shard again shutdown and we're going over an incomplete shard again
@ -777,16 +719,79 @@ history_cb (void *cls,
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Attempted to import transaction %llu (%s) twice. " "Attempted to import transaction %llu (%s) twice. "
"This should happen rarely (if not, ask for support).\n", "This should happen rarely (if not, ask for support).\n",
(unsigned long long) serial_id, (unsigned long long) cd->serial_id,
wa->job_name); wa->job_name);
db_plugin->rollback (db_plugin->cls);
wa->latest_row_off = cd->serial_id;
wa->started_transaction = false;
/* already existed, ok, let's just continue */ /* already existed, ok, let's just continue */
break; return true;
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
wa->latest_row_off = cd->serial_id;
/* normal case */ /* normal case */
break; break;
} }
wa->latest_row_off = serial_id; }
return GNUNET_OK; do_commit (wa);
if (check_shard_done (wa))
account_completed (wa);
else
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
wa);
return true;
}
/**
* Callbacks of this type are used to serve the result of asking
* the bank for the transaction history.
*
* @param cls closure with the `struct WireAccount *` we are processing
* @param reply response we got from the bank
*/
static void
history_cb (void *cls,
const struct TALER_BANK_CreditHistoryResponse *reply)
{
struct WireAccount *wa = cls;
bool ok;
GNUNET_assert (NULL == task);
wa->hh = NULL;
switch (reply->http_status)
{
case 0:
ok = false;
case MHD_HTTP_OK:
ok = process_reply (wa,
reply->details.success.details,
reply->details.success.details_length);
break;
case MHD_HTTP_NO_CONTENT:
ok = true;
break;
case MHD_HTTP_NOT_FOUND:
ok = ignore_account_404;
break;
default:
ok = false;
break;
}
if (! ok)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Error fetching history: %s (%u)\n",
TALER_ErrorCode_get_hint (reply->ec),
reply->http_status);
if (! (exit_on_error || test_mode) )
{
account_completed (wa);
return;
}
GNUNET_SCHEDULER_shutdown ();
return;
}
} }

View File

@ -26,6 +26,8 @@
#include "pg_helper.h" #include "pg_helper.h"
#include "pg_setup_wire_target.h" #include "pg_setup_wire_target.h"
#include "pg_compute_shard.h" #include "pg_compute_shard.h"
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
TEH_PG_insert_deposit (void *cls, TEH_PG_insert_deposit (void *cls,
struct GNUNET_TIME_Timestamp exchange_timestamp, struct GNUNET_TIME_Timestamp exchange_timestamp,

View File

@ -260,6 +260,11 @@ struct TALER_BANK_CreditHistoryHandle;
*/ */
struct TALER_BANK_CreditDetails struct TALER_BANK_CreditDetails
{ {
/**
* Serial ID of the wire transfer.
*/
uint64_t serial_id;
/** /**
* Amount that was transferred * Amount that was transferred
*/ */
@ -271,49 +276,85 @@ struct TALER_BANK_CreditDetails
struct GNUNET_TIME_Timestamp execution_date; struct GNUNET_TIME_Timestamp execution_date;
/** /**
* Reserve public key encoded in the wire * Reserve public key encoded in the wire transfer subject.
* transfer subject.
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
/** /**
* payto://-URL of the source account that * payto://-URL of the source account that send the funds.
* send the funds.
*/ */
const char *debit_account_uri; const char *debit_account_uri;
/** /**
* payto://-URL of the target account that * payto://-URL of the target account that received the funds.
* received the funds.
*/ */
const char *credit_account_uri; const char *credit_account_uri;
}; };
/**
* Response details for a history request.
*/
struct TALER_BANK_CreditHistoryResponse
{
/**
* HTTP status. Note that #MHD_HTTP_OK and #MHD_HTTP_NO_CONTENT are both
* successful replies, but @e details will only contain @e success information
* if this is set to #MHD_HTTP_OK.
*/
unsigned int http_status;
/**
* Taler error code, #TALER_EC_NONE on success.
*/
enum TALER_ErrorCode ec;
/**
* Full response, NULL if body was not in JSON format.
*/
const json_t *response;
/**
* Details returned depending on the @e http_status.
*/
union
{
/**
* Details if status was #MHD_HTTP_OK
*/
struct
{
/**
* Array of transactions recevied.
*/
const struct TALER_BANK_CreditDetails *details;
/**
* Length of the @e details array.
*/
unsigned int details_length;
} success;
} 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 for the credit 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 reply details about the response
* 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 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 enum GNUNET_GenericReturnValue typedef void
(*TALER_BANK_CreditHistoryCallback)( (*TALER_BANK_CreditHistoryCallback)(
void *cls, void *cls,
unsigned int http_status, const struct TALER_BANK_CreditHistoryResponse *reply);
enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct TALER_BANK_CreditDetails *details,
const json_t *json);
/** /**
@ -369,6 +410,11 @@ struct TALER_BANK_DebitHistoryHandle;
*/ */
struct TALER_BANK_DebitDetails struct TALER_BANK_DebitDetails
{ {
/**
* Serial ID of the wire transfer.
*/
uint64_t serial_id;
/** /**
* Amount that was transferred * Amount that was transferred
*/ */
@ -390,44 +436,81 @@ struct TALER_BANK_DebitDetails
const char *exchange_base_url; const char *exchange_base_url;
/** /**
* payto://-URI of the source account that * payto://-URI of the source account that send the funds.
* send the funds.
*/ */
const char *debit_account_uri; const char *debit_account_uri;
/** /**
* payto://-URI of the target account that * payto://-URI of the target account that received the funds.
* received the funds.
*/ */
const char *credit_account_uri; const char *credit_account_uri;
}; };
/**
* Response details for a history request.
*/
struct TALER_BANK_DebitHistoryResponse
{
/**
* HTTP status. Note that #MHD_HTTP_OK and #MHD_HTTP_NO_CONTENT are both
* successful replies, but @e details will only contain @e success information
* if this is set to #MHD_HTTP_OK.
*/
unsigned int http_status;
/**
* Taler error code, #TALER_EC_NONE on success.
*/
enum TALER_ErrorCode ec;
/**
* Full response, NULL if body was not in JSON format.
*/
const json_t *response;
/**
* Details returned depending on the @e http_status.
*/
union
{
/**
* Details if status was #MHD_HTTP_OK
*/
struct
{
/**
* Array of transactions initiated.
*/
const struct TALER_BANK_DebitDetails *details;
/**
* Length of the @e details array.
*/
unsigned int details_length;
} success;
} 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 for the debit transaction history. * the bank for the debit transaction history.
* *
* @param cls closure * @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request * @param reply details about the response
* 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 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 enum GNUNET_GenericReturnValue typedef void
(*TALER_BANK_DebitHistoryCallback)( (*TALER_BANK_DebitHistoryCallback)(
void *cls, void *cls,
unsigned int http_status, const struct TALER_BANK_DebitHistoryResponse *reply);
enum TALER_ErrorCode ec,
uint64_t serial_id,
const struct TALER_BANK_DebitDetails *details,
const json_t *json);
/** /**