summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2023-09-17 11:27:33 +0200
committerChristian Grothoff <christian@grothoff.org>2023-09-17 11:27:33 +0200
commit9ae46f367e606f9b3866b361459e05a71be9f310 (patch)
tree8ebc0edf84cc745a75b9c8ff4b550a40a08c9ca8
parent535c57b2f9660e4178b31e2a0c47444b00348b71 (diff)
downloadexchange-9ae46f367e606f9b3866b361459e05a71be9f310.tar.gz
exchange-9ae46f367e606f9b3866b361459e05a71be9f310.tar.bz2
exchange-9ae46f367e606f9b3866b361459e05a71be9f310.zip
first cut for API to get coin history
-rw-r--r--src/auditor/taler-helper-auditor-aggregation.c5
-rw-r--r--src/auditor/taler-helper-auditor-coins.c5
-rw-r--r--src/exchange/Makefile.am5
-rw-r--r--src/exchange/taler-exchange-httpd.c53
-rw-r--r--src/exchange/taler-exchange-httpd_coins_get.c150
-rw-r--r--src/exchange/taler-exchange-httpd_coins_get.h53
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c55
-rw-r--r--src/exchange/taler-exchange-httpd_link.c25
-rw-r--r--src/exchange/taler-exchange-httpd_link.h4
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c30
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h31
-rw-r--r--src/exchangedb/pg_get_coin_transactions.c2
-rw-r--r--src/exchangedb/pg_get_coin_transactions.h9
-rw-r--r--src/exchangedb/test_exchangedb.c13
-rw-r--r--src/include/taler_exchangedb_plugin.h6
15 files changed, 375 insertions, 71 deletions
diff --git a/src/auditor/taler-helper-auditor-aggregation.c b/src/auditor/taler-helper-auditor-aggregation.c
index 72498ee07..81892c16c 100644
--- a/src/auditor/taler-helper-auditor-aggregation.c
+++ b/src/auditor/taler-helper-auditor-aggregation.c
@@ -767,6 +767,7 @@ wire_transfer_information_cb (
struct TALER_CoinPublicInfo coin;
enum GNUNET_DB_QueryStatus qs;
struct TALER_PaytoHashP hpt;
+ uint64_t etag = 0;
TALER_payto_hash (account_pay_uri,
&hpt);
@@ -779,8 +780,12 @@ wire_transfer_information_cb (
"h-payto does not match payto URI");
}
/* Obtain coin's transaction history */
+ /* TODO: could use 'etag' mechanism to only fetch transactions
+ we did not yet process, instead of going over them
+ again and again.*/
qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
coin_pub,
+ &etag,
&tl);
if ( (qs < 0) ||
(NULL == tl) )
diff --git a/src/auditor/taler-helper-auditor-coins.c b/src/auditor/taler-helper-auditor-coins.c
index bfdc11c7e..8c3d66b98 100644
--- a/src/auditor/taler-helper-auditor-coins.c
+++ b/src/auditor/taler-helper-auditor-coins.c
@@ -435,9 +435,14 @@ check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_Amount refunded;
struct TALER_Amount deposit_fee;
bool have_refund;
+ uint64_t etag = 0;
+ /* TODO: could use 'etag' mechanism to only fetch transactions
+ we did not yet process, instead of going over them
+ again and again. */
qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
coin_pub,
+ &etag,
&tl);
if (0 >= qs)
return qs;
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 607ea919e..dded2b1f0 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -124,14 +124,15 @@ taler_exchange_wirewatch_LDADD = \
taler_exchange_httpd_SOURCES = \
taler-exchange-httpd.c taler-exchange-httpd.h \
+ taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \
+ taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \
taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \
taler-exchange-httpd_aml-decision-get.c \
taler-exchange-httpd_aml-decisions-get.c \
taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \
taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \
- taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \
- taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \
+ taler-exchange-httpd_coins_get.c taler-exchange-httpd_coins_get.h \
taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \
taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \
taler-exchange-httpd_config.c taler-exchange-httpd_config.h \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index 149c60ca3..ad4b50d23 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -36,6 +36,7 @@
#include "taler-exchange-httpd_auditors.h"
#include "taler-exchange-httpd_batch-deposit.h"
#include "taler-exchange-httpd_batch-withdraw.h"
+#include "taler-exchange-httpd_coins_get.h"
#include "taler-exchange-httpd_config.h"
#include "taler-exchange-httpd_contract.h"
#include "taler-exchange-httpd_csr.h"
@@ -369,6 +370,55 @@ handle_post_coins (struct TEH_RequestContext *rc,
/**
+ * Handle a GET "/coins/$COIN_PUB[/$OP]" request. Parses the "coin_pub"
+ * EdDSA key of the coin and demultiplexes based on $OP.
+ *
+ * @param rc request context
+ * @param root uploaded JSON data
+ * @param args array of additional options
+ * @return MHD result code
+ */
+static MHD_RESULT
+handle_get_coins (struct TEH_RequestContext *rc,
+ const char *const args[2])
+{
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ if (NULL == args[0])
+ {
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ rc->url);
+ }
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &coin_pub,
+ sizeof (coin_pub)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
+ args[0]);
+ }
+
+ if (NULL == args[1])
+ return TEH_handler_coins_get (rc,
+ &coin_pub);
+ if (0 == strcmp (args[1],
+ "link"))
+ return TEH_handler_link (rc,
+ &coin_pub);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ rc->url);
+}
+
+
+/**
* Signature of functions that handle operations
* authorized by AML officers.
*
@@ -1537,8 +1587,9 @@ handle_mhd_request (void *cls,
{
.url = "coins",
.method = MHD_HTTP_METHOD_GET,
- .handler.get = TEH_handler_link,
+ .handler.get = &handle_get_coins,
.nargs = 2,
+ .nargs_is_upper_bound = true
},
/* refreshes/$RCH/reveal */
{
diff --git a/src/exchange/taler-exchange-httpd_coins_get.c b/src/exchange/taler-exchange-httpd_coins_get.c
new file mode 100644
index 000000000..f7f0409c8
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_coins_get.c
@@ -0,0 +1,150 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_coins_get.c
+ * @brief Handle GET /coins/$COIN_PUB requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_mhd_lib.h"
+#include "taler_json_lib.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_coins_get.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Add the headers we want to set for every /keys response.
+ *
+ * @param cls the key state to use
+ * @param[in,out] response the response to modify
+ */
+static void
+add_response_headers (void *cls,
+ struct MHD_Response *response)
+{
+ (void) cls;
+ TALER_MHD_add_global_headers (response);
+}
+
+
+MHD_RESULT
+TEH_handler_coins_get (struct TEH_RequestContext *rc,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_EXCHANGEDB_TransactionList *tl;
+ const char *etags;
+ uint64_t etag = 0;
+
+ etags = MHD_lookup_connection_value (rc->connection,
+ MHD_HEADER_KIND,
+ MHD_HTTP_HEADER_IF_NONE_MATCH);
+ if (NULL != etags)
+ {
+ char dummy;
+ unsigned long long ev;
+
+ if (1 != sscanf (etags,
+ "%llu%c",
+ &ev,
+ &dummy))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Client send malformed `If-None-Match' header `%s'\n",
+ etags);
+ }
+ else
+ {
+ etag = (uint64_t) ev;
+ }
+ }
+ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+ coin_pub,
+ &etag,
+ &tl);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get_coin_history");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0); /* single-shot query should never have soft-errors */
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ "get_coin_history");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ if (0 == etag)
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN,
+ NULL);
+ return TEH_RESPONSE_reply_not_modified (rc->connection,
+ etags,
+ &add_response_headers,
+ NULL);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ {
+ json_t *history;
+ char etagp[24];
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+
+ GNUNET_snprintf (etagp,
+ sizeof (etagp),
+ "%llu",
+ (unsigned long long) etag);
+ history = TEH_RESPONSE_compile_transaction_history (coin_pub,
+ tl);
+ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+ tl);
+ tl = NULL;
+ if (NULL == history)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
+ "Failed to compile coin history");
+ }
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ GNUNET_JSON_pack_array_steal ("history",
+ history));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_ETAG,
+ etagp));
+ ret = MHD_queue_response (rc->connection,
+ MHD_HTTP_OK,
+ resp);
+ GNUNET_break (MHD_YES == ret);
+ MHD_destroy_response (resp);
+ return ret;
+ }
+ }
+ GNUNET_break (0);
+ return MHD_NO;
+}
+
+
+/* end of taler-exchange-httpd_coins_get.c */
diff --git a/src/exchange/taler-exchange-httpd_coins_get.h b/src/exchange/taler-exchange-httpd_coins_get.h
new file mode 100644
index 000000000..712269c3b
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_coins_get.h
@@ -0,0 +1,53 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2023 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_coins_get.h
+ * @brief Handle GET /coins/$COIN_PUB requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_COINS_GET_H
+#define TALER_EXCHANGE_HTTPD_COINS_GET_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Shutdown reserves-get subsystem. Resumes all
+ * suspended long-polling clients and cleans up
+ * data structures.
+ */
+void
+TEH_reserves_get_cleanup (void);
+
+
+/**
+ * Handle a GET "/coins/$COIN_PUB" request. Parses the
+ * given "coins_pub" in @a args (which should contain the
+ * EdDSA public key of a reserve) and then respond with the
+ * transaction history of the coin.
+ *
+ * @param rc request context
+ * @param coin_pub public key of the coin
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_coins_get (struct TEH_RequestContext *rc,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 2389a21dd..b9194d894 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -2317,16 +2317,14 @@ add_denom_key_cb (void *cls,
/**
* Add the headers we want to set for every /keys response.
*
- * @param ksh the key state to use
- * @param wsh wire state to use
+ * @param cls the key state to use
* @param[in,out] response the response to modify
- * @return #GNUNET_OK on success
*/
-static enum GNUNET_GenericReturnValue
-setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
- struct WireStateHandle *wsh,
+static void
+setup_general_response_headers (void *cls,
struct MHD_Response *response)
{
+ struct TEH_KeyStateHandle *ksh = cls;
char dat[128];
TALER_MHD_add_global_headers (response);
@@ -2352,7 +2350,7 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
ksh->rekey_frequency);
a = GNUNET_TIME_relative_to_absolute (r);
km = GNUNET_TIME_absolute_to_timestamp (a);
- we = GNUNET_TIME_absolute_to_timestamp (wsh->cache_expiration);
+ we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration);
m = GNUNET_TIME_timestamp_min (we,
km);
TALER_MHD_get_date_string (m.abs_time,
@@ -2380,7 +2378,6 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
MHD_add_response_header (response,
MHD_HTTP_HEADER_CACHE_CONTROL,
"public,max-age=3600"));
- return GNUNET_OK;
}
@@ -2688,10 +2685,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
keys_json,
MHD_RESPMEM_MUST_FREE);
GNUNET_assert (NULL != krd.response_uncompressed);
- GNUNET_assert (GNUNET_OK ==
- setup_general_response_headers (ksh,
- wsh,
- krd.response_uncompressed));
+ setup_general_response_headers (ksh,
+ krd.response_uncompressed);
GNUNET_break (MHD_YES ==
MHD_add_response_header (krd.response_uncompressed,
MHD_HTTP_HEADER_ETAG,
@@ -2711,10 +2706,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
MHD_add_response_header (krd.response_compressed,
MHD_HTTP_HEADER_CONTENT_ENCODING,
"deflate")) );
- GNUNET_assert (GNUNET_OK ==
- setup_general_response_headers (ksh,
- wsh,
- krd.response_compressed));
+ setup_general_response_headers (ksh,
+ krd.response_compressed);
/* Set cache control headers: our response varies depending on these headers */
GNUNET_break (MHD_YES ==
MHD_add_response_header (wsh->wire_reply,
@@ -3877,9 +3870,7 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
{
struct GNUNET_TIME_Timestamp last_issue_date;
const char *etag;
- struct WireStateHandle *wsh;
- wsh = get_wire_state ();
etag = MHD_lookup_connection_value (rc->connection,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_IF_NONE_MATCH);
@@ -3967,29 +3958,11 @@ TEH_keys_get_handler (struct TEH_RequestContext *rc,
if ( (NULL != etag) &&
(0 == strcmp (etag,
krd->etag)) )
- {
- MHD_RESULT ret;
- struct MHD_Response *resp;
-
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- TALER_MHD_add_global_headers (resp);
- GNUNET_break (GNUNET_OK ==
- setup_general_response_headers (ksh,
- wsh,
- resp));
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (resp,
- MHD_HTTP_HEADER_ETAG,
- krd->etag));
- ret = MHD_queue_response (rc->connection,
- MHD_HTTP_NOT_MODIFIED,
- resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (resp);
- return ret;
- }
+ return TEH_RESPONSE_reply_not_modified (rc->connection,
+ krd->etag,
+ &setup_general_response_headers,
+ ksh);
+
return MHD_queue_response (rc->connection,
MHD_HTTP_OK,
(MHD_YES ==
diff --git a/src/exchange/taler-exchange-httpd_link.c b/src/exchange/taler-exchange-httpd_link.c
index 9b7e297bc..3d92a11a3 100644
--- a/src/exchange/taler-exchange-httpd_link.c
+++ b/src/exchange/taler-exchange-httpd_link.c
@@ -39,7 +39,7 @@ struct HTD_Context
/**
* Public key of the coin for which we are running link.
*/
- struct TALER_CoinSpendPublicKeyP coin_pub;
+ const struct TALER_CoinSpendPublicKeyP *coin_pub;
/**
* Json array with transfer data we collect.
@@ -153,7 +153,7 @@ link_transaction (void *cls,
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->get_link_data (TEH_plugin->cls,
- &ctx->coin_pub,
+ ctx->coin_pub,
&handle_link_data,
ctx);
if (NULL == ctx->mlist)
@@ -178,26 +178,13 @@ link_transaction (void *cls,
MHD_RESULT
TEH_handler_link (struct TEH_RequestContext *rc,
- const char *const args[2])
+ const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
- struct HTD_Context ctx;
+ struct HTD_Context ctx = {
+ .coin_pub = coin_pub
+ };
MHD_RESULT mhd_ret;
- memset (&ctx,
- 0,
- sizeof (ctx));
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (args[0],
- strlen (args[0]),
- &ctx.coin_pub,
- sizeof (ctx.coin_pub)))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
- args[0]);
- }
ctx.mlist = json_array ();
GNUNET_assert (NULL != ctx.mlist);
if (GNUNET_OK !=
diff --git a/src/exchange/taler-exchange-httpd_link.h b/src/exchange/taler-exchange-httpd_link.h
index 01679e877..255c0ca57 100644
--- a/src/exchange/taler-exchange-httpd_link.h
+++ b/src/exchange/taler-exchange-httpd_link.h
@@ -32,12 +32,12 @@
* Handle a "/coins/$COIN_PUB/link" request.
*
* @param rc request context
- * @param args array of additional options (length: 2, first is the coin_pub, second must be "link")
+ * @param coin_pub the coin public key
* @return MHD result code
*/
MHD_RESULT
TEH_handler_link (struct TEH_RequestContext *rc,
- const char *const args[2]);
+ const struct TALER_CoinSpendPublicKeyP *coin_pub);
#endif
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index 7d2d7a9b5..5ae232c36 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -653,6 +653,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
struct TALER_EXCHANGEDB_TransactionList *tl;
enum GNUNET_DB_QueryStatus qs;
json_t *history;
+ uint64_t etag = 0;
TEH_plugin->rollback (TEH_plugin->cls);
if (GNUNET_OK !=
@@ -667,6 +668,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (
}
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
coin_pub,
+ &etag,
&tl);
TEH_plugin->rollback (TEH_plugin->cls);
if (0 > qs)
@@ -1187,4 +1189,32 @@ TEH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection,
}
+MHD_RESULT
+TEH_RESPONSE_reply_not_modified (
+ struct MHD_Connection *connection,
+ const char *etags,
+ TEH_RESPONSE_SetHeaders cb,
+ void *cb_cls)
+{
+ MHD_RESULT ret;
+ struct MHD_Response *resp;
+
+ resp = MHD_create_response_from_buffer (0,
+ NULL,
+ MHD_RESPMEM_PERSISTENT);
+ cb (cb_cls,
+ resp);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_ETAG,
+ etags));
+ ret = MHD_queue_response (connection,
+ MHD_HTTP_NOT_MODIFIED,
+ resp);
+ GNUNET_break (MHD_YES == ret);
+ MHD_destroy_response (resp);
+ return ret;
+}
+
+
/* end of taler-exchange-httpd_responses.c */
diff --git a/src/exchange/taler-exchange-httpd_responses.h b/src/exchange/taler-exchange-httpd_responses.h
index a57fa495e..9b525929b 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2022 Taler Systems SA
+ Copyright (C) 2014-2023 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -226,4 +226,33 @@ TEH_RESPONSE_compile_transaction_history (
const struct TALER_EXCHANGEDB_TransactionList *tl);
+/**
+ * Callback used to set headers in a response.
+ *
+ * @param cls closure
+ * @param[in,out] resp response to modify
+ */
+typedef void
+(*TEH_RESPONSE_SetHeaders)(void *cls,
+ struct MHD_Response *resp);
+
+
+/**
+ * Generate a HTTP "Not modified" response with the
+ * given @a etags.
+ *
+ * @param connection connection to queue response on
+ * @param etags ETag header to set
+ * @param cb callback to modify response headers
+ * @param cb_cls closure for @a cb
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_RESPONSE_reply_not_modified (
+ struct MHD_Connection *connection,
+ const char *etags,
+ TEH_RESPONSE_SetHeaders cb,
+ void *cb_cls);
+
+
#endif
diff --git a/src/exchangedb/pg_get_coin_transactions.c b/src/exchangedb/pg_get_coin_transactions.c
index 4f4317920..704e7c5c5 100644
--- a/src/exchangedb/pg_get_coin_transactions.c
+++ b/src/exchangedb/pg_get_coin_transactions.c
@@ -684,6 +684,7 @@ enum GNUNET_DB_QueryStatus
TEH_PG_get_coin_transactions (
void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t *etag,
struct TALER_EXCHANGEDB_TransactionList **tlp)
{
struct PostgresClosure *pg = cls;
@@ -729,6 +730,7 @@ TEH_PG_get_coin_transactions (
.db_cls = cls
};
+ *etag = 0; // FIXME: etag not yet implemented!
PREPARE (pg, // done!
"get_deposit_with_coin_pub",
"SELECT"
diff --git a/src/exchangedb/pg_get_coin_transactions.h b/src/exchangedb/pg_get_coin_transactions.h
index c95fd0947..d49b97bc6 100644
--- a/src/exchangedb/pg_get_coin_transactions.h
+++ b/src/exchangedb/pg_get_coin_transactions.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
+ Copyright (C) 2022, 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
@@ -29,16 +29,21 @@
/**
* Compile a list of all (historic) transactions performed with the given coin
* (/refresh/melt, /deposit, /refund and /recoup operations).
+ * Should return 0 if @a etag is already current, otherwise
+ * return the full history and update @a etag. @a etag
+ * should be set to the last row ID of the given coin
+ * in the coin history table.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param coin_pub coin to investigate
- * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @param[in,out] etag known etag, updated to current etag * @param[out] tlp set to list of transactions, NULL if coin is fresh
* @return database transaction status
*/
enum GNUNET_DB_QueryStatus
TEH_PG_get_coin_transactions (
void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t *etag,
struct TALER_EXCHANGEDB_TransactionList **tlp);
#endif
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 56925acf0..217df2bb4 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1723,9 +1723,11 @@ run (void *cls)
/* Just to test fetching a coin with melt history */
struct TALER_EXCHANGEDB_TransactionList *tl;
enum GNUNET_DB_QueryStatus qs;
+ uint64_t etag = 0;
qs = plugin->get_coin_transactions (plugin->cls,
&refresh.coin.coin_pub,
+ &etag,
&tl);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
plugin->free_coin_transaction_list (plugin->cls,
@@ -1972,9 +1974,14 @@ run (void *cls)
&audit_refund_cb,
NULL));
FAILIF (1 != auditor_row_cnt);
- qs = plugin->get_coin_transactions (plugin->cls,
- &refund.coin.coin_pub,
- &tl);
+ {
+ uint64_t etag = 0;
+
+ qs = plugin->get_coin_transactions (plugin->cls,
+ &refund.coin.coin_pub,
+ &etag,
+ &tl);
+ }
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
GNUNET_assert (NULL != tl);
matched = 0;
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index ee382ebe8..8fd9ce19f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4696,15 +4696,21 @@ struct TALER_EXCHANGEDB_Plugin
/**
* Compile a list of all (historic) transactions performed
* with the given coin (melt, refund, recoup and deposit operations).
+ * Should return 0 if @a etag is already current, otherwise
+ * return the full history and update @a etag. @a etag
+ * should be set to the last row ID of the given coin
+ * in the coin history table.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param coin_pub coin to investigate
+ * @param[in,out] etag known etag, updated to current etag
* @param[out] tlp set to list of transactions, NULL if coin is fresh
* @return database transaction status
*/
enum GNUNET_DB_QueryStatus
(*get_coin_transactions)(void *cls,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ uint64_t *etag,
struct TALER_EXCHANGEDB_TransactionList **tlp);