diff options
author | Christian Grothoff <grothoff@gnunet.org> | 2022-03-20 13:20:45 +0100 |
---|---|---|
committer | Christian Grothoff <grothoff@gnunet.org> | 2022-03-20 13:20:45 +0100 |
commit | 427417b8352c2036dc6f5c0ca6bd20c0b7edd225 (patch) | |
tree | 54b96b474e9af87586e12763333b04257849798a | |
parent | dee45bf02284716d5dea18e94193d74e64f7e5bf (diff) | |
download | exchange-427417b8352c2036dc6f5c0ca6bd20c0b7edd225.tar.xz |
towards support for new reserve history/status APIs
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.c | 64 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_keys.h | 80 | ||||
-rw-r--r-- | src/include/taler_exchange_service.h | 286 | ||||
-rw-r--r-- | src/include/taler_testing_lib.h | 33 | ||||
-rw-r--r-- | src/lib/Makefile.am | 4 | ||||
-rw-r--r-- | src/lib/exchange_api_common.c | 26 | ||||
-rw-r--r-- | src/lib/exchange_api_handle.c | 21 | ||||
-rw-r--r-- | src/lib/exchange_api_reserves_get.c | 100 | ||||
-rw-r--r-- | src/lib/exchange_api_reserves_history.c | 192 | ||||
-rw-r--r-- | src/lib/exchange_api_reserves_status.c | 183 | ||||
-rw-r--r-- | src/lib/exchange_api_withdraw2.c | 4 | ||||
-rw-r--r-- | src/testing/Makefile.am | 2 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_bank_admin_add_incoming.c | 2 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_exec_closer.c | 2 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_recoup.c | 2 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_reserve_history.c | 413 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_reserve_status.c | 413 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_status.c | 226 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_withdraw.c | 2 |
19 files changed, 1480 insertions, 575 deletions
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c index 1012a8c03..658e5a347 100644 --- a/src/exchange/taler-exchange-httpd_keys.c +++ b/src/exchange/taler-exchange-httpd_keys.c @@ -280,31 +280,6 @@ struct SigningKey }; -/** - * Set of global fees (and options) for a time range. - */ -struct GlobalFee -{ - /** - * Kept in a DLL. - */ - struct GlobalFee *next; - - /** - * Kept in a DLL. - */ - struct GlobalFee *prev; - - struct GNUNET_TIME_Timestamp start_date; - struct GNUNET_TIME_Timestamp end_date; - struct GNUNET_TIME_Relative purse_timeout; - struct GNUNET_TIME_Relative kyc_timeout; - struct GNUNET_TIME_Relative history_expiration; - struct TALER_MasterSignatureP master_sig; - struct TALER_GlobalFeeSet fees; - uint32_t purse_account_limit; -}; - struct TEH_KeyStateHandle { @@ -324,12 +299,12 @@ struct TEH_KeyStateHandle /** * Head of DLL of our global fees. */ - struct GlobalFee *gf_head; + struct TEH_GlobalFee *gf_head; /** * Tail of DLL of our global fees. */ - struct GlobalFee *gf_tail; + struct TEH_GlobalFee *gf_tail; /** * json array with the auditors of this exchange. Contains exactly @@ -1215,7 +1190,7 @@ static void destroy_key_state (struct TEH_KeyStateHandle *ksh, bool free_helper) { - struct GlobalFee *gf; + struct TEH_GlobalFee *gf; clear_response_cache (ksh); while (NULL != (gf = ksh->gf_head)) @@ -2282,9 +2257,9 @@ global_fee_info_cb ( const struct TALER_MasterSignatureP *master_sig) { struct TEH_KeyStateHandle *ksh = cls; - struct GlobalFee *gf; + struct TEH_GlobalFee *gf; - gf = GNUNET_new (struct GlobalFee); + gf = GNUNET_new (struct TEH_GlobalFee); gf->start_date = start_date; gf->end_date = end_date; gf->fees = *fees; @@ -2517,11 +2492,32 @@ TEH_keys_get_state (void) } +const struct TEH_GlobalFee * +TEH_keys_global_fee_by_time ( + struct TEH_KeyStateHandle *ksh, + struct GNUNET_TIME_Timestamp ts) +{ + for (const struct TEH_GlobalFee *gf = ksh->gf_head; + NULL != gf; + gf = gf->next) + { + if (GNUNET_TIME_timestamp_cmp (ts, + >=, + gf->start_date) && + GNUNET_TIME_timestamp_cmp (ts, + <, + gf->end_date)) + return gf; + } + return NULL; +} + + struct TEH_DenominationKey * -TEH_keys_denomination_by_hash (const struct - TALER_DenominationHashP *h_denom_pub, - struct MHD_Connection *conn, - MHD_RESULT *mret) +TEH_keys_denomination_by_hash ( + const struct TALER_DenominationHashP *h_denom_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret) { struct TEH_KeyStateHandle *ksh; diff --git a/src/exchange/taler-exchange-httpd_keys.h b/src/exchange/taler-exchange-httpd_keys.h index ee9412a65..732ee032d 100644 --- a/src/exchange/taler-exchange-httpd_keys.h +++ b/src/exchange/taler-exchange-httpd_keys.h @@ -83,6 +83,64 @@ struct TEH_DenominationKey /** + * Set of global fees (and options) for a time range. + */ +struct TEH_GlobalFee +{ + /** + * Kept in a DLL. + */ + struct TEH_GlobalFee *next; + + /** + * Kept in a DLL. + */ + struct TEH_GlobalFee *prev; + + /** + * Beginning of the validity period (inclusive). + */ + struct GNUNET_TIME_Timestamp start_date; + + /** + * End of the validity period (exclusive). + */ + struct GNUNET_TIME_Timestamp end_date; + + /** + * How long do unmerged purses stay around at most? + */ + struct GNUNET_TIME_Relative purse_timeout; + + /** + * How long do we keep accounts without KYC? + */ + struct GNUNET_TIME_Relative kyc_timeout; + + /** + * What is the longest history we return? + */ + struct GNUNET_TIME_Relative history_expiration; + + /** + * Signature affirming these details. + */ + struct TALER_MasterSignatureP master_sig; + + /** + * Fee structure for operations that do not depend + * on a denomination or wire method. + */ + struct TALER_GlobalFeeSet fees; + + /** + * Number of free purses per account. + */ + uint32_t purse_account_limit; +}; + + +/** * Snapshot of the (coin and signing) keys (including private keys) of * the exchange. There can be multiple instances of this struct, as it is * reference counted and only destroyed once the last user is done @@ -130,6 +188,20 @@ TEH_keys_update_states (void); /** + * Look up global fee structure by @a ts. + * + * @param ksh key state state to look in + * @param ts timestamp to lookup global fees at + * @return the global fee details, or + * NULL if none are configured for @a ts + */ +const struct TEH_GlobalFee * +TEH_keys_global_fee_by_time ( + struct TEH_KeyStateHandle *ksh, + struct GNUNET_TIME_Timestamp ts); + + +/** * Look up the issue for a denom public key. Note that the result * must only be used in this thread and only until another key or * key state is resolved. @@ -141,10 +213,10 @@ TEH_keys_update_states (void); * or NULL if @a h_denom_pub could not be found */ struct TEH_DenominationKey * -TEH_keys_denomination_by_hash (const struct - TALER_DenominationHashP *h_denom_pub, - struct MHD_Connection *conn, - MHD_RESULT *mret); +TEH_keys_denomination_by_hash ( + const struct TALER_DenominationHashP *h_denom_pub, + struct MHD_Connection *conn, + MHD_RESULT *mret); /** diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index 56940669d..f59e1b46c 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -637,6 +637,19 @@ TALER_EXCHANGE_get_denomination_key ( /** + * Obtain the global fee details from the exchange. + * + * @param keys the exchange's key set + * @param ts time for when to fetch the fees + * @return details about the fees, NULL if no fees are known at @a ts + */ +const struct TALER_EXCHANGE_GlobalFee * +TALER_EXCHANGE_get_global_fee ( + const struct TALER_EXCHANGE_Keys *keys, + struct GNUNET_TIME_Timestamp ts); + + +/** * Create a copy of a denomination public key. * * @param key key to copy @@ -1281,13 +1294,6 @@ TALER_EXCHANGE_csr_withdraw_cancel ( /* ********************* GET /reserves/$RESERVE_PUB *********************** */ - -/** - * @brief A /reserves/ GET Handle - */ -struct TALER_EXCHANGE_ReservesGetHandle; - - /** * Ways how a reserve's balance may change. */ @@ -1320,7 +1326,7 @@ enum TALER_EXCHANGE_ReserveTransactionType /** * @brief Entry in the reserve's transaction history. */ -struct TALER_EXCHANGE_ReserveHistory +struct TALER_EXCHANGE_ReserveHistoryEntry { /** @@ -1454,22 +1460,58 @@ struct TALER_EXCHANGE_ReserveHistory /** + * @brief A /reserves/ GET Handle + */ +struct TALER_EXCHANGE_ReservesGetHandle; + + +/** + * @brief Reserve summary. + */ +struct TALER_EXCHANGE_ReserveSummary +{ + + /** + * High-level HTTP response details. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on @e hr.http_status. + */ + union + { + + /** + * Information returned on success, if + * @e hr.http_status is #MHD_HTTP_OK + */ + struct + { + + /** + * Reserve balance. + */ + struct TALER_Amount balance; + + } ok; + + } details; + +}; + + +/** * Callbacks of this type are used to serve the result of submitting a * reserve status request to a exchange. * * @param cls closure - * @param hr HTTP response data - * @param balance current balance in the reserve, NULL on error - * @param history_length number of entries in the transaction history, 0 on error - * @param history detailed transaction history, NULL on error + * @param rs HTTP response data */ typedef void (*TALER_EXCHANGE_ReservesGetCallback) ( void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history); + const struct TALER_EXCHANGE_ReserveSummary *rs); /** @@ -1510,6 +1552,214 @@ TALER_EXCHANGE_reserves_get_cancel ( struct TALER_EXCHANGE_ReservesGetHandle *rgh); +/** + * @brief A /reserves/$RID/status Handle + */ +struct TALER_EXCHANGE_ReservesStatusHandle; + + +/** + * @brief Reserve status details. + */ +struct TALER_EXCHANGE_ReserveStatus +{ + + /** + * High-level HTTP response details. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on @e hr.http_status. + */ + union + { + + /** + * Information returned on success, if + * @e hr.http_status is #MHD_HTTP_OK + */ + struct + { + + /** + * Reserve balance. + */ + struct TALER_Amount balance; + + /** + * Reserve history. + */ + const struct TALER_EXCHANGE_ReserveHistoryEntry *history; + + /** + * Length of the @e history array. + */ + unsigned int history_len; + + /** + * KYC passed? + */ + bool kyc_ok; + + /** + * KYC required to withdraw? + */ + bool kyc_required; + + } ok; + + } details; + +}; + + +/** + * Callbacks of this type are used to serve the result of submitting a + * reserve status request to a exchange. + * + * @param cls closure + * @param rs HTTP response data + */ +typedef void +(*TALER_EXCHANGE_ReservesStatusCallback) ( + void *cls, + const struct TALER_EXCHANGE_ReserveStatus *rs); + + +/** + * Submit a request to obtain the reserve status. + * + * @param exchange the exchange handle; the exchange must be ready to operate + * @param reserve_priv private key of the reserve to inspect + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_EXCHANGE_ReservesStatusHandle * +TALER_EXCHANGE_reserves_status ( + struct TALER_EXCHANGE_Handle *exchange, + const struct TALER_ReservePrivateKeyP *reserve_priv, + TALER_EXCHANGE_ReservesStatusCallback cb, + void *cb_cls); + + +/** + * Cancel a reserve status request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param rsh the reserve request handle + */ +void +TALER_EXCHANGE_reserves_status_cancel ( + struct TALER_EXCHANGE_ReservesStatusHandle *rsh); + + +/** + * @brief A /reserves/$RID/history Handle + */ +struct TALER_EXCHANGE_ReservesHistoryHandle; + + +/** + * @brief Reserve history details. + */ +struct TALER_EXCHANGE_ReserveHistory +{ + + /** + * High-level HTTP response details. + */ + struct TALER_EXCHANGE_HttpResponse hr; + + /** + * Details depending on @e hr.http_status. + */ + union + { + + /** + * Information returned on success, if + * @e hr.http_status is #MHD_HTTP_OK + */ + struct + { + + /** + * Reserve balance. + */ + struct TALER_Amount balance; + + /** + * Reserve history. + */ + const struct TALER_EXCHANGE_ReserveHistoryEntry *history; + + /** + * Length of the @e history array. + */ + unsigned int history_len; + + /** + * KYC passed? + */ + bool kyc_ok; + + /** + * KYC required to withdraw? + */ + bool kyc_required; + + } ok; + + } details; + +}; + + +/** + * Callbacks of this type are used to serve the result of submitting a + * reserve history request to a exchange. + * + * @param cls closure + * @param rs HTTP response data + */ +typedef void +(*TALER_EXCHANGE_ReservesHistoryCallback) ( + void *cls, + const struct TALER_EXCHANGE_ReserveHistory *rs); + + +/** + * Submit a request to obtain the reserve history. + * + * @param exchange the exchange handle; the exchange must be ready to operate + * @param reserve_priv private key of the reserve to inspect + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_EXCHANGE_ReservesHistoryHandle * +TALER_EXCHANGE_reserves_history ( + struct TALER_EXCHANGE_Handle *exchange, + const struct TALER_ReservePrivateKeyP *reserve_priv, + TALER_EXCHANGE_ReservesHistoryCallback cb, + void *cb_cls); + + +/** + * Cancel a reserve history request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param rsh the reserve request handle + */ +void +TALER_EXCHANGE_reserves_history_cancel ( + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh); + + /* ********************* POST /reserves/$RESERVE_PUB/withdraw *********************** */ @@ -2451,7 +2701,7 @@ TALER_EXCHANGE_parse_reserve_history ( const char *currency, struct TALER_Amount *balance, unsigned int history_length, - struct TALER_EXCHANGE_ReserveHistory *rhistory); + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory); /** @@ -2462,7 +2712,7 @@ TALER_EXCHANGE_parse_reserve_history ( */ void TALER_EXCHANGE_free_reserve_history ( - struct TALER_EXCHANGE_ReserveHistory *rhistory, + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory, unsigned int len); diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h index a1bc5d0d9..829f9b8a6 100644 --- a/src/include/taler_testing_lib.h +++ b/src/include/taler_testing_lib.h @@ -1382,15 +1382,38 @@ TALER_TESTING_cmd_status (const char *label, const char *expected_balance, unsigned int expected_response_code); + /** - * Index of the deposit value trait of a deposit command. + * Create a POST "/reserves/$RID/history" command. + * + * @param label the command label. + * @param reserve_reference reference to the reserve to check. + * @param expected_balance expected balance for the reserve. + * @param expected_response_code expected HTTP response code. + * @return the command. */ -#define TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE 0 +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_history (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code); + /** - * Index of the deposit fee trait of a deposit command. + * Create a POST "/reserves/$RID/status" command. + * + * @param label the command label. + * @param reserve_reference reference to the reserve to check. + * @param expected_balance expected balance for the reserve. + * @param expected_response_code expected HTTP response code. + * @return the command. */ -#define TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_FEE 1 +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_status (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code); + /** * Create a "deposit" command. @@ -2455,7 +2478,7 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits, op (contract_terms, const json_t) \ op (wire_details, const json_t) \ op (exchange_keys, const json_t) \ - op (reserve_history, const struct TALER_EXCHANGE_ReserveHistory) \ + op (reserve_history, const struct TALER_EXCHANGE_ReserveHistoryEntry) \ op (exchange_url, const char *) \ op (exchange_bank_account_url, const char *) \ op (taler_uri, const char *) \ diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 7d49f53cd..730c833d7 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -18,7 +18,7 @@ lib_LTLIBRARIES = \ libtalerexchange.la libtalerexchange_la_LDFLAGS = \ - -version-info 4:0:0 \ + -version-info 5:0:0 \ -no-undefined libtalerexchange_la_SOURCES = \ exchange_api_auditor_add_denomination.c \ @@ -51,6 +51,8 @@ libtalerexchange_la_SOURCES = \ exchange_api_refreshes_reveal.c \ exchange_api_refund.c \ exchange_api_reserves_get.c \ + exchange_api_reserves_history.c \ + exchange_api_reserves_status.c \ exchange_api_transfers_get.c \ exchange_api_withdraw.c \ exchange_api_withdraw2.c \ diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c index 160f62dc6..c9bff71f1 100644 --- a/src/lib/exchange_api_common.c +++ b/src/lib/exchange_api_common.c @@ -34,7 +34,7 @@ TALER_EXCHANGE_parse_reserve_history ( const char *currency, struct TALER_Amount *balance, unsigned int history_length, - struct TALER_EXCHANGE_ReserveHistory *rhistory) + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory) { struct GNUNET_HashCode uuid[history_length]; unsigned int uuid_off; @@ -50,7 +50,7 @@ TALER_EXCHANGE_parse_reserve_history ( uuid_off = 0; for (unsigned int off = 0; off<history_length; off++) { - struct TALER_EXCHANGE_ReserveHistory *rh = &rhistory[off]; + struct TALER_EXCHANGE_ReserveHistoryEntry *rh = &rhistory[off]; json_t *transaction; struct TALER_Amount amount; const char *type; @@ -368,14 +368,20 @@ TALER_EXCHANGE_parse_reserve_history ( } /* check balance = total_in - total_out < withdraw-amount */ - if (0 > - TALER_amount_subtract (balance, - &total_in, - &total_out)) + if (NULL != balance) { - /* total_in < total_out, why did the exchange ever allow this!? */ - GNUNET_break_op (0); - return GNUNET_SYSERR; + /* if balance is NULL, we may have a partial history + in which case the subtraction may fail, so we do + not even check that invariant in this case. */ + if (0 > + TALER_amount_subtract (balance, + &total_in, + &total_out)) + { + /* total_in < total_out, why did the exchange ever allow this!? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } } return GNUNET_OK; } @@ -383,7 +389,7 @@ TALER_EXCHANGE_parse_reserve_history ( void TALER_EXCHANGE_free_reserve_history ( - struct TALER_EXCHANGE_ReserveHistory *rhistory, + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory, unsigned int len) { for (unsigned int i = 0; i<len; i++) diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c index f7e877913..e0fcd2b93 100644 --- a/src/lib/exchange_api_handle.c +++ b/src/lib/exchange_api_handle.c @@ -2181,6 +2181,27 @@ TALER_EXCHANGE_get_denomination_key ( } +const struct TALER_EXCHANGE_GlobalFee * +TALER_EXCHANGE_get_global_fee ( + const struct TALER_EXCHANGE_Keys *keys, + struct GNUNET_TIME_Timestamp ts) +{ + for (unsigned int i = 0; i<keys->num_global_fees; i++) + { + const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i]; + + if (GNUNET_TIME_timestamp_cmp (ts, + >=, + gf->start_date) && + GNUNET_TIME_timestamp_cmp (ts, + <, + gf->end_date)) + return gf; + } + return NULL; +} + + struct TALER_EXCHANGE_DenomPublicKey * TALER_EXCHANGE_copy_denomination_key ( const struct TALER_EXCHANGE_DenomPublicKey *key) diff --git a/src/lib/exchange_api_reserves_get.c b/src/lib/exchange_api_reserves_get.c index a25350e5b..4c2886e0b 100644 --- a/src/lib/exchange_api_reserves_get.c +++ b/src/lib/exchange_api_reserves_get.c @@ -83,19 +83,15 @@ static enum GNUNET_GenericReturnValue handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh, const json_t *j) { - json_t *history; - unsigned int len; - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; + struct TALER_EXCHANGE_ReserveSummary rs = { + .hr.reply = j, + .hr.http_status = MHD_HTTP_OK + }; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("balance", - &balance), + &rs.details.ok.balance), GNUNET_JSON_spec_end () }; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = MHD_HTTP_OK - }; if (GNUNET_OK != GNUNET_JSON_parse (j, @@ -106,55 +102,9 @@ handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh, GNUNET_break_op (0); return GNUNET_SYSERR; } - history = json_object_get (j, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - len = json_array_size (history); - { - struct TALER_EXCHANGE_ReserveHistory *rhistory; - - rhistory = GNUNET_new_array (len, - struct TALER_EXCHANGE_ReserveHistory); - if (GNUNET_OK != - TALER_EXCHANGE_parse_reserve_history (rgh->exchange, - history, - &rgh->reserve_pub, - balance.currency, - &balance_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - TALER_EXCHANGE_free_reserve_history (rhistory, - len); - return GNUNET_SYSERR; - } - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) - { - /* exchange cannot add up balances!? */ - GNUNET_break_op (0); - TALER_EXCHANGE_free_reserve_history (rhistory, - len); - return GNUNET_SYSERR; - } - if (NULL != rgh->cb) - { - rgh->cb (rgh->cb_cls, - &hr, - &balance, - len, - rhistory); - rgh->cb = NULL; - } - TALER_EXCHANGE_free_reserve_history (rhistory, - len); - } + rgh->cb (rgh->cb_cls, + &rs); + rgh->cb = NULL; return GNUNET_OK; } @@ -174,61 +124,59 @@ handle_reserves_get_finished (void *cls, { struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls; const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = (unsigned int) response_code + struct TALER_EXCHANGE_ReserveSummary rs = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code }; rgh->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: if (GNUNET_OK != handle_reserves_get_ok (rgh, j)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; } break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; default: /* unexpected response code */ GNUNET_break_op (0); - hr.ec = TALER_JSON_get_error_code (j); - hr.hint = TALER_JSON_get_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d for reserves get\n", (unsigned int) response_code, - (int) hr.ec); + (int) rs.hr.ec); break; } if (NULL != rgh->cb) { rgh->cb (rgh->cb_cls, - &hr, - NULL, - 0, NULL); + &rs); rgh->cb = NULL; } TALER_EXCHANGE_reserves_get_cancel (rgh); diff --git a/src/lib/exchange_api_reserves_history.c b/src/lib/exchange_api_reserves_history.c index f7191b2ad..c0f6c42d2 100644 --- a/src/lib/exchange_api_reserves_history.c +++ b/src/lib/exchange_api_reserves_history.c @@ -81,35 +81,32 @@ struct TALER_EXCHANGE_ReservesHistoryHandle * We received an #MHD_HTTP_OK history code. Handle the JSON * response. * - * @param rgh handle of the request + * @param rsh handle of the request * @param j JSON response * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh, +handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rsh, const json_t *j) { json_t *history; unsigned int len; - bool kyc_ok; - bool kyc_required; - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; + struct TALER_Amount history_balance; + struct TALER_EXCHANGE_ReserveHistory rs = { + .hr.reply = j, + .hr.http_status = MHD_HTTP_OK + }; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("balance", - &balance), + &rs.details.ok.balance), GNUNET_JSON_spec_bool ("kyc_passed", - &kyc_ok), + &rs.details.ok.kyc_ok), GNUNET_JSON_spec_bool ("kyc_required", - &kyc_required), + &rs.details.ok.kyc_required), GNUNET_JSON_spec_json ("history", &history), GNUNET_JSON_spec_end () }; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_history = MHD_HTTP_OK - }; if (GNUNET_OK != GNUNET_JSON_parse (j, @@ -122,16 +119,19 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh, } len = json_array_size (history); { - struct TALER_EXCHANGE_ReserveHistory *rhistory; + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory; rhistory = GNUNET_new_array (len, - struct TALER_EXCHANGE_ReserveHistory); + struct TALER_EXCHANGE_ReserveHistoryEntry); + // FIXME: even this history could be partial + // (if the reserve is too old!); update API + // and return incoming & outgoing totals separately? if (GNUNET_OK != - TALER_EXCHANGE_parse_reserve_history (rgh->exchange, + TALER_EXCHANGE_parse_reserve_history (rsh->exchange, history, - &rgh->reserve_pub, - balance.currency, - &balance_from_history, + &rsh->reserve_pub, + rs.details.ok.balance.currency, + &history_balance, len, rhistory)) { @@ -141,25 +141,13 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh, GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) + if (NULL != rsh->cb) { - /* exchange cannot add up balances!? */ - GNUNET_break_op (0); - TALER_EXCHANGE_free_reserve_history (rhistory, - len); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - if (NULL != rgh->cb) - { - rgh->cb (rgh->cb_cls, - &hr, - &balance, - len, - rhistory); - rgh->cb = NULL; + rs.details.ok.history = rhistory; + rs.details.ok.history_len = len; + rsh->cb (rsh->cb_cls, + &rs); + rsh->cb = NULL; } TALER_EXCHANGE_free_reserve_history (rhistory, len); @@ -182,75 +170,72 @@ handle_reserves_history_finished (void *cls, long response_code, const void *response) { - struct TALER_EXCHANGE_ReservesHistoryHandle *rgh = cls; + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh = cls; const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_history = (unsigned int) response_code + struct TALER_EXCHANGE_ReserveHistory rs = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code }; - rgh->job = NULL; + rsh->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: if (GNUNET_OK != - handle_reserves_history_ok (rgh, + handle_reserves_history_ok (rsh, j)) { - hr.http_history = 0; - hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; } break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ GNUNET_break (0); - hr.ec = TALER_JSON_history_error_code (j); - hr.hint = TALER_JSON_history_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_FORBIDDEN: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ GNUNET_break (0); - hr.ec = TALER_JSON_history_error_code (j); - hr.hint = TALER_JSON_history_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_history_error_code (j); - hr.hint = TALER_JSON_history_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_history_error_code (j); - hr.hint = TALER_JSON_history_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; default: /* unexpected response code */ GNUNET_break_op (0); - hr.ec = TALER_JSON_history_error_code (j); - hr.hint = TALER_JSON_history_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d for reserves history\n", (unsigned int) response_code, - (int) hr.ec); + (int) rs.hr.ec); break; } - if (NULL != rgh->cb) + if (NULL != rsh->cb) { - rgh->cb (rgh->cb_cls, - &hr, - NULL, - 0, - NULL); - rgh->cb = NULL; + rsh->cb (rsh->cb_cls, + &rs); + rsh->cb = NULL; } - TALER_EXCHANGE_reserves_history_cancel (rgh); + TALER_EXCHANGE_reserves_history_cancel (rsh); } @@ -261,12 +246,11 @@ TALER_EXCHANGE_reserves_history ( TALER_EXCHANGE_ReservesHistoryCallback cb, void *cb_cls) { - struct TALER_EXCHANGE_ReservesHistoryHandle *rgh; + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh; struct GNUNET_CURL_Context *ctx; CURL *eh; char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; - const struct TALER_Amount *history_fee; - const struct TALER_EXCHANGE_Keys *keys; + struct TALER_ReserveSignatureP reserve_sig; struct GNUNET_TIME_Timestamp ts = GNUNET_TIME_timestamp_get (); @@ -276,22 +260,19 @@ TALER_EXCHANGE_reserves_history ( GNUNET_break (0); return NULL; } - keys = TALER_EXCHANGE_get_keys (exchange); - // FIXME: extract history_fee from keys! - history_fee = FIXME; - rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle); - rgh->exchange = exchange; - rgh->cb = cb; - rgh->cb_cls = cb_cls; + rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle); + rsh->exchange = exchange; + rsh->cb = cb; + rsh->cb_cls = cb_cls; GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &rgh->reserve_pub.eddsa_pub); + &rsh->reserve_pub.eddsa_pub); { char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; char *end; end = GNUNET_STRINGS_data_to_string ( - &rgh->reserve_pub, - sizeof (rgh->reserve_pub), + &rsh->reserve_pub, + sizeof (rsh->reserve_pub), pub_str, sizeof (pub_str)); *end = '\0'; @@ -300,68 +281,67 @@ TALER_EXCHANGE_reserves_history ( "/reserves/%s/history", pub_str); } - rgh->url = TEAH_path_to_url (exchange, + rsh->url = TEAH_path_to_url (exchange, arg_str); - if (NULL == rgh->url) + if (NULL == rsh->url) { - GNUNET_free (rgh); + GNUNET_free (rsh); return NULL; } - eh = TALER_EXCHANGE_curl_easy_history_ (rgh->url); + eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url); if (NULL == eh) { GNUNET_break (0); - GNUNET_free (rgh->url); - GNUNET_free (rgh); + GNUNET_free (rsh->url); + GNUNET_free (rsh); return NULL; } TALER_wallet_reserve_history_sign (ts, - history_fee, + NULL, /* FIXME: fee! */ reserve_priv, &reserve_sig); { json_t *history_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_timestamp ("request_timestamp", - &ts), + ts), GNUNET_JSON_pack_data_auto ("reserve_sig", &reserve_sig)); if (GNUNET_OK != - TALER_curl_easy_post (&rgh->post_ctx, + TALER_curl_easy_post (&rsh->post_ctx, eh, history_obj)) - ) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (history_obj); - GNUNET_free (rgh->url); - GNUNET_free (rgh); - return NULL; - } + { + GNUNET_break (0); + curl_easy_cleanup (eh); json_decref (history_obj); + GNUNET_free (rsh->url); + GNUNET_free (rsh); + return NULL; + } + json_decref (history_obj); } ctx = TEAH_handle_to_context (exchange); - rgh->job = GNUNET_CURL_job_add (ctx, + rsh->job = GNUNET_CURL_job_add (ctx, eh, &handle_reserves_history_finished, - rgh); - return rgh; + rsh); + return rsh; } void TALER_EXCHANGE_reserves_history_cancel ( - struct TALER_EXCHANGE_ReservesHistoryHandle *rgh) + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh) { - if (NULL != rgh->job) + if (NULL != rsh->job) { - GNUNET_CURL_job_cancel (rgh->job); - rgh->job = NULL; + GNUNET_CURL_job_cancel (rsh->job); + rsh->job = NULL; } - TALER_curl_easy_post_finished (&rgh->post_ctx); - GNUNET_free (rgh->url); - GNUNET_free (rgh); + TALER_curl_easy_post_finished (&rsh->post_ctx); + GNUNET_free (rsh->url); + GNUNET_free (rsh); } diff --git a/src/lib/exchange_api_reserves_status.c b/src/lib/exchange_api_reserves_status.c index 2758a3a28..a99a080bc 100644 --- a/src/lib/exchange_api_reserves_status.c +++ b/src/lib/exchange_api_reserves_status.c @@ -81,35 +81,31 @@ struct TALER_EXCHANGE_ReservesStatusHandle * We received an #MHD_HTTP_OK status code. Handle the JSON * response. * - * @param rgh handle of the request + * @param rsh handle of the request * @param j JSON response * @return #GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh, +handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rsh, const json_t *j) { json_t *history; unsigned int len; - bool kyc_ok; - bool kyc_required; - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; + struct TALER_EXCHANGE_ReserveStatus rs = { + .hr.reply = j, + .hr.http_status = MHD_HTTP_OK + }; struct GNUNET_JSON_Specification spec[] = { TALER_JSON_spec_amount_any ("balance", - &balance), + &rs.details.ok.balance), GNUNET_JSON_spec_bool ("kyc_passed", - &kyc_ok), + &rs.details.ok.kyc_ok), GNUNET_JSON_spec_bool ("kyc_required", - &kyc_required), + &rs.details.ok.kyc_required), GNUNET_JSON_spec_json ("history", &history), GNUNET_JSON_spec_end () }; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = MHD_HTTP_OK - }; if (GNUNET_OK != GNUNET_JSON_parse (j, @@ -122,16 +118,16 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh, } len = json_array_size (history); { - struct TALER_EXCHANGE_ReserveHistory *rhistory; + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory; rhistory = GNUNET_new_array (len, - struct TALER_EXCHANGE_ReserveHistory); + struct TALER_EXCHANGE_ReserveHistoryEntry); if (GNUNET_OK != - TALER_EXCHANGE_parse_reserve_history (rgh->exchange, + TALER_EXCHANGE_parse_reserve_history (rsh->exchange, history, - &rgh->reserve_pub, - balance.currency, - &balance_from_history, + &rsh->reserve_pub, + rs.details.ok.balance.currency, + NULL, len, rhistory)) { @@ -141,27 +137,13 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh, GNUNET_JSON_parse_free (spec); return GNUNET_SYSERR; } - // FIXME: status history is allowed to be - // partial, so this is NOT ok... - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) + if (NULL != rsh->cb) { - /* exchange cannot add up balances!? */ - GNUNET_break_op (0); - TALER_EXCHANGE_free_reserve_history (rhistory, - len); - GNUNET_JSON_parse_free (spec); - return GNUNET_SYSERR; - } - if (NULL != rgh->cb) - { - rgh->cb (rgh->cb_cls, - &hr, - &balance, - len, - rhistory); - rgh->cb = NULL; + rs.details.ok.history = rhistory; + rs.details.ok.history_len = len; + rsh->cb (rsh->cb_cls, + &rs); + rsh->cb = NULL; } TALER_EXCHANGE_free_reserve_history (rhistory, len); @@ -184,75 +166,72 @@ handle_reserves_status_finished (void *cls, long response_code, const void *response) { - struct TALER_EXCHANGE_ReservesStatusHandle *rgh = cls; + struct TALER_EXCHANGE_ReservesStatusHandle *rsh = cls; const json_t *j = response; - struct TALER_EXCHANGE_HttpResponse hr = { - .reply = j, - .http_status = (unsigned int) response_code + struct TALER_EXCHANGE_ReserveStatus rs = { + .hr.reply = j, + .hr.http_status = (unsigned int) response_code }; - rgh->job = NULL; + rsh->job = NULL; switch (response_code) { case 0: - hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; + rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; case MHD_HTTP_OK: if (GNUNET_OK != - handle_reserves_status_ok (rgh, + handle_reserves_status_ok (rsh, j)) { - hr.http_status = 0; - hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; + rs.hr.http_status = 0; + rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; } break; case MHD_HTTP_BAD_REQUEST: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ GNUNET_break (0); - hr.ec = TALER_JSON_status_error_code (j); - hr.hint = TALER_JSON_status_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_FORBIDDEN: /* This should never happen, either us or the exchange is buggy (or API version conflict); just pass JSON reply to the application */ GNUNET_break (0); - hr.ec = TALER_JSON_status_error_code (j); - hr.hint = TALER_JSON_status_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_NOT_FOUND: /* Nothing really to verify, this should never happen, we should pass the JSON reply to the application */ - hr.ec = TALER_JSON_status_error_code (j); - hr.hint = TALER_JSON_status_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; case MHD_HTTP_INTERNAL_SERVER_ERROR: /* Server had an internal issue; we should retry, but this API leaves this to the application */ - hr.ec = TALER_JSON_status_error_code (j); - hr.hint = TALER_JSON_status_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); break; default: /* unexpected response code */ GNUNET_break_op (0); - hr.ec = TALER_JSON_status_error_code (j); - hr.hint = TALER_JSON_status_error_hint (j); + rs.hr.ec = TALER_JSON_get_error_code (j); + rs.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected response code %u/%d for reserves status\n", (unsigned int) response_code, - (int) hr.ec); + (int) rs.hr.ec); break; } - if (NULL != rgh->cb) + if (NULL != rsh->cb) { - rgh->cb (rgh->cb_cls, - &hr, - NULL, - 0, - NULL); - rgh->cb = NULL; + rsh->cb (rsh->cb_cls, + &rs); + rsh->cb = NULL; } - TALER_EXCHANGE_reserves_status_cancel (rgh); + TALER_EXCHANGE_reserves_status_cancel (rsh); } @@ -263,10 +242,11 @@ TALER_EXCHANGE_reserves_status ( TALER_EXCHANGE_ReservesStatusCallback cb, void *cb_cls) { - struct TALER_EXCHANGE_ReservesStatusHandle *rgh; + struct TALER_EXCHANGE_ReservesStatusHandle *rsh; struct GNUNET_CURL_Context *ctx; CURL *eh; char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32]; + struct TALER_ReserveSignatureP reserve_sig; struct GNUNET_TIME_Timestamp ts = GNUNET_TIME_timestamp_get (); @@ -276,19 +256,19 @@ TALER_EXCHANGE_reserves_status ( GNUNET_break (0); return NULL; } - rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle); - rgh->exchange = exchange; - rgh->cb = cb; - rgh->cb_cls = cb_cls; + rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle); + rsh->exchange = exchange; + rsh->cb = cb; + rsh->cb_cls = cb_cls; GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &rgh->reserve_pub.eddsa_pub); + &rsh->reserve_pub.eddsa_pub); { char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2]; char *end; end = GNUNET_STRINGS_data_to_string ( - &rgh->reserve_pub, - sizeof (rgh->reserve_pub), + &rsh->reserve_pub, + sizeof (rsh->reserve_pub), pub_str, sizeof (pub_str)); *end = '\0'; @@ -297,19 +277,19 @@ TALER_EXCHANGE_reserves_status ( "/reserves/%s/status", pub_str); } - rgh->url = TEAH_path_to_url (exchange, + rsh->url = TEAH_path_to_url (exchange, arg_str); - if (NULL == rgh->url) + if (NULL == rsh->url) { - GNUNET_free (rgh); + GNUNET_free (rsh); return NULL; } - eh = TALER_EXCHANGE_curl_easy_status_ (rgh->url); + eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url); if (NULL == eh) { GNUNET_break (0); - GNUNET_free (rgh->url); - GNUNET_free (rgh); + GNUNET_free (rsh->url); + GNUNET_free (rsh); return NULL; } TALER_wallet_reserve_status_sign (ts, @@ -318,46 +298,45 @@ TALER_EXCHANGE_reserves_status ( { json_t *status_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_timestamp ("request_timestamp", - &ts), + ts), GNUNET_JSON_pack_data_auto ("reserve_sig", &reserve_sig)); if (GNUNET_OK != - TALER_curl_easy_post (&rgh->post_ctx, + TALER_curl_easy_post (&rsh->post_ctx, eh, status_obj)) - ) - { - GNUNET_break (0); - curl_easy_cleanup (eh); - json_decref (status_obj); - GNUNET_free (rgh->url); - GNUNET_free (rgh); - return NULL; - } + { + GNUNET_break (0); + curl_easy_cleanup (eh); json_decref (status_obj); + GNUNET_free (rsh->url); + GNUNET_free (rsh); + return NULL; + } + json_decref (status_obj); } ctx = TEAH_handle_to_context (exchange); - rgh->job = GNUNET_CURL_job_add (ctx, + rsh->job = GNUNET_CURL_job_add (ctx, eh, &handle_reserves_status_finished, - rgh); - return rgh; + rsh); + return rsh; } void TALER_EXCHANGE_reserves_status_cancel ( - struct TALER_EXCHANGE_ReservesStatusHandle *rgh) + struct TALER_EXCHANGE_ReservesStatusHandle *rsh) { - if (NULL != rgh->job) + if (NULL != rsh->job) { - GNUNET_CURL_job_cancel (rgh->job); - rgh->job = NULL; + GNUNET_CURL_job_cancel (rsh->job); + rsh->job = NULL; } - TALER_curl_easy_post_finished (&rgh->post_ctx); - GNUNET_free (rgh->url); - GNUNET_free (rgh); + TALER_curl_easy_post_finished (&rsh->post_ctx); + GNUNET_free (rsh->url); + GNUNET_free (rsh); } diff --git a/src/lib/exchange_api_withdraw2.c b/src/lib/exchange_api_withdraw2.c index ade6fe8a7..bc138db0f 100644 --- a/src/lib/exchange_api_withdraw2.c +++ b/src/lib/exchange_api_withdraw2.c @@ -177,14 +177,14 @@ reserve_withdraw_payment_required ( total incoming and outgoing amounts */ len = json_array_size (history); { - struct TALER_EXCHANGE_ReserveHistory *rhistory; + struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory; /* Use heap allocation as "len" may be very big and thus this may not fit on the stack. Use "GNUNET_malloc_large" as a malicious exchange may theoretically try to crash us by giving a history that does not fit into our memory. */ rhistory = GNUNET_malloc_large ( - sizeof (struct TALER_EXCHANGE_ReserveHistory) + sizeof (struct TALER_EXCHANGE_ReserveHistoryEntry) * len); if (NULL == rhistory) { diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 39cc6cbed..30148d71a 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -74,6 +74,8 @@ libtalertesting_la_SOURCES = \ testing_api_cmd_recoup_refresh.c \ testing_api_cmd_refund.c \ testing_api_cmd_refresh.c \ + testing_api_cmd_reserve_history.c \ + testing_api_cmd_reserve_status.c \ testing_api_cmd_revoke.c \ testing_api_cmd_revoke_denom_key.c \ testing_api_cmd_revoke_sign_key.c \ diff --git a/src/testing/testing_api_cmd_bank_admin_add_incoming.c b/src/testing/testing_api_cmd_bank_admin_add_incoming.c index 5b1d8b8a9..07ee4068b 100644 --- a/src/testing/testing_api_cmd_bank_admin_add_incoming.c +++ b/src/testing/testing_api_cmd_bank_admin_add_incoming.c @@ -107,7 +107,7 @@ struct AdminAddIncomingState * the "sender_url" field is set to a 'const char *' and * MUST NOT be free()'ed. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * Set to the wire transfer's unique ID. diff --git a/src/testing/testing_api_cmd_exec_closer.c b/src/testing/testing_api_cmd_exec_closer.c index 442fb4ce0..57346f333 100644 --- a/src/testing/testing_api_cmd_exec_closer.c +++ b/src/testing/testing_api_cmd_exec_closer.c @@ -49,7 +49,7 @@ struct CloserState * expect_close is true. Will be of type * #TALER_EXCHANGE_RTT_RESERVE_CLOSED. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * If the closer filled a reserve (@e expect_close is set), this is set to diff --git a/src/testing/testing_api_cmd_recoup.c b/src/testing/testing_api_cmd_recoup.c index 85256c207..ef2716226 100644 --- a/src/testing/testing_api_cmd_recoup.c +++ b/src/testing/testing_api_cmd_recoup.c @@ -62,7 +62,7 @@ struct RecoupState * Reserve history entry, set if this recoup actually filled up a reserve. * Otherwise `reserve_history.type` will be zero. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; }; diff --git a/src/testing/testing_api_cmd_reserve_history.c b/src/testing/testing_api_cmd_reserve_history.c new file mode 100644 index 000000000..fc94d8443 --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_history.c @@ -0,0 +1,413 @@ +/* + This file is part of TALER + Copyright (C) 2014-2020 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 testing/testing_api_cmd_history.c + * @brief Implement the /reserve/history test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "history" CMD. + */ +struct HistoryState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve history" operation. + */ + struct TALER_EXCHANGE_ReservesHistoryHandle *rsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Compare @a h1 and @a h2. + * + * @param h1 a history entry + * @param h2 a history entry + * @return 0 if @a h1 and @a h2 are equal + */ +static int +history_entry_cmp (const struct TALER_EXCHANGE_ReserveHistoryEntry *h1, + const struct TALER_EXCHANGE_ReserveHistoryEntry *h2) +{ + if (h1->type != h2->type) + return 1; + switch (h1->type) + { + case TALER_EXCHANGE_RTT_CREDIT: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == strcasecmp (h1->details.in_details.sender_url, + h2->details.in_details.sender_url)) && + (h1->details.in_details.wire_reference == + h2->details.in_details.wire_reference) && + (GNUNET_TIME_timestamp_cmp (h1->details.in_details.timestamp, + ==, + h2->details.in_details.timestamp)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_WITHDRAWAL: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.withdraw.fee, + &h2->details.withdraw.fee)) ) + /* testing_api_cmd_withdraw doesn't set the out_authorization_sig, + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + return 0; + return 1; + case TALER_EXCHANGE_RTT_RECOUP: + /* exchange_sig, exchange_pub and timestamp are NOT available + from the original recoup response, hence here NOT check(able/ed) */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + GNUNET_memcmp (&h1->details.recoup_details.coin_pub, + &h2->details.recoup_details.coin_pub)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_CLOSE: + /* testing_api_cmd_exec_closer doesn't set the + receiver_account_details, exchange_sig, exchange_pub or wtid or timestamp + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.close_details.fee, + &h2->details.close_details.fee)) ) + return 0; + return 1; + } + GNUNET_assert (0); + return 1; +} + + +/** + * Check if @a cmd changed the reserve, if so, find the + * entry in @a history and set the respective index in @a found + * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR. + * + * @param reserve_pub public key of the reserve for which we have the @a history + * @param cmd command to analyze for impact on history + * @param history_length number of entries in @a history and @a found + * @param history history to check + * @param[in,out] found array to update + * @return #GNUNET_OK if @a cmd action on reserve was found in @a history + */ +static enum GNUNET_GenericReturnValue +analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_TESTING_Command *cmd, + unsigned int history_length, + const struct TALER_EXCHANGE_ReserveHistoryEntry *history, + int *found) +{ + if (TALER_TESTING_cmd_is_batch (cmd)) + { + struct TALER_TESTING_Command *cur; + struct TALER_TESTING_Command **bcmd; + + cur = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_batch_cmds (cmd, + &bcmd)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != (*bcmd)[i].label; i++) + { + struct TALER_TESTING_Command *step = &(*bcmd)[i]; + + if (step == cur) + break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ + if (GNUNET_OK != + analyze_command (reserve_pub, + step, + history_length, + history, + found)) + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + else + { + const struct TALER_ReservePublicKeyP *rp; + const struct TALER_EXCHANGE_ReserveHistoryEntry *he; + + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (cmd, + &rp)) + return GNUNET_OK; /* command does nothing for reserves */ + if (0 != + GNUNET_memcmp (rp, + reserve_pub)) + return GNUNET_OK; /* command affects some _other_ reserve */ + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_history (cmd, + &he)) + { + /* NOTE: only for debugging... */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s' has the reserve_pub trait, but does not reserve history trait\n", + cmd->label); + return GNUNET_OK; /* command does nothing for reserves */ + } + for (unsigned int i = 0; i<history_length; i++) + { + if (found[i]) + continue; /* already found, skip */ + if (0 == + history_entry_cmp (he, + &history[i])) + { + found[i] = GNUNET_YES; + return GNUNET_OK; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command `%s' reserve history entry not found\n", + cmd->label); + return GNUNET_SYSERR; + } +} + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_history_cb (void *cls, + const struct TALER_EXCHANGE_ReserveHistory *rs) +{ + struct HistoryState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + struct TALER_Amount eb; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected amount in reserve: %s\n", + TALER_amount_to_string (&rs->details.ok.balance)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + { + int found[rs->details.ok.history_len]; + + memset (found, + 0, + sizeof (found)); + for (unsigned int i = 0; i<= (unsigned int) is->ip; i++) + { + struct TALER_TESTING_Command *cmd = &is->commands[i]; + + if (GNUNET_OK != + analyze_command (&ss->reserve_pub, + cmd, + rs->details.ok.history_len, + rs->details.ok.history, + found)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Entry for command `%s' missing in history\n", + cmd->label); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + for (unsigned int i = 0; i<rs->details.ok.history_len; i++) + if (! found[i]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "History entry at index %u of type %d not justified by command history\n", + i, + rs->details.ok.history[i].type); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +history_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct HistoryState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + + ss->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for history query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_reserves_history (is->exchange, + ss->reserve_priv, + &reserve_history_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve history" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +history_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct HistoryState *ss = cls; + + if (NULL != ss->rsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + ss->is->ip, + cmd->label); + TALER_EXCHANGE_reserves_history_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_history (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code) +{ + struct HistoryState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct HistoryState); + ss->reserve_reference = reserve_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &history_run, + .cleanup = &history_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_reserve_status.c b/src/testing/testing_api_cmd_reserve_status.c new file mode 100644 index 000000000..10f3ee99b --- /dev/null +++ b/src/testing/testing_api_cmd_reserve_status.c @@ -0,0 +1,413 @@ +/* + This file is part of TALER + Copyright (C) 2014-2020 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 testing/testing_api_cmd_status.c + * @brief Implement the /reserve/$RID/status test command. + * @author Marcello Stanisci + */ +#include "platform.h" +#include "taler_json_lib.h" +#include <gnunet/gnunet_curl_lib.h> +#include "taler_testing_lib.h" + + +/** + * State for a "status" CMD. + */ +struct StatusState +{ + /** + * Label to the command which created the reserve to check, + * needed to resort the reserve key. + */ + const char *reserve_reference; + + /** + * Handle to the "reserve status" operation. + */ + struct TALER_EXCHANGE_ReservesStatusHandle *rsh; + + /** + * Expected reserve balance. + */ + const char *expected_balance; + + /** + * Private key of the reserve being analyzed. + */ + const struct TALER_ReservePrivateKeyP *reserve_priv; + + /** + * Public key of the reserve being analyzed. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Expected HTTP response code. + */ + unsigned int expected_response_code; + + /** + * Interpreter state. + */ + struct TALER_TESTING_Interpreter *is; +}; + + +/** + * Compare @a h1 and @a h2. + * + * @param h1 a history entry + * @param h2 a history entry + * @return 0 if @a h1 and @a h2 are equal + */ +static int +history_entry_cmp (const struct TALER_EXCHANGE_ReserveHistoryEntry *h1, + const struct TALER_EXCHANGE_ReserveHistoryEntry *h2) +{ + if (h1->type != h2->type) + return 1; + switch (h1->type) + { + case TALER_EXCHANGE_RTT_CREDIT: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == strcasecmp (h1->details.in_details.sender_url, + h2->details.in_details.sender_url)) && + (h1->details.in_details.wire_reference == + h2->details.in_details.wire_reference) && + (GNUNET_TIME_timestamp_cmp (h1->details.in_details.timestamp, + ==, + h2->details.in_details.timestamp)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_WITHDRAWAL: + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.withdraw.fee, + &h2->details.withdraw.fee)) ) + /* testing_api_cmd_withdraw doesn't set the out_authorization_sig, + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + return 0; + return 1; + case TALER_EXCHANGE_RTT_RECOUP: + /* exchange_sig, exchange_pub and timestamp are NOT available + from the original recoup response, hence here NOT check(able/ed) */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + GNUNET_memcmp (&h1->details.recoup_details.coin_pub, + &h2->details.recoup_details.coin_pub)) ) + return 0; + return 1; + case TALER_EXCHANGE_RTT_CLOSE: + /* testing_api_cmd_exec_closer doesn't set the + receiver_account_details, exchange_sig, exchange_pub or wtid or timestamp + so we cannot test for it here. but if the amount matches, + that should be good enough. */ + if ( (0 == + TALER_amount_cmp (&h1->amount, + &h2->amount)) && + (0 == + TALER_amount_cmp (&h1->details.close_details.fee, + &h2->details.close_details.fee)) ) + return 0; + return 1; + } + GNUNET_assert (0); + return 1; +} + + +/** + * Check if @a cmd changed the reserve, if so, find the + * entry in @a history and set the respective index in @a found + * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR. + * + * @param reserve_pub public key of the reserve for which we have the @a history + * @param cmd command to analyze for impact on history + * @param history_length number of entries in @a history and @a found + * @param history history to check + * @param[in,out] found array to update + * @return #GNUNET_OK if @a cmd action on reserve was found in @a history + */ +static enum GNUNET_GenericReturnValue +analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_TESTING_Command *cmd, + unsigned int history_length, + const struct TALER_EXCHANGE_ReserveHistoryEntry *history, + int *found) +{ + if (TALER_TESTING_cmd_is_batch (cmd)) + { + struct TALER_TESTING_Command *cur; + struct TALER_TESTING_Command **bcmd; + + cur = TALER_TESTING_cmd_batch_get_current (cmd); + if (GNUNET_OK != + TALER_TESTING_get_trait_batch_cmds (cmd, + &bcmd)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != (*bcmd)[i].label; i++) + { + struct TALER_TESTING_Command *step = &(*bcmd)[i]; + + if (step == cur) + break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ + if (GNUNET_OK != + analyze_command (reserve_pub, + step, + history_length, + history, + found)) + return GNUNET_SYSERR; + } + return GNUNET_OK; + } + else + { + const struct TALER_ReservePublicKeyP *rp; + const struct TALER_EXCHANGE_ReserveHistoryEntry *he; + + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (cmd, + &rp)) + return GNUNET_OK; /* command does nothing for reserves */ + if (0 != + GNUNET_memcmp (rp, + reserve_pub)) + return GNUNET_OK; /* command affects some _other_ reserve */ + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_history (cmd, + &he)) + { + /* NOTE: only for debugging... */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Command `%s' has the reserve_pub trait, but does not reserve history trait\n", + cmd->label); + return GNUNET_OK; /* command does nothing for reserves */ + } + for (unsigned int i = 0; i<history_length; i++) + { + if (found[i]) + continue; /* already found, skip */ + if (0 == + history_entry_cmp (he, + &history[i])) + { + found[i] = GNUNET_YES; + return GNUNET_OK; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Command `%s' reserve history entry not found\n", + cmd->label); + return GNUNET_SYSERR; + } +} + + +/** + * Check that the reserve balance and HTTP response code are + * both acceptable. + * + * @param cls closure. + * @param rs HTTP response details + */ +static void +reserve_status_cb (void *cls, + const struct TALER_EXCHANGE_ReserveStatus *rs) +{ + struct StatusState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; + struct TALER_Amount eb; + + ss->rsh = NULL; + if (ss->expected_response_code != rs->hr.http_status) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected HTTP response code: %d in %s:%u\n", + rs->hr.http_status, + __FILE__, + __LINE__); + json_dumpf (rs->hr.reply, + stderr, + 0); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + if (MHD_HTTP_OK != rs->hr.http_status) + { + TALER_TESTING_interpreter_next (is); + return; + } + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (ss->expected_balance, + &eb)); + + if (0 != TALER_amount_cmp (&eb, + &rs->details.ok.balance)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected amount in reserve: %s\n", + TALER_amount_to_string (&rs->details.ok.balance)); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + { + int found[rs->details.ok.history_len]; + + memset (found, + 0, + sizeof (found)); + for (unsigned int i = 0; i<= (unsigned int) is->ip; i++) + { + struct TALER_TESTING_Command *cmd = &is->commands[i]; + + if (GNUNET_OK != + analyze_command (&ss->reserve_pub, + cmd, + rs->details.ok.history_len, + rs->details.ok.history, + found)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Entry for command `%s' missing in history\n", + cmd->label); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + for (unsigned int i = 0; i<rs->details.ok.history_len; i++) + if (! found[i]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "History entry at index %u of type %d not justified by command status\n", + i, + rs->details.ok.history[i].type); + TALER_TESTING_interpreter_fail (ss->is); + return; + } + } + TALER_TESTING_interpreter_next (is); +} + + +/** + * Run the command. + * + * @param cls closure. + * @param cmd the command being executed. + * @param is the interpreter state. + */ +static void +status_run (void *cls, + const struct TALER_TESTING_Command *cmd, + struct TALER_TESTING_Interpreter *is) +{ + struct StatusState *ss = cls; + const struct TALER_TESTING_Command *create_reserve; + + ss->is = is; + create_reserve + = TALER_TESTING_interpreter_lookup_command (is, + ss->reserve_reference); + + if (NULL == create_reserve) + { + GNUNET_break (0); + TALER_TESTING_interpreter_fail (is); + return; + } + if (GNUNET_OK != + TALER_TESTING_get_trait_reserve_priv (create_reserve, + &ss->reserve_priv)) + { + GNUNET_break (0); + TALER_LOG_ERROR ("Failed to find reserve_priv for status query\n"); + TALER_TESTING_interpreter_fail (is); + return; + } + GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv, + &ss->reserve_pub.eddsa_pub); + ss->rsh = TALER_EXCHANGE_reserves_status (is->exchange, + ss->reserve_priv, + &reserve_status_cb, + ss); +} + + +/** + * Cleanup the state from a "reserve status" CMD, and possibly + * cancel a pending operation thereof. + * + * @param cls closure. + * @param cmd the command which is being cleaned up. + */ +static void +status_cleanup (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct StatusState *ss = cls; + + if (NULL != ss->rsh) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Command %u (%s) did not complete\n", + ss->is->ip, + cmd->label); + TALER_EXCHANGE_reserves_status_cancel (ss->rsh); + ss->rsh = NULL; + } + GNUNET_free (ss); +} + + +struct TALER_TESTING_Command +TALER_TESTING_cmd_reserve_status (const char *label, + const char *reserve_reference, + const char *expected_balance, + unsigned int expected_response_code) +{ + struct StatusState *ss; + + GNUNET_assert (NULL != reserve_reference); + ss = GNUNET_new (struct StatusState); + ss->reserve_reference = reserve_reference; + ss->expected_balance = expected_balance; + ss->expected_response_code = expected_response_code; + { + struct TALER_TESTING_Command cmd = { + .cls = ss, + .label = label, + .run = &status_run, + .cleanup = &status_cleanup + }; + + return cmd; + } +} diff --git a/src/testing/testing_api_cmd_status.c b/src/testing/testing_api_cmd_status.c index f13f60073..9d499571b 100644 --- a/src/testing/testing_api_cmd_status.c +++ b/src/testing/testing_api_cmd_status.c @@ -18,7 +18,7 @@ */ /** * @file testing/testing_api_cmd_status.c - * @brief Implement the /reserve/status test command. + * @brief Implement the GET /reserve/$RID test command. * @author Marcello Stanisci */ #include "platform.h" @@ -66,251 +66,51 @@ struct StatusState /** - * Compare @a h1 and @a h2. - * - * @param h1 a history entry - * @param h2 a history entry - * @return 0 if @a h1 and @a h2 are equal - */ -static int -history_entry_cmp (const struct TALER_EXCHANGE_ReserveHistory *h1, - const struct TALER_EXCHANGE_ReserveHistory *h2) -{ - if (h1->type != h2->type) - return 1; - switch (h1->type) - { - case TALER_EXCHANGE_RTT_CREDIT: - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == strcasecmp (h1->details.in_details.sender_url, - h2->details.in_details.sender_url)) && - (h1->details.in_details.wire_reference == - h2->details.in_details.wire_reference) && - (GNUNET_TIME_timestamp_cmp (h1->details.in_details.timestamp, - ==, - h2->details.in_details.timestamp)) ) - return 0; - return 1; - case TALER_EXCHANGE_RTT_WITHDRAWAL: - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - TALER_amount_cmp (&h1->details.withdraw.fee, - &h2->details.withdraw.fee)) ) - /* testing_api_cmd_withdraw doesn't set the out_authorization_sig, - so we cannot test for it here. but if the amount matches, - that should be good enough. */ - return 0; - return 1; - case TALER_EXCHANGE_RTT_RECOUP: - /* exchange_sig, exchange_pub and timestamp are NOT available - from the original recoup response, hence here NOT check(able/ed) */ - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - GNUNET_memcmp (&h1->details.recoup_details.coin_pub, - &h2->details.recoup_details.coin_pub)) ) - return 0; - return 1; - case TALER_EXCHANGE_RTT_CLOSE: - /* testing_api_cmd_exec_closer doesn't set the - receiver_account_details, exchange_sig, exchange_pub or wtid or timestamp - so we cannot test for it here. but if the amount matches, - that should be good enough. */ - if ( (0 == - TALER_amount_cmp (&h1->amount, - &h2->amount)) && - (0 == - TALER_amount_cmp (&h1->details.close_details.fee, - &h2->details.close_details.fee)) ) - return 0; - return 1; - } - GNUNET_assert (0); - return 1; -} - - -/** - * Check if @a cmd changed the reserve, if so, find the - * entry in @a history and set the respective index in @a found - * to #GNUNET_YES. If the entry is not found, return #GNUNET_SYSERR. - * - * @param reserve_pub public key of the reserve for which we have the @a history - * @param cmd command to analyze for impact on history - * @param history_length number of entries in @a history and @a found - * @param history history to check - * @param[in,out] found array to update - * @return #GNUNET_OK if @a cmd action on reserve was found in @a history - */ -static enum GNUNET_GenericReturnValue -analyze_command (const struct TALER_ReservePublicKeyP *reserve_pub, - const struct TALER_TESTING_Command *cmd, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history, - int *found) -{ - if (TALER_TESTING_cmd_is_batch (cmd)) - { - struct TALER_TESTING_Command *cur; - struct TALER_TESTING_Command **bcmd; - - cur = TALER_TESTING_cmd_batch_get_current (cmd); - if (GNUNET_OK != - TALER_TESTING_get_trait_batch_cmds (cmd, - &bcmd)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - for (unsigned int i = 0; NULL != (*bcmd)[i].label; i++) - { - struct TALER_TESTING_Command *step = &(*bcmd)[i]; - - if (step == cur) - break; /* if *we* are in a batch, make sure not to analyze commands past 'now' */ - if (GNUNET_OK != - analyze_command (reserve_pub, - step, - history_length, - history, - found)) - return GNUNET_SYSERR; - } - return GNUNET_OK; - } - else - { - const struct TALER_ReservePublicKeyP *rp; - const struct TALER_EXCHANGE_ReserveHistory *he; - - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (cmd, - &rp)) - return GNUNET_OK; /* command does nothing for reserves */ - if (0 != - GNUNET_memcmp (rp, - reserve_pub)) - return GNUNET_OK; /* command affects some _other_ reserve */ - if (GNUNET_OK != - TALER_TESTING_get_trait_reserve_history (cmd, - &he)) - { - /* NOTE: only for debugging... */ - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Command `%s' has the reserve_pub trait, but does not reserve history trait\n", - cmd->label); - return GNUNET_OK; /* command does nothing for reserves */ - } - for (unsigned int i = 0; i<history_length; i++) - { - if (found[i]) - continue; /* already found, skip */ - if (0 == - history_entry_cmp (he, - &history[i])) - { - found[i] = GNUNET_YES; - return GNUNET_OK; - } - } - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Command `%s' reserve history entry not found\n", - cmd->label); - return GNUNET_SYSERR; - } -} - - -/** * Check that the reserve balance and HTTP response code are * both acceptable. * * @param cls closure. - * @param hr HTTP response details - * @param balance current balance in the reserve, NULL on error. - * @param history_length number of entries in the transaction - * history, 0 on error. - * @param history detailed transaction history, NULL on error. + * @param rs HTTP response details */ static void reserve_status_cb (void *cls, - const struct TALER_EXCHANGE_HttpResponse *hr, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_EXCHANGE_ReserveHistory *history) + const struct TALER_EXCHANGE_ReserveSummary *rs) { struct StatusState *ss = cls; struct TALER_TESTING_Interpreter *is = ss->is; struct TALER_Amount eb; ss->rsh = NULL; - if (ss->expected_response_code != hr->http_status) + if (ss->expected_response_code != rs->hr.http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected HTTP response code: %d in %s:%u\n", - hr->http_status, + rs->hr.http_status, __FILE__, __LINE__); - json_dumpf (hr->reply, + json_dumpf (rs->hr.reply, stderr, 0); TALER_TESTING_interpreter_fail (ss->is); return; } - + if (MHD_HTTP_OK != ss->expected_response_code) + { + TALER_TESTING_interpreter_next (is); + return; + } GNUNET_assert (GNUNET_OK == TALER_string_to_amount (ss->expected_balance, &eb)); - if (0 != TALER_amount_cmp (&eb, - balance)) + &rs->details.ok.balance)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unexpected amount in reserve: %s\n", - TALER_amount_to_string (balance)); + TALER_amount_to_string (&rs->details.ok.balance)); TALER_TESTING_interpreter_fail (ss->is); return; } - { - int found[history_length]; - - memset (found, - 0, - sizeof (found)); - for (unsigned int i = 0; i<= (unsigned int) is->ip; i++) - { - struct TALER_TESTING_Command *cmd = &is->commands[i]; - - if (GNUNET_OK != - analyze_command (ss->reserve_pubp, - cmd, - history_length, - history, - found)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Entry for command `%s' missing in history\n", - cmd->label); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - } - for (unsigned int i = 0; i<history_length; i++) - if (! found[i]) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "History entry at index %u of type %d not justified by command history\n", - i, - history[i].type); - TALER_TESTING_interpreter_fail (ss->is); - return; - } - } TALER_TESTING_interpreter_next (is); } diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c index 1c24d5a6c..a38233980 100644 --- a/src/testing/testing_api_cmd_withdraw.c +++ b/src/testing/testing_api_cmd_withdraw.c @@ -148,7 +148,7 @@ struct WithdrawState * Reserve history entry that corresponds to this operation. * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL. */ - struct TALER_EXCHANGE_ReserveHistory reserve_history; + struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history; /** * Withdraw handle (while operation is running). |