diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-09-19 13:11:26 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-09-19 13:11:26 +0200 |
commit | 2f7f82536d53cf015f46782c1a81280849fef935 (patch) | |
tree | b7736dbcaccfda35c52a894c56447b61b31a3084 | |
parent | 2afcc8c70202c10b71f98c9e4b9766ae08656459 (diff) |
work on reserve history API
-rw-r--r-- | src/exchange/taler-exchange-httpd_coins_get.c | 10 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_common_kyc.c | 3 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_history.c | 303 | ||||
-rw-r--r-- | src/exchangedb/pg_get_reserve_history.c | 13 | ||||
-rw-r--r-- | src/exchangedb/pg_get_reserve_history.h | 12 | ||||
-rw-r--r-- | src/exchangedb/test_exchangedb.c | 3 | ||||
-rw-r--r-- | src/include/taler_exchange_service.h | 14 | ||||
-rw-r--r-- | src/include/taler_exchangedb_plugin.h | 12 |
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); |