summaryrefslogtreecommitdiff
path: root/src/bank-lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/bank-lib')
-rw-r--r--src/bank-lib/Makefile.am34
-rw-r--r--src/bank-lib/bank_api_admin.c52
-rw-r--r--src/bank-lib/bank_api_common.h2
-rw-r--r--src/bank-lib/bank_api_credit.c149
-rw-r--r--src/bank-lib/bank_api_debit.c144
-rw-r--r--src/bank-lib/bank_api_transfer.c49
-rw-r--r--src/bank-lib/fakebank.c2538
-rw-r--r--src/bank-lib/fakebank.h691
-rw-r--r--src/bank-lib/fakebank_api_check.c238
-rw-r--r--src/bank-lib/fakebank_bank.c524
-rw-r--r--src/bank-lib/fakebank_bank.h54
-rw-r--r--src/bank-lib/fakebank_bank_accounts_withdrawals.c101
-rw-r--r--src/bank-lib/fakebank_bank_accounts_withdrawals.h50
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts.c80
-rw-r--r--src/bank-lib/fakebank_bank_get_accounts.h48
-rw-r--r--src/bank-lib/fakebank_bank_get_root.c58
-rw-r--r--src/bank-lib/fakebank_bank_get_root.h44
-rw-r--r--src/bank-lib/fakebank_bank_get_withdrawals.c87
-rw-r--r--src/bank-lib/fakebank_bank_get_withdrawals.h51
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals.c196
-rw-r--r--src/bank-lib/fakebank_bank_post_accounts_withdrawals.h54
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_abort.c74
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_abort.h48
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_confirm.c107
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_confirm.h48
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_id_op.c241
-rw-r--r--src/bank-lib/fakebank_bank_post_withdrawals_id_op.h58
-rw-r--r--src/bank-lib/fakebank_bank_testing_register.c128
-rw-r--r--src/bank-lib/fakebank_bank_testing_register.h53
-rw-r--r--src/bank-lib/fakebank_common_lookup.c103
-rw-r--r--src/bank-lib/fakebank_common_lookup.h62
-rw-r--r--src/bank-lib/fakebank_common_lp.c344
-rw-r--r--src/bank-lib/fakebank_common_lp.h100
-rw-r--r--src/bank-lib/fakebank_common_make_admin_transfer.c117
-rw-r--r--src/bank-lib/fakebank_common_make_admin_transfer.h56
-rw-r--r--src/bank-lib/fakebank_common_parser.c138
-rw-r--r--src/bank-lib/fakebank_common_parser.h50
-rw-r--r--src/bank-lib/fakebank_common_transact.c261
-rw-r--r--src/bank-lib/fakebank_common_transact.h76
-rw-r--r--src/bank-lib/fakebank_stop.c192
-rw-r--r--src/bank-lib/fakebank_tbi.c167
-rw-r--r--src/bank-lib/fakebank_tbi.h54
-rw-r--r--src/bank-lib/fakebank_tbi_get_withdrawal_operation.c141
-rw-r--r--src/bank-lib/fakebank_tbi_get_withdrawal_operation.h51
-rw-r--r--src/bank-lib/fakebank_tbi_post_withdrawal_operation.c231
-rw-r--r--src/bank-lib/fakebank_tbi_post_withdrawal_operation.h53
-rw-r--r--src/bank-lib/fakebank_tbr.c94
-rw-r--r--src/bank-lib/fakebank_tbr.h58
-rw-r--r--src/bank-lib/fakebank_tbr_get_history.c312
-rw-r--r--src/bank-lib/fakebank_tbr_get_history.h52
-rw-r--r--src/bank-lib/fakebank_tbr_get_root.c50
-rw-r--r--src/bank-lib/fakebank_tbr_get_root.h45
-rw-r--r--src/bank-lib/fakebank_twg.c124
-rw-r--r--src/bank-lib/fakebank_twg.h56
-rw-r--r--src/bank-lib/fakebank_twg_admin_add_incoming.c160
-rw-r--r--src/bank-lib/fakebank_twg_admin_add_incoming.h52
-rw-r--r--src/bank-lib/fakebank_twg_get_root.c58
-rw-r--r--src/bank-lib/fakebank_twg_get_root.h46
-rw-r--r--src/bank-lib/fakebank_twg_history.c537
-rw-r--r--src/bank-lib/fakebank_twg_history.h67
-rw-r--r--src/bank-lib/fakebank_twg_transfer.c178
-rw-r--r--src/bank-lib/fakebank_twg_transfer.h55
-rw-r--r--src/bank-lib/taler-exchange-wire-gateway-client.c282
-rw-r--r--src/bank-lib/taler-fakebank-run.c56
-rwxr-xr-xsrc/bank-lib/test_bank.sh49
65 files changed, 7723 insertions, 2805 deletions
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index ffd428a6f..a292dcece 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -51,14 +51,42 @@ libtalerbank_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
- $(LIBGNURLCURL_LIBS) \
+ -lcurl \
$(XLIB)
libtalerfakebank_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalerfakebank_la_SOURCES = \
- fakebank.c
+ fakebank.c fakebank.h \
+ fakebank_api_check.c \
+ fakebank_common_lookup.c fakebank_common_lookup.h \
+ fakebank_common_lp.c fakebank_common_lp.h \
+ fakebank_common_make_admin_transfer.c fakebank_common_make_admin_transfer.h \
+ fakebank_common_parser.c fakebank_common_parser.h \
+ fakebank_common_transact.c fakebank_common_transact.h \
+ fakebank_stop.c \
+ fakebank_bank.c fakebank_bank.h \
+ fakebank_bank_accounts_withdrawals.c fakebank_bank_accounts_withdrawals.h \
+ fakebank_bank_get_accounts.c fakebank_bank_get_accounts.h \
+ fakebank_bank_get_withdrawals.c fakebank_bank_get_withdrawals.h \
+ fakebank_bank_get_root.c fakebank_bank_get_root.h \
+ fakebank_bank_post_accounts_withdrawals.c fakebank_bank_post_accounts_withdrawals.h \
+ fakebank_bank_post_withdrawals_abort.c fakebank_bank_post_withdrawals_abort.h \
+ fakebank_bank_post_withdrawals_confirm.c fakebank_bank_post_withdrawals_confirm.h \
+ fakebank_bank_post_withdrawals_id_op.c fakebank_bank_post_withdrawals_id_op.h \
+ fakebank_bank_testing_register.c fakebank_bank_testing_register.h \
+ fakebank_tbr.c fakebank_tbr.h \
+ fakebank_tbr_get_history.c fakebank_tbr_get_history.h \
+ fakebank_tbr_get_root.c fakebank_tbr_get_root.h \
+ fakebank_tbi.c fakebank_tbi.h \
+ fakebank_tbi_get_withdrawal_operation.c fakebank_tbi_get_withdrawal_operation.h \
+ fakebank_tbi_post_withdrawal_operation.c fakebank_tbi_post_withdrawal_operation.h \
+ fakebank_twg.c fakebank_twg.h \
+ fakebank_twg_admin_add_incoming.c fakebank_twg_admin_add_incoming.h \
+ fakebank_twg_get_root.c fakebank_twg_get_root.h \
+ fakebank_twg_history.c fakebank_twg_history.h \
+ fakebank_twg_transfer.c fakebank_twg_transfer.h
libtalerfakebank_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/mhd/libtalermhd.la \
@@ -66,7 +94,7 @@ libtalerfakebank_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
- $(LIBGNURLCURL_LIBS) \
+ -lcurl \
-lmicrohttpd \
-lpthread \
$(XLIB)
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
index 77b1a38e4..f12ab6ee2 100644
--- a/src/bank-lib/bank_api_admin.c
+++ b/src/bank-lib/bank_api_admin.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015--2020 Taler Systems SA
+ Copyright (C) 2015--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -16,7 +16,7 @@
*/
/**
* @file bank-lib/bank_api_admin.c
- * @brief Implementation of the /admin/ requests of the bank's HTTP API
+ * @brief Implementation of the /admin/add-incoming requests of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
@@ -27,7 +27,7 @@
/**
- * @brief An admin/add-incoming Handle
+ * @brief An /admin/add-incoming Handle
*/
struct TALER_BANK_AdminAddIncomingHandle
{
@@ -74,25 +74,25 @@ handle_admin_add_incoming_finished (void *cls,
const void *response)
{
struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
- uint64_t row_id = UINT64_MAX;
- struct GNUNET_TIME_Timestamp timestamp;
- enum TALER_ErrorCode ec;
const json_t *j = response;
+ struct TALER_BANK_AdminAddIncomingResponse ir = {
+ .http_status = response_code,
+ .response = response
+ };
aai->job = NULL;
- timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
switch (response_code)
{
case 0:
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id",
- &row_id),
+ &ir.details.ok.serial_id),
GNUNET_JSON_spec_timestamp ("timestamp",
- &timestamp),
+ &ir.details.ok.timestamp),
GNUNET_JSON_spec_end ()
};
@@ -102,42 +102,41 @@ handle_admin_add_incoming_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- response_code = 0;
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ ir.http_status = 0;
+ ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- ec = TALER_EC_NONE;
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the password is invalid; we should
pass the JSON reply to the application */
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, maybe account really does not exist.
We should pass the JSON reply to the application */
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_CONFLICT:
/* Nothing to verify, we used the same wire subject
twice? */
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
default:
/* unexpected response code */
@@ -145,15 +144,11 @@ handle_admin_add_incoming_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
- ec = TALER_JSON_get_error_code (j);
+ ir.ec = TALER_JSON_get_error_code (j);
break;
}
aai->cb (aai->cb_cls,
- response_code,
- ec,
- row_id,
- timestamp,
- j);
+ &ir);
TALER_BANK_admin_add_incoming_cancel (aai);
}
@@ -215,9 +210,10 @@ TALER_BANK_admin_add_incoming (
"Requesting administrative transaction at `%s' for reserve %s\n",
aai->request_url,
TALER_B2S (reserve_pub));
- aai->post_ctx.headers = curl_slist_append
- (aai->post_ctx.headers,
- "Content-Type: application/json");
+ aai->post_ctx.headers
+ = curl_slist_append (
+ aai->post_ctx.headers,
+ "Content-Type: application/json");
eh = curl_easy_init ();
if ( (NULL == eh) ||
diff --git a/src/bank-lib/bank_api_common.h b/src/bank-lib/bank_api_common.h
index ac059e9ed..e288a7e6f 100644
--- a/src/bank-lib/bank_api_common.h
+++ b/src/bank-lib/bank_api_common.h
@@ -36,7 +36,7 @@
* @param auth authentication data to use
* @return #GNUNET_OK in success
*/
-int
+enum GNUNET_GenericReturnValue
TALER_BANK_setup_auth_ (CURL *easy,
const struct TALER_BANK_AuthenticationData *auth);
diff --git a/src/bank-lib/bank_api_credit.c b/src/bank-lib/bank_api_credit.c
index 1ea2e269e..124415b80 100644
--- a/src/bank-lib/bank_api_credit.c
+++ b/src/bank-lib/bank_api_credit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2017--2021 Taler Systems SA
+ Copyright (C) 2017--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -77,62 +77,67 @@ static enum GNUNET_GenericReturnValue
parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
const json_t *history)
{
- json_t *history_array;
+ struct TALER_BANK_CreditHistoryResponse chr = {
+ .http_status = MHD_HTTP_OK,
+ .ec = TALER_EC_NONE,
+ .response = history
+ };
+ const json_t *history_array;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("incoming_transactions",
+ &history_array),
+ GNUNET_JSON_spec_string ("credit_account",
+ &chr.details.ok.credit_account_uri),
+ GNUNET_JSON_spec_end ()
+ };
- if (NULL == (history_array = json_object_get (history,
- "incoming_transactions")))
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (history,
+ spec,
+ NULL,
+ NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- if (! json_is_array (history_array))
{
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- for (unsigned int i = 0; i<json_array_size (history_array); i++)
- {
- struct TALER_BANK_CreditDetails td;
- uint64_t row_id;
- struct GNUNET_JSON_Specification hist_spec[] = {
- TALER_JSON_spec_amount_any ("amount",
- &td.amount),
- GNUNET_JSON_spec_timestamp ("date",
- &td.execution_date),
- GNUNET_JSON_spec_uint64 ("row_id",
- &row_id),
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &td.reserve_pub),
- GNUNET_JSON_spec_string ("debit_account",
- &td.debit_account_uri),
- GNUNET_JSON_spec_string ("credit_account",
- &td.credit_account_uri),
- GNUNET_JSON_spec_end ()
- };
- json_t *transaction = json_array_get (history_array,
- i);
+ size_t len = json_array_size (history_array);
+ struct TALER_BANK_CreditDetails cd[GNUNET_NZL (len)];
- if (GNUNET_OK !=
- GNUNET_JSON_parse (transaction,
- hist_spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- hh->hcb (hh->hcb_cls,
- MHD_HTTP_OK,
- TALER_EC_NONE,
- row_id,
- &td,
- transaction))
+ GNUNET_break_op (0 != len);
+ for (size_t i = 0; i<len; i++)
{
- hh->hcb = NULL;
- GNUNET_JSON_parse_free (hist_spec);
- return GNUNET_OK;
+ struct TALER_BANK_CreditDetails *td = &cd[i];
+ struct GNUNET_JSON_Specification hist_spec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &td->amount),
+ GNUNET_JSON_spec_timestamp ("date",
+ &td->execution_date),
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &td->serial_id),
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &td->reserve_pub),
+ GNUNET_JSON_spec_string ("debit_account",
+ &td->debit_account_uri),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *transaction = json_array_get (history_array,
+ i);
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (transaction,
+ hist_spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
}
- GNUNET_JSON_parse_free (hist_spec);
+ chr.details.ok.details_length = len;
+ chr.details.ok.details = cd;
+ hh->hcb (hh->hcb_cls,
+ &chr);
}
return GNUNET_OK;
}
@@ -152,69 +157,67 @@ handle_credit_history_finished (void *cls,
const void *response)
{
struct TALER_BANK_CreditHistoryHandle *hh = cls;
- const json_t *j = response;
- enum TALER_ErrorCode ec;
+ struct TALER_BANK_CreditHistoryResponse chr = {
+ .http_status = response_code,
+ .response = response
+ };
hh->job = NULL;
switch (response_code)
{
case 0:
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
parse_account_history (hh,
- j))
+ chr.response))
{
GNUNET_break_op (0);
- response_code = 0;
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ json_dumpf (chr.response,
+ stderr,
+ JSON_INDENT (2));
+ chr.http_status = 0;
+ chr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
- ec = TALER_EC_NONE;
- break;
+ TALER_BANK_credit_history_cancel (hh);
+ return;
case MHD_HTTP_NO_CONTENT:
- ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
- ec = TALER_JSON_get_error_code (j);
+ chr.ec = TALER_JSON_get_error_code (chr.response);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the HTTP Authentication
failed. May happen if HTTP authentication is used and the
user supplied a wrong username/password combination. */
- ec = TALER_JSON_get_error_code (j);
+ chr.ec = TALER_JSON_get_error_code (chr.response);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify: the bank is either unaware
of the endpoint (not a bank), or of the account.
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;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- ec = TALER_JSON_get_error_code (j);
+ chr.ec = TALER_JSON_get_error_code (chr.response);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
- ec = TALER_JSON_get_error_code (j);
+ chr.ec = TALER_JSON_get_error_code (chr.response);
break;
}
- if (NULL != hh->hcb)
- hh->hcb (hh->hcb_cls,
- response_code,
- ec,
- 0LLU,
- NULL,
- j);
+ hh->hcb (hh->hcb_cls,
+ &chr);
TALER_BANK_credit_history_cancel (hh);
}
@@ -248,12 +251,15 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
{
if ( (0 < num_results) &&
(! GNUNET_TIME_relative_is_zero (timeout)) )
+ /* 0 == start_row is implied, go with timeout into future */
GNUNET_snprintf (url,
sizeof (url),
"history/incoming?delta=%lld&long_poll_ms=%llu",
(long long) num_results,
tms);
else
+ /* Going back from current transaction or have no timeout;
+ hence timeout makes no sense */
GNUNET_snprintf (url,
sizeof (url),
"history/incoming?delta=%lld",
@@ -263,6 +269,7 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
{
if ( (0 < num_results) &&
(! GNUNET_TIME_relative_is_zero (timeout)) )
+ /* going forward from num_result */
GNUNET_snprintf (url,
sizeof (url),
"history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu",
@@ -270,6 +277,8 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
(unsigned long long) start_row,
tms);
else
+ /* going backwards or have no timeout;
+ hence timeout makes no sense */
GNUNET_snprintf (url,
sizeof (url),
"history/incoming?delta=%lld&start=%llu",
diff --git a/src/bank-lib/bank_api_debit.c b/src/bank-lib/bank_api_debit.c
index 2a76495bd..58dc0a736 100644
--- a/src/bank-lib/bank_api_debit.c
+++ b/src/bank-lib/bank_api_debit.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2017--2021 Taler Systems SA
+ Copyright (C) 2017--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -77,64 +77,69 @@ static enum GNUNET_GenericReturnValue
parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
const json_t *history)
{
- json_t *history_array;
+ struct TALER_BANK_DebitHistoryResponse dhr = {
+ .http_status = MHD_HTTP_OK,
+ .ec = TALER_EC_NONE,
+ .response = history
+ };
+ const json_t *history_array;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_array_const ("outgoing_transactions",
+ &history_array),
+ GNUNET_JSON_spec_string ("debit_account",
+ &dhr.details.ok.debit_account_uri),
+ GNUNET_JSON_spec_end ()
+ };
- if (NULL == (history_array = json_object_get (history,
- "outgoing_transactions")))
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (history,
+ spec,
+ NULL,
+ NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- if (! json_is_array (history_array))
{
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- for (unsigned int i = 0; i<json_array_size (history_array); i++)
- {
- struct TALER_BANK_DebitDetails td;
- uint64_t row_id;
- struct GNUNET_JSON_Specification hist_spec[] = {
- TALER_JSON_spec_amount_any ("amount",
- &td.amount),
- GNUNET_JSON_spec_timestamp ("date",
- &td.execution_date),
- GNUNET_JSON_spec_uint64 ("row_id",
- &row_id),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &td.wtid),
- GNUNET_JSON_spec_string ("credit_account",
- &td.credit_account_uri),
- GNUNET_JSON_spec_string ("debit_account",
- &td.debit_account_uri),
- GNUNET_JSON_spec_string ("exchange_base_url",
- &td.exchange_base_url),
- GNUNET_JSON_spec_end ()
- };
- json_t *transaction = json_array_get (history_array,
- i);
+ size_t len = json_array_size (history_array);
+ struct TALER_BANK_DebitDetails dd[GNUNET_NZL (len)];
- if (GNUNET_OK !=
- GNUNET_JSON_parse (transaction,
- hist_spec,
- NULL, NULL))
- {
- GNUNET_break_op (0);
- return GNUNET_SYSERR;
- }
- if (GNUNET_OK !=
- hh->hcb (hh->hcb_cls,
- MHD_HTTP_OK,
- TALER_EC_NONE,
- row_id,
- &td,
- transaction))
+ GNUNET_break_op (0 != len);
+ for (unsigned int i = 0; i<len; i++)
{
- hh->hcb = NULL;
- GNUNET_JSON_parse_free (hist_spec);
- return GNUNET_OK;
+ struct TALER_BANK_DebitDetails *td = &dd[i];
+ struct GNUNET_JSON_Specification hist_spec[] = {
+ TALER_JSON_spec_amount_any ("amount",
+ &td->amount),
+ GNUNET_JSON_spec_timestamp ("date",
+ &td->execution_date),
+ GNUNET_JSON_spec_uint64 ("row_id",
+ &td->serial_id),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &td->wtid),
+ GNUNET_JSON_spec_string ("credit_account",
+ &td->credit_account_uri),
+ GNUNET_JSON_spec_string ("exchange_base_url",
+ &td->exchange_base_url),
+ GNUNET_JSON_spec_end ()
+ };
+ json_t *transaction = json_array_get (history_array,
+ i);
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (transaction,
+ hist_spec,
+ NULL,
+ NULL))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
}
- GNUNET_JSON_parse_free (hist_spec);
+ dhr.details.ok.details_length = len;
+ dhr.details.ok.details = dd;
+ hh->hcb (hh->hcb_cls,
+ &dhr);
}
return GNUNET_OK;
}
@@ -154,69 +159,64 @@ handle_debit_history_finished (void *cls,
const void *response)
{
struct TALER_BANK_DebitHistoryHandle *hh = cls;
- const json_t *j = response;
- enum TALER_ErrorCode ec;
+ struct TALER_BANK_DebitHistoryResponse dhr = {
+ .http_status = response_code,
+ .response = response
+ };
hh->job = NULL;
switch (response_code)
{
case 0:
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
if (GNUNET_OK !=
parse_account_history (hh,
- j))
+ dhr.response))
{
GNUNET_break_op (0);
- response_code = 0;
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ dhr.http_status = 0;
+ dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
- ec = TALER_EC_NONE;
- break;
+ TALER_BANK_debit_history_cancel (hh);
+ return;
case MHD_HTTP_NO_CONTENT:
- ec = TALER_EC_NONE;
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
- ec = TALER_JSON_get_error_code (j);
+ dhr.ec = TALER_JSON_get_error_code (dhr.response);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says the HTTP Authentication
failed. May happen if HTTP authentication is used and the
user supplied a wrong username/password combination. */
- ec = TALER_JSON_get_error_code (j);
+ dhr.ec = TALER_JSON_get_error_code (dhr.response);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify: the bank is either unaware
of the endpoint (not a bank), or of the account.
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;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- ec = TALER_JSON_get_error_code (j);
+ dhr.ec = TALER_JSON_get_error_code (dhr.response);
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
(unsigned int) response_code);
- ec = TALER_JSON_get_error_code (j);
+ dhr.ec = TALER_JSON_get_error_code (dhr.response);
break;
}
- if (NULL != hh->hcb)
- hh->hcb (hh->hcb_cls,
- response_code,
- ec,
- 0LLU,
- NULL,
- j);
+ hh->hcb (hh->hcb_cls,
+ &dhr);
TALER_BANK_debit_history_cancel (hh);
}
diff --git a/src/bank-lib/bank_api_transfer.c b/src/bank-lib/bank_api_transfer.c
index 3b50018d3..0748a0d7e 100644
--- a/src/bank-lib/bank_api_transfer.c
+++ b/src/bank-lib/bank_api_transfer.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2015--2020 Taler Systems SA
+ Copyright (C) 2015--2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
wp->account_len = htonl ((uint32_t) d_len);
wp->exchange_url_len = htonl ((uint32_t) u_len);
end = (char *) &wp[1];
- memcpy (end,
- destination_account_payto_uri,
- d_len);
- memcpy (end + d_len,
- exchange_base_url,
- u_len);
+ GNUNET_memcpy (end,
+ destination_account_payto_uri,
+ d_len);
+ GNUNET_memcpy (end + d_len,
+ exchange_base_url,
+ u_len);
*buf = (char *) wp;
}
@@ -158,23 +158,24 @@ handle_transfer_finished (void *cls,
{
struct TALER_BANK_TransferHandle *th = cls;
const json_t *j = response;
- uint64_t row_id = UINT64_MAX;
- struct GNUNET_TIME_Timestamp timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
- enum TALER_ErrorCode ec;
+ struct TALER_BANK_TransferResponse tr = {
+ .http_status = response_code,
+ .response = j
+ };
th->job = NULL;
switch (response_code)
{
case 0:
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
case MHD_HTTP_OK:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("row_id",
- &row_id),
+ &tr.details.ok.row_id),
GNUNET_JSON_spec_timestamp ("timestamp",
- &timestamp),
+ &tr.details.ok.timestamp),
GNUNET_JSON_spec_end ()
};
@@ -184,39 +185,38 @@ handle_transfer_finished (void *cls,
NULL, NULL))
{
GNUNET_break_op (0);
- response_code = 0;
- ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ tr.http_status = 0;
+ tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break;
}
- ec = TALER_EC_NONE;
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
GNUNET_break_op (0);
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says our credentials are
invalid. We should pass the JSON reply to the application. */
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, endpoint wrong -- could be user unknown */
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_CONFLICT:
/* Nothing really to verify. Server says we used the same transfer request
UID before, but with different details. Should not happen if the user
properly used #TALER_BANK_prepare_transfer() and our PRNG is not
broken... */
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
default:
/* unexpected response code */
@@ -224,14 +224,11 @@ handle_transfer_finished (void *cls,
"Unexpected response code %u\n",
(unsigned int) response_code);
GNUNET_break (0);
- ec = TALER_JSON_get_error_code (j);
+ tr.ec = TALER_JSON_get_error_code (j);
break;
}
th->cb (th->cb_cls,
- response_code,
- ec,
- row_id,
- timestamp);
+ &tr);
TALER_BANK_transfer_cancel (th);
}
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index db30851d8..3a004dc80 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- (C) 2016-2021 Taler Systems SA
+ (C) 2016-2024 Taler Systems SA
TALER is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -21,8 +21,6 @@
* @brief library that fakes being a Taler bank for testcases
* @author Christian Grothoff <christian@grothoff.org>
*/
-// TODO: support adding WAD transfers
-
#include "platform.h"
#include <pthread.h>
#include <poll.h>
@@ -33,1292 +31,10 @@
#include "taler_bank_service.h"
#include "taler_mhd_lib.h"
#include <gnunet/gnunet_mhd_compat.h>
-
-/**
- * Maximum POST request size (for /admin/add-incoming)
- */
-#define REQUEST_BUFFER_MAX (4 * 1024)
-
-/**
- * How long are exchange base URLs allowed to be at most?
- * Set to a relatively low number as this does contribute
- * significantly to our RAM consumption.
- */
-#define MAX_URL_LEN 64
-
-/**
- * Per account information.
- */
-struct Account;
-
-
-/**
- * Types of long polling activities.
- */
-enum LongPollType
-{
- /**
- * Transfer TO the exchange.
- */
- LP_CREDIT,
-
- /**
- * Transfer FROM the exchange.
- */
- LP_DEBIT
-
-};
-
-/**
- * Client waiting for activity on this account.
- */
-struct LongPoller
-{
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *next;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *prev;
-
- /**
- * Account this long poller is waiting on.
- */
- struct Account *account;
-
- /**
- * Entry in the heap for this long poller.
- */
- struct GNUNET_CONTAINER_HeapNode *hn;
-
- /**
- * Client that is waiting for transactions.
- */
- struct MHD_Connection *conn;
-
- /**
- * When will this long poller time out?
- */
- struct GNUNET_TIME_Absolute timeout;
-
- /**
- * What does the @e connection wait for?
- */
- enum LongPollType type;
-
-};
-
-
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction;
-
-/**
- * Per account information.
- */
-struct Account
-{
-
- /**
- * Inbound transactions for this account in a MDLL.
- */
- struct Transaction *in_head;
-
- /**
- * Inbound transactions for this account in a MDLL.
- */
- struct Transaction *in_tail;
-
- /**
- * Outbound transactions for this account in a MDLL.
- */
- struct Transaction *out_head;
-
- /**
- * Outbound transactions for this account in a MDLL.
- */
- struct Transaction *out_tail;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *lp_head;
-
- /**
- * Kept in a DLL.
- */
- struct LongPoller *lp_tail;
-
- /**
- * Account name (string, not payto!)
- */
- char *account_name;
-
- /**
- * Current account balance.
- */
- struct TALER_Amount balance;
-
- /**
- * true if the balance is negative.
- */
- bool is_negative;
-
-};
-
-
-/**
- * Details about a transcation we (as the simulated bank) received.
- */
-struct Transaction
-{
- /**
- * We store inbound transactions in a MDLL.
- */
- struct Transaction *next_in;
-
- /**
- * We store inbound transactions in a MDLL.
- */
- struct Transaction *prev_in;
-
- /**
- * We store outbound transactions in a MDLL.
- */
- struct Transaction *next_out;
-
- /**
- * We store outbound transactions in a MDLL.
- */
- struct Transaction *prev_out;
-
- /**
- * Amount to be transferred.
- */
- struct TALER_Amount amount;
-
- /**
- * Account to debit.
- */
- struct Account *debit_account;
-
- /**
- * Account to credit.
- */
- struct Account *credit_account;
-
- /**
- * Random unique identifier for the request.
- * Used to detect idempotent requests.
- */
- struct GNUNET_HashCode request_uid;
-
- /**
- * When did the transaction happen?
- */
- struct GNUNET_TIME_Timestamp date;
-
- /**
- * Number of this transaction.
- */
- uint64_t row_id;
-
- /**
- * What does the @e subject contain?
- */
- enum
- {
- /**
- * Transfer TO the exchange.
- */
- T_CREDIT,
-
- /**
- * Transfer FROM the exchange.
- */
- T_DEBIT,
-
- /**
- * Exchange-to-exchange WAD transfer.
- */
- T_WAD,
- } type;
-
- /**
- * Wire transfer subject.
- */
- union
- {
-
- /**
- * Used if @e type is T_DEBIT.
- */
- struct
- {
-
- /**
- * Subject of the transfer.
- */
- struct TALER_WireTransferIdentifierRawP wtid;
-
- /**
- * Base URL of the exchange.
- */
- char exchange_base_url[MAX_URL_LEN];
-
- } debit;
-
- /**
- * Used if @e type is T_CREDIT.
- */
- struct
- {
-
- /**
- * Reserve public key of the credit operation.
- */
- struct TALER_ReservePublicKeyP reserve_pub;
-
- } credit;
-
- /**
- * Used if @e type is T_WAD.
- */
- struct
- {
-
- /**
- * Subject of the transfer.
- */
- struct TALER_WadIdentifierP wad;
-
- /**
- * Base URL of the originating exchange.
- */
- char origin_base_url[MAX_URL_LEN];
-
- } wad;
-
- } subject;
-
- /**
- * Has this transaction not yet been subjected to
- * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
- * should thus be counted in #TALER_FAKEBANK_check_empty()?
- */
- bool unchecked;
-};
-
-
-/**
- * Handle for the fake bank.
- */
-struct TALER_FAKEBANK_Handle
-{
- /**
- * We store transactions in a revolving array.
- */
- struct Transaction **transactions;
-
- /**
- * HTTP server we run to pretend to be the "test" bank.
- */
- struct MHD_Daemon *mhd_bank;
-
- /**
- * Task running HTTP server for the "test" bank,
- * unless we are using a thread pool (then NULL).
- */
- struct GNUNET_SCHEDULER_Task *mhd_task;
-
- /**
- * Task for expiring long-polling connections,
- * unless we are using a thread pool (then NULL).
- */
- struct GNUNET_SCHEDULER_Task *lp_task;
-
- /**
- * Task for expiring long-polling connections, unless we are using the
- * GNUnet scheduler (then NULL).
- */
- pthread_t lp_thread;
-
- /**
- * MIN-heap of long pollers, sorted by timeout.
- */
- struct GNUNET_CONTAINER_Heap *lp_heap;
-
- /**
- * Hashmap of reserve public keys to
- * `struct Transaction` with that reserve public
- * key. Used to prevent public-key re-use.
- */
- struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
-
- /**
- * Lock for accessing @a rpubs map.
- */
- pthread_mutex_t rpubs_lock;
-
- /**
- * Hashmap of hashes of account names to `struct Account`.
- */
- struct GNUNET_CONTAINER_MultiHashMap *accounts;
-
- /**
- * Lock for accessing @a accounts hash map.
- */
- pthread_mutex_t accounts_lock;
-
- /**
- * Hashmap of hashes of transaction request_uids to `struct Transaction`.
- */
- struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
-
- /**
- * Lock for accessing @a uuid_map.
- */
- pthread_mutex_t uuid_map_lock;
-
- /**
- * Lock for accessing the internals of
- * accounts and transaction array entries.
- */
- pthread_mutex_t big_lock;
-
- /**
- * Current transaction counter.
- */
- uint64_t serial_counter;
-
- /**
- * Number of transactions we keep in memory (at most).
- */
- uint64_t ram_limit;
-
- /**
- * Currency used by the fakebank.
- */
- char *currency;
-
- /**
- * BaseURL of the fakebank.
- */
- char *my_baseurl;
-
- /**
- * Our port number.
- */
- uint16_t port;
-
-#ifdef __linux__
- /**
- * Event FD to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event;
-#else
- /**
- * Pipe input to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event_in;
-
- /**
- * Pipe output to signal @a lp_thread a change in
- * @a lp_heap.
- */
- int lp_event_out;
-#endif
-
- /**
- * Set to true once we are shutting down.
- */
- bool in_shutdown;
-
- /**
- * Should we run MHD immediately again?
- */
- bool mhd_again;
-
-#if EPOLL_SUPPORT
- /**
- * Boxed @e mhd_fd.
- */
- struct GNUNET_NETWORK_Handle *mhd_rfd;
-
- /**
- * File descriptor to use to wait for MHD.
- */
- int mhd_fd;
-#endif
-};
-
-
-/**
- * Special address "con_cls" can point to to indicate that the handler has
- * been called more than once already (was previously suspended).
- */
-static int special_ptr;
-
-
-/**
- * Task run whenever HTTP server operations are pending.
- *
- * @param cls the `struct TALER_FAKEBANK_Handle`
- */
-static void
-run_mhd (void *cls);
-
-
-/**
- * Trigger the @a lp. Frees associated resources,
- * except the entry of @a lp in the timeout heap.
- * Must be called while the ``big lock`` is held.
- *
- * @param[in] lp long poller to trigger
- * @param[in,out] h fakebank handle
- */
-static void
-lp_trigger (struct LongPoller *lp,
- struct TALER_FAKEBANK_Handle *h)
-{
- struct Account *acc = lp->account;
-
- GNUNET_CONTAINER_DLL_remove (acc->lp_head,
- acc->lp_tail,
- lp);
- MHD_resume_connection (lp->conn);
- GNUNET_free (lp);
- h->mhd_again = true;
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- if (NULL != h->mhd_task)
- GNUNET_SCHEDULER_cancel (h->mhd_task);
- h->mhd_task =
- GNUNET_SCHEDULER_add_now (&run_mhd,
- h);
- }
-}
-
-
-/**
- * Thread that is run to wake up connections that have hit their timeout. Runs
- * until in_shutdown is set to true. Must be send signals via lp_event on
- * shutdown and/or whenever the heap changes to an earlier timeout.
- *
- * @param cls a `struct TALER_FAKEBANK_Handle *`
- * @return NULL
- */
-static void *
-lp_expiration_thread (void *cls)
-{
- struct TALER_FAKEBANK_Handle *h = cls;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- while (! h->in_shutdown)
- {
- struct LongPoller *lp;
- int timeout_ms;
-
- lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
- while ( (NULL != lp) &&
- GNUNET_TIME_absolute_is_past (lp->timeout))
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- lp_trigger (lp,
- h);
- lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
- }
- if (NULL != lp)
- {
- struct GNUNET_TIME_Relative rem;
- unsigned long long left_ms;
-
- rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
- left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
- if (left_ms > INT_MAX)
- timeout_ms = INT_MAX;
- else
- timeout_ms = (int) left_ms;
- }
- else
- {
- timeout_ms = -1; /* infinity */
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- {
- struct pollfd p = {
-#ifdef __linux__
- .fd = h->lp_event,
-#else
- .fd = h->lp_event_out,
-#endif
- .events = POLLIN
- };
- int ret;
-
- ret = poll (&p,
- 1,
- timeout_ms);
- if (-1 == ret)
- {
- if (EINTR != errno)
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "poll");
- }
- else if (1 == ret)
- {
- /* clear event */
- uint64_t ev;
- ssize_t iret;
-
-#ifdef __linux__
- iret = read (h->lp_event,
-#else
- iret = read (h->lp_event_out,
-#endif
- &ev,
- sizeof (ev));
- if (-1 == iret)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "read");
- }
- else
- {
- GNUNET_break (sizeof (uint64_t) == iret);
- }
- }
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return NULL;
-}
-
-
-/**
- * Lookup account with @a name, and if it does not exist, create it.
- *
- * @param[in,out] h bank to lookup account at
- * @param name account name to resolve
- * @return account handle (never NULL)
- */
-static struct Account *
-lookup_account (struct TALER_FAKEBANK_Handle *h,
- const char *name)
-{
- struct GNUNET_HashCode hc;
- size_t slen;
- struct Account *account;
-
- memset (&hc,
- 0,
- sizeof (hc));
- slen = strlen (name);
- GNUNET_CRYPTO_hash (name,
- slen,
- &hc);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->accounts_lock));
- account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
- &hc);
- if (NULL == account)
- {
- account = GNUNET_new (struct Account);
- account->account_name = GNUNET_strdup (name);
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (h->currency,
- &account->balance));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (h->accounts,
- &hc,
- account,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->accounts_lock));
- return account;
-}
-
-
-/**
- * Generate log messages for failed check operation.
- *
- * @param h handle to output transaction log for
- */
-static void
-check_log (struct TALER_FAKEBANK_Handle *h)
-{
- for (uint64_t i = 0; i<h->ram_limit; i++)
- {
- struct Transaction *t = h->transactions[i];
-
- if (NULL == t)
- continue;
- if (! t->unchecked)
- continue;
- switch (t->type)
- {
- case T_DEBIT:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- t->subject.debit.exchange_base_url,
- "DEBIT");
- break;
- case T_CREDIT:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- TALER_B2S (&t->subject.credit.reserve_pub),
- "CREDIT");
- break;
- case T_WAD:
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "%s -> %s (%s) %s[%s] (%s)\n",
- t->debit_account->account_name,
- t->credit_account->account_name,
- TALER_amount2s (&t->amount),
- t->subject.wad.origin_base_url,
- TALER_B2S (&t->subject.wad),
- "WAD");
- break;
- }
- }
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
- const struct TALER_Amount *want_amount,
- const char *want_debit,
- const char *want_credit,
- const char *exchange_base_url,
- struct TALER_WireTransferIdentifierRawP *wtid)
-{
- struct Account *debit_account;
- struct Account *credit_account;
-
- GNUNET_assert (0 ==
- strcasecmp (want_amount->currency,
- h->currency));
- debit_account = lookup_account (h,
- want_debit);
- credit_account = lookup_account (h,
- want_credit);
- for (struct Transaction *t = debit_account->out_tail;
- NULL != t;
- t = t->prev_out)
- {
- if ( (t->unchecked) &&
- (credit_account == t->credit_account) &&
- (T_DEBIT == t->type) &&
- (0 == TALER_amount_cmp (want_amount,
- &t->amount)) &&
- (0 == strcasecmp (exchange_base_url,
- t->subject.debit.exchange_base_url)) )
- {
- *wtid = t->subject.debit.wtid;
- t->unchecked = false;
- return GNUNET_OK;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Did not find matching transaction! I have:\n");
- check_log (h);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted: %s->%s (%s) from exchange %s (DEBIT)\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- exchange_base_url);
- return GNUNET_SYSERR;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
- const struct TALER_Amount *want_amount,
- const char *want_debit,
- const char *want_credit,
- const struct TALER_ReservePublicKeyP *reserve_pub)
-{
- struct Account *debit_account;
- struct Account *credit_account;
-
- GNUNET_assert (0 == strcasecmp (want_amount->currency,
- h->currency));
- debit_account = lookup_account (h,
- want_debit);
- credit_account = lookup_account (h,
- want_credit);
- for (struct Transaction *t = credit_account->in_tail;
- NULL != t;
- t = t->prev_in)
- {
- if ( (t->unchecked) &&
- (debit_account == t->debit_account) &&
- (T_CREDIT == t->type) &&
- (0 == TALER_amount_cmp (want_amount,
- &t->amount)) &&
- (0 == GNUNET_memcmp (reserve_pub,
- &t->subject.credit.reserve_pub)) )
- {
- t->unchecked = false;
- return GNUNET_OK;
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Did not find matching transaction!\nI have:\n");
- check_log (h);
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "I wanted:\n%s -> %s (%s) with subject %s (CREDIT)\n",
- want_debit,
- want_credit,
- TALER_amount2s (want_amount),
- TALER_B2S (reserve_pub));
- return GNUNET_SYSERR;
-}
-
-
-/**
- * Update @a account balance by @a amount.
- *
- * The @a big_lock must already be locked when calling
- * this function.
- *
- * @param[in,out] account account to update
- * @param amount balance change
- * @param debit true to subtract, false to add @a amount
- */
-static void
-update_balance (struct Account *account,
- const struct TALER_Amount *amount,
- bool debit)
-{
- if (debit == account->is_negative)
- {
- GNUNET_assert (0 <=
- TALER_amount_add (&account->balance,
- &account->balance,
- amount));
- return;
- }
- if (0 <= TALER_amount_cmp (&account->balance,
- amount))
- {
- GNUNET_assert (0 <=
- TALER_amount_subtract (&account->balance,
- &account->balance,
- amount));
- }
- else
- {
- GNUNET_assert (0 <=
- TALER_amount_subtract (&account->balance,
- amount,
- &account->balance));
- account->is_negative = ! account->is_negative;
- }
-}
-
-
-/**
- * Add transaction to the debit and credit accounts,
- * updating the balances as needed.
- *
- * The transaction @a t must already be locked
- * when calling this function!
- *
- * @param[in,out] h bank handle
- * @param[in,out] t transaction to add to account lists
- */
-static void
-post_transaction (struct TALER_FAKEBANK_Handle *h,
- struct Transaction *t)
-{
- struct Account *debit_acc = t->debit_account;
- struct Account *credit_acc = t->credit_account;
- uint64_t row_id;
- struct Transaction *old;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- row_id = ++h->serial_counter;
- old = h->transactions[row_id % h->ram_limit];
- h->transactions[row_id % h->ram_limit] = t;
- t->row_id = row_id;
- GNUNET_CONTAINER_MDLL_insert_tail (out,
- debit_acc->out_head,
- debit_acc->out_tail,
- t);
- update_balance (debit_acc,
- &t->amount,
- true);
- GNUNET_CONTAINER_MDLL_insert_tail (in,
- credit_acc->in_head,
- credit_acc->in_tail,
- t);
- update_balance (credit_acc,
- &t->amount,
- false);
- if (NULL != old)
- {
- struct Account *da;
- struct Account *ca;
-
- da = old->debit_account;
- ca = old->credit_account;
- /* slot was already in use, must clean out old
- entry first! */
- GNUNET_CONTAINER_MDLL_remove (out,
- da->out_head,
- da->out_tail,
- old);
- GNUNET_CONTAINER_MDLL_remove (in,
- ca->in_head,
- ca->in_tail,
- old);
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- if ( (NULL != old) &&
- (T_DEBIT == old->type) )
- {
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
- &old->request_uid,
- old));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- }
- GNUNET_free (old);
-}
-
-
-/**
- * Trigger long pollers that might have been waiting
- * for @a t.
- *
- * @param h fakebank handle
- * @param t transaction to notify on
- */
-static void
-notify_transaction (struct TALER_FAKEBANK_Handle *h,
- struct Transaction *t)
-{
- struct Account *debit_acc = t->debit_account;
- struct Account *credit_acc = t->credit_account;
- struct LongPoller *nxt;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- for (struct LongPoller *lp = debit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_DEBIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- for (struct LongPoller *lp = credit_acc->lp_head;
- NULL != lp;
- lp = nxt)
- {
- nxt = lp->next;
- if (LP_CREDIT == lp->type)
- {
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_node (lp->hn));
- lp_trigger (lp,
- h);
- }
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
-}
-
-
-/**
- * Tell the fakebank to create another wire transfer *from* an exchange.
- *
- * @param h fake bank handle
- * @param debit_account account to debit
- * @param credit_account account to credit
- * @param amount amount to transfer
- * @param subject wire transfer subject to use
- * @param exchange_base_url exchange URL
- * @param request_uid unique number to make the request unique, or NULL to create one
- * @param[out] ret_row_id pointer to store the row ID of this transaction
- * @param[out] timestamp set to the time of the transfer
- * @return #GNUNET_YES if the transfer was successful,
- * #GNUNET_SYSERR if the request_uid was reused for a different transfer
- */
-static enum GNUNET_GenericReturnValue
-make_transfer (
- struct TALER_FAKEBANK_Handle *h,
- const char *debit_account,
- const char *credit_account,
- const struct TALER_Amount *amount,
- const struct TALER_WireTransferIdentifierRawP *subject,
- const char *exchange_base_url,
- const struct GNUNET_HashCode *request_uid,
- uint64_t *ret_row_id,
- struct GNUNET_TIME_Timestamp *timestamp)
-{
- struct Transaction *t;
- struct Account *debit_acc;
- struct Account *credit_acc;
- size_t url_len;
-
- GNUNET_assert (0 == strcasecmp (amount->currency,
- h->currency));
- GNUNET_assert (NULL != debit_account);
- GNUNET_assert (NULL != credit_account);
- GNUNET_break (0 != strncasecmp ("payto://",
- debit_account,
- strlen ("payto://")));
- GNUNET_break (0 != strncasecmp ("payto://",
- credit_account,
- strlen ("payto://")));
- url_len = strlen (exchange_base_url);
- GNUNET_assert (url_len < MAX_URL_LEN);
- debit_acc = lookup_account (h,
- debit_account);
- credit_acc = lookup_account (h,
- credit_account);
- if (NULL != request_uid)
- {
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
- request_uid);
- if (NULL != t)
- {
- if ( (debit_acc != t->debit_account) ||
- (credit_acc != t->credit_account) ||
- (0 != TALER_amount_cmp (amount,
- &t->amount)) ||
- (T_DEBIT != t->type) ||
- (0 != GNUNET_memcmp (subject,
- &t->subject.debit.wtid)) )
- {
- /* Transaction exists, but with different details. */
- GNUNET_break (0);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- return GNUNET_SYSERR;
- }
- *ret_row_id = t->row_id;
- *timestamp = t->date;
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- return GNUNET_OK;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- }
- t = GNUNET_new (struct Transaction);
- t->unchecked = true;
- t->debit_account = debit_acc;
- t->credit_account = credit_acc;
- t->amount = *amount;
- t->date = GNUNET_TIME_timestamp_get ();
- if (NULL != timestamp)
- *timestamp = t->date;
- t->type = T_DEBIT;
- memcpy (t->subject.debit.exchange_base_url,
- exchange_base_url,
- url_len);
- t->subject.debit.wtid = *subject;
- if (NULL == request_uid)
- GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
- &t->request_uid);
- else
- t->request_uid = *request_uid;
- post_transaction (h,
- t);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->uuid_map_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multihashmap_put (
- h->uuid_map,
- &t->request_uid,
- t,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->uuid_map_lock));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
- (unsigned long long) t->row_id,
- debit_account,
- credit_account,
- TALER_amount2s (amount),
- TALER_B2S (subject),
- exchange_base_url);
- *ret_row_id = t->row_id;
- notify_transaction (h,
- t);
- return GNUNET_OK;
-}
-
-
-/**
- * Tell the fakebank to create another wire transfer *to* an exchange.
- *
- * @param h fake bank handle
- * @param debit_account account to debit
- * @param credit_account account to credit
- * @param amount amount to transfer
- * @param reserve_pub reserve public key to use in subject
- * @param[out] row_id serial_id of the transfer
- * @param[out] timestamp when was the transfer made
- * @return #GNUNET_OK on success
- */
-static enum GNUNET_GenericReturnValue
-make_admin_transfer (
- struct TALER_FAKEBANK_Handle *h,
- const char *debit_account,
- const char *credit_account,
- const struct TALER_Amount *amount,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- uint64_t *row_id,
- struct GNUNET_TIME_Timestamp *timestamp)
-{
- struct Transaction *t;
- const struct GNUNET_PeerIdentity *pid;
- struct Account *debit_acc;
- struct Account *credit_acc;
-
- GNUNET_static_assert (sizeof (*pid) ==
- sizeof (*reserve_pub));
- pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
- GNUNET_assert (NULL != debit_account);
- GNUNET_assert (NULL != credit_account);
- GNUNET_assert (0 == strcasecmp (amount->currency,
- h->currency));
- GNUNET_break (0 != strncasecmp ("payto://",
- debit_account,
- strlen ("payto://")));
- GNUNET_break (0 != strncasecmp ("payto://",
- credit_account,
- strlen ("payto://")));
- debit_acc = lookup_account (h,
- debit_account);
- credit_acc = lookup_account (h,
- credit_account);
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->rpubs_lock));
- t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
- pid);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->rpubs_lock));
- if (NULL != t)
- {
- /* duplicate reserve public key not allowed */
- GNUNET_break_op (0);
- return GNUNET_NO;
- }
-
- t = GNUNET_new (struct Transaction);
- t->unchecked = true;
- t->debit_account = debit_acc;
- t->credit_account = credit_acc;
- t->amount = *amount;
- t->date = GNUNET_TIME_timestamp_get ();
- if (NULL != timestamp)
- *timestamp = t->date;
- t->type = T_CREDIT;
- t->subject.credit.reserve_pub = *reserve_pub;
- post_transaction (h,
- t);
- if (NULL != row_id)
- *row_id = t->row_id;
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->rpubs_lock));
- GNUNET_assert (GNUNET_OK ==
- GNUNET_CONTAINER_multipeermap_put (
- h->rpubs,
- pid,
- t,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->rpubs_lock));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Making transfer from %s to %s over %s and subject %s at row %llu\n",
- debit_account,
- credit_account,
- TALER_amount2s (amount),
- TALER_B2S (reserve_pub),
- (unsigned long long) t->row_id);
- notify_transaction (h,
- t);
- return GNUNET_OK;
-}
-
-
-enum GNUNET_GenericReturnValue
-TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
-{
- for (uint64_t i = 0; i<h->ram_limit; i++)
- {
- struct Transaction *t = h->transactions[i];
-
- if ( (NULL != t) &&
- (t->unchecked) )
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Expected empty transaction set, but I have:\n");
- check_log (h);
- return GNUNET_SYSERR;
- }
- }
- return GNUNET_OK;
-}
-
-
-static int
-free_account (void *cls,
- const struct GNUNET_HashCode *key,
- void *val)
-{
- struct Account *account = val;
-
- (void) cls;
- (void) key;
- GNUNET_assert (NULL == account->lp_head);
- GNUNET_free (account->account_name);
- GNUNET_free (account);
- return GNUNET_OK;
-}
-
-
-void
-TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
-{
- if (NULL != h->lp_task)
- {
- GNUNET_SCHEDULER_cancel (h->lp_task);
- h->lp_task = NULL;
- }
-#if EPOLL_SUPPORT
- if (NULL != h->mhd_rfd)
- {
- GNUNET_NETWORK_socket_free_memory_only_ (h->mhd_rfd);
- h->mhd_rfd = NULL;
- }
-#endif
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- uint64_t val = 1;
- void *ret;
- struct LongPoller *lp;
-
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- h->in_shutdown = true;
- while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
- lp_trigger (lp,
- h);
- GNUNET_break (sizeof (val) ==
-#ifdef __linux__
- write (h->lp_event,
-#else
- write (h->lp_event_in,
-#endif
- &val,
- sizeof (val)));
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_break (0 ==
- pthread_join (h->lp_thread,
- &ret));
- GNUNET_break (NULL == ret);
-#ifdef __linux__
- GNUNET_break (0 == close (h->lp_event));
- h->lp_event = -1;
-#else
- GNUNET_break (0 == close (h->lp_event_in));
- GNUNET_break (0 == close (h->lp_event_out));
- h->lp_event_in = -1;
- h->lp_event_out = -1;
-#endif
- }
- else
- {
- struct LongPoller *lp;
-
- while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
- lp_trigger (lp,
- h);
- }
- if (NULL != h->mhd_bank)
- {
- MHD_stop_daemon (h->mhd_bank);
- h->mhd_bank = NULL;
- }
- if (NULL != h->mhd_task)
- {
- GNUNET_SCHEDULER_cancel (h->mhd_task);
- h->mhd_task = NULL;
- }
- if (NULL != h->accounts)
- {
- GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
- &free_account,
- NULL);
- GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
- }
- GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
- GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
- GNUNET_CONTAINER_heap_destroy (h->lp_heap);
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->big_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->uuid_map_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->accounts_lock));
- GNUNET_assert (0 ==
- pthread_mutex_destroy (&h->rpubs_lock));
- for (uint64_t i = 0; i<h->ram_limit; i++)
- GNUNET_free (h->transactions[i]);
- GNUNET_free (h->transactions);
- GNUNET_free (h->my_baseurl);
- GNUNET_free (h->currency);
- GNUNET_free (h);
-}
+#include "fakebank.h"
+#include "fakebank_bank.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_tbi.h"
/**
@@ -1327,9 +43,9 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
* @a con_cls that might still need to be cleaned up. Call the
* respective function to free the memory.
*
- * @param cls client-defined closure
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
* @param connection connection handle
- * @param con_cls value as set by the last call to
+ * @param con_cls a `struct ConnectionContext *`
* the #MHD_AccessHandlerCallback
* @param toe reason for request termination
* @see #MHD_OPTION_NOTIFY_COMPLETED
@@ -1341,1079 +57,16 @@ handle_mhd_completion_callback (void *cls,
void **con_cls,
enum MHD_RequestTerminationCode toe)
{
- /* struct TALER_FAKEBANK_Handle *h = cls; */
- (void) cls;
- (void) connection;
- (void) toe;
- if (NULL == *con_cls)
- return;
- if (&special_ptr == *con_cls)
- return;
- GNUNET_JSON_post_parser_cleanup (*con_cls);
- *con_cls = NULL;
-}
-
-
-/**
- * Handle incoming HTTP request for /admin/add/incoming.
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account account into which to deposit the funds (credit)
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct Buffer *`)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- uint64_t row_id;
- struct GNUNET_TIME_Timestamp timestamp;
-
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- con_cls,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
- {
- const char *debit_account;
- struct TALER_Amount amount;
- struct TALER_ReservePublicKeyP reserve_pub;
- char *debit;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("reserve_pub",
- &reserve_pub),
- GNUNET_JSON_spec_string ("debit_account",
- &debit_account),
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- if (0 != strcasecmp (amount.currency,
- h->currency))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Currency `%s' does not match our configuration\n",
- amount.currency);
- json_decref (json);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_GENERIC_CURRENCY_MISMATCH,
- NULL);
- }
- debit = TALER_xtalerbank_account_from_payto (debit_account);
- if (NULL == debit)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- debit_account);
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s\n",
- debit,
- account,
- TALER_B2S (&reserve_pub),
- TALER_amount2s (&amount));
- ret = make_admin_transfer (h,
- debit,
- account,
- &amount,
- &reserve_pub,
- &row_id,
- &timestamp);
- GNUNET_free (debit);
- if (GNUNET_OK != ret)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Reserve public key not unique\n");
- json_decref (json);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- NULL);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- timestamp));
-}
-
-
-/**
- * Handle incoming HTTP request for /transfer.
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account account making the transfer
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure for request (a `struct Buffer *`)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_transfer (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- enum GNUNET_JSON_PostResult pr;
- json_t *json;
- uint64_t row_id;
- struct GNUNET_TIME_Timestamp ts;
-
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
- connection,
- con_cls,
- upload_data,
- upload_data_size,
- &json);
- switch (pr)
- {
- case GNUNET_JSON_PR_OUT_OF_MEMORY:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_CONTINUE:
- return MHD_YES;
- case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_JSON_INVALID:
- GNUNET_break (0);
- return MHD_NO;
- case GNUNET_JSON_PR_SUCCESS:
- break;
- }
- {
- struct GNUNET_HashCode uuid;
- struct TALER_WireTransferIdentifierRawP wtid;
- const char *credit_account;
- char *credit;
- const char *base_url;
- struct TALER_Amount amount;
- enum GNUNET_GenericReturnValue ret;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("request_uid",
- &uuid),
- TALER_JSON_spec_amount ("amount",
- h->currency,
- &amount),
- GNUNET_JSON_spec_string ("exchange_base_url",
- &base_url),
- GNUNET_JSON_spec_fixed_auto ("wtid",
- &wtid),
- GNUNET_JSON_spec_string ("credit_account",
- &credit_account),
- GNUNET_JSON_spec_end ()
- };
-
- if (GNUNET_OK !=
- (ret = TALER_MHD_parse_json_data (connection,
- json,
- spec)))
- {
- GNUNET_break_op (0);
- json_decref (json);
- return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
- }
- {
- int ret;
-
- credit = TALER_xtalerbank_account_from_payto (credit_account);
- if (NULL == credit)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
- credit_account);
- }
- ret = make_transfer (h,
- account,
- credit,
- &amount,
- &wtid,
- base_url,
- &uuid,
- &row_id,
- &ts);
- if (GNUNET_OK != ret)
- {
- MHD_RESULT res;
- char *uids;
-
- GNUNET_break (0);
- uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
- sizeof (uuid));
- json_decref (json);
- res = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_CONFLICT,
- TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
- uids);
- GNUNET_free (uids);
- return res;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
- account,
- credit,
- TALER_B2S (&wtid),
- TALER_amount2s (&amount),
- base_url);
- GNUNET_free (credit);
- }
- }
- json_decref (json);
-
- /* Finally build response object */
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_uint64 ("row_id",
- row_id),
- GNUNET_JSON_pack_timestamp ("timestamp",
- ts));
-}
-
-
-/**
- * Handle incoming HTTP request for / (home page).
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @return MHD result code
- */
-static MHD_RESULT
-handle_home_page (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection)
-{
- MHD_RESULT ret;
- struct MHD_Response *resp;
-#define HELLOMSG "Hello, Fakebank!"
-
- (void) h;
- resp = MHD_create_response_from_buffer (
- strlen (HELLOMSG),
- HELLOMSG,
- MHD_RESPMEM_MUST_COPY);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- resp);
- MHD_destroy_response (resp);
- return ret;
-}
-
-
-/**
- * This is the "base" structure for both the /history and the
- * /history-range API calls.
- */
-struct HistoryArgs
-{
-
- /**
- * Bank account number of the requesting client.
- */
- uint64_t account_number;
-
- /**
- * Index of the starting transaction, exclusive (!).
- */
- uint64_t start_idx;
-
- /**
- * Requested number of results and order
- * (positive: ascending, negative: descending)
- */
- int64_t delta;
-
- /**
- * Timeout for long polling.
- */
- struct GNUNET_TIME_Relative lp_timeout;
-
- /**
- * true if starting point was given.
- */
- bool have_start;
-
-};
-
-
-/**
- * Parse URL history arguments, of _both_ APIs:
- * /history/incoming and /history/outgoing.
- *
- * @param h bank handle to work on
- * @param connection MHD connection.
- * @param[out] ha will contain the parsed values.
- * @return #GNUNET_OK only if the parsing succeeds,
- * #GNUNET_SYSERR if it failed,
- * #GNUNET_NO if it failed and an error was returned
- */
-static enum GNUNET_GenericReturnValue
-parse_history_common_args (const struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct HistoryArgs *ha)
-{
- const char *start;
- const char *delta;
- const char *long_poll_ms;
- unsigned long long lp_timeout;
- unsigned long long sval;
- long long d;
- char dummy;
-
- start = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "start");
- ha->have_start = (NULL != start);
- delta = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "delta");
- long_poll_ms = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "long_poll_ms");
- lp_timeout = 0;
- if ( (NULL == delta) ||
- (1 != sscanf (delta,
- "%lld%c",
- &d,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != long_poll_ms) &&
- (1 != sscanf (long_poll_ms,
- "%llu%c",
- &lp_timeout,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "long_poll_ms"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if ( (NULL != start) &&
- (1 != sscanf (start,
- "%llu%c",
- &sval,
- &dummy)) )
- {
- /* Fail if one of the above failed. */
- /* Invalid request, given that this is fakebank we impolitely
- * just kill the connection instead of returning a nice error.
- */
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "start"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- if (NULL == start)
- ha->start_idx = (d > 0) ? 0 : h->serial_counter;
- else
- ha->start_idx = (uint64_t) sval;
- ha->delta = (int64_t) d;
- if (0 == ha->delta)
- {
- GNUNET_break_op (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "delta"))
- ? GNUNET_NO
- : GNUNET_SYSERR;
- }
- ha->lp_timeout
- = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- lp_timeout);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request for %lld records from %llu\n",
- (long long) ha->delta,
- (unsigned long long) ha->start_idx);
- return GNUNET_OK;
-}
-
-
-/**
- * Task run when a long poller is about to time out.
- * Only used in single-threaded mode.
- *
- * @param cls a `struct TALER_FAKEBANK_Handle *`
- */
-static void
-lp_timeout (void *cls)
-{
struct TALER_FAKEBANK_Handle *h = cls;
- struct LongPoller *lp;
+ struct ConnectionContext *cc = *con_cls;
- h->lp_task = NULL;
- while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
- {
- if (GNUNET_TIME_absolute_is_future (lp->timeout))
- break;
- GNUNET_assert (lp ==
- GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Timeout reached for long poller %p\n",
- lp->conn);
- lp_trigger (lp,
- h);
- }
- if (NULL == lp)
+ (void) h;
+ (void) connection;
+ (void) toe;
+ if (NULL == cc)
return;
- h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
- &lp_timeout,
- h);
-}
-
-
-/**
- * Reschedule the timeout task of @a h for time @a t.
- *
- * @param h fakebank handle
- * @param t when will the next connection timeout expire
- */
-static void
-reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
- struct GNUNET_TIME_Absolute t)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Scheduling timeout task for %s\n",
- GNUNET_STRINGS_absolute_time_to_string (t));
-#ifdef __linux__
- if (-1 != h->lp_event)
-#else
- if (-1 != h->lp_event_in && -1 != h->lp_event_out)
-#endif
- {
- uint64_t num = 1;
-
- GNUNET_break (sizeof (num) ==
-#ifdef __linux__
- write (h->lp_event,
-#else
- write (h->lp_event_in,
-#endif
- &num,
- sizeof (num)));
- }
- else
- {
- if (NULL != h->lp_task)
- GNUNET_SCHEDULER_cancel (h->lp_task);
- h->lp_task = GNUNET_SCHEDULER_add_at (t,
- &lp_timeout,
- h);
- }
-}
-
-
-/**
- * Start long-polling for @a connection and @a acc
- * for transfers in @a dir. Must be called with the
- * "big lock" held.
- *
- * @param[in,out] h fakebank handle
- * @param[in,out] connection to suspend
- * @param[in,out] acc account affected
- * @param lp_timeout how long to suspend
- * @param dir direction of transfers to watch for
- */
-static void
-start_lp (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- struct Account *acc,
- struct GNUNET_TIME_Relative lp_timeout,
- enum LongPollType dir)
-{
- struct LongPoller *lp;
- bool toc;
-
- lp = GNUNET_new (struct LongPoller);
- lp->account = acc;
- lp->conn = connection;
- lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
- lp->type = dir;
- lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
- lp,
- lp->timeout.abs_value_us);
- toc = (lp ==
- GNUNET_CONTAINER_heap_peek (h->lp_heap));
- GNUNET_CONTAINER_DLL_insert (acc->lp_head,
- acc->lp_tail,
- lp);
- MHD_suspend_connection (connection);
- if (toc)
- reschedule_lp_timeout (h,
- lp->timeout);
-
-}
-
-
-/**
- * Handle incoming HTTP request for /history/outgoing
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request (NULL or &special_ptr)
- */
-static MHD_RESULT
-handle_debit_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct HistoryArgs ha;
- struct Account *acc;
- struct Transaction *pos;
- json_t *history;
- char *debit_payto;
- enum GNUNET_GenericReturnValue ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/outgoing connection %p\n",
- connection);
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- if (&special_ptr == *con_cls)
- ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
- acc = lookup_account (h,
- account);
- GNUNET_asprintf (&debit_payto,
- "payto://x-taler-bank/localhost/%s",
- account);
- history = json_array ();
- if (NULL == history)
- {
- GNUNET_break (0);
- GNUNET_free (debit_payto);
- return MHD_NO;
- }
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- if (! ha.have_start)
- {
- pos = (0 > ha.delta)
- ? acc->out_tail
- : acc->out_head;
- }
- else
- {
- struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
- overflow = (t->row_id != ha.start_idx);
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->debit_account != acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- GNUNET_free (debit_payto);
- if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
- (0 < ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal (
- "outgoing_transactions",
- history));
- }
- *con_cls = &special_ptr;
- start_lp (h,
- connection,
- acc,
- ha.lp_timeout,
- LP_DEBIT);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- json_decref (history);
- return MHD_YES;
- }
- if (t->debit_account != acc)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Invalid start specified, transaction %llu not with account %s!\n",
- (unsigned long long) ha.start_idx,
- account);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_free (debit_payto);
- json_decref (history);
- return MHD_NO;
- }
- if (skip)
- {
- /* range is exclusive, skip the matching entry */
- if (0 > ha.delta)
- pos = t->prev_out;
- else
- pos = t->next_out;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld debit transactions starting (inclusive) from %llu\n",
- (long long) ha.delta,
- (unsigned long long) pos->row_id);
- else
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No debit transactions exist after given starting point\n");
- while ( (0 != ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
- char *credit_payto;
-
- if (T_DEBIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected CREDIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > ha.delta)
- pos = pos->prev_in;
- if (0 < ha.delta)
- pos = pos->next_in;
- continue;
- }
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->credit_account->account_name);
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("credit_account",
- credit_payto),
- GNUNET_JSON_pack_string ("debit_account",
- debit_payto), // FIXME: inefficient to return this here always!
- GNUNET_JSON_pack_string ("exchange_base_url",
- pos->subject.debit.exchange_base_url),
- GNUNET_JSON_pack_data_auto ("wtid",
- &pos->subject.debit.wtid));
- GNUNET_assert (NULL != trans);
- GNUNET_free (credit_payto);
- GNUNET_assert (0 ==
- json_array_append_new (history,
- trans));
- if (ha.delta > 0)
- ha.delta--;
- else
- ha.delta++;
- if (0 > ha.delta)
- pos = pos->prev_out;
- if (0 < ha.delta)
- pos = pos->next_out;
- }
- if ( (0 == json_array_size (history)) &&
- (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
- (0 < ha.delta))
- {
- *con_cls = &special_ptr;
- start_lp (h,
- connection,
- acc,
- ha.lp_timeout,
- LP_DEBIT);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- json_decref (history);
- return MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_free (debit_payto);
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal (
- "outgoing_transactions",
- history));
-}
-
-
-/**
- * Handle incoming HTTP request for /history/incoming
- *
- * @param h the fakebank handle
- * @param connection the connection
- * @param account which account the request is about
- * @param con_cls closure for request (NULL or &special_ptr)
- * @return MHD result code
- */
-static MHD_RESULT
-handle_credit_history (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- void **con_cls)
-{
- struct HistoryArgs ha;
- struct Account *acc;
- const struct Transaction *pos;
- json_t *history;
- char *credit_payto;
- enum GNUNET_GenericReturnValue ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling /history/incoming connection %p (%d)\n",
- connection,
- (*con_cls == &special_ptr));
- if (GNUNET_OK !=
- (ret = parse_history_common_args (h,
- connection,
- &ha)))
- {
- GNUNET_break_op (0);
- return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
- }
- if (&special_ptr == *con_cls)
- ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
- *con_cls = &special_ptr;
- acc = lookup_account (h,
- account);
- history = json_array ();
- GNUNET_assert (NULL != history);
- GNUNET_asprintf (&credit_payto,
- "payto://x-taler-bank/localhost/%s",
- account);
- GNUNET_assert (0 ==
- pthread_mutex_lock (&h->big_lock));
- if (! ha.have_start)
- {
- pos = (0 > ha.delta)
- ? acc->in_tail
- : acc->in_head;
- }
- else
- {
- struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
- bool overflow;
- uint64_t dir;
- bool skip = true;
-
- overflow = ( (NULL != t) && (t->row_id != ha.start_idx) );
- dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
- /* If account does not match, linear scan for
- first matching account. */
- while ( (! overflow) &&
- (NULL != t) &&
- (t->credit_account != acc) )
- {
- skip = false;
- t = h->transactions[(t->row_id + dir) % h->ram_limit];
- if ( (NULL != t) &&
- (t->row_id == ha.start_idx) )
- overflow = true; /* full circle, give up! */
- }
- if ( (NULL == t) ||
- overflow)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No transactions available, suspending request\n");
- GNUNET_free (credit_payto);
- if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
- (0 < ha.delta))
- {
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal (
- "incoming_transactions",
- history));
- }
- *con_cls = &special_ptr;
- start_lp (h,
- connection,
- acc,
- ha.lp_timeout,
- LP_CREDIT);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- json_decref (history);
- return MHD_YES;
- }
- if (skip)
- {
- /* range from application is exclusive, skip the
- matching entry */
- if (0 > ha.delta)
- pos = t->prev_in;
- else
- pos = t->next_in;
- }
- else
- {
- pos = t;
- }
- }
- if (NULL != pos)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning %lld credit transactions starting (inclusive) from %llu\n",
- (long long) ha.delta,
- (unsigned long long) pos->row_id);
- else
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No credit transactions exist after given starting point\n");
- while ( (0 != ha.delta) &&
- (NULL != pos) )
- {
- json_t *trans;
- char *debit_payto;
-
- if (T_CREDIT != pos->type)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Unexpected DEBIT transaction #%llu for account `%s'\n",
- (unsigned long long) pos->row_id,
- account);
- if (0 > ha.delta)
- pos = pos->prev_in;
- if (0 < ha.delta)
- pos = pos->next_in;
- continue;
- }
- GNUNET_asprintf (&debit_payto,
- "payto://x-taler-bank/localhost/%s",
- pos->debit_account->account_name);
- trans = GNUNET_JSON_PACK (
- GNUNET_JSON_pack_uint64 ("row_id",
- pos->row_id),
- GNUNET_JSON_pack_timestamp ("date",
- pos->date),
- TALER_JSON_pack_amount ("amount",
- &pos->amount),
- GNUNET_JSON_pack_string ("credit_account",
- credit_payto), // FIXME: inefficient to repeat this always here!
- GNUNET_JSON_pack_string ("debit_account",
- debit_payto),
- GNUNET_JSON_pack_data_auto ("reserve_pub",
- &pos->subject.credit.reserve_pub));
- GNUNET_assert (NULL != trans);
- GNUNET_free (debit_payto);
- GNUNET_assert (0 ==
- json_array_append_new (history,
- trans));
- if (ha.delta > 0)
- ha.delta--;
- else
- ha.delta++;
- if (0 > ha.delta)
- pos = pos->prev_in;
- if (0 < ha.delta)
- pos = pos->next_in;
- }
- if ( (0 == json_array_size (history)) &&
- (! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
- (0 < ha.delta))
- {
- *con_cls = &special_ptr;
- start_lp (h,
- connection,
- acc,
- ha.lp_timeout,
- LP_CREDIT);
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- json_decref (history);
- return MHD_YES;
- }
- GNUNET_assert (0 ==
- pthread_mutex_unlock (&h->big_lock));
- GNUNET_free (credit_payto);
- return TALER_MHD_REPLY_JSON_PACK (connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal (
- "incoming_transactions",
- history));
-}
-
-
-/**
- * Handle incoming HTTP request.
- *
- * @param h our handle
- * @param connection the connection
- * @param url the requested url
- * @param method the method (POST, GET, ...)
- * @param account which account should process the request
- * @param upload_data request data
- * @param upload_data_size size of @a upload_data in bytes
- * @param con_cls closure
- * @return MHD result code
- */
-static MHD_RESULT
-serve (struct TALER_FAKEBANK_Handle *h,
- struct MHD_Connection *connection,
- const char *account,
- const char *url,
- const char *method,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Fakebank, serving URL `%s' for account `%s'\n",
- url,
- account);
- if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_GET))
- {
- if ( (0 == strcmp (url,
- "/history/incoming")) &&
- (NULL != account) )
- return handle_credit_history (h,
- connection,
- account,
- con_cls);
- if ( (0 == strcmp (url,
- "/history/outgoing")) &&
- (NULL != account) )
- return handle_debit_history (h,
- connection,
- account,
- con_cls);
- if (0 == strcmp (url,
- "/"))
- return handle_home_page (h,
- connection);
- }
- else if (0 == strcasecmp (method,
- MHD_HTTP_METHOD_POST))
- {
- if ( (0 == strcmp (url,
- "/admin/add-incoming")) &&
- (NULL != account) )
- return handle_admin_add_incoming (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- if ( (0 == strcmp (url,
- "/transfer")) &&
- (NULL != account) )
- return handle_transfer (h,
- connection,
- account,
- upload_data,
- upload_data_size,
- con_cls);
- }
- /* Unexpected URL path, just close the connection. */
- TALER_LOG_ERROR ("Breaking URL: %s %s\n",
- method,
- url);
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
- url);
+ cc->ctx_cleaner (cc->ctx);
+ GNUNET_free (cc);
}
@@ -2441,35 +94,28 @@ handle_mhd_request (void *cls,
void **con_cls)
{
struct TALER_FAKEBANK_Handle *h = cls;
- char *account = NULL;
- char *end;
- MHD_RESULT ret;
(void) version;
if (0 == strncmp (url,
- "/taler-wire-gateway/",
- strlen ("/taler-wire-gateway/")))
- url += strlen ("/taler-wire-gateway");
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Handling request for `%s'\n",
- url);
- if ( (strlen (url) > 1) &&
- (NULL != (end = strchr (url + 1, '/'))) )
- {
- account = GNUNET_strndup (url + 1,
- end - url - 1);
- url = end;
- }
- ret = serve (h,
- connection,
- account,
- url,
- method,
- upload_data,
- upload_data_size,
- con_cls);
- GNUNET_free (account);
- return ret;
+ "/taler-integration/",
+ strlen ("/taler-integration/")))
+ {
+ url += strlen ("/taler-integration");
+ return TALER_FAKEBANK_tbi_main_ (h,
+ connection,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+ return TALER_FAKEBANK_bank_main_ (h,
+ connection,
+ url,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
}
@@ -2500,7 +146,7 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
h->mhd_task =
GNUNET_SCHEDULER_add_read_net (tv,
h->mhd_rfd,
- &run_mhd,
+ &TALER_FAKEBANK_run_mhd_,
h);
}
@@ -2574,7 +220,7 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
tv,
wrs,
wws,
- &run_mhd,
+ &TALER_FAKEBANK_run_mhd_,
h);
if (NULL != wrs)
GNUNET_NETWORK_fdset_destroy (wrs);
@@ -2591,8 +237,8 @@ schedule_httpd (struct TALER_FAKEBANK_Handle *h)
*
* @param cls the `struct TALER_FAKEBANK_Handle`
*/
-static void
-run_mhd (void *cls)
+void
+TALER_FAKEBANK_run_mhd_ (void *cls)
{
struct TALER_FAKEBANK_Handle *h = cls;
@@ -2630,6 +276,34 @@ TALER_FAKEBANK_start2 (uint16_t port,
uint64_t ram_limit,
unsigned int num_threads)
{
+ struct TALER_Amount zero;
+
+ if (GNUNET_OK !=
+ TALER_amount_set_zero (currency,
+ &zero))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return TALER_FAKEBANK_start3 ("localhost",
+ port,
+ NULL,
+ currency,
+ ram_limit,
+ num_threads,
+ &zero);
+}
+
+
+struct TALER_FAKEBANK_Handle *
+TALER_FAKEBANK_start3 (const char *hostname,
+ uint16_t port,
+ const char *exchange_url,
+ const char *currency,
+ uint64_t ram_limit,
+ unsigned int num_threads,
+ const struct TALER_Amount *signup_bonus)
+{
struct TALER_FAKEBANK_Handle *h;
if (SIZE_MAX / sizeof (struct Transaction *) < ram_limit)
@@ -2640,7 +314,16 @@ TALER_FAKEBANK_start2 (uint16_t port,
return NULL;
}
GNUNET_assert (strlen (currency) < TALER_CURRENCY_LEN);
+ if (0 != strcmp (signup_bonus->currency,
+ currency))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
h = GNUNET_new (struct TALER_FAKEBANK_Handle);
+ h->signup_bonus = *signup_bonus;
+ if (NULL != exchange_url)
+ h->exchange_url = GNUNET_strdup (exchange_url);
#ifdef __linux__
h->lp_event = -1;
#else
@@ -2697,27 +380,30 @@ TALER_FAKEBANK_start2 (uint16_t port,
}
h->lp_heap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
h->currency = GNUNET_strdup (currency);
+ h->hostname = GNUNET_strdup (hostname);
GNUNET_asprintf (&h->my_baseurl,
- "http://localhost:%u/",
+ "http://%s:%u/",
+ h->hostname,
(unsigned int) port);
if (0 == num_threads)
{
- h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
+ h->mhd_bank = MHD_start_daemon (
+ MHD_USE_DEBUG
#if EPOLL_SUPPORT
- | MHD_USE_EPOLL
+ | MHD_USE_EPOLL
#endif
- | MHD_USE_DUAL_STACK
- | MHD_ALLOW_SUSPEND_RESUME,
- port,
- NULL, NULL,
- &handle_mhd_request, h,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback, h,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_CONNECTION_LIMIT,
- (unsigned int) 65536,
- MHD_OPTION_END);
+ | MHD_USE_DUAL_STACK
+ | MHD_ALLOW_SUSPEND_RESUME,
+ port,
+ NULL, NULL,
+ &handle_mhd_request, h,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, h,
+ MHD_OPTION_LISTEN_BACKLOG_SIZE,
+ (unsigned int) 1024,
+ MHD_OPTION_CONNECTION_LIMIT,
+ (unsigned int) 65536,
+ MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
TALER_FAKEBANK_stop (h);
@@ -2745,6 +431,7 @@ TALER_FAKEBANK_start2 (uint16_t port,
#else
{
int pipefd[2];
+
if (0 != pipe (pipefd))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
@@ -2759,7 +446,7 @@ TALER_FAKEBANK_start2 (uint16_t port,
if (0 !=
pthread_create (&h->lp_thread,
NULL,
- &lp_expiration_thread,
+ &TALER_FAKEBANK_lp_expiration_thread_,
h))
{
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
@@ -2776,24 +463,25 @@ TALER_FAKEBANK_start2 (uint16_t port,
TALER_FAKEBANK_stop (h);
return NULL;
}
- h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG
- | MHD_USE_AUTO_INTERNAL_THREAD
- | MHD_ALLOW_SUSPEND_RESUME
- | MHD_USE_TURBO
- | MHD_USE_TCP_FASTOPEN
- | MHD_USE_DUAL_STACK,
- port,
- NULL, NULL,
- &handle_mhd_request, h,
- MHD_OPTION_NOTIFY_COMPLETED,
- &handle_mhd_completion_callback, h,
- MHD_OPTION_LISTEN_BACKLOG_SIZE,
- (unsigned int) 1024,
- MHD_OPTION_CONNECTION_LIMIT,
- (unsigned int) 65536,
- MHD_OPTION_THREAD_POOL_SIZE,
- num_threads,
- MHD_OPTION_END);
+ h->mhd_bank = MHD_start_daemon (
+ MHD_USE_DEBUG
+ | MHD_USE_AUTO_INTERNAL_THREAD
+ | MHD_ALLOW_SUSPEND_RESUME
+ | MHD_USE_TURBO
+ | MHD_USE_TCP_FASTOPEN
+ | MHD_USE_DUAL_STACK,
+ port,
+ NULL, NULL,
+ &handle_mhd_request, h,
+ MHD_OPTION_NOTIFY_COMPLETED,
+ &handle_mhd_completion_callback, h,
+ MHD_OPTION_LISTEN_BACKLOG_SIZE,
+ (unsigned int) 1024,
+ MHD_OPTION_CONNECTION_LIMIT,
+ (unsigned int) 65536,
+ MHD_OPTION_THREAD_POOL_SIZE,
+ num_threads,
+ MHD_OPTION_END);
if (NULL == h->mhd_bank)
{
GNUNET_break (0);
diff --git a/src/bank-lib/fakebank.h b/src/bank-lib/fakebank.h
new file mode 100644
index 000000000..a9d61d8b1
--- /dev/null
+++ b/src/bank-lib/fakebank.h
@@ -0,0 +1,691 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank.h
+ * @brief general state of the fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_H
+#define FAKEBANK_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * How long are exchange base URLs allowed to be at most?
+ * Set to a relatively low number as this does contribute
+ * significantly to our RAM consumption.
+ */
+#define MAX_URL_LEN 64
+
+
+/**
+ * Maximum POST request size.
+ */
+#define REQUEST_BUFFER_MAX (4 * 1024)
+
+
+/**
+ * Per account information.
+ */
+struct Account;
+
+
+/**
+ * Types of long polling activities.
+ */
+enum LongPollType
+{
+ /**
+ * Transfer TO the exchange.
+ */
+ LP_CREDIT,
+
+ /**
+ * Transfer FROM the exchange.
+ */
+ LP_DEBIT,
+
+ /**
+ * Withdraw operation completion/abort.
+ */
+ LP_WITHDRAW
+
+};
+
+/**
+ * Client waiting for activity on this account.
+ */
+struct LongPoller
+{
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *prev;
+
+ /**
+ * Fakebank this long poller belongs with.
+ */
+ struct TALER_FAKEBANK_Handle *h;
+
+ /**
+ * Account this long poller is waiting on.
+ */
+ struct Account *account;
+
+ /**
+ * Withdraw operation we are waiting on,
+ * only if @e type is #LP_WITHDRAW, otherwise NULL.
+ */
+ const struct WithdrawalOperation *wo;
+
+ /**
+ * Entry in the heap for this long poller.
+ */
+ struct GNUNET_CONTAINER_HeapNode *hn;
+
+ /**
+ * Client that is waiting for transactions.
+ */
+ struct MHD_Connection *conn;
+
+ /**
+ * When will this long poller time out?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * What does the @e connection wait for?
+ */
+ enum LongPollType type;
+
+};
+
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction;
+
+
+/**
+ * Information we keep per withdraw operation.
+ */
+struct WithdrawalOperation
+{
+ /**
+ * Unique (random) operation ID.
+ */
+ struct GNUNET_ShortHashCode wopid;
+
+ /**
+ * Debited account.
+ */
+ struct Account *debit_account;
+
+ /**
+ * Target exchange account, or NULL if unknown.
+ */
+ const struct Account *exchange_account;
+
+ /**
+ * RowID of the resulting transaction, if any. Otherwise 0.
+ */
+ uint64_t row_id;
+
+ /**
+ * Amount transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Public key of the reserve, wire transfer subject.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * When was the transaction made? 0 if not yet.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Was the withdrawal aborted?
+ */
+ bool aborted;
+
+ /**
+ * Did the bank confirm the withdrawal?
+ */
+ bool confirmation_done;
+
+ /**
+ * Is @e reserve_pub initialized?
+ */
+ bool selection_done;
+
+};
+
+
+/**
+ * Per account information.
+ */
+struct Account
+{
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_head;
+
+ /**
+ * Inbound transactions for this account in a MDLL.
+ */
+ struct Transaction *in_tail;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_head;
+
+ /**
+ * Outbound transactions for this account in a MDLL.
+ */
+ struct Transaction *out_tail;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *lp_head;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct LongPoller *lp_tail;
+
+ /**
+ * Account name (string, not payto!)
+ */
+ char *account_name;
+
+ /**
+ * Receiver name for payto:// URIs.
+ */
+ char *receiver_name;
+
+ /**
+ * Payto URI for this account.
+ */
+ char *payto_uri;
+
+ /**
+ * Password set for the account (if any).
+ */
+ char *password;
+
+ /**
+ * Current account balance.
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * true if the balance is negative.
+ */
+ bool is_negative;
+
+};
+
+
+/**
+ * Details about a transcation we (as the simulated bank) received.
+ */
+struct Transaction
+{
+ /**
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *next_in;
+
+ /**
+ * We store inbound transactions in a MDLL.
+ */
+ struct Transaction *prev_in;
+
+ /**
+ * We store outbound transactions in a MDLL.
+ */
+ struct Transaction *next_out;
+
+ /**
+ * We store outbound transactions in a MDLL.
+ */
+ struct Transaction *prev_out;
+
+ /**
+ * Amount to be transferred.
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Account to debit.
+ */
+ struct Account *debit_account;
+
+ /**
+ * Account to credit.
+ */
+ struct Account *credit_account;
+
+ /**
+ * Random unique identifier for the request.
+ * Used to detect idempotent requests.
+ */
+ struct GNUNET_HashCode request_uid;
+
+ /**
+ * When did the transaction happen?
+ */
+ struct GNUNET_TIME_Timestamp date;
+
+ /**
+ * Number of this transaction.
+ */
+ uint64_t row_id;
+
+ /**
+ * What does the @e subject contain?
+ */
+ enum
+ {
+ /**
+ * Transfer TO the exchange.
+ */
+ T_CREDIT,
+
+ /**
+ * Transfer FROM the exchange.
+ */
+ T_DEBIT,
+
+ /**
+ * Exchange-to-exchange WAD transfer.
+ */
+ T_WAD,
+ } type;
+
+ /**
+ * Wire transfer subject.
+ */
+ union
+ {
+
+ /**
+ * Used if @e type is T_DEBIT.
+ */
+ struct
+ {
+
+ /**
+ * Subject of the transfer.
+ */
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Base URL of the exchange.
+ */
+ char exchange_base_url[MAX_URL_LEN];
+
+ } debit;
+
+ /**
+ * Used if @e type is T_CREDIT.
+ */
+ struct
+ {
+
+ /**
+ * Reserve public key of the credit operation.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ } credit;
+
+ /**
+ * Used if @e type is T_WAD.
+ */
+ struct
+ {
+
+ /**
+ * Subject of the transfer.
+ */
+ struct TALER_WadIdentifierP wad;
+
+ /**
+ * Base URL of the originating exchange.
+ */
+ char origin_base_url[MAX_URL_LEN];
+
+ } wad;
+
+ } subject;
+
+ /**
+ * Has this transaction not yet been subjected to
+ * #TALER_FAKEBANK_check_credit() or #TALER_FAKEBANK_check_debit() and
+ * should thus be counted in #TALER_FAKEBANK_check_empty()?
+ */
+ bool unchecked;
+};
+
+
+/**
+ * Function called to clean up context of a connection.
+ *
+ * @param ctx context to clean up
+ */
+typedef void
+(*ConnectionCleaner)(void *ctx);
+
+/**
+ * Universal context we keep per connection.
+ */
+struct ConnectionContext
+{
+ /**
+ * Function we call upon completion to clean up.
+ */
+ ConnectionCleaner ctx_cleaner;
+
+ /**
+ * Request-handler specific context.
+ */
+ void *ctx;
+};
+
+
+/**
+ * This is the "base" structure for both the /history and the
+ * /history-range API calls.
+ */
+struct HistoryArgs
+{
+
+ /**
+ * Bank account number of the requesting client.
+ */
+ uint64_t account_number;
+
+ /**
+ * Index of the starting transaction, exclusive (!).
+ */
+ uint64_t start_idx;
+
+ /**
+ * Requested number of results and order
+ * (positive: ascending, negative: descending)
+ */
+ int64_t delta;
+
+ /**
+ * Timeout for long polling.
+ */
+ struct GNUNET_TIME_Relative lp_timeout;
+
+ /**
+ * true if starting point was given.
+ */
+ bool have_start;
+
+};
+
+
+/**
+ * Context we keep per history request.
+ */
+struct HistoryContext
+{
+ /**
+ * When does this request time out.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Client arguments for this request.
+ */
+ struct HistoryArgs ha;
+
+ /**
+ * Account the request is about.
+ */
+ struct Account *acc;
+
+ /**
+ * JSON object we are building to return.
+ */
+ json_t *history;
+
+};
+
+
+/**
+ * Context we keep per get withdrawal operation request.
+ */
+struct WithdrawContext
+{
+ /**
+ * When does this request time out.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * The withdrawal operation this is about.
+ */
+ struct WithdrawalOperation *wo;
+
+};
+
+
+/**
+ * Handle for the fake bank.
+ */
+struct TALER_FAKEBANK_Handle
+{
+ /**
+ * We store transactions in a revolving array.
+ */
+ struct Transaction **transactions;
+
+ /**
+ * HTTP server we run to pretend to be the "test" bank.
+ */
+ struct MHD_Daemon *mhd_bank;
+
+ /**
+ * Task running HTTP server for the "test" bank,
+ * unless we are using a thread pool (then NULL).
+ */
+ struct GNUNET_SCHEDULER_Task *mhd_task;
+
+ /**
+ * Task for expiring long-polling connections,
+ * unless we are using a thread pool (then NULL).
+ */
+ struct GNUNET_SCHEDULER_Task *lp_task;
+
+ /**
+ * Task for expiring long-polling connections, unless we are using the
+ * GNUnet scheduler (then NULL).
+ */
+ pthread_t lp_thread;
+
+ /**
+ * MIN-heap of long pollers, sorted by timeout.
+ */
+ struct GNUNET_CONTAINER_Heap *lp_heap;
+
+ /**
+ * Hashmap of reserve public keys to
+ * `struct Transaction` with that reserve public
+ * key. Used to prevent public-key reuse.
+ */
+ struct GNUNET_CONTAINER_MultiPeerMap *rpubs;
+
+ /**
+ * Hashmap of short hashes (wopids) to
+ * `struct WithdrawalOperation`.
+ * Used to lookup withdrawal operations.
+ */
+ struct GNUNET_CONTAINER_MultiShortmap *wops;
+
+ /**
+ * (Base) URL to suggest for the exchange. Can
+ * be NULL if there is no suggestion to be made.
+ */
+ char *exchange_url;
+
+ /**
+ * Lock for accessing @a rpubs map.
+ */
+ pthread_mutex_t rpubs_lock;
+
+ /**
+ * Hashmap of hashes of account names to `struct Account`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *accounts;
+
+ /**
+ * Lock for accessing @a accounts hash map.
+ */
+ pthread_mutex_t accounts_lock;
+
+ /**
+ * Hashmap of hashes of transaction request_uids to `struct Transaction`.
+ */
+ struct GNUNET_CONTAINER_MultiHashMap *uuid_map;
+
+ /**
+ * Lock for accessing @a uuid_map.
+ */
+ pthread_mutex_t uuid_map_lock;
+
+ /**
+ * Lock for accessing the internals of
+ * accounts and transaction array entries.
+ */
+ pthread_mutex_t big_lock;
+
+ /**
+ * How much money should be put into new accounts
+ * on /register.
+ */
+ struct TALER_Amount signup_bonus;
+
+ /**
+ * Current transaction counter.
+ */
+ uint64_t serial_counter;
+
+ /**
+ * Number of transactions we keep in memory (at most).
+ */
+ uint64_t ram_limit;
+
+ /**
+ * Currency used by the fakebank.
+ */
+ char *currency;
+
+ /**
+ * Hostname of the fakebank.
+ */
+ char *hostname;
+
+ /**
+ * BaseURL of the fakebank.
+ */
+ char *my_baseurl;
+
+ /**
+ * Our port number.
+ */
+ uint16_t port;
+
+#ifdef __linux__
+ /**
+ * Event FD to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event;
+#else
+ /**
+ * Pipe input to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event_in;
+
+ /**
+ * Pipe output to signal @a lp_thread a change in
+ * @a lp_heap.
+ */
+ int lp_event_out;
+#endif
+
+ /**
+ * Set to true once we are shutting down.
+ */
+ bool in_shutdown;
+
+ /**
+ * Should we run MHD immediately again?
+ */
+ bool mhd_again;
+
+#if EPOLL_SUPPORT
+ /**
+ * Boxed @e mhd_fd.
+ */
+ struct GNUNET_NETWORK_Handle *mhd_rfd;
+
+ /**
+ * File descriptor to use to wait for MHD.
+ */
+ int mhd_fd;
+#endif
+};
+
+
+/**
+ * Task run whenever HTTP server operations are pending.
+ *
+ * @param cls the `struct TALER_FAKEBANK_Handle`
+ */
+void
+TALER_FAKEBANK_run_mhd_ (void *cls);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_api_check.c b/src/bank-lib/fakebank_api_check.c
new file mode 100644
index 000000000..04656ebab
--- /dev/null
+++ b/src/bank-lib/fakebank_api_check.c
@@ -0,0 +1,238 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_api_check.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Generate log messages for failed check operation.
+ *
+ * @param h handle to output transaction log for
+ */
+static void
+check_log (struct TALER_FAKEBANK_Handle *h)
+{
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ {
+ struct Transaction *t = h->transactions[i];
+
+ if (NULL == t)
+ continue;
+ if (! t->unchecked)
+ continue;
+ switch (t->type)
+ {
+ case T_DEBIT:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ t->subject.debit.exchange_base_url,
+ "DEBIT");
+ break;
+ case T_CREDIT:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ TALER_B2S (&t->subject.credit.reserve_pub),
+ "CREDIT");
+ break;
+ case T_WAD:
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "%s -> %s (%s) %s[%s] (%s)\n",
+ t->debit_account->account_name,
+ t->credit_account->account_name,
+ TALER_amount2s (&t->amount),
+ t->subject.wad.origin_base_url,
+ TALER_B2S (&t->subject.wad),
+ "WAD");
+ break;
+ }
+ }
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_debit (struct TALER_FAKEBANK_Handle *h,
+ const struct TALER_Amount *want_amount,
+ const char *want_debit,
+ const char *want_credit,
+ const char *exchange_base_url,
+ struct TALER_WireTransferIdentifierRawP *wtid)
+{
+ struct Account *debit_account;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 ==
+ strcasecmp (want_amount->currency,
+ h->currency));
+ debit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_debit,
+ NULL);
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_credit,
+ NULL);
+ if (NULL == debit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT), but debit account does not even exist!\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == credit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT), but credit account does not even exist!\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+ }
+ for (struct Transaction *t = debit_account->out_tail;
+ NULL != t;
+ t = t->prev_out)
+ {
+ if ( (t->unchecked) &&
+ (credit_account == t->credit_account) &&
+ (T_DEBIT == t->type) &&
+ (0 == TALER_amount_cmp (want_amount,
+ &t->amount)) &&
+ (0 == strcasecmp (exchange_base_url,
+ t->subject.debit.exchange_base_url)) )
+ {
+ *wtid = t->subject.debit.wtid;
+ t->unchecked = false;
+ return GNUNET_OK;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Did not find matching transaction! I have:\n");
+ check_log (h);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted: %s->%s (%s) from exchange %s (DEBIT)\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ exchange_base_url);
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_credit (struct TALER_FAKEBANK_Handle *h,
+ const struct TALER_Amount *want_amount,
+ const char *want_debit,
+ const char *want_credit,
+ const struct TALER_ReservePublicKeyP *reserve_pub)
+{
+ struct Account *debit_account;
+ struct Account *credit_account;
+
+ GNUNET_assert (0 == strcasecmp (want_amount->currency,
+ h->currency));
+ debit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_debit,
+ NULL);
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ want_credit,
+ NULL);
+ if (NULL == debit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but debit account is unknown.\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+ }
+ if (NULL == credit_account)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT) but credit account is unknown.\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+ }
+ for (struct Transaction *t = credit_account->in_tail;
+ NULL != t;
+ t = t->prev_in)
+ {
+ if ( (t->unchecked) &&
+ (debit_account == t->debit_account) &&
+ (T_CREDIT == t->type) &&
+ (0 == TALER_amount_cmp (want_amount,
+ &t->amount)) &&
+ (0 == GNUNET_memcmp (reserve_pub,
+ &t->subject.credit.reserve_pub)) )
+ {
+ t->unchecked = false;
+ return GNUNET_OK;
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Did not find matching transaction!\nI have:\n");
+ check_log (h);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "I wanted:\n%s -> %s (%s) with subject %s (CREDIT)\n",
+ want_debit,
+ want_credit,
+ TALER_amount2s (want_amount),
+ TALER_B2S (reserve_pub));
+ return GNUNET_SYSERR;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h)
+{
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ {
+ struct Transaction *t = h->transactions[i];
+
+ if ( (NULL != t) &&
+ (t->unchecked) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Expected empty transaction set, but I have:\n");
+ check_log (h);
+ return GNUNET_SYSERR;
+ }
+ }
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_bank.c b/src/bank-lib/fakebank_bank.c
new file mode 100644
index 000000000..7c2d39ab4
--- /dev/null
+++ b/src/bank-lib/fakebank_bank.c
@@ -0,0 +1,524 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank.c
+ * @brief Main dispatcher for the Taler Bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank.h"
+#include "fakebank_tbr.h"
+#include "fakebank_twg.h"
+#include "fakebank_bank_get_accounts.h"
+#include "fakebank_bank_get_withdrawals.h"
+#include "fakebank_bank_get_root.h"
+#include "fakebank_bank_post_accounts_withdrawals.h"
+#include "fakebank_bank_post_withdrawals_abort.h"
+#include "fakebank_bank_post_withdrawals_confirm.h"
+#include "fakebank_bank_post_withdrawals_id_op.h"
+#include "fakebank_bank_testing_register.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET;
+
+ if ( (0 == strcmp (url,
+ "/")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET / */
+ return TALER_FAKEBANK_bank_get_root_ (h,
+ connection);
+ }
+
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /config */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "4:0:4"), /* not sure, API versions are not properly marked up! */
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("implementation",
+ "urn:net:taler:specs:bank:fakebank"),
+ GNUNET_JSON_pack_string ("name",
+ "taler-corebank"));
+ }
+
+ if ( (0 == strcmp (url,
+ "/public-accounts")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /public-accounts */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_array_steal ("public_accounts",
+ json_array ()));
+ }
+
+ /* account registration API */
+ if ( (0 == strcmp (url,
+ "/accounts")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ /* POST /accounts */
+ return TALER_FAKEBANK_bank_testing_register_ (h,
+ connection,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+
+ if ( (0 == strcmp (url,
+ "/accounts")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /accounts */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (0 == strcmp (url,
+ "/cashout-rate")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /cashout-rate */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (0 == strcmp (url,
+ "/cashouts")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /cashouts */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (0 == strncmp (url,
+ "/withdrawals/",
+ strlen ("/withdrawals/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /withdrawals/$WID */
+ const char *wid;
+
+ wid = &url[strlen ("/withdrawals/")];
+ return TALER_FAKEBANK_bank_get_withdrawals_ (h,
+ connection,
+ wid);
+ }
+
+ if ( (0 == strncmp (url,
+ "/withdrawals/",
+ strlen ("/withdrawals/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ /* POST /withdrawals/$WID* */
+ const char *wid = url + strlen ("/withdrawals/");
+ const char *opid = strchr (wid,
+ '/');
+ char *wi;
+
+ if (NULL == opid)
+ {
+ /* POST /withdrawals/$WID (not defined) */
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+ }
+ wi = GNUNET_strndup (wid,
+ opid - wid);
+ if (0 == strcmp (opid,
+ "/abort"))
+ {
+ /* POST /withdrawals/$WID/abort */
+ MHD_RESULT ret;
+
+ ret = TALER_FAKEBANK_bank_withdrawals_abort_ (h,
+ connection,
+ wi);
+ GNUNET_free (wi);
+ return ret;
+ }
+ if (0 == strcmp (opid,
+ "/confirm"))
+ {
+ /* POST /withdrawals/$WID/confirm */
+ MHD_RESULT ret;
+
+ ret = TALER_FAKEBANK_bank_withdrawals_confirm_ (h,
+ connection,
+ wi);
+ GNUNET_free (wi);
+ return ret;
+ }
+ }
+
+ if (0 == strncmp (url,
+ "/accounts/",
+ strlen ("/accounts/")))
+ {
+ const char *acc_name = &url[strlen ("/accounts/")];
+ const char *end_acc = strchr (acc_name,
+ '/');
+
+ if ( (NULL != end_acc) &&
+ (0 == strncmp (end_acc,
+ "/taler-wire-gateway/",
+ strlen ("/taler-wire-gateway/"))) )
+ {
+ /* $METHOD /accounts/$ACCOUNT/taler-wire-gateway/ */
+ char *acc;
+ MHD_RESULT ret;
+
+ acc = GNUNET_strndup (acc_name,
+ end_acc - acc_name);
+ end_acc += strlen ("/taler-wire-gateway");
+ ret = TALER_FAKEBANK_twg_main_ (h,
+ connection,
+ acc,
+ end_acc,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free (acc);
+ return ret;
+ }
+
+ if ( (NULL != end_acc) &&
+ (0 == strncmp (end_acc,
+ "/taler-revenue/",
+ strlen ("/taler-revenue/"))) )
+ {
+ /* $METHOD /accounts/$ACCOUNT/taler-revenue/ */
+ char *acc;
+ MHD_RESULT ret;
+
+ acc = GNUNET_strndup (acc_name,
+ end_acc - acc_name);
+ end_acc += strlen ("/taler-revenue");
+ ret = TALER_FAKEBANK_tbr_main_ (h,
+ connection,
+ acc,
+ end_acc,
+ method,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free (acc);
+ return ret;
+ }
+
+ if ( (NULL == end_acc) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /accounts/$ACCOUNT */
+ return TALER_FAKEBANK_bank_get_accounts_ (h,
+ connection,
+ acc_name);
+ }
+
+ if ( (NULL == end_acc) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_PATCH)) )
+ {
+ /* PATCH /accounts/$USERNAME */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (NULL == end_acc) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_DELETE)) )
+ {
+ /* DELETE /accounts/$USERNAME */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (NULL != end_acc) &&
+ (0 == strcmp ("/auth",
+ end_acc)) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_PATCH)) )
+ {
+ /* PATCH /accounts/$USERNAME/auth */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+ if ( (NULL != end_acc) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /accounts/$ACCOUNT/+ */
+
+ if (0 == strcmp (end_acc,
+ "/transactions"))
+ {
+ /* GET /accounts/$USERNAME/transactions */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strncmp (end_acc,
+ "/transactions/",
+ strlen ("/transactions/")))
+ {
+ /* GET /accounts/$USERNAME/transactions/$TID */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strcmp (end_acc,
+ "/withdrawals"))
+ {
+ /* GET /accounts/$USERNAME/withdrawals */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strcmp (end_acc,
+ "/cashouts"))
+ {
+ /* GET /accounts/$USERNAME/cashouts */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strncmp (end_acc,
+ "/cashouts/",
+ strlen ("/cashouts/")))
+ {
+ /* GET /accounts/$USERNAME/cashouts/$CID */
+ GNUNET_break (0); /* not implemented */
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+
+
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ acc_name);
+ }
+
+ if ( (NULL != end_acc) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ /* POST /accounts/$ACCOUNT/+ */
+ char *acc;
+
+ acc = GNUNET_strndup (acc_name,
+ end_acc - acc_name);
+ if (0 == strcmp (end_acc,
+ "/cashouts"))
+ {
+ /* POST /accounts/$USERNAME/cashouts */
+ GNUNET_break (0); /* not implemented */
+ GNUNET_free (acc);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strncmp (end_acc,
+ "/cashouts/",
+ strlen ("/cashouts/")))
+ {
+ /* POST /accounts/$USERNAME/cashouts/+ */
+ const char *cid = end_acc + strlen ("/cashouts/");
+ const char *opid = strchr (cid,
+ '/');
+ char *ci;
+
+ if (NULL == opid)
+ {
+ /* POST /accounts/$ACCOUNT/cashouts/$CID (not defined) */
+ GNUNET_break_op (0);
+ GNUNET_free (acc);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ acc_name);
+ }
+ ci = GNUNET_strndup (cid,
+ opid - cid);
+ if (0 == strcmp (opid,
+ "/abort"))
+ {
+ GNUNET_break (0); /* not implemented */
+ GNUNET_free (ci);
+ GNUNET_free (acc);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ if (0 == strcmp (opid,
+ "/confirm"))
+ {
+ GNUNET_break (0); /* not implemented */
+ GNUNET_free (ci);
+ GNUNET_free (acc);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ TALER_EC_GENERIC_CLIENT_INTERNAL_ERROR,
+ url);
+ }
+ }
+
+ if (0 == strcmp (end_acc,
+ "/withdrawals"))
+ {
+ /* POST /accounts/$ACCOUNT/withdrawals */
+ MHD_RESULT ret;
+
+ ret = TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ h,
+ connection,
+ acc,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free (acc);
+ return ret;
+ }
+
+ if (0 == strncmp (end_acc,
+ "/withdrawals/",
+ strlen ("/withdrawals/")))
+ {
+ /* POST /accounts/$ACCOUNT/withdrawals/$WID/$OP */
+ MHD_RESULT ret;
+ const char *pos = &end_acc[strlen ("/withdrawals/")];
+ const char *op = strchr (pos, '/');
+
+ if (NULL != op)
+ {
+ char *wid = GNUNET_strndup (pos,
+ op - pos);
+
+ ret = TALER_FAKEBANK_bank_withdrawals_id_op_ (
+ h,
+ connection,
+ acc,
+ wid,
+ op,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ GNUNET_free (wid);
+ GNUNET_free (acc);
+ return ret;
+ }
+ GNUNET_free (acc);
+ }
+ }
+ }
+
+ GNUNET_break_op (0);
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_bank.h b/src/bank-lib/fakebank_bank.h
new file mode 100644
index 000000000..1c51f88f8
--- /dev/null
+++ b/src/bank-lib/fakebank_bank.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank.h
+ * @brief Main dispatcher for the Taler Bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_H
+#define FAKEBANK_BANK_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request to the Taler bank API.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_accounts_withdrawals.c b/src/bank-lib/fakebank_bank_accounts_withdrawals.c
new file mode 100644
index 000000000..bb435d975
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_accounts_withdrawals.c
@@ -0,0 +1,101 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_accounts_withdrawals.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_accounts_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ if (wo->debit_account != acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ account_name);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aborted",
+ wo->aborted),
+ GNUNET_JSON_pack_bool ("selection_done",
+ wo->selection_done),
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("selected_exchange_account",
+ wo->exchange_account->payto_uri)),
+ GNUNET_JSON_pack_allow_null (
+ wo->selection_done
+ ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
+ &wo->reserve_pub)
+ : GNUNET_JSON_pack_string ("selected_reserve_pub",
+ NULL)),
+ TALER_JSON_pack_amount ("amount",
+ &wo->amount));
+}
diff --git a/src/bank-lib/fakebank_bank_accounts_withdrawals.h b/src/bank-lib/fakebank_bank_accounts_withdrawals.h
new file mode 100644
index 000000000..2a598dee9
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_accounts_withdrawals.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_accounts_withdrawals.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_ACCOUNTS_WITHDRAWALS_H
+#define FAKEBANK_BANK_ACCOUNTS_WITHDRAWALS_H
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle GET /accounts/${account_name}/withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_accounts.c b/src/bank-lib/fakebank_bank_get_accounts.c
new file mode 100644
index 000000000..e85387d2a
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts.c
@@ -0,0 +1,80 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts.c
+ * @brief implements the Taler Bank API "GET /accounts/" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_accounts.h"
+#include "fakebank_common_lookup.h"
+
+/**
+ * Handle GET /accounts/${account_name} request of the Taler bank API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name)
+{
+ struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("payto_uri",
+ acc->payto_uri),
+ GNUNET_JSON_pack_object_steal (
+ "balance",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("credit_debit_indicator",
+ acc->is_negative
+ ? "debit"
+ : "credit"),
+ TALER_JSON_pack_amount ("amount",
+ &acc->balance))));
+}
diff --git a/src/bank-lib/fakebank_bank_get_accounts.h b/src/bank-lib/fakebank_bank_get_accounts.h
new file mode 100644
index 000000000..7d3872133
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_accounts.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_accounts.h
+ * @brief implements the Taler Bank API "GET /accounts/" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_ACCOUNTS_H
+#define FAKEBANK_BANK_GET_ACCOUNTS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle GET /accounts/${account_name} request of the Taler bank API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_accounts_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_root.c b/src/bank-lib/fakebank_bank_get_root.c
new file mode 100644
index 000000000..8c34697b4
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_root.c
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_root.c
+ * @brief handle a GET "/" request for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_root.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_root_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_bank_get_root.h b/src/bank-lib/fakebank_bank_get_root.h
new file mode 100644
index 000000000..2eedb94a1
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_root.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_root.c
+ * @brief handle a GET "/" request for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_ROOT_H
+#define FAKEBANK_BANK_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_root_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_get_withdrawals.c b/src/bank-lib/fakebank_bank_get_withdrawals.c
new file mode 100644
index 000000000..7f65e8660
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_withdrawals.c
@@ -0,0 +1,87 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_withdrawals.c
+ * @brief implements the Taler Bank API "GET /withdrawals/$WID" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Handle GET /withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_bool ("aborted",
+ wo->aborted),
+ GNUNET_JSON_pack_bool ("selection_done",
+ wo->selection_done),
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("selected_exchange_account",
+ wo->exchange_account->payto_uri)),
+ GNUNET_JSON_pack_allow_null (
+ wo->selection_done
+ ? GNUNET_JSON_pack_data_auto ("selected_reserve_pub",
+ &wo->reserve_pub)
+ : GNUNET_JSON_pack_string ("selected_reserve_pub",
+ NULL)),
+ TALER_JSON_pack_amount ("amount",
+ &wo->amount));
+}
diff --git a/src/bank-lib/fakebank_bank_get_withdrawals.h b/src/bank-lib/fakebank_bank_get_withdrawals.h
new file mode 100644
index 000000000..62a96866c
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_get_withdrawals.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_get_withdrawals.h
+ * @brief implements the Taler Bank API "GET /accounts/ACC/withdrawals/WID" handler
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_GET_WITHDRAWALS_H
+#define FAKEBANK_BANK_GET_WITHDRAWALS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_get_withdrawals.h"
+
+
+/**
+ * Handle GET /withdrawals/{withdrawal_id} request
+ * to the Taler bank access API.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param withdrawal_id withdrawal ID to return status of
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_get_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c
new file mode 100644
index 000000000..7fbb93352
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.c
@@ -0,0 +1,196 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals.c
+ * @brief implementation of the bank API's POST /accounts/AID/withdrawals endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_accounts_withdrawals.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Execute POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param amount amount to withdraw
+ * @return MHD result code
+ */
+static MHD_RESULT
+do_post_account_withdrawals (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const struct TALER_Amount *amount)
+{
+ struct Account *acc;
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account_name,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account_name);
+ }
+ wo = GNUNET_new (struct WithdrawalOperation);
+ wo->debit_account = acc;
+ wo->amount = *amount;
+ if (NULL == h->wops)
+ {
+ h->wops = GNUNET_CONTAINER_multishortmap_create (32,
+ GNUNET_YES);
+ }
+ while (1)
+ {
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
+ &wo->wopid,
+ sizeof (wo->wopid));
+ if (GNUNET_OK ==
+ GNUNET_CONTAINER_multishortmap_put (h->wops,
+ &wo->wopid,
+ wo,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
+ break;
+ }
+ {
+ char *wopids;
+ char *uri;
+ MHD_RESULT res;
+
+ wopids = GNUNET_STRINGS_data_to_string_alloc (&wo->wopid,
+ sizeof (wo->wopid));
+ GNUNET_asprintf (&uri,
+ "taler+http://withdraw/%s:%u/taler-integration/%s",
+ h->hostname,
+ (unsigned int) h->port,
+ wopids);
+ GNUNET_free (wopids);
+ res = TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("taler_withdraw_uri",
+ uri),
+ GNUNET_JSON_pack_data_auto ("withdrawal_id",
+ &wo->wopid));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ GNUNET_free (uri);
+ return res;
+ }
+}
+
+
+/**
+ * Handle POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ struct TALER_Amount amount;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ res = do_post_account_withdrawals (h,
+ connection,
+ account_name,
+ &amount);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h
new file mode 100644
index 000000000..1becf1efc
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_accounts_withdrawals.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_accounts_withdrawals.c
+ * @brief implementation of the bank API's POST /accounts/AID/withdrawals endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_H
+#define FAKEBANK_BANK_POST_ACCOUNTS_WITHDRAWALS_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/$account_name/withdrawals request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account_name name of the account
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_post_account_withdrawals_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account_name,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_abort.c b/src/bank-lib/fakebank_bank_post_withdrawals_abort.c
new file mode 100644
index 000000000..f8ebf1b93
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_abort.c
@@ -0,0 +1,74 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_abort.c
+ * @brief implement bank API withdrawals /abort endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_withdrawals_abort.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_abort_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ if (wo->confirmation_done)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
+ withdrawal_id);
+ }
+ wo->aborted = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_json (connection,
+ json_object (), /* FIXME: #7301 */
+ MHD_HTTP_OK);
+}
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_abort.h b/src/bank-lib/fakebank_bank_post_withdrawals_abort.h
new file mode 100644
index 000000000..920b0b802
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_abort.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_abort.h
+ * @brief implement bank API withdrawals /abort endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_WITHDRAWALS_ABORT_H
+#define FAKEBANK_BANK_POST_WITHDRAWALS_ABORT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /withdrawals/{withdrawal_id}/abort request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_abort_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_confirm.c b/src/bank-lib/fakebank_bank_post_withdrawals_confirm.c
new file mode 100644
index 000000000..2fa67c970
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_confirm.c
@@ -0,0 +1,107 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_confirm.c
+ * @brief implement bank API withdrawals /confirm endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_withdrawals_confirm.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_make_admin_transfer.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_confirm_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ if (NULL == wo->exchange_account)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED,
+ NULL);
+ }
+ if (wo->aborted)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
+ withdrawal_id);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (GNUNET_OK !=
+ TALER_FAKEBANK_make_admin_transfer_ (
+ h,
+ wo->debit_account->account_name,
+ wo->exchange_account->account_name,
+ &wo->amount,
+ &wo->reserve_pub,
+ &wo->row_id,
+ &wo->timestamp))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ /* Re-acquiring the lock and continuing to operate on 'wo'
+ is currently (!) acceptable because we NEVER free 'wo'
+ until shutdown. We may want to revise this if keeping
+ all withdraw operations in RAM becomes an issue... */
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo->confirmation_done = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_json (connection,
+ json_object (),
+ MHD_HTTP_OK);
+}
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_confirm.h b/src/bank-lib/fakebank_bank_post_withdrawals_confirm.h
new file mode 100644
index 000000000..56cd2deda
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_confirm.h
@@ -0,0 +1,48 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_confirm.h
+ * @brief implement bank API withdrawals /confirm endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_WITHDRAWALS_CONFIRM_H
+#define FAKEBANK_BANK_POST_WITHDRAWALS_CONFIRM_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/confirm request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_confirm_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *withdrawal_id);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_id_op.c b/src/bank-lib/fakebank_bank_post_withdrawals_id_op.c
new file mode 100644
index 000000000..fe5cc982d
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_id_op.c
@@ -0,0 +1,241 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_id_op.c
+ * @brief implement bank API POST /accounts/$ACCOUNT/withdrawals/$WID/$OP endpoint(s)
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_post_withdrawals_id_op.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_make_admin_transfer.h"
+
+
+/**
+ * Handle POST /accounts/$ACC/withdrawals/{withdrawal_id}/confirm request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account name of the account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+static MHD_RESULT
+bank_withdrawals_confirm (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *withdrawal_id)
+{
+ const struct Account *acc;
+ struct WithdrawalOperation *wo;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Account %s is unknown\n",
+ account);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if ( (NULL == wo) ||
+ (acc != wo->debit_account) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ if (NULL == wo->exchange_account)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_BANK_POST_WITHDRAWAL_OPERATION_REQUIRED,
+ NULL);
+ }
+ if (wo->aborted)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_CONFIRM_ABORT_CONFLICT,
+ withdrawal_id);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (GNUNET_OK !=
+ TALER_FAKEBANK_make_admin_transfer_ (
+ h,
+ wo->debit_account->account_name,
+ wo->exchange_account->account_name,
+ &wo->amount,
+ &wo->reserve_pub,
+ &wo->row_id,
+ &wo->timestamp))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ /* Re-acquiring the lock and continuing to operate on 'wo'
+ is currently (!) acceptable because we NEVER free 'wo'
+ until shutdown. We may want to revise this if keeping
+ all withdraw operations in RAM becomes an issue... */
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo->confirmation_done = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+}
+
+
+/**
+ * Handle POST /accounts/$ACC/withdrawals/{withdrawal_id}/abort request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account name of the account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @return MHD result code
+ */
+static MHD_RESULT
+bank_withdrawals_abort (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *withdrawal_id)
+{
+ struct WithdrawalOperation *wo;
+ const struct Account *acc;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Account %s is unknown\n",
+ account);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ withdrawal_id);
+ if ( (NULL == wo) ||
+ (acc != wo->debit_account) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ withdrawal_id);
+ }
+ if (wo->confirmation_done)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_ABORT_CONFIRM_CONFLICT,
+ withdrawal_id);
+ }
+ wo->aborted = true;
+ TALER_FAKEBANK_notify_withdrawal_ (h,
+ wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_id_op_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *withdrawal_id,
+ const char *op,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 == strcmp (op,
+ "/confirm"))
+ {
+ return bank_withdrawals_confirm (h,
+ connection,
+ account,
+ withdrawal_id);
+ }
+ if (0 == strcmp (op,
+ "/abort"))
+ {
+ return bank_withdrawals_abort (h,
+ connection,
+ account,
+ withdrawal_id);
+ }
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ op);
+}
diff --git a/src/bank-lib/fakebank_bank_post_withdrawals_id_op.h b/src/bank-lib/fakebank_bank_post_withdrawals_id_op.h
new file mode 100644
index 000000000..a2d40e66f
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_post_withdrawals_id_op.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_post_withdrawals_id_op.h
+ * @brief implement bank API POST /accounts/$ACCOUNT/withdrawals/$WID/$OP endpoint(s)
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_POST_WITHDRAWALS_ID_OP_H
+#define FAKEBANK_BANK_POST_WITHDRAWALS_ID_OP_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle POST /accounts/{account_name}/withdrawals/{withdrawal_id}/${OP} request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param account name of the account
+ * @param withdrawal_id the withdrawal operation identifier
+ * @param op operation to be performed, includes leading "/"
+ * @param upload_data data uploaded
+ * @param[in,out] upload_data_size number of bytes in @a upload_data
+ * @param[in,out] con_cls application context that can be used
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_withdrawals_id_op_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *withdrawal_id,
+ const char *op,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_bank_testing_register.c b/src/bank-lib/fakebank_bank_testing_register.c
new file mode 100644
index 000000000..e5720f1a6
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_testing_register.c
@@ -0,0 +1,128 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_testing_register.c
+ * @brief implementation of /testing/register endpoint for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_bank_testing_register.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_bank_testing_register_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ const char *username;
+ const char *password;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_string ("username",
+ &username),
+ GNUNET_JSON_spec_string ("password",
+ &password),
+ GNUNET_JSON_spec_end ()
+ };
+ enum GNUNET_GenericReturnValue ret;
+ struct Account *acc;
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ username,
+ NULL);
+ if (NULL != acc)
+ {
+ if (0 != strcmp (password,
+ acc->password))
+ {
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_REGISTER_CONFLICT,
+ "password");
+ }
+ }
+ else
+ {
+ acc = TALER_FAKEBANK_lookup_account_ (h,
+ username,
+ username);
+ GNUNET_assert (NULL != acc);
+ acc->password = GNUNET_strdup (password);
+ acc->balance = h->signup_bonus; /* magic money creation! */
+ }
+ res = TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_bank_testing_register.h b/src/bank-lib/fakebank_bank_testing_register.h
new file mode 100644
index 000000000..d8744ecc2
--- /dev/null
+++ b/src/bank-lib/fakebank_bank_testing_register.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_bank_testing_register.h
+ * @brief implementation of /testing/register endpoint for the bank API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_BANK_TESTING_REGISTER_H
+#define FAKEBANK_BANK_TESTING_REGISTER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+
+/**
+ * Handle POST /testing/register request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_bank_testing_register_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_lookup.c b/src/bank-lib/fakebank_common_lookup.c
new file mode 100644
index 000000000..b4f853871
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lookup.c
@@ -0,0 +1,103 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lookup.c
+ * @brief common helper functions related to lookups
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+
+struct WithdrawalOperation *
+TALER_FAKEBANK_lookup_withdrawal_operation_ (struct TALER_FAKEBANK_Handle *h,
+ const char *wopid)
+{
+ struct GNUNET_ShortHashCode sh;
+
+ if (NULL == h->wops)
+ return NULL;
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (wopid,
+ strlen (wopid),
+ &sh,
+ sizeof (sh)))
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ return GNUNET_CONTAINER_multishortmap_get (h->wops,
+ &sh);
+}
+
+
+struct Account *
+TALER_FAKEBANK_lookup_account_ (struct TALER_FAKEBANK_Handle *h,
+ const char *name,
+ const char *receiver_name)
+{
+ struct GNUNET_HashCode hc;
+ size_t slen;
+ struct Account *account;
+
+ memset (&hc,
+ 0,
+ sizeof (hc));
+ slen = strlen (name);
+ GNUNET_CRYPTO_hash (name,
+ slen,
+ &hc);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->accounts_lock));
+ account = GNUNET_CONTAINER_multihashmap_get (h->accounts,
+ &hc);
+ if (NULL == account)
+ {
+ if (NULL == receiver_name)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->accounts_lock));
+ return NULL;
+ }
+ account = GNUNET_new (struct Account);
+ account->account_name = GNUNET_strdup (name);
+ account->receiver_name = GNUNET_strdup (receiver_name);
+ GNUNET_asprintf (&account->payto_uri,
+ "payto://x-taler-bank/%s/%s?receiver-name=%s",
+ h->hostname,
+ account->account_name,
+ account->receiver_name);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (h->currency,
+ &account->balance));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (h->accounts,
+ &hc,
+ account,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->accounts_lock));
+ return account;
+}
diff --git a/src/bank-lib/fakebank_common_lookup.h b/src/bank-lib/fakebank_common_lookup.h
new file mode 100644
index 000000000..b93447743
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lookup.h
@@ -0,0 +1,62 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lookup.h
+ * @brief common helper functions related to lookups
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+
+#ifndef FAKEBANK_COMMON_LOOKUP_H
+#define FAKEBANK_COMMON_LOOKUP_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Lookup account with @a name, and if it does not exist, create it.
+ *
+ * @param[in,out] h bank to lookup account at
+ * @param name account name to resolve
+ * @param receiver_name receiver name in payto:// URI,
+ * NULL if the account must already exist
+ * @return account handle, NULL if account does not yet exist
+ */
+struct Account *
+TALER_FAKEBANK_lookup_account_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *name,
+ const char *receiver_name);
+
+
+/**
+ * Find withdrawal operation @a wopid in @a h.
+ *
+ * @param h fakebank handle
+ * @param wopid withdrawal operation ID as a string
+ * @return NULL if operation was not found
+ */
+struct WithdrawalOperation *
+TALER_FAKEBANK_lookup_withdrawal_operation_ (struct TALER_FAKEBANK_Handle *h,
+ const char *wopid);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_lp.c b/src/bank-lib/fakebank_common_lp.c
new file mode 100644
index 000000000..22a9e3ab4
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lp.c
@@ -0,0 +1,344 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lp.c
+ * @brief long-polling support for fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include <poll.h>
+#ifdef __linux__
+#include <sys/eventfd.h>
+#endif
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+void
+TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp)
+{
+ struct TALER_FAKEBANK_Handle *h = lp->h;
+ struct Account *acc = lp->account;
+
+ GNUNET_CONTAINER_DLL_remove (acc->lp_head,
+ acc->lp_tail,
+ lp);
+ MHD_resume_connection (lp->conn);
+ GNUNET_free (lp);
+ h->mhd_again = true;
+#ifdef __linux__
+ if (-1 == h->lp_event)
+#else
+ if ( (-1 == h->lp_event_in) &&
+ (-1 == h->lp_event_out) )
+#endif
+ {
+ if (NULL != h->mhd_task)
+ GNUNET_SCHEDULER_cancel (h->mhd_task);
+ h->mhd_task =
+ GNUNET_SCHEDULER_add_now (&TALER_FAKEBANK_run_mhd_,
+ h);
+ }
+}
+
+
+void *
+TALER_FAKEBANK_lp_expiration_thread_ (void *cls)
+{
+ struct TALER_FAKEBANK_Handle *h = cls;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ while (! h->in_shutdown)
+ {
+ struct LongPoller *lp;
+ int timeout_ms;
+
+ lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
+ while ( (NULL != lp) &&
+ GNUNET_TIME_absolute_is_past (lp->timeout))
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ lp = GNUNET_CONTAINER_heap_peek (h->lp_heap);
+ }
+ if (NULL != lp)
+ {
+ struct GNUNET_TIME_Relative rem;
+ unsigned long long left_ms;
+
+ rem = GNUNET_TIME_absolute_get_remaining (lp->timeout);
+ left_ms = rem.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
+ if (left_ms > INT_MAX)
+ timeout_ms = INT_MAX;
+ else
+ timeout_ms = (int) left_ms;
+ }
+ else
+ {
+ timeout_ms = -1; /* infinity */
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ {
+ struct pollfd p = {
+#ifdef __linux__
+ .fd = h->lp_event,
+#else
+ .fd = h->lp_event_out,
+#endif
+ .events = POLLIN
+ };
+ int ret;
+
+ ret = poll (&p,
+ 1,
+ timeout_ms);
+ if (-1 == ret)
+ {
+ if (EINTR != errno)
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "poll");
+ }
+ else if (1 == ret)
+ {
+ /* clear event */
+ uint64_t ev;
+ ssize_t iret;
+
+#ifdef __linux__
+ iret = read (h->lp_event,
+ &ev,
+ sizeof (ev));
+#else
+ iret = read (h->lp_event_out,
+ &ev,
+ sizeof (ev));
+#endif
+ if (-1 == iret)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "read");
+ }
+ else
+ {
+ GNUNET_break (sizeof (uint64_t) == iret);
+ }
+ }
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return NULL;
+}
+
+
+/**
+ * Trigger long pollers that might have been waiting
+ * for @a t.
+ *
+ * @param h fakebank handle
+ * @param t transaction to notify on
+ */
+void
+TALER_FAKEBANK_notify_transaction_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ struct LongPoller *nxt;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ for (struct LongPoller *lp = debit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_DEBIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+ for (struct LongPoller *lp = credit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if (LP_CREDIT == lp->type)
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+}
+
+
+/**
+ * Notify long pollers that a @a wo was updated.
+ * Must be called with the "big_lock" still held.
+ *
+ * @param h fakebank handle
+ * @param wo withdraw operation that finished
+ */
+void
+TALER_FAKEBANK_notify_withdrawal_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const struct WithdrawalOperation *wo)
+{
+ struct Account *debit_acc = wo->debit_account;
+ struct LongPoller *nxt;
+
+ for (struct LongPoller *lp = debit_acc->lp_head;
+ NULL != lp;
+ lp = nxt)
+ {
+ nxt = lp->next;
+ if ( (LP_WITHDRAW == lp->type) &&
+ (wo == lp->wo) )
+ {
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_node (lp->hn));
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ }
+}
+
+
+/**
+ * Task run when a long poller is about to time out.
+ * Only used in single-threaded mode.
+ *
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
+ */
+static void
+lp_timeout (void *cls)
+{
+ struct TALER_FAKEBANK_Handle *h = cls;
+ struct LongPoller *lp;
+
+ h->lp_task = NULL;
+ while (NULL != (lp = GNUNET_CONTAINER_heap_peek (h->lp_heap)))
+ {
+ if (GNUNET_TIME_absolute_is_future (lp->timeout))
+ break;
+ GNUNET_assert (lp ==
+ GNUNET_CONTAINER_heap_remove_root (h->lp_heap));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Timeout reached for long poller %p\n",
+ lp->conn);
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ if (NULL == lp)
+ return;
+ h->lp_task = GNUNET_SCHEDULER_add_at (lp->timeout,
+ &lp_timeout,
+ h);
+}
+
+
+/**
+ * Reschedule the timeout task of @a h for time @a t.
+ *
+ * @param h fakebank handle
+ * @param t when will the next connection timeout expire
+ */
+static void
+reschedule_lp_timeout (struct TALER_FAKEBANK_Handle *h,
+ struct GNUNET_TIME_Absolute t)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Scheduling timeout task for %s\n",
+ GNUNET_STRINGS_absolute_time_to_string (t));
+#ifdef __linux__
+ if (-1 != h->lp_event)
+#else
+ if (-1 != h->lp_event_in && -1 != h->lp_event_out)
+#endif
+ {
+ uint64_t num = 1;
+
+ GNUNET_break (sizeof (num) ==
+#ifdef __linux__
+ write (h->lp_event,
+ &num,
+ sizeof (num)));
+#else
+ write (h->lp_event_in,
+ &num,
+ sizeof (num)));
+#endif
+ }
+ else
+ {
+ if (NULL != h->lp_task)
+ GNUNET_SCHEDULER_cancel (h->lp_task);
+ h->lp_task = GNUNET_SCHEDULER_add_at (t,
+ &lp_timeout,
+ h);
+ }
+}
+
+
+void
+TALER_FAKEBANK_start_lp_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct Account *acc,
+ struct GNUNET_TIME_Relative lp_timeout,
+ enum LongPollType dir,
+ const struct WithdrawalOperation *wo)
+{
+ struct LongPoller *lp;
+ bool toc;
+
+ lp = GNUNET_new (struct LongPoller);
+ lp->account = acc;
+ lp->h = h;
+ lp->wo = wo;
+ lp->conn = connection;
+ lp->timeout = GNUNET_TIME_relative_to_absolute (lp_timeout);
+ lp->type = dir;
+ lp->hn = GNUNET_CONTAINER_heap_insert (h->lp_heap,
+ lp,
+ lp->timeout.abs_value_us);
+ toc = (lp ==
+ GNUNET_CONTAINER_heap_peek (h->lp_heap));
+ GNUNET_CONTAINER_DLL_insert (acc->lp_head,
+ acc->lp_tail,
+ lp);
+ MHD_suspend_connection (connection);
+ if (toc)
+ reschedule_lp_timeout (h,
+ lp->timeout);
+
+}
diff --git a/src/bank-lib/fakebank_common_lp.h b/src/bank-lib/fakebank_common_lp.h
new file mode 100644
index 000000000..37094e12b
--- /dev/null
+++ b/src/bank-lib/fakebank_common_lp.h
@@ -0,0 +1,100 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_lp.h
+ * @brief long-polling support for fakebank
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_LP_H
+#define FAKEBANK_COMMON_LP_H
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * Trigger the @a lp. Frees associated resources, except the entry of @a lp in
+ * the timeout heap. Must be called while the ``big lock`` is held.
+ *
+ * @param[in] lp long poller to trigger
+ */
+void
+TALER_FAKEBANK_lp_trigger_ (struct LongPoller *lp);
+
+
+/**
+ * Trigger long pollers that might have been waiting
+ * for @a t.
+ *
+ * @param h fakebank handle
+ * @param t transaction to notify on
+ */
+void
+TALER_FAKEBANK_notify_transaction_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t);
+
+
+/**
+ * Notify long pollers that a @a wo was updated.
+ * Must be called with the "big_lock" still held.
+ *
+ * @param h fakebank handle
+ * @param wo withdraw operation that finished
+ */
+void
+TALER_FAKEBANK_notify_withdrawal_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const struct WithdrawalOperation *wo);
+
+
+/**
+ * Start long-polling for @a connection and @a acc
+ * for transfers in @a dir. Must be called with the
+ * "big lock" held.
+ *
+ * @param[in,out] h fakebank handle
+ * @param[in,out] connection to suspend
+ * @param[in,out] acc account affected
+ * @param lp_timeout how long to suspend
+ * @param dir direction of transfers to watch for
+ * @param wo withdraw operation to watch, only
+ * if @a dir is #LP_WITHDRAW
+ */
+void
+TALER_FAKEBANK_start_lp_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct Account *acc,
+ struct GNUNET_TIME_Relative lp_timeout,
+ enum LongPollType dir,
+ const struct WithdrawalOperation *wo);
+
+
+/**
+ * Main routine of a thread that is run to wake up connections that have hit
+ * their timeout. Runs until in_shutdown is set to true. Must be send signals
+ * via lp_event on shutdown and/or whenever the heap changes to an earlier
+ * timeout.
+ *
+ * @param cls a `struct TALER_FAKEBANK_Handle *`
+ * @return NULL
+ */
+void *
+TALER_FAKEBANK_lp_expiration_thread_ (void *cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_make_admin_transfer.c b/src/bank-lib/fakebank_common_make_admin_transfer.c
new file mode 100644
index 000000000..4a11d412c
--- /dev/null
+++ b/src/bank-lib/fakebank_common_make_admin_transfer.c
@@ -0,0 +1,117 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_make_admin_transfer.c
+ * @brief routine to create transfers to the exchange
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_transact.h"
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_admin_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t *row_id,
+ struct GNUNET_TIME_Timestamp *timestamp)
+{
+ struct Transaction *t;
+ const struct GNUNET_PeerIdentity *pid;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+
+ GNUNET_static_assert (sizeof (*pid) ==
+ sizeof (*reserve_pub));
+ pid = (const struct GNUNET_PeerIdentity *) reserve_pub;
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ debit_account,
+ strlen ("payto://")));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ credit_account,
+ strlen ("payto://")));
+ debit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ debit_account,
+ debit_account);
+ credit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ credit_account,
+ credit_account);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
+ t = GNUNET_CONTAINER_multipeermap_get (h->rpubs,
+ pid);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ if (NULL != t)
+ {
+ /* duplicate reserve public key not allowed */
+ GNUNET_break_op (0);
+ return GNUNET_NO;
+ }
+
+ t = GNUNET_new (struct Transaction);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
+ t->amount = *amount;
+ t->date = GNUNET_TIME_timestamp_get ();
+ if (NULL != timestamp)
+ *timestamp = t->date;
+ t->type = T_CREDIT;
+ t->subject.credit.reserve_pub = *reserve_pub;
+ TALER_FAKEBANK_transact_ (h,
+ t);
+ if (NULL != row_id)
+ *row_id = t->row_id;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->rpubs_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multipeermap_put (
+ h->rpubs,
+ pid,
+ t,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->rpubs_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer from %s to %s over %s and subject %s at row %llu\n",
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (reserve_pub),
+ (unsigned long long) t->row_id);
+ TALER_FAKEBANK_notify_transaction_ (h,
+ t);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_make_admin_transfer.h b/src/bank-lib/fakebank_common_make_admin_transfer.h
new file mode 100644
index 000000000..841cfb481
--- /dev/null
+++ b/src/bank-lib/fakebank_common_make_admin_transfer.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_make_admin_transfer.h
+ * @brief routine to create transfers to the exchange
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_MAKE_ADMIN_TRANSFER_H
+#define FAKEBANK_COMMON_MAKE_ADMIN_TRANSFER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Tell the fakebank to create another wire transfer *to* an exchange.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param reserve_pub reserve public key to use in subject
+ * @param[out] row_id serial_id of the transfer
+ * @param[out] timestamp when was the transfer made
+ * @return #GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_admin_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t *row_id,
+ struct GNUNET_TIME_Timestamp *timestamp);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_parser.c b/src/bank-lib/fakebank_common_parser.c
new file mode 100644
index 000000000..cf2dc5a74
--- /dev/null
+++ b/src/bank-lib/fakebank_common_parser.c
@@ -0,0 +1,138 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_parser.c
+ * @brief functions to help parse REST requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_common_parse_history_args (
+ const struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct HistoryArgs *ha)
+{
+ const char *start;
+ const char *delta;
+ const char *long_poll_ms;
+ unsigned long long lp_timeout;
+ unsigned long long sval;
+ long long d;
+ char dummy;
+
+ start = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "start");
+ ha->have_start = (NULL != start);
+ delta = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "delta");
+ long_poll_ms = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "long_poll_ms");
+ lp_timeout = 0;
+ if ( (NULL == delta) ||
+ (1 != sscanf (delta,
+ "%lld%c",
+ &d,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if ( (NULL != long_poll_ms) &&
+ (1 != sscanf (long_poll_ms,
+ "%llu%c",
+ &lp_timeout,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "long_poll_ms"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if ( (NULL != start) &&
+ (1 != sscanf (start,
+ "%llu%c",
+ &sval,
+ &dummy)) )
+ {
+ /* Fail if one of the above failed. */
+ /* Invalid request, given that this is fakebank we impolitely
+ * just kill the connection instead of returning a nice error.
+ */
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "start"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ if (NULL == start)
+ ha->start_idx = (d > 0) ? 0 : UINT64_MAX;
+ else
+ ha->start_idx = (uint64_t) sval;
+ ha->delta = (int64_t) d;
+ if (0 == ha->delta)
+ {
+ GNUNET_break_op (0);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "delta"))
+ ? GNUNET_NO
+ : GNUNET_SYSERR;
+ }
+ ha->lp_timeout
+ = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ lp_timeout);
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Request for %lld records from %llu\n",
+ (long long) ha->delta,
+ (unsigned long long) ha->start_idx);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_parser.h b/src/bank-lib/fakebank_common_parser.h
new file mode 100644
index 000000000..8e0d14649
--- /dev/null
+++ b/src/bank-lib/fakebank_common_parser.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_parser.h
+ * @brief functions to help parse REST requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_PARSER_H
+#define FAKEBANK_COMMON_PARSER_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Parse URL history arguments, of _both_ APIs:
+ * /history/incoming and /history/outgoing.
+ *
+ * @param h bank handle to work on
+ * @param connection MHD connection.
+ * @param[out] ha will contain the parsed values.
+ * @return #GNUNET_OK only if the parsing succeeds,
+ * #GNUNET_SYSERR if it failed,
+ * #GNUNET_NO if it failed and an error was returned
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_common_parse_history_args (
+ const struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ struct HistoryArgs *ha);
+
+#endif
diff --git a/src/bank-lib/fakebank_common_transact.c b/src/bank-lib/fakebank_common_transact.c
new file mode 100644
index 000000000..a099ef966
--- /dev/null
+++ b/src/bank-lib/fakebank_common_transact.c
@@ -0,0 +1,261 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_transact.c
+ * @brief actual transaction logic for FAKEBANK
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_transact.h"
+
+
+/**
+ * Update @a account balance by @a amount.
+ *
+ * The @a big_lock must already be locked when calling
+ * this function.
+ *
+ * @param[in,out] account account to update
+ * @param amount balance change
+ * @param debit true to subtract, false to add @a amount
+ */
+static void
+update_balance (struct Account *account,
+ const struct TALER_Amount *amount,
+ bool debit)
+{
+ if (debit == account->is_negative)
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_add (&account->balance,
+ &account->balance,
+ amount));
+ return;
+ }
+ if (0 <= TALER_amount_cmp (&account->balance,
+ amount))
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ &account->balance,
+ amount));
+ }
+ else
+ {
+ GNUNET_assert (0 <=
+ TALER_amount_subtract (&account->balance,
+ amount,
+ &account->balance));
+ account->is_negative = ! account->is_negative;
+ }
+}
+
+
+/**
+ * Add transaction to the debit and credit accounts,
+ * updating the balances as needed.
+ *
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in,out] h bank handle
+ * @param[in,out] t transaction to add to account lists
+ */
+void
+TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t)
+{
+ struct Account *debit_acc = t->debit_account;
+ struct Account *credit_acc = t->credit_account;
+ uint64_t row_id;
+ struct Transaction *old;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ row_id = ++h->serial_counter;
+ old = h->transactions[row_id % h->ram_limit];
+ h->transactions[row_id % h->ram_limit] = t;
+ t->row_id = row_id;
+ GNUNET_CONTAINER_MDLL_insert_tail (out,
+ debit_acc->out_head,
+ debit_acc->out_tail,
+ t);
+ update_balance (debit_acc,
+ &t->amount,
+ true);
+ GNUNET_CONTAINER_MDLL_insert_tail (in,
+ credit_acc->in_head,
+ credit_acc->in_tail,
+ t);
+ update_balance (credit_acc,
+ &t->amount,
+ false);
+ if (NULL != old)
+ {
+ struct Account *da;
+ struct Account *ca;
+
+ da = old->debit_account;
+ ca = old->credit_account;
+ /* slot was already in use, must clean out old
+ entry first! */
+ GNUNET_CONTAINER_MDLL_remove (out,
+ da->out_head,
+ da->out_tail,
+ old);
+ GNUNET_CONTAINER_MDLL_remove (in,
+ ca->in_head,
+ ca->in_tail,
+ old);
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if ( (NULL != old) &&
+ (T_DEBIT == old->type) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (h->uuid_map,
+ &old->request_uid,
+ old));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ GNUNET_free (old);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *subject,
+ const char *exchange_base_url,
+ const struct GNUNET_HashCode *request_uid,
+ uint64_t *ret_row_id,
+ struct GNUNET_TIME_Timestamp *timestamp)
+{
+ struct Transaction *t;
+ struct Account *debit_acc;
+ struct Account *credit_acc;
+ size_t url_len;
+
+ GNUNET_assert (0 == strcasecmp (amount->currency,
+ h->currency));
+ GNUNET_assert (NULL != debit_account);
+ GNUNET_assert (NULL != credit_account);
+ GNUNET_break (0 != strncasecmp ("payto://",
+ debit_account,
+ strlen ("payto://")));
+ GNUNET_break (0 != strncasecmp ("payto://",
+ credit_account,
+ strlen ("payto://")));
+ url_len = strlen (exchange_base_url);
+ GNUNET_assert (url_len < MAX_URL_LEN);
+ debit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ debit_account,
+ debit_account);
+ credit_acc = TALER_FAKEBANK_lookup_account_ (h,
+ credit_account,
+ credit_account);
+ if (NULL != request_uid)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ t = GNUNET_CONTAINER_multihashmap_get (h->uuid_map,
+ request_uid);
+ if (NULL != t)
+ {
+ if ( (debit_acc != t->debit_account) ||
+ (credit_acc != t->credit_account) ||
+ (0 != TALER_amount_cmp (amount,
+ &t->amount)) ||
+ (T_DEBIT != t->type) ||
+ (0 != GNUNET_memcmp (subject,
+ &t->subject.debit.wtid)) )
+ {
+ /* Transaction exists, but with different details. */
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_SYSERR;
+ }
+ *ret_row_id = t->row_id;
+ *timestamp = t->date;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ return GNUNET_OK;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ }
+ t = GNUNET_new (struct Transaction);
+ t->unchecked = true;
+ t->debit_account = debit_acc;
+ t->credit_account = credit_acc;
+ t->amount = *amount;
+ t->date = GNUNET_TIME_timestamp_get ();
+ if (NULL != timestamp)
+ *timestamp = t->date;
+ t->type = T_DEBIT;
+ GNUNET_memcpy (t->subject.debit.exchange_base_url,
+ exchange_base_url,
+ url_len);
+ t->subject.debit.wtid = *subject;
+ if (NULL == request_uid)
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
+ &t->request_uid);
+ else
+ t->request_uid = *request_uid;
+ TALER_FAKEBANK_transact_ (h,
+ t);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->uuid_map_lock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (
+ h->uuid_map,
+ &t->request_uid,
+ t,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->uuid_map_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Making transfer %llu from %s to %s over %s and subject %s; for exchange: %s\n",
+ (unsigned long long) t->row_id,
+ debit_account,
+ credit_account,
+ TALER_amount2s (amount),
+ TALER_B2S (subject),
+ exchange_base_url);
+ *ret_row_id = t->row_id;
+ TALER_FAKEBANK_notify_transaction_ (h,
+ t);
+ return GNUNET_OK;
+}
diff --git a/src/bank-lib/fakebank_common_transact.h b/src/bank-lib/fakebank_common_transact.h
new file mode 100644
index 000000000..0914785e9
--- /dev/null
+++ b/src/bank-lib/fakebank_common_transact.h
@@ -0,0 +1,76 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_common_transact.h
+ * @brief actual transaction logic for FAKEBANK
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_COMMON_TRANSACT_H
+#define FAKEBANK_COMMON_TRANSACT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Add transaction to the debit and credit accounts,
+ * updating the balances as needed.
+ *
+ * The transaction @a t must already be locked
+ * when calling this function!
+ *
+ * @param[in,out] h bank handle
+ * @param[in,out] t transaction to add to account lists
+ */
+void
+TALER_FAKEBANK_transact_ (struct TALER_FAKEBANK_Handle *h,
+ struct Transaction *t);
+
+
+/**
+ * Tell the fakebank to create another wire transfer *from* an exchange.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @param request_uid unique number to make the request unique, or NULL to create one
+ * @param[out] ret_row_id pointer to store the row ID of this transaction
+ * @param[out] timestamp set to the time of the transfer
+ * @return #GNUNET_YES if the transfer was successful,
+ * #GNUNET_SYSERR if the request_uid was reused for a different transfer
+ */
+enum GNUNET_GenericReturnValue
+TALER_FAKEBANK_make_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ const char *debit_account,
+ const char *credit_account,
+ const struct TALER_Amount *amount,
+ const struct TALER_WireTransferIdentifierRawP *subject,
+ const char *exchange_base_url,
+ const struct GNUNET_HashCode *request_uid,
+ uint64_t *ret_row_id,
+ struct GNUNET_TIME_Timestamp *timestamp);
+
+#endif
diff --git a/src/bank-lib/fakebank_stop.c b/src/bank-lib/fakebank_stop.c
new file mode 100644
index 000000000..e31d47523
--- /dev/null
+++ b/src/bank-lib/fakebank_stop.c
@@ -0,0 +1,192 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_stop.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include <poll.h>
+#ifdef __linux__
+#include <sys/eventfd.h>
+#endif
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lp.h"
+
+
+/**
+ * Helper function to free memory when finished.
+ *
+ * @param cls NULL
+ * @param key key of the account to free (ignored)
+ * @param val a `struct Account` to free.
+ */
+static enum GNUNET_GenericReturnValue
+free_account (void *cls,
+ const struct GNUNET_HashCode *key,
+ void *val)
+{
+ struct Account *account = val;
+
+ (void) cls;
+ (void) key;
+ GNUNET_assert (NULL == account->lp_head);
+ GNUNET_free (account->account_name);
+ GNUNET_free (account->receiver_name);
+ GNUNET_free (account->payto_uri);
+ GNUNET_free (account->password);
+ GNUNET_free (account);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Helper function to free memory when finished.
+ *
+ * @param cls NULL
+ * @param key key of the operation to free (ignored)
+ * @param val a `struct WithdrawalOperation *` to free.
+ */
+static enum GNUNET_GenericReturnValue
+free_withdraw_op (void *cls,
+ const struct GNUNET_ShortHashCode *key,
+ void *val)
+{
+ struct WithdrawalOperation *wo = val;
+
+ (void) cls;
+ (void) key;
+ GNUNET_free (wo);
+ return GNUNET_OK;
+}
+
+
+void
+TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
+{
+ if (NULL != h->lp_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->lp_task);
+ h->lp_task = NULL;
+ }
+#if EPOLL_SUPPORT
+ if (NULL != h->mhd_rfd)
+ {
+ GNUNET_NETWORK_socket_free_memory_only_ (h->mhd_rfd);
+ h->mhd_rfd = NULL;
+ }
+#endif
+#ifdef __linux__
+ if (-1 != h->lp_event)
+#else
+ if (-1 != h->lp_event_in && -1 != h->lp_event_out)
+#endif
+ {
+ uint64_t val = 1;
+ void *ret;
+ struct LongPoller *lp;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ h->in_shutdown = true;
+ while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+#ifdef __linux__
+ GNUNET_break (sizeof (val) ==
+ write (h->lp_event,
+ &val,
+ sizeof (val)));
+#else
+ GNUNET_break (sizeof (val) ==
+ write (h->lp_event_in,
+ &val,
+ sizeof (val)));
+#endif
+ GNUNET_break (0 ==
+ pthread_join (h->lp_thread,
+ &ret));
+ GNUNET_break (NULL == ret);
+#ifdef __linux__
+ GNUNET_break (0 == close (h->lp_event));
+ h->lp_event = -1;
+#else
+ GNUNET_break (0 == close (h->lp_event_in));
+ GNUNET_break (0 == close (h->lp_event_out));
+ h->lp_event_in = -1;
+ h->lp_event_out = -1;
+#endif
+ }
+ else
+ {
+ struct LongPoller *lp;
+
+ while (NULL != (lp = GNUNET_CONTAINER_heap_remove_root (h->lp_heap)))
+ TALER_FAKEBANK_lp_trigger_ (lp);
+ }
+ if (NULL != h->mhd_bank)
+ {
+ MHD_stop_daemon (h->mhd_bank);
+ h->mhd_bank = NULL;
+ }
+ if (NULL != h->mhd_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->mhd_task);
+ h->mhd_task = NULL;
+ }
+ if (NULL != h->accounts)
+ {
+ GNUNET_CONTAINER_multihashmap_iterate (h->accounts,
+ &free_account,
+ NULL);
+ GNUNET_CONTAINER_multihashmap_destroy (h->accounts);
+ }
+ if (NULL != h->wops)
+ {
+ GNUNET_CONTAINER_multishortmap_iterate (h->wops,
+ &free_withdraw_op,
+ NULL);
+ GNUNET_CONTAINER_multishortmap_destroy (h->wops);
+ }
+ GNUNET_CONTAINER_multihashmap_destroy (h->uuid_map);
+ GNUNET_CONTAINER_multipeermap_destroy (h->rpubs);
+ GNUNET_CONTAINER_heap_destroy (h->lp_heap);
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->big_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->uuid_map_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->accounts_lock));
+ GNUNET_assert (0 ==
+ pthread_mutex_destroy (&h->rpubs_lock));
+ for (uint64_t i = 0; i<h->ram_limit; i++)
+ GNUNET_free (h->transactions[i]);
+ GNUNET_free (h->transactions);
+ GNUNET_free (h->my_baseurl);
+ GNUNET_free (h->currency);
+ GNUNET_free (h->exchange_url);
+ GNUNET_free (h->hostname);
+ GNUNET_free (h);
+}
diff --git a/src/bank-lib/fakebank_tbi.c b/src/bank-lib/fakebank_tbi.c
new file mode 100644
index 000000000..463ec7d31
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi.c
@@ -0,0 +1,167 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi.c
+ * @brief main entry point to the Taler Bank Integration (TBI) API implementation
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_tbi.h"
+#include "fakebank_tbi_get_withdrawal_operation.h"
+#include "fakebank_tbi_post_withdrawal_operation.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_HEAD))
+ method = MHD_HTTP_METHOD_GET;
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ struct TALER_Amount zero;
+
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (h->currency,
+ &zero));
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "1:0:1"),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("implementation",
+ "urn:net:taler:specs:bank:fakebank"),
+ GNUNET_JSON_pack_bool ("allow_conversion",
+ false),
+ GNUNET_JSON_pack_bool ("allow_registrations",
+ true),
+ GNUNET_JSON_pack_bool ("allow_deletions",
+ false),
+ GNUNET_JSON_pack_bool ("allow_edit_name",
+ false),
+ GNUNET_JSON_pack_bool ("allow_edit_cashout_payto_uri",
+ false),
+ TALER_JSON_pack_amount ("default_debit_threshold",
+ &zero),
+ GNUNET_JSON_pack_array_steal ("supported_tan_channels",
+ json_array ()),
+ GNUNET_JSON_pack_object_steal (
+ "currency_specification",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("name",
+ h->currency),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_uint64 ("num_fractional_input_digits",
+ 2),
+ GNUNET_JSON_pack_uint64 ("num_fractional_normal_digits",
+ 2),
+ GNUNET_JSON_pack_uint64 ("num_fractional_trailing_zero_digits",
+ 2),
+ GNUNET_JSON_pack_object_steal (
+ "alt_unit_names",
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("0",
+ h->currency))),
+ GNUNET_JSON_pack_string ("name",
+ h->currency))),
+ GNUNET_JSON_pack_string ("name",
+ "taler-bank-integration"));
+ }
+ if ( (0 == strncmp (url,
+ "/withdrawal-operation/",
+ strlen ("/withdrawal-operation/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ const char *wopid = &url[strlen ("/withdrawal-operation/")];
+ const char *lp_s
+ = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ "long_poll_ms");
+ struct GNUNET_TIME_Relative lp = GNUNET_TIME_UNIT_ZERO;
+
+ if (NULL != lp_s)
+ {
+ unsigned long long d;
+ char dummy;
+
+ if (1 != sscanf (lp_s,
+ "%llu%c",
+ &d,
+ &dummy))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
+ "long_poll_ms");
+ }
+ lp = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
+ d);
+ }
+ return TALER_FAKEBANK_tbi_get_withdrawal_operation_ (h,
+ connection,
+ wopid,
+ lp,
+ con_cls);
+
+ }
+ if ( (0 == strncmp (url,
+ "/withdrawal-operation/",
+ strlen ("/withdrawal-operation/"))) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST)) )
+ {
+ const char *wopid = &url[strlen ("/withdrawal-operation/")];
+
+ return TALER_FAKEBANK_tbi_post_withdrawal (h,
+ connection,
+ wopid,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_tbi.h b/src/bank-lib/fakebank_tbi.h
new file mode 100644
index 000000000..ef9f35fa2
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi.h
@@ -0,0 +1,54 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi.c
+ * @brief main entry point to the Taler Bank Integration (TBI) API implementation
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+#ifndef FAKEBANK_TBI_H
+#define FAKEBANK_TBI_H
+
+/**
+ * Handle incoming HTTP request to the bank integration API.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_main_ (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c
new file mode 100644
index 000000000..4749bda77
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.c
@@ -0,0 +1,141 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_get_withdrawal_operation.c
+ * @brief Implementation of the GET /withdrawal-operation/ request of the Taler Bank Integration API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_tbi_get_withdrawal_operation.h"
+
+/**
+ * Function called to clean up a withdraw context.
+ *
+ * @param cls a `struct WithdrawContext *`
+ */
+static void
+withdraw_cleanup (void *cls)
+{
+ struct WithdrawContext *wc = cls;
+
+ GNUNET_free (wc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_get_withdrawal_operation_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ struct GNUNET_TIME_Relative lp,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct WithdrawContext *wc;
+ const char *status_string;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &withdraw_cleanup;
+ *con_cls = cc;
+ wc = GNUNET_new (struct WithdrawContext);
+ cc->ctx = wc;
+ wc->wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ wopid);
+ if (NULL == wc->wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ wopid);
+ }
+ wc->timeout = GNUNET_TIME_relative_to_absolute (lp);
+ }
+ else
+ {
+ wc = cc->ctx;
+ }
+ if (GNUNET_TIME_absolute_is_past (wc->timeout) ||
+ h->in_shutdown ||
+ wc->wo->confirmation_done ||
+ wc->wo->aborted)
+ {
+ json_t *wt;
+
+ wt = json_array ();
+ GNUNET_assert (NULL != wt);
+ GNUNET_assert (0 ==
+ json_array_append_new (wt,
+ json_string ("x-taler-bank")));
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (wc->wo->aborted)
+ status_string = "aborted";
+ else if (wc->wo->confirmation_done)
+ status_string = "confirmed";
+ else if (wc->wo->selection_done)
+ status_string = "selected";
+ else
+ status_string = "pending";
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ // FIXME: deprecated field, should be removed in the future.
+ GNUNET_JSON_pack_bool ("aborted",
+ wc->wo->aborted),
+ // FIXME: deprecated field, should be removed in the future.
+ GNUNET_JSON_pack_bool ("selection_done",
+ wc->wo->selection_done),
+ // FIXME: deprecated field, should be removed in the future.
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wc->wo->confirmation_done),
+ GNUNET_JSON_pack_string ("status",
+ status_string),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("suggested_exchange",
+ h->exchange_url)),
+ TALER_JSON_pack_amount ("amount",
+ &wc->wo->amount),
+ GNUNET_JSON_pack_array_steal ("wire_types",
+ wt));
+ }
+
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ wc->wo->debit_account,
+ GNUNET_TIME_absolute_get_remaining (wc->timeout),
+ LP_WITHDRAW,
+ wc->wo);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+}
diff --git a/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h
new file mode 100644
index 000000000..b42e5a768
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_get_withdrawal_operation.h
@@ -0,0 +1,51 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_get_withdrawal_operation.h
+ * @brief Implementation of the GET /withdrawal-operation/ request of the Taler Bank Integration API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBI_GET_WITHDRAWAL_OPERATION_H
+#define FAKEBANK_TBI_GET_WITHDRAWAL_OPERATION_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle GET /withdrawal-operation/{wopid} request.
+ *
+ * @param h the handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param lp how long is the long-polling timeout
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_get_withdrawal_operation_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ struct GNUNET_TIME_Relative lp,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c
new file mode 100644
index 000000000..38b92e494
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.c
@@ -0,0 +1,231 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_post_withdrawal_operation.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_tbi_post_withdrawal_operation.h"
+
+
+/**
+ * Execute POST /withdrawal-operation/ request.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param reserve_pub public key of the reserve
+ * @param exchange_payto_uri payto://-URI of the exchange
+ * @return MHD result code
+ */
+static MHD_RESULT
+do_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *exchange_payto_uri)
+{
+ struct WithdrawalOperation *wo;
+ char *credit_name;
+ struct Account *credit_account;
+ const char *status_string;
+
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ wo = TALER_FAKEBANK_lookup_withdrawal_operation_ (h,
+ wopid);
+ if (NULL == wo)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_TRANSACTION_NOT_FOUND,
+ wopid);
+ }
+ if ( (wo->selection_done) &&
+ (0 != GNUNET_memcmp (&wo->reserve_pub,
+ reserve_pub)) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
+ "reserve public key changed");
+ }
+ {
+ /* check if reserve_pub is already in use */
+ const struct GNUNET_PeerIdentity *pid;
+
+ pid = (const struct GNUNET_PeerIdentity *) &wo->reserve_pub;
+ if (GNUNET_CONTAINER_multipeermap_contains (h->rpubs,
+ pid))
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ }
+ credit_name = TALER_xtalerbank_account_from_payto (exchange_payto_uri);
+ if (NULL == credit_name)
+ {
+ GNUNET_break_op (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ NULL);
+ }
+ credit_account = TALER_FAKEBANK_lookup_account_ (h,
+ credit_name,
+ NULL);
+ if (NULL == credit_account)
+ {
+ MHD_RESULT res;
+
+ GNUNET_break_op (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ res = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ credit_name);
+ GNUNET_free (credit_name);
+ return res;
+ }
+ GNUNET_free (credit_name);
+ if ( (NULL != wo->exchange_account) &&
+ (credit_account != wo->exchange_account) )
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT,
+ "exchange account changed");
+ }
+ wo->exchange_account = credit_account;
+ wo->reserve_pub = *reserve_pub;
+ wo->selection_done = true;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (wo->aborted)
+ status_string = "aborted";
+ else if (wo->confirmation_done)
+ status_string = "confirmed";
+ else
+ status_string = "selected";
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ // FIXME: Deprecated field, should be deleted in the future.
+ GNUNET_JSON_pack_bool ("transfer_done",
+ wo->confirmation_done),
+ GNUNET_JSON_pack_string ("status",
+ status_string));
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbi_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ MHD_RESULT res;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+
+ {
+ struct TALER_ReservePublicKeyP reserve_pub;
+ const char *exchange_payto_url;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &reserve_pub),
+ GNUNET_JSON_spec_string ("selected_exchange",
+ &exchange_payto_url),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ res = do_post_withdrawal (h,
+ connection,
+ wopid,
+ &reserve_pub,
+ exchange_payto_url);
+ }
+ json_decref (json);
+ return res;
+}
diff --git a/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h
new file mode 100644
index 000000000..8873bb5f6
--- /dev/null
+++ b/src/bank-lib/fakebank_tbi_post_withdrawal_operation.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbi_post_withdrawal_operation.c
+ * @brief Implementation of the Taler Bank Integration API for POAT /withdrawal-operation/ requests
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBI_POST_WITHDRAWAL_OPERATION_H
+#define FAKEBANK_TBI_POST_WITHDRAWAL_OPERATION_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle POST /withdrawal-operation/ request.
+ *
+ * @param h our fakebank handle
+ * @param connection the connection
+ * @param wopid the withdrawal operation identifier
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbi_post_withdrawal (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *wopid,
+ const void *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr.c b/src/bank-lib/fakebank_tbr.c
new file mode 100644
index 000000000..0f0e5bdc1
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr.c
@@ -0,0 +1,94 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr.c
+ * @brief main entry point for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_tbr_get_history.h"
+#include "fakebank_tbr_get_root.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Fakebank - Anastasis API: serving URL `%s' for account `%s'\n",
+ url,
+ account);
+
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /config */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "0:0:0"),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("implementation",
+ "urn:net:taler:specs:bank:fakebank"),
+ GNUNET_JSON_pack_string ("name",
+ "taler-revenue"));
+ }
+
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ if ( (0 == strcmp (url,
+ "/history")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_tbr_get_history (h,
+ connection,
+ account,
+ con_cls);
+ if (0 == strcmp (url,
+ "/"))
+ return TALER_FAKEBANK_tbr_get_root (h,
+ connection);
+ }
+ /* Unexpected URL path, just close the connection. */
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_tbr.h b/src/bank-lib/fakebank_tbr.h
new file mode 100644
index 000000000..fb9bdfefc
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr.h
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr.h
+ * @brief main entry point for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+#ifndef FAKEBANK_TBR_H
+#define FAKEBANK_TBR_H
+
+/**
+ * Handle incoming HTTP request to the Taler Bank Revenue API.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param account which account should process the request
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr_get_history.c b/src/bank-lib/fakebank_tbr_get_history.c
new file mode 100644
index 000000000..a6cfaad8d
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_history.c
@@ -0,0 +1,312 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_history.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_parser.h"
+#include "fakebank_tbr_get_history.h"
+
+
+/**
+ * Function called to clean up a history context.
+ *
+ * @param cls a `struct HistoryContext *`
+ */
+static void
+history_cleanup (void *cls)
+{
+ struct HistoryContext *hc = cls;
+
+ json_decref (hc->history);
+ GNUNET_free (hc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_history (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ const struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+ bool in_shutdown;
+ const char *acc_payto_uri;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /accounts/%s/taler-revenue/history request\n",
+ account);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ if (UINT64_MAX == hc->ha.start_idx)
+ hc->ha.start_idx = h->serial_counter;
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Account %s is unknown\n",
+ account);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->in_tail
+ : hc->acc->in_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->credit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ in_shutdown = h->in_shutdown;
+ /* FIXME: these conditions are unclear to me. */
+ if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
+ (0 < hc->ha.delta))
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Transactions lost due to RAM limits\n");
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ }
+ goto finish;
+ }
+ if (in_shutdown)
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (skip)
+ {
+ /* range from application is exclusive, skip the
+ matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_in;
+ else
+ pos = t->next_in;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld credit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+ char *subject;
+
+ if (T_DEBIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected CREDIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+
+ {
+ char *wtids;
+
+ wtids = GNUNET_STRINGS_data_to_string_alloc (
+ &pos->subject.debit.wtid,
+ sizeof (pos->subject.debit.wtid));
+ GNUNET_asprintf (&subject,
+ "%s %s",
+ wtids,
+ pos->subject.debit.exchange_base_url);
+ GNUNET_free (wtids);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Found transaction over %s with subject %s\n",
+ TALER_amount2s (&pos->amount),
+ subject);
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "RESERVE"),
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("debit_account",
+ pos->debit_account->payto_uri),
+ GNUNET_JSON_pack_string ("subject",
+ subject));
+ GNUNET_free (subject);
+ GNUNET_assert (NULL != trans);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ in_shutdown = h->in_shutdown;
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Zero transactions found\n");
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "credit_account",
+ acc_payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "incoming_transactions",
+ h));
+ }
+}
diff --git a/src/bank-lib/fakebank_tbr_get_history.h b/src/bank-lib/fakebank_tbr_get_history.h
new file mode 100644
index 000000000..997c9a86b
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_history.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_history.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBR_GET_HISTORY_H
+#define FAKEBANK_TBR_GET_HISTORY_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /history
+ * of the taler-revenue API. This one can return transactions
+ * created by debits from the exchange!
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request (NULL or &special_ptr)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_history (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_tbr_get_root.c b/src/bank-lib/fakebank_tbr_get_root.c
new file mode 100644
index 000000000..6e518d661
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_root.c
@@ -0,0 +1,50 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_root.c
+ * @brief return the main "/" page for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_root (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank (Bank Revenue API here)!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_tbr_get_root.h b/src/bank-lib/fakebank_tbr_get_root.h
new file mode 100644
index 000000000..eda8060bf
--- /dev/null
+++ b/src/bank-lib/fakebank_tbr_get_root.h
@@ -0,0 +1,45 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_tbr_get_root.h
+ * @brief return the main "/" page for the Taler Bank Revenue API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TBR_GET_ROOT_H
+#define FAKEBANK_TBR_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_tbr_get_root (struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg.c b/src/bank-lib/fakebank_twg.c
new file mode 100644
index 000000000..9356e69ba
--- /dev/null
+++ b/src/bank-lib/fakebank_twg.c
@@ -0,0 +1,124 @@
+/*
+ This file is part of TALER
+ (C) 2016-2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg.c
+ * @brief main entry point for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_twg.h"
+#include "fakebank_twg_admin_add_incoming.h"
+#include "fakebank_twg_get_root.h"
+#include "fakebank_twg_history.h"
+#include "fakebank_twg_transfer.h"
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Fakebank TWG, serving URL `%s' for account `%s'\n",
+ url,
+ account);
+ if ( (0 == strcmp (url,
+ "/config")) &&
+ (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET)) )
+ {
+ /* GET /config */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string ("version",
+ "0:0:0"),
+ GNUNET_JSON_pack_string ("currency",
+ h->currency),
+ GNUNET_JSON_pack_string ("implementation",
+ "urn:net:taler:specs:bank:fakebank"),
+ GNUNET_JSON_pack_string ("name",
+ "taler-wire-gateway"));
+ }
+ if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_GET))
+ {
+ if ( (0 == strcmp (url,
+ "/history/incoming")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_get_credit_history_ (h,
+ connection,
+ account,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/history/outgoing")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_get_debit_history_ (h,
+ connection,
+ account,
+ con_cls);
+ if (0 == strcmp (url,
+ "/"))
+ return TALER_FAKEBANK_twg_get_root_ (h,
+ connection);
+ }
+ else if (0 == strcasecmp (method,
+ MHD_HTTP_METHOD_POST))
+ {
+ if ( (0 == strcmp (url,
+ "/admin/add-incoming")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_twg_admin_add_incoming_ (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ if ( (0 == strcmp (url,
+ "/transfer")) &&
+ (NULL != account) )
+ return TALER_FAKEBANK_handle_transfer_ (h,
+ connection,
+ account,
+ upload_data,
+ upload_data_size,
+ con_cls);
+ }
+ /* Unexpected URL path, just close the connection. */
+ TALER_LOG_ERROR ("Breaking URL: %s %s\n",
+ method,
+ url);
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ url);
+}
diff --git a/src/bank-lib/fakebank_twg.h b/src/bank-lib/fakebank_twg.h
new file mode 100644
index 000000000..de808a21f
--- /dev/null
+++ b/src/bank-lib/fakebank_twg.h
@@ -0,0 +1,56 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg.h
+ * @brief main entry point for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_H
+#define FAKEBANK_TWG_H
+
+#include "taler_fakebank_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * Handle incoming HTTP request to the Taler Wire Gateway
+ * API.
+ *
+ * @param h our handle
+ * @param connection the connection
+ * @param url the requested url
+ * @param method the method (POST, GET, ...)
+ * @param account which account should process the request
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_main_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *url,
+ const char *method,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_admin_add_incoming.c b/src/bank-lib/fakebank_twg_admin_add_incoming.c
new file mode 100644
index 000000000..2db4f1fe5
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_admin_add_incoming.c
@@ -0,0 +1,160 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_admin_add_incoming.c
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_make_admin_transfer.h"
+#include "fakebank_twg_admin_add_incoming.h"
+
+MHD_RESULT
+TALER_FAKEBANK_twg_admin_add_incoming_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ uint64_t row_id;
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+ {
+ const char *debit_account;
+ struct TALER_Amount amount;
+ struct TALER_ReservePublicKeyP reserve_pub;
+ char *debit;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+ &reserve_pub),
+ GNUNET_JSON_spec_string ("debit_account",
+ &debit_account),
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ if (0 != strcasecmp (amount.currency,
+ h->currency))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Currency `%s' does not match our configuration\n",
+ amount.currency);
+ json_decref (json);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_GENERIC_CURRENCY_MISMATCH,
+ NULL);
+ }
+ debit = TALER_xtalerbank_account_from_payto (debit_account);
+ if (NULL == debit)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ debit_account);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s\n",
+ debit,
+ account,
+ TALER_B2S (&reserve_pub),
+ TALER_amount2s (&amount));
+ ret = TALER_FAKEBANK_make_admin_transfer_ (h,
+ debit,
+ account,
+ &amount,
+ &reserve_pub,
+ &row_id,
+ &timestamp);
+ GNUNET_free (debit);
+ if (GNUNET_OK != ret)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Reserve public key not unique\n");
+ json_decref (json);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
+ NULL);
+ }
+ }
+ json_decref (json);
+
+ /* Finally build response object */
+ return TALER_MHD_REPLY_JSON_PACK (connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_uint64 ("row_id",
+ row_id),
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ timestamp));
+}
diff --git a/src/bank-lib/fakebank_twg_admin_add_incoming.h b/src/bank-lib/fakebank_twg_admin_add_incoming.h
new file mode 100644
index 000000000..220a10fc8
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_admin_add_incoming.h
@@ -0,0 +1,52 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_admin_add_incoming.h
+ * @brief library that fakes being a Taler bank for testcases
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_ADMIN_ADD_INCOMING_H
+#define FAKEBANK_TWG_ADMIN_ADD_INCOMING_H
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+/**
+ * Handle incoming HTTP request for /admin/add/incoming.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account into which to deposit the funds (credit)
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_admin_add_incoming_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_get_root.c b/src/bank-lib/fakebank_twg_get_root.c
new file mode 100644
index 000000000..09589890e
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_get_root.c
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_get_root.c
+ * @brief return the "/" page for the taler wire gateway
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_root_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+#define HELLOMSG "Hello, Fakebank (Taler Wire Gateway)!"
+
+ (void) h;
+ resp = MHD_create_response_from_buffer (
+ strlen (HELLOMSG),
+ HELLOMSG,
+ MHD_RESPMEM_MUST_COPY);
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ resp);
+ MHD_destroy_response (resp);
+ return ret;
+}
diff --git a/src/bank-lib/fakebank_twg_get_root.h b/src/bank-lib/fakebank_twg_get_root.h
new file mode 100644
index 000000000..8bbcf4192
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_get_root.h
@@ -0,0 +1,46 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_get_root.h
+ * @brief return the "/" page for the taler wire gateway
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_GET_ROOT_H
+#define FAKEBANK_TWG_GET_ROOT_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for "/" (home page).
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_root_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection);
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_history.c b/src/bank-lib/fakebank_twg_history.c
new file mode 100644
index 000000000..5dfc160b0
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_history.c
@@ -0,0 +1,537 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_history.c
+ * @brief routines to return account histories for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_lookup.h"
+#include "fakebank_common_lp.h"
+#include "fakebank_common_parser.h"
+
+/**
+ * Function called to clean up a history context.
+ *
+ * @param cls a `struct HistoryContext *`
+ */
+static void
+history_cleanup (void *cls)
+{
+ struct HistoryContext *hc = cls;
+
+ json_decref (hc->history);
+ GNUNET_free (hc);
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_get_debit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+ bool in_shutdown;
+ const char *acc_payto_uri;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /history/outgoing connection %p\n",
+ connection);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ if (UINT64_MAX == hc->ha.start_idx)
+ hc->ha.start_idx = h->serial_counter;
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->out_tail
+ : hc->acc->out_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ overflow = (t->row_id != hc->ha.start_idx);
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->debit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ /* FIXME: these conditions are unclear to me. */
+ if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ in_shutdown = h->in_shutdown;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ {
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ }
+ goto finish;
+ }
+ if (h->in_shutdown)
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ in_shutdown = h->in_shutdown;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_DEBIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (t->debit_account != hc->acc)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Invalid start specified, transaction %llu not with account %s!\n",
+ (unsigned long long) hc->ha.start_idx,
+ account);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_NO;
+ }
+ if (skip)
+ {
+ /* range is exclusive, skip the matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_out;
+ else
+ pos = t->next_out;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld debit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+ char *credit_payto;
+
+ if (T_DEBIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected CREDIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+ GNUNET_asprintf (&credit_payto,
+ "payto://x-taler-bank/localhost/%s?receiver-name=%s",
+ pos->credit_account->account_name,
+ pos->credit_account->receiver_name);
+
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("credit_account",
+ credit_payto),
+ GNUNET_JSON_pack_string ("exchange_base_url",
+ pos->subject.debit.exchange_base_url),
+ GNUNET_JSON_pack_data_auto ("wtid",
+ &pos->subject.debit.wtid));
+ GNUNET_assert (NULL != trans);
+ GNUNET_free (credit_payto);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_out;
+ if (0 < hc->ha.delta)
+ pos = pos->next_out;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_DEBIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ in_shutdown = h->in_shutdown;
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "debit_account",
+ acc_payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "outgoing_transactions",
+ h));
+ }
+}
+
+
+MHD_RESULT
+TALER_FAKEBANK_twg_get_credit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ struct HistoryContext *hc;
+ const struct Transaction *pos;
+ enum GNUNET_GenericReturnValue ret;
+ bool in_shutdown;
+ const char *acc_payto_uri;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &history_cleanup;
+ *con_cls = cc;
+ hc = GNUNET_new (struct HistoryContext);
+ cc->ctx = hc;
+ hc->history = json_array ();
+ if (NULL == hc->history)
+ {
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Handling /history/incoming connection %p\n",
+ connection);
+ if (GNUNET_OK !=
+ (ret = TALER_FAKEBANK_common_parse_history_args (h,
+ connection,
+ &hc->ha)))
+ {
+ GNUNET_break_op (0);
+ return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
+ }
+ hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ if (UINT64_MAX == hc->ha.start_idx)
+ hc->ha.start_idx = h->serial_counter;
+ hc->acc = TALER_FAKEBANK_lookup_account_ (h,
+ account,
+ NULL);
+ if (NULL == hc->acc)
+ {
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_BANK_UNKNOWN_ACCOUNT,
+ account);
+ }
+ }
+ else
+ {
+ hc = cc->ctx;
+ GNUNET_assert (0 ==
+ pthread_mutex_lock (&h->big_lock));
+ }
+
+ if (! hc->ha.have_start)
+ {
+ pos = (0 > hc->ha.delta)
+ ? hc->acc->in_tail
+ : hc->acc->in_head;
+ }
+ else
+ {
+ struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
+ bool overflow;
+ uint64_t dir;
+ bool skip = true;
+
+ overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
+ dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
+ /* If account does not match, linear scan for
+ first matching account. */
+ while ( (! overflow) &&
+ (NULL != t) &&
+ (t->credit_account != hc->acc) )
+ {
+ skip = false;
+ t = h->transactions[(t->row_id + dir) % h->ram_limit];
+ if ( (NULL != t) &&
+ (t->row_id == hc->ha.start_idx) )
+ overflow = true; /* full circle, give up! */
+ }
+ if ( (NULL == t) ||
+ overflow)
+ {
+ in_shutdown = h->in_shutdown;
+ /* FIXME: these conditions are unclear to me. */
+ if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
+ (0 < hc->ha.delta))
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ if (overflow)
+ return TALER_MHD_reply_with_ec (
+ connection,
+ TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
+ NULL);
+ goto finish;
+ }
+ if (in_shutdown)
+ {
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ goto finish;
+ }
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (
+ hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ if (skip)
+ {
+ /* range from application is exclusive, skip the
+ matching entry */
+ if (0 > hc->ha.delta)
+ pos = t->prev_in;
+ else
+ pos = t->next_in;
+ }
+ else
+ {
+ pos = t;
+ }
+ }
+ if (NULL != pos)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Returning %lld credit transactions starting (inclusive) from %llu\n",
+ (long long) hc->ha.delta,
+ (unsigned long long) pos->row_id);
+ while ( (0 != hc->ha.delta) &&
+ (NULL != pos) )
+ {
+ json_t *trans;
+
+ if (T_CREDIT != pos->type)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Unexpected DEBIT transaction #%llu for account `%s'\n",
+ (unsigned long long) pos->row_id,
+ account);
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ continue;
+ }
+ trans = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_uint64 ("row_id",
+ pos->row_id),
+ GNUNET_JSON_pack_timestamp ("date",
+ pos->date),
+ TALER_JSON_pack_amount ("amount",
+ &pos->amount),
+ GNUNET_JSON_pack_string ("debit_account",
+ pos->debit_account->payto_uri),
+ GNUNET_JSON_pack_data_auto ("reserve_pub",
+ &pos->subject.credit.reserve_pub));
+ GNUNET_assert (NULL != trans);
+ GNUNET_assert (0 ==
+ json_array_append_new (hc->history,
+ trans));
+ if (hc->ha.delta > 0)
+ hc->ha.delta--;
+ else
+ hc->ha.delta++;
+ if (0 > hc->ha.delta)
+ pos = pos->prev_in;
+ if (0 < hc->ha.delta)
+ pos = pos->next_in;
+ }
+ if ( (0 == json_array_size (hc->history)) &&
+ (! h->in_shutdown) &&
+ (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
+ (0 < hc->ha.delta))
+ {
+ TALER_FAKEBANK_start_lp_ (h,
+ connection,
+ hc->acc,
+ GNUNET_TIME_absolute_get_remaining (hc->timeout),
+ LP_CREDIT,
+ NULL);
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+ return MHD_YES;
+ }
+ in_shutdown = h->in_shutdown;
+ acc_payto_uri = hc->acc->payto_uri;
+ GNUNET_assert (0 ==
+ pthread_mutex_unlock (&h->big_lock));
+finish:
+ if (0 == json_array_size (hc->history))
+ {
+ GNUNET_break (in_shutdown ||
+ (! GNUNET_TIME_absolute_is_future (hc->timeout)));
+ return TALER_MHD_reply_static (connection,
+ MHD_HTTP_NO_CONTENT,
+ NULL,
+ NULL,
+ 0);
+ }
+ {
+ json_t *h = hc->history;
+
+ hc->history = NULL;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_string (
+ "credit_account",
+ acc_payto_uri),
+ GNUNET_JSON_pack_array_steal (
+ "incoming_transactions",
+ h));
+ }
+}
diff --git a/src/bank-lib/fakebank_twg_history.h b/src/bank-lib/fakebank_twg_history.h
new file mode 100644
index 000000000..c49678aef
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_history.h
@@ -0,0 +1,67 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_history.c
+ * @brief routines to return account histories for the Taler Wire Gateway API
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_HISTORY_H
+#define FAKEBANK_TWG_HISTORY_H
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /history/outgoing
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_debit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+
+/**
+ * Handle incoming HTTP request for /history/incoming
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account which account the request is about
+ * @param con_cls closure for request (NULL or &special_ptr)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_twg_get_credit_history_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ void **con_cls);
+
+
+#endif
diff --git a/src/bank-lib/fakebank_twg_transfer.c b/src/bank-lib/fakebank_twg_transfer.c
new file mode 100644
index 000000000..fef314a52
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_transfer.c
@@ -0,0 +1,178 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_transfer.c
+ * @brief implementation of the Taler Wire Gateway "/transfer" endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#include "platform.h"
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+#include "fakebank_common_transact.h"
+#include "fakebank_twg_transfer.h"
+
+
+/**
+ * Handle incoming HTTP request for /transfer.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account making the transfer
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_handle_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct ConnectionContext *cc = *con_cls;
+ enum GNUNET_JSON_PostResult pr;
+ json_t *json;
+ uint64_t row_id;
+ struct GNUNET_TIME_Timestamp ts;
+
+ if (NULL == cc)
+ {
+ cc = GNUNET_new (struct ConnectionContext);
+ cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
+ *con_cls = cc;
+ }
+ pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ connection,
+ &cc->ctx,
+ upload_data,
+ upload_data_size,
+ &json);
+ switch (pr)
+ {
+ case GNUNET_JSON_PR_OUT_OF_MEMORY:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_CONTINUE:
+ return MHD_YES;
+ case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ GNUNET_break (0);
+ return MHD_NO;
+ case GNUNET_JSON_PR_SUCCESS:
+ break;
+ }
+ {
+ struct GNUNET_HashCode uuid;
+ struct TALER_WireTransferIdentifierRawP wtid;
+ const char *credit_account;
+ char *credit;
+ const char *base_url;
+ struct TALER_Amount amount;
+ enum GNUNET_GenericReturnValue ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto ("request_uid",
+ &uuid),
+ TALER_JSON_spec_amount ("amount",
+ h->currency,
+ &amount),
+ GNUNET_JSON_spec_string ("exchange_base_url",
+ &base_url),
+ GNUNET_JSON_spec_fixed_auto ("wtid",
+ &wtid),
+ GNUNET_JSON_spec_string ("credit_account",
+ &credit_account),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ (ret = TALER_MHD_parse_json_data (connection,
+ json,
+ spec)))
+ {
+ GNUNET_break_op (0);
+ json_decref (json);
+ return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
+ }
+ {
+ enum GNUNET_GenericReturnValue ret;
+
+ credit = TALER_xtalerbank_account_from_payto (credit_account);
+ if (NULL == credit)
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
+ credit_account);
+ }
+ ret = TALER_FAKEBANK_make_transfer_ (h,
+ account,
+ credit,
+ &amount,
+ &wtid,
+ base_url,
+ &uuid,
+ &row_id,
+ &ts);
+ if (GNUNET_OK != ret)
+ {
+ MHD_RESULT res;
+ char *uids;
+
+ GNUNET_break (0);
+ uids = GNUNET_STRINGS_data_to_string_alloc (&uuid,
+ sizeof (uuid));
+ json_decref (json);
+ res = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_BANK_TRANSFER_REQUEST_UID_REUSED,
+ uids);
+ GNUNET_free (uids);
+ return res;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Receiving incoming wire transfer: %s->%s, subject: %s, amount: %s, from %s\n",
+ account,
+ credit,
+ TALER_B2S (&wtid),
+ TALER_amount2s (&amount),
+ base_url);
+ GNUNET_free (credit);
+ }
+ }
+ json_decref (json);
+
+ /* Finally build response object */
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_uint64 ("row_id",
+ row_id),
+ GNUNET_JSON_pack_timestamp ("timestamp",
+ ts));
+}
diff --git a/src/bank-lib/fakebank_twg_transfer.h b/src/bank-lib/fakebank_twg_transfer.h
new file mode 100644
index 000000000..2019565bf
--- /dev/null
+++ b/src/bank-lib/fakebank_twg_transfer.h
@@ -0,0 +1,55 @@
+/*
+ This file is part of TALER
+ (C) 2016-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with TALER; see the file COPYING. If not,
+ see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file bank-lib/fakebank_twg_transfer.h
+ * @brief implementation of the Taler Wire Gateway "/transfer" endpoint
+ * @author Christian Grothoff <christian@grothoff.org>
+ */
+#ifndef FAKEBANK_TWG_TRANSFER_H
+#define FAKEBANK_TWG_TRANSFER_H
+
+
+#include "taler_fakebank_lib.h"
+#include "taler_bank_service.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+#include "fakebank.h"
+
+
+/**
+ * Handle incoming HTTP request for /transfer.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param account account making the transfer
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct ConnectionContext *`)
+ * @return MHD result code
+ */
+MHD_RESULT
+TALER_FAKEBANK_handle_transfer_ (
+ struct TALER_FAKEBANK_Handle *h,
+ struct MHD_Connection *connection,
+ const char *account,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls);
+
+#endif
diff --git a/src/bank-lib/taler-exchange-wire-gateway-client.c b/src/bank-lib/taler-exchange-wire-gateway-client.c
index ab16573a7..b0d387b71 100644
--- a/src/bank-lib/taler-exchange-wire-gateway-client.c
+++ b/src/bank-lib/taler-exchange-wire-gateway-client.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2017-2021 Taler Systems SA
+ Copyright (C) 2017-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
@@ -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.
*
* @param cls closure
- * @param http_status HTTP status code from server
- * @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
+ * @param reply response we got from the bank
*/
-static enum GNUNET_GenericReturnValue
+static void
credit_history_cb (void *cls,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- uint64_t serial_id,
- const struct TALER_BANK_CreditDetails *details,
- const json_t *json)
+ const struct TALER_BANK_CreditHistoryResponse *reply)
{
(void) cls;
chh = NULL;
- if (MHD_HTTP_OK != http_status)
+ switch (reply->http_status)
{
- if ( (MHD_HTTP_NO_CONTENT != http_status) ||
- (TALER_EC_NONE != ec) )
+ case 0:
+ fprintf (stderr,
+ "Failed to obtain HTTP reply from `%s'\n",
+ auth.wire_gateway_url);
+ global_ret = 2;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ fprintf (stdout,
+ "No transactions.\n");
+ global_ret = 0;
+ break;
+ case MHD_HTTP_OK:
+ for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
- if (0 == http_status)
- {
- fprintf (stderr,
- "Failed to obtain HTTP reply from `%s'\n",
- 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;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_NO;
+ const struct TALER_BANK_CreditDetails *cd =
+ &reply->details.ok.details[i];
+
+ /* If credit/debit accounts were specified, use as a filter */
+ if ( (NULL != credit_account) &&
+ (0 != strcasecmp (credit_account,
+ reply->details.ok.credit_account_uri) ) )
+ continue;
+ if ( (NULL != debit_account) &&
+ (0 != strcasecmp (debit_account,
+ cd->debit_account_uri) ) )
+ continue;
+ fprintf (stdout,
+ "%llu: %s->%s (%s) over %s at %s\n",
+ (unsigned long long) cd->serial_id,
+ cd->debit_account_uri,
+ reply->details.ok.credit_account_uri,
+ TALER_B2S (&cd->reserve_pub),
+ TALER_amount2s (&cd->amount),
+ GNUNET_TIME_timestamp2s (cd->execution_date));
}
- fprintf (stdout,
- "End of transactions list.\n");
global_ret = 0;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_NO;
+ 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;
}
-
- /* If credit/debit accounts were specified, use as a filter */
- if ( (NULL != credit_account) &&
- (0 != strcasecmp (credit_account,
- details->credit_account_uri) ) )
- return GNUNET_OK;
- if ( (NULL != debit_account) &&
- (0 != strcasecmp (debit_account,
- details->debit_account_uri) ) )
- return GNUNET_OK;
-
- fprintf (stdout,
- "%llu: %s->%s (%s) over %s at %s\n",
- (unsigned long long) serial_id,
- details->debit_account_uri,
- details->credit_account_uri,
- TALER_B2S (&details->reserve_pub),
- TALER_amount2s (&details->amount),
- GNUNET_TIME_timestamp2s (details->execution_date));
- return GNUNET_OK;
+ 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 http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
- * 0 if the bank's reply is bogus (fails to follow the protocol),
- * #MHD_HTTP_NO_CONTENT if there are no more results; on success the
- * last callback is always of this status (even if `abs(num_results)` were
- * already returned).
- * @param ec 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
+ * @param reply response details
*/
-static enum GNUNET_GenericReturnValue
+static void
debit_history_cb (void *cls,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- uint64_t serial_id,
- const struct TALER_BANK_DebitDetails *details,
- const json_t *json)
+ const struct TALER_BANK_DebitHistoryResponse *reply)
{
(void) cls;
dhh = NULL;
- if (MHD_HTTP_OK != http_status)
+ switch (reply->http_status)
{
- if ( (MHD_HTTP_NO_CONTENT != http_status) ||
- (TALER_EC_NONE != ec) )
+ case 0:
+ fprintf (stderr,
+ "Failed to obtain HTTP reply from `%s'\n",
+ auth.wire_gateway_url);
+ global_ret = 2;
+ break;
+ case MHD_HTTP_NO_CONTENT:
+ fprintf (stdout,
+ "No transactions.\n");
+ global_ret = 0;
+ break;
+ case MHD_HTTP_OK:
+ for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
{
- if (0 == http_status)
- {
- fprintf (stderr,
- "Failed to obtain HTTP reply from `%s'\n",
- 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;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_NO;
+ const struct TALER_BANK_DebitDetails *dd =
+ &reply->details.ok.details[i];
+
+ /* If credit/debit accounts were specified, use as a filter */
+ if ( (NULL != credit_account) &&
+ (0 != strcasecmp (credit_account,
+ dd->credit_account_uri) ) )
+ continue;
+ if ( (NULL != debit_account) &&
+ (0 != strcasecmp (debit_account,
+ reply->details.ok.debit_account_uri) ) )
+ continue;
+ fprintf (stdout,
+ "%llu: %s->%s (%s) over %s at %s\n",
+ (unsigned long long) dd->serial_id,
+ reply->details.ok.debit_account_uri,
+ dd->credit_account_uri,
+ TALER_B2S (&dd->wtid),
+ TALER_amount2s (&dd->amount),
+ GNUNET_TIME_timestamp2s (dd->execution_date));
}
- fprintf (stdout,
- "End of transactions list.\n");
global_ret = 0;
- GNUNET_SCHEDULER_shutdown ();
- return GNUNET_NO;
+ 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;
}
-
- /* If credit/debit accounts were specified, use as a filter */
- if ( (NULL != credit_account) &&
- (0 != strcasecmp (credit_account,
- details->credit_account_uri) ) )
- return GNUNET_OK;
- if ( (NULL != debit_account) &&
- (0 != strcasecmp (debit_account,
- details->debit_account_uri) ) )
- return GNUNET_OK;
-
- fprintf (stdout,
- "%llu: %s->%s (%s) over %s at %s\n",
- (unsigned long long) serial_id,
- details->debit_account_uri,
- details->credit_account_uri,
- TALER_B2S (&details->wtid),
- TALER_amount2s (&details->amount),
- GNUNET_TIME_timestamp2s (details->execution_date));
- return GNUNET_OK;
+ GNUNET_SCHEDULER_shutdown ();
}
@@ -381,34 +357,28 @@ execute_debit_history (void)
* execution.
*
* @param cls closure
- * @param response_code HTTP status code
- * @param ec taler error code
- * @param row_id unique ID of the wire transfer in the bank's records
- * @param timestamp when did the transaction go into effect
+ * @param tr response details
*/
static void
confirmation_cb (void *cls,
- unsigned int response_code,
- enum TALER_ErrorCode ec,
- uint64_t row_id,
- struct GNUNET_TIME_Timestamp timestamp)
+ const struct TALER_BANK_TransferResponse *tr)
{
(void) cls;
eh = NULL;
- if (MHD_HTTP_OK != response_code)
+ if (MHD_HTTP_OK != tr->http_status)
{
fprintf (stderr,
"The wire transfer didn't execute correctly (%u/%d).\n",
- response_code,
- ec);
+ tr->http_status,
+ tr->ec);
GNUNET_SCHEDULER_shutdown ();
return;
}
fprintf (stdout,
"Wire transfer #%llu executed successfully at %s.\n",
- (unsigned long long) row_id,
- GNUNET_TIME_timestamp2s (timestamp));
+ (unsigned long long) tr->details.ok.row_id,
+ GNUNET_TIME_timestamp2s (tr->details.ok.timestamp));
global_ret = 0;
GNUNET_SCHEDULER_shutdown ();
}
@@ -488,39 +458,29 @@ execute_wire_transfer (void)
* Function called with the result of the operation.
*
* @param cls closure
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
- * 0 if the bank's reply is bogus (fails to follow the protocol)
- * @param ec detailed error code
- * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
- * @param timestamp timestamp when the transaction got settled at the bank.
- * @param json detailed response from the HTTPD, or NULL if reply was not in JSON
+ * @param air response details
*/
static void
res_cb (void *cls,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- uint64_t serial_id,
- struct GNUNET_TIME_Timestamp timestamp,
- const json_t *json)
+ const struct TALER_BANK_AdminAddIncomingResponse *air)
{
(void) cls;
- (void) timestamp;
op = NULL;
- switch (ec)
+ switch (air->http_status)
{
- case TALER_EC_NONE:
+ case MHD_HTTP_OK:
global_ret = 0;
fprintf (stdout,
"%llu\n",
- (unsigned long long) serial_id);
+ (unsigned long long) air->details.ok.serial_id);
break;
default:
fprintf (stderr,
"Operation failed with status code %u/%u\n",
- (unsigned int) ec,
- http_status);
- if (NULL != json)
- json_dumpf (json,
+ (unsigned int) air->ec,
+ air->http_status);
+ if (NULL != air->response)
+ json_dumpf (air->response,
stderr,
JSON_INDENT (2));
break;
diff --git a/src/bank-lib/taler-fakebank-run.c b/src/bank-lib/taler-fakebank-run.c
index 4e3706071..c15145ecb 100644
--- a/src/bank-lib/taler-fakebank-run.c
+++ b/src/bank-lib/taler-fakebank-run.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2016, 2017 Taler Systems SA
+ Copyright (C) 2016-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -52,6 +52,10 @@ static struct TALER_FAKEBANK_Handle *fb;
*/
static struct GNUNET_SCHEDULER_Task *keepalive;
+/**
+ * Amount to credit an account with on /register.
+ */
+static struct TALER_Amount signup_bonus;
/**
* Stop the process.
@@ -103,6 +107,8 @@ run (void *cls,
unsigned long long port = 8082;
unsigned long long ram = 1024 * 128; /* 128 k entries */
char *currency_string;
+ char *hostname;
+ char *exchange_url;
(void) cls;
(void) args;
@@ -125,6 +131,23 @@ run (void *cls,
port);
}
if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "bank",
+ "SUGGESTED_EXCHANGE",
+ &exchange_url))
+ {
+ /* no suggested exchange */
+ exchange_url = NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "bank",
+ "HOSTNAME",
+ &hostname))
+ {
+ hostname = GNUNET_strdup ("localhost");
+ }
+ if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
"bank",
"RAM_LIMIT",
@@ -142,17 +165,37 @@ run (void *cls,
go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
TALER_MHD_setup (go);
}
- fb = TALER_FAKEBANK_start2 ((uint16_t) port,
+ if (GNUNET_OK !=
+ TALER_amount_is_valid (&signup_bonus))
+ {
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (currency_string,
+ &signup_bonus));
+ }
+ if (0 != strcmp (currency_string,
+ signup_bonus.currency))
+ {
+ fprintf (stderr,
+ "Signup bonus and main currency do not match\n");
+ ret = EXIT_INVALIDARGUMENT;
+ return;
+ }
+ fb = TALER_FAKEBANK_start3 (hostname,
+ (uint16_t) port,
+ exchange_url,
currency_string,
ram,
- num_threads);
+ num_threads,
+ &signup_bonus);
+ GNUNET_free (hostname);
+ GNUNET_free (exchange_url);
+ GNUNET_free (currency_string);
if (NULL == fb)
{
GNUNET_break (0);
ret = EXIT_FAILURE;
return;
}
- GNUNET_free (currency_string);
keepalive = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&keepalive_task,
NULL);
@@ -183,6 +226,11 @@ main (int argc,
"NUM_THREADS",
"size of the thread pool",
&num_threads),
+ TALER_getopt_get_amount ('s',
+ "signup-bonus",
+ "AMOUNT",
+ "amount to credit newly registered account (created out of thin air)",
+ &signup_bonus),
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue iret;
diff --git a/src/bank-lib/test_bank.sh b/src/bank-lib/test_bank.sh
index 46be326b4..5ee2bd836 100755
--- a/src/bank-lib/test_bank.sh
+++ b/src/bank-lib/test_bank.sh
@@ -1,19 +1,20 @@
#!/bin/bash
-
+# This file is in the public domain.
+# shellcheck disable=SC2317
set -eu
# Exit, with status code "skip" (no 'real' failure)
function exit_skip() {
- echo $1
+ echo "$1"
exit 77
}
# Cleanup to run whenever we exit
function cleanup()
{
- for n in `jobs -p`
+ for n in $(jobs -p)
do
- kill $n 2> /dev/null || true
+ kill "$n" 2> /dev/null || true
done
wait
}
@@ -23,21 +24,30 @@ trap cleanup EXIT
echo -n "Launching bank..."
-taler-fakebank-run -c test_bank.conf -L DEBUG &> bank.log &
+taler-fakebank-run \
+ -c test_bank.conf \
+ -L DEBUG &> bank.log &
# Wait for bank to be available (usually the slowest)
-for n in `seq 1 50`
+for n in $(seq 1 50)
do
echo -n "."
sleep 0.2
OK=0
# bank
- wget --tries=1 --timeout=1 http://localhost:8899/ -o /dev/null -O /dev/null >/dev/null || continue
+ wget \
+ --tries=1 \
+ --timeout=1 \
+ http://localhost:8899/ \
+ -o /dev/null \
+ -O /dev/null \
+ >/dev/null \
+ || continue
OK=1
break
done
-if [ 1 != $OK ]
+if [ 1 != "$OK" ]
then
exit_skip "Failed to launch services (bank)"
fi
@@ -47,31 +57,40 @@ echo "OK"
echo -n "Making wire transfer to exchange ..."
taler-exchange-wire-gateway-client \
- -b http://localhost:8899/exchange/ \
+ -b http://localhost:8899/accounts/exchange/taler-wire-gateway/ \
-S 0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00 \
- -D payto://x-taler-bank/localhost:8899/user \
+ -D payto://x-taler-bank/localhost:8899/user?receiver-name=user \
-a TESTKUDOS:4 > /dev/null
echo " OK"
echo -n "Requesting exchange incoming transaction list ..."
-./taler-exchange-wire-gateway-client -b http://localhost:8899/exchange/ -i | grep TESTKUDOS:4 > /dev/null
+./taler-exchange-wire-gateway-client \
+ -b http://localhost:8899/accounts/exchange/taler-wire-gateway/ \
+ -i \
+ | grep TESTKUDOS:4 \
+ > /dev/null
echo " OK"
echo -n "Making wire transfer from exchange..."
./taler-exchange-wire-gateway-client \
- -b http://localhost:8899/exchange/ \
+ -b http://localhost:8899/accounts/exchange/taler-wire-gateway/ \
-S 0ZSX8SH0M30KHX8K3Y1DAMVGDQV82XEF9DG1HC4QMQ3QWYT4AF00 \
- -C payto://x-taler-bank/localhost:8899/merchant \
- -a TESTKUDOS:2 > /dev/null
+ -C payto://x-taler-bank/localhost:8899/merchant?receiver-name=merchant \
+ -a TESTKUDOS:2 \
+ -L DEBUG > /dev/null
echo " OK"
echo -n "Requesting exchange's outgoing transaction list..."
-./taler-exchange-wire-gateway-client -b http://localhost:8899/exchange/ -o | grep TESTKUDOS:2 > /dev/null
+./taler-exchange-wire-gateway-client \
+ -b http://localhost:8899/accounts/exchange/taler-wire-gateway/ \
+ -o \
+ | grep TESTKUDOS:2 \
+ > /dev/null
echo " OK"