aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2023-09-19 13:11:26 +0200
committerChristian Grothoff <christian@grothoff.org>2023-09-19 13:11:26 +0200
commit2f7f82536d53cf015f46782c1a81280849fef935 (patch)
treeb7736dbcaccfda35c52a894c56447b61b31a3084
parent2afcc8c70202c10b71f98c9e4b9766ae08656459 (diff)
work on reserve history API
-rw-r--r--src/exchange/taler-exchange-httpd_coins_get.c10
-rw-r--r--src/exchange/taler-exchange-httpd_common_kyc.c3
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_history.c303
-rw-r--r--src/exchangedb/pg_get_reserve_history.c13
-rw-r--r--src/exchangedb/pg_get_reserve_history.h12
-rw-r--r--src/exchangedb/test_exchangedb.c3
-rw-r--r--src/include/taler_exchange_service.h14
-rw-r--r--src/include/taler_exchangedb_plugin.h12
8 files changed, 208 insertions, 162 deletions
diff --git a/src/exchange/taler-exchange-httpd_coins_get.c b/src/exchange/taler-exchange-httpd_coins_get.c
index 7553bf199..e7b54337a 100644
--- a/src/exchange/taler-exchange-httpd_coins_get.c
+++ b/src/exchange/taler-exchange-httpd_coins_get.c
@@ -30,7 +30,7 @@
/**
- * Add the headers we want to set for every /keys response.
+ * Add the headers we want to set for every response.
*
* @param cls the key state to use
* @param[in,out] response the response to modify
@@ -44,7 +44,7 @@ add_response_headers (void *cls,
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
MHD_HTTP_HEADER_CACHE_CONTROL,
- "no-cache, public"));
+ "no-cache"));
}
@@ -540,7 +540,7 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
{
struct TALER_EXCHANGEDB_TransactionList *tl = NULL;
uint64_t start_off = 0;
- uint64_t etag_in = 0;
+ uint64_t etag_in;
uint64_t etag_out;
char etagp[24];
struct MHD_Response *resp;
@@ -591,6 +591,7 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Client send malformed `If-None-Match' header `%s'\n",
etags);
+ etag_in = start_off;
}
else
{
@@ -622,7 +623,6 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
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,
@@ -680,6 +680,8 @@ TEH_handler_coins_get (struct TEH_RequestContext *rc,
history));
http_status = MHD_HTTP_OK;
}
+ add_response_headers (NULL,
+ resp);
GNUNET_break (MHD_YES ==
MHD_add_response_header (resp,
MHD_HTTP_HEADER_ETAG,
diff --git a/src/exchange/taler-exchange-httpd_common_kyc.c b/src/exchange/taler-exchange-httpd_common_kyc.c
index dfd9b710d..5d4b4c111 100644
--- a/src/exchange/taler-exchange-httpd_common_kyc.c
+++ b/src/exchange/taler-exchange-httpd_common_kyc.c
@@ -23,6 +23,7 @@
#include "taler-exchange-httpd_common_kyc.h"
#include "taler_attributes.h"
#include "taler_error_codes.h"
+#include "taler_kyclogic_lib.h"
#include "taler_exchangedb_plugin.h"
#include <gnunet/gnunet_common.h>
@@ -167,7 +168,7 @@ kyc_aml_finished (void *cls,
&kyc_prox,
kat->provider_section,
num_checks,
- provided_checks,
+ (const char **) provided_checks,
birthday,
GNUNET_TIME_timestamp_get (),
kat->provider_user_id,
diff --git a/src/exchange/taler-exchange-httpd_reserves_history.c b/src/exchange/taler-exchange-httpd_reserves_history.c
index a73b5ab69..056d4b0ef 100644
--- a/src/exchange/taler-exchange-httpd_reserves_history.c
+++ b/src/exchange/taler-exchange-httpd_reserves_history.c
@@ -29,40 +29,6 @@
#include "taler-exchange-httpd_reserves_history.h"
#include "taler-exchange-httpd_responses.h"
-/**
- * How far do we allow a client's time to be off when
- * checking the request timestamp?
- */
-#define TIMESTAMP_TOLERANCE \
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
-
-
-/**
- * Closure for #reserve_history_transaction.
- */
-struct ReserveHistoryContext
-{
- /**
- * Public key of the reserve the inquiry is about.
- */
- const struct TALER_ReservePublicKeyP *reserve_pub;
-
- /**
- * History of the reserve, set in the callback.
- */
- struct TALER_EXCHANGEDB_ReserveHistory *rh;
-
- /**
- * Requested startin offset for the reserve history.
- */
- uint64_t start_off;
-
- /**
- * Current reserve balance.
- */
- struct TALER_Amount balance;
-};
-
/**
* Compile the history of a reserve into a JSON object.
@@ -95,7 +61,6 @@ compile_reserve_history (
GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("type",
"CREDIT"),
- // FIXME: offset missing! (here and in all other cases!)
GNUNET_JSON_pack_timestamp ("timestamp",
bank->execution_date),
GNUNET_JSON_pack_string ("sender_account_url",
@@ -364,74 +329,21 @@ compile_reserve_history (
/**
- * Send reserve history to client.
+ * Add the headers we want to set for every /keys response.
*
- * @param connection connection to the client
- * @param rhc reserve history to return
- * @return MHD result code
+ * @param cls the key state to use
+ * @param[in,out] response the response to modify
*/
-static MHD_RESULT
-reply_reserve_history_success (struct MHD_Connection *connection,
- const struct ReserveHistoryContext *rhc)
+static void
+add_response_headers (void *cls,
+ struct MHD_Response *response)
{
- const struct TALER_EXCHANGEDB_ReserveHistory *rh = rhc->rh;
- json_t *json_history;
-
- json_history = compile_reserve_history (rh);
- if (NULL == json_history)
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
- NULL);
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- TALER_JSON_pack_amount ("balance",
- &rhc->balance),
- GNUNET_JSON_pack_array_steal ("history",
- json_history));
-}
-
-
-/**
- * Function implementing /reserves/ HISTORY transaction.
- * Execute a /reserves/ HISTORY. Given the public key of a reserve,
- * return the associated transaction history. Runs the
- * transaction logic; IF it returns a non-error code, the transaction
- * logic MUST NOT queue a MHD response. IF it returns an hard error,
- * the transaction logic MUST queue a MHD response and set @a mhd_ret.
- * IF it returns the soft error code, the function MAY be called again
- * to retry and MUST not queue a MHD response.
- *
- * @param cls a `struct ReserveHistoryContext *`
- * @param connection MHD request which triggered the transaction
- * @param[out] mhd_ret set to MHD response history for @a connection,
- * if transaction failed (!); unused
- * @return transaction history
- */
-static enum GNUNET_DB_QueryStatus
-reserve_history_transaction (void *cls,
- struct MHD_Connection *connection,
- MHD_RESULT *mhd_ret)
-{
- struct ReserveHistoryContext *rsc = cls;
- enum GNUNET_DB_QueryStatus qs;
-
- qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
- rsc->reserve_pub,
- rsc->start_off,
- &rsc->balance,
- &rsc->rh);
- if (GNUNET_DB_STATUS_HARD_ERROR == qs)
- {
- GNUNET_break (0);
- *mhd_ret
- = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- "get_reserve_history");
- }
- return qs;
+ (void) cls;
+ TALER_MHD_add_global_headers (response);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "no-cache"));
}
@@ -440,56 +352,165 @@ TEH_handler_reserves_history (
struct TEH_RequestContext *rc,
const struct TALER_ReservePublicKeyP *reserve_pub)
{
- struct ReserveHistoryContext rsc = {
- .reserve_pub = reserve_pub
- };
- MHD_RESULT mhd_ret;
- struct TALER_ReserveSignatureP reserve_sig;
- bool required = true;
-
- TALER_MHD_parse_request_header_auto (rc->connection,
- TALER_RESERVE_HISTORY_SIGNATURE_HEADER,
- &reserve_sig,
- required);
+ struct TALER_EXCHANGEDB_ReserveHistory *rh = NULL;
+ uint64_t start_off = 0;
+ struct TALER_Amount balance;
+ uint64_t etag_in;
+ uint64_t etag_out;
+ char etagp[24];
+ struct MHD_Response *resp;
+ unsigned int http_status;
+
TALER_MHD_parse_request_number (rc->connection,
"start",
- &rsc.start_off);
- rsc.reserve_pub = reserve_pub;
+ &start_off);
+ {
+ struct TALER_ReserveSignatureP reserve_sig;
+ bool required = true;
+
+ TALER_MHD_parse_request_header_auto (rc->connection,
+ TALER_RESERVE_HISTORY_SIGNATURE_HEADER,
+ &reserve_sig,
+ required);
+
+ if (GNUNET_OK !=
+ TALER_wallet_reserve_history_verify (start_off,
+ reserve_pub,
+ &reserve_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVE_HISTORY_BAD_SIGNATURE,
+ NULL);
+ }
+ }
+
+ /* Get etag */
+ {
+ const char *etags;
+
+ 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);
+ etag_in = 0;
+ }
+ else
+ {
+ etag_in = (uint64_t) ev;
+ }
+ }
+ else
+ {
+ etag_in = start_off;
+ }
+ }
+
+ {
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
+ reserve_pub,
+ start_off,
+ etag_in,
+ &etag_out,
+ &balance,
+ &rh);
+ 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_reserve_history");
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ "get_reserve_history");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ /* Handled below */
+ break;
+ }
+ }
- if (GNUNET_OK !=
- TALER_wallet_reserve_history_verify (rsc.start_off,
- reserve_pub,
- &reserve_sig))
+ GNUNET_snprintf (etagp,
+ sizeof (etagp),
+ "\"%llu\"",
+ (unsigned long long) etag_out);
+ if (etag_in == etag_out)
+ {
+ return TEH_RESPONSE_reply_not_modified (rc->connection,
+ etagp,
+ &add_response_headers,
+ NULL);
+ }
+ if (NULL == rh)
{
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_RESERVE_HISTORY_BAD_SIGNATURE,
- NULL);
+ /* 204: empty history */
+ resp = MHD_create_response_from_buffer (0,
+ "",
+ MHD_RESPMEM_PERSISTENT);
+ http_status = MHD_HTTP_NO_CONTENT;
}
- rsc.rh = NULL;
- if (GNUNET_OK !=
- TEH_DB_run_transaction (rc->connection,
- "get reserve history",
- TEH_MT_REQUEST_OTHER,
- &mhd_ret,
- &reserve_history_transaction,
- &rsc))
+ else
{
- return mhd_ret;
+ json_t *history;
+
+ http_status = MHD_HTTP_OK;
+ history = compile_reserve_history (rh);
+ TEH_plugin->free_reserve_history (TEH_plugin->cls,
+ rh);
+ rh = 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,
+ NULL);
+ }
+ resp = TALER_MHD_MAKE_JSON_PACK (
+ TALER_JSON_pack_amount ("balance",
+ &balance),
+ GNUNET_JSON_pack_array_steal ("history",
+ history));
}
- if (NULL == rsc.rh)
+ add_response_headers (NULL,
+ resp);
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_ETAG,
+ etagp));
{
- return TALER_MHD_reply_with_error (rc->connection,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
- NULL);
+ MHD_RESULT ret;
+
+ ret = MHD_queue_response (rc->connection,
+ http_status,
+ resp);
+ GNUNET_break (MHD_YES == ret);
+ MHD_destroy_response (resp);
+ return ret;
}
- mhd_ret = reply_reserve_history_success (rc->connection,
- &rsc);
- TEH_plugin->free_reserve_history (TEH_plugin->cls,
- rsc.rh);
- return mhd_ret;
}
diff --git a/src/exchangedb/pg_get_reserve_history.c b/src/exchangedb/pg_get_reserve_history.c
index ba1db2a16..9c7ff39de 100644
--- a/src/exchangedb/pg_get_reserve_history.c
+++ b/src/exchangedb/pg_get_reserve_history.c
@@ -540,11 +540,14 @@ add_close_requests (void *cls,
enum GNUNET_DB_QueryStatus
-TEH_PG_get_reserve_history (void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- uint64_t start_off,
- struct TALER_Amount *balance,
- struct TALER_EXCHANGEDB_ReserveHistory **rhp)
+TEH_PG_get_reserve_history (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
+ struct TALER_Amount *balance,
+ struct TALER_EXCHANGEDB_ReserveHistory **rhp)
{
struct PostgresClosure *pg = cls;
struct ReserveHistoryContext rhc;
diff --git a/src/exchangedb/pg_get_reserve_history.h b/src/exchangedb/pg_get_reserve_history.h
index ca6740c6c..15765f127 100644
--- a/src/exchangedb/pg_get_reserve_history.h
+++ b/src/exchangedb/pg_get_reserve_history.h
@@ -27,12 +27,18 @@
/**
- * Get all of the transaction history associated with the specified
- * reserve.
+ * Compile a list of (historic) transactions performed with the given reserve
+ * (withdraw, incoming wire, open, close operations). Should return 0 if the @a
+ * reserve_pub is unknown, otherwise determine @a etag_out and if it is past @a
+ * etag_in return the history after @a start_off. @a etag_out should be set
+ * to the last row ID of the given @a reserve_pub in the reserve history table.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param reserve_pub public key of the reserve
* @param start_off maximum starting offset in history to exclude from returning
+ * @param etag_in up to this offset the client already has a response, do not
+ * return anything unless @a etag_out will be larger
+ * @param[out] etag_out set to the latest history offset known for this @a coin_pub
* @param[out] balance set to the reserve balance
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
* @return transaction status
@@ -42,6 +48,8 @@ TEH_PG_get_reserve_history (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
struct TALER_Amount *balance,
struct TALER_EXCHANGEDB_ReserveHistory **rhp);
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index f2df1f382..50ce1e045 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1863,10 +1863,13 @@ run (void *cls)
/* check reserve history */
{
struct TALER_Amount balance;
+ uint64_t etag_out;
qs = plugin->get_reserve_history (plugin->cls,
&reserve_pub,
0,
+ 0,
+ &etag_out,
&balance,
&rh);
}
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index f973cfb0d..c344a93ae 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1649,13 +1649,6 @@ struct TALER_EXCHANGE_ReserveHistoryEntry
struct TALER_Amount amount;
/**
- * Index of this entry in the reserve history.
- * Useful to filter requests by starting offset.
- * Offsets are not necessarily contiguous.
- */
- uint64_t entry_off;
-
- /**
* Details depending on @e type.
*/
union
@@ -2064,6 +2057,13 @@ struct TALER_EXCHANGE_ReserveHistory
struct TALER_Amount total_out;
/**
+ * Current etag / last entry in the history.
+ * Useful to filter requests by starting offset.
+ * Offsets are not necessarily contiguous.
+ */
+ uint64_t etag;
+
+ /**
* Reserve history.
*/
const struct TALER_EXCHANGE_ReserveHistoryEntry *history;
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index fc11a292f..484b1155e 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4221,12 +4221,18 @@ struct TALER_EXCHANGEDB_Plugin
/**
- * Get all of the transaction history associated with the specified
- * reserve.
+ * Compile a list of (historic) transactions performed with the given reserve
+ * (withdraw, incoming wire, open, close operations). Should return 0 if the @a
+ * reserve_pub is unknown, otherwise determine @a etag_out and if it is past @a
+ * etag_in return the history after @a start_off. @a etag_out should be set
+ * to the last row ID of the given @a reserve_pub in the reserve history table.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the reserve
* @param start_off maximum starting offset in history to exclude from returning
+ * @param etag_in up to this offset the client already has a response, do not
+ * return anything unless @a etag_out will be larger
+ * @param[out] etag_out set to the latest history offset known for this @a coin_pub
* @param[out] balance set to the reserve balance
* @param[out] rhp set to known transaction history (NULL if reserve is unknown)
* @return transaction status
@@ -4235,6 +4241,8 @@ struct TALER_EXCHANGEDB_Plugin
(*get_reserve_history)(void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
uint64_t start_off,
+ uint64_t etag_in,
+ uint64_t *etag_out,
struct TALER_Amount *balance,
struct TALER_EXCHANGEDB_ReserveHistory **rhp);