diff options
Diffstat (limited to 'src/bank-lib')
66 files changed, 7739 insertions, 2833 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", - ×tamp), + &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.c b/src/bank-lib/bank_api_common.c index f749e0a92..2c47429ad 100644 --- a/src/bank-lib/bank_api_common.c +++ b/src/bank-lib/bank_api_common.c @@ -23,11 +23,11 @@ #include "bank_api_common.h" -int +enum GNUNET_GenericReturnValue TALER_BANK_setup_auth_ (CURL *easy, const struct TALER_BANK_AuthenticationData *auth) { - int ret; + enum GNUNET_GenericReturnValue ret; ret = GNUNET_OK; switch (auth->method) 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 bbfc9cec8..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 @@ -65,18 +65,7 @@ struct WirePackP GNUNET_NETWORK_STRUCT_END -/** - * Prepare for execution of a wire transfer from the exchange to some - * merchant. - * - * @param destination_account_payto_uri payto:// URL identifying where to send the money - * @param amount amount to transfer, already rounded - * @param exchange_base_url base URL of this exchange (included in subject - * to facilitate use of tracking API by merchant backend) - * @param wtid wire transfer identifier to use - * @param[out] buf set to transfer data to persist, NULL on error - * @param[out] buf_size set to number of bytes in @a buf, 0 on error - */ + void TALER_BANK_prepare_transfer ( const char *destination_account_payto_uri, @@ -110,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; } @@ -169,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", - ×tamp), + &tr.details.ok.timestamp), GNUNET_JSON_spec_end () }; @@ -195,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 */ @@ -235,29 +224,15 @@ 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); } -/** - * Execute a wire transfer. - * - * @param ctx curl context for our event loop - * @param auth authentication data to authenticate with the bank - * @param buf buffer with the prepared execution details - * @param buf_size number of bytes in @a buf - * @param cc function to call upon success - * @param cc_cls closure for @a cc - * @return NULL on error - */ struct TALER_BANK_TransferHandle * TALER_BANK_transfer ( struct GNUNET_CURL_Context *ctx, @@ -366,21 +341,6 @@ TALER_BANK_transfer ( } -/** - * Abort execution of a wire transfer. For example, because we are shutting - * down. Note that if an execution is aborted, it may or may not still - * succeed. - * - * The caller MUST run #TALER_BANK_transfer() again for the same request as - * soon as possible, to ensure that the request either ultimately succeeds or - * ultimately fails. Until this has been done, the transaction is in limbo - * (i.e. may or may not have been committed). - * - * This function cannot be used on a request handle if a response is already - * served for it. - * - * @param th the wire transfer request handle - */ void TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th) { diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c index 11993e558..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,1061 +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); - 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, - ×tamp); - 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); - 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. - */ - 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); } @@ -2423,31 +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; - 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; + if (0 == strncmp (url, + "/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); } @@ -2478,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); } @@ -2552,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); @@ -2569,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; @@ -2608,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) @@ -2618,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 @@ -2675,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); @@ -2723,6 +431,7 @@ TALER_FAKEBANK_start2 (uint16_t port, #else { int pipefd[2]; + if (0 != pipe (pipefd)) { GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, @@ -2737,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, @@ -2754,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, + ×tamp); + 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 7fa730469..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. @@ -101,8 +105,10 @@ run (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg) { unsigned long long port = 8082; - unsigned long long ram = 1024 * 1024 * 128; /* 128 M entries */ + 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,16 +226,24 @@ 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 }; - - if (GNUNET_OK != - GNUNET_PROGRAM_run (argc, argv, - "taler-fakebank-run", - "Runs the fakebank", - options, - &run, - NULL)) + enum GNUNET_GenericReturnValue iret; + + iret = GNUNET_PROGRAM_run (argc, argv, + "taler-fakebank-run", + "Runs the fakebank", + options, + &run, + NULL); + if (GNUNET_SYSERR == iret) return EXIT_INVALIDARGUMENT; + if (GNUNET_NO == iret) + return EXIT_SUCCESS; return ret; } 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" |