summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c140
-rw-r--r--src/exchange/taler-exchange-httpd_management_extensions.c6
-rw-r--r--src/exchange/taler-exchange-httpd_management_global_fees.c3
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c174
-rw-r--r--src/include/taler_exchange_service.h59
-rw-r--r--src/include/taler_exchangedb_plugin.h2
-rw-r--r--src/include/taler_json_lib.h24
-rw-r--r--src/lib/exchange_api_handle.c111
-rw-r--r--src/lib/exchange_api_reserves_history.c368
-rw-r--r--src/lib/exchange_api_reserves_status.c364
10 files changed, 1231 insertions, 20 deletions
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 95278ab80..1012a8c03 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -57,7 +57,7 @@
* #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
* exchange_api_handle.c!
*/
-#define EXCHANGE_PROTOCOL_VERSION "12:0:0"
+#define EXCHANGE_PROTOCOL_VERSION "13:0:1"
/**
@@ -280,6 +280,31 @@ 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
{
@@ -297,12 +322,28 @@ struct TEH_KeyStateHandle
struct GNUNET_CONTAINER_MultiPeerMap *signkey_map;
/**
+ * Head of DLL of our global fees.
+ */
+ struct GlobalFee *gf_head;
+
+ /**
+ * Tail of DLL of our global fees.
+ */
+ struct GlobalFee *gf_tail;
+
+ /**
* json array with the auditors of this exchange. Contains exactly
* the information needed for the "auditors" field of the /keys response.
*/
json_t *auditors;
/**
+ * json array with the global fees of this exchange. Contains exactly
+ * the information needed for the "global_fees" field of the /keys response.
+ */
+ json_t *global_fees;
+
+ /**
* Sorted array of responses to /keys (MUST be sorted by cherry-picking date) of
* length @e krd_array_length;
*/
@@ -548,7 +589,7 @@ suspend_request (struct MHD_Connection *connection)
* @param value a `struct TEH_DenominationKey`
* @return #GNUNET_OK
*/
-static int
+static enum GNUNET_GenericReturnValue
check_dk (void *cls,
const struct GNUNET_HashCode *hc,
void *value)
@@ -1174,7 +1215,16 @@ static void
destroy_key_state (struct TEH_KeyStateHandle *ksh,
bool free_helper)
{
+ struct GlobalFee *gf;
+
clear_response_cache (ksh);
+ while (NULL != (gf = ksh->gf_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (ksh->gf_head,
+ ksh->gf_tail,
+ gf);
+ GNUNET_free (gf);
+ }
GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
&clear_denomination_cb,
ksh);
@@ -1185,6 +1235,8 @@ destroy_key_state (struct TEH_KeyStateHandle *ksh,
GNUNET_CONTAINER_multipeermap_destroy (ksh->signkey_map);
json_decref (ksh->auditors);
ksh->auditors = NULL;
+ json_decref (ksh->global_fees);
+ ksh->global_fees = NULL;
if (free_helper)
{
destroy_key_helpers (ksh->helpers);
@@ -1817,6 +1869,8 @@ create_krd (struct TEH_KeyStateHandle *ksh,
denoms),
GNUNET_JSON_pack_array_incref ("auditors",
ksh->auditors),
+ GNUNET_JSON_pack_array_incref ("global_fees",
+ ksh->global_fees),
GNUNET_JSON_pack_timestamp ("list_issue_date",
last_cpd),
GNUNET_JSON_pack_data_auto ("eddsa_pub",
@@ -1825,7 +1879,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
&exchange_sig));
GNUNET_assert (NULL != keys);
- // Set wallet limit if KYC is configured
+ /* Set wallet limit if KYC is configured */
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
(GNUNET_OK ==
TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) )
@@ -1839,7 +1893,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
&TEH_kyc_config.wallet_balance_limit)));
}
- // Signal support for the configured, enabled extensions.
+ /* Signal support for the configured, enabled extensions. */
{
json_t *extensions = json_object ();
bool has_extensions = false;
@@ -2202,6 +2256,70 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
/**
+ * Called with information about global fees.
+ *
+ * @param cls `struct TEH_KeyStateHandle *` we are building
+ * @param fees the global fees we charge
+ * @param purse_timeout when do purses time out
+ * @param kyc_timeout when do reserves without KYC time out
+ * @param history_expiration how long are account histories preserved
+ * @param purse_account_limit how many purses are free per account
+ * @param start_date from when are these fees valid (start date)
+ * @param end_date until when are these fees valid (end date, exclusive)
+ * @param master_sig master key signature affirming that this is the correct
+ * fee (of purpose #TALER_SIGNATURE_MASTER_GLOBAL_FEES)
+ */
+static void
+global_fee_info_cb (
+ void *cls,
+ const struct TALER_GlobalFeeSet *fees,
+ struct GNUNET_TIME_Relative purse_timeout,
+ struct GNUNET_TIME_Relative kyc_timeout,
+ struct GNUNET_TIME_Relative history_expiration,
+ uint32_t purse_account_limit,
+ struct GNUNET_TIME_Timestamp start_date,
+ struct GNUNET_TIME_Timestamp end_date,
+ const struct TALER_MasterSignatureP *master_sig)
+{
+ struct TEH_KeyStateHandle *ksh = cls;
+ struct GlobalFee *gf;
+
+ gf = GNUNET_new (struct GlobalFee);
+ gf->start_date = start_date;
+ gf->end_date = end_date;
+ gf->fees = *fees;
+ gf->purse_timeout = purse_timeout;
+ gf->kyc_timeout = kyc_timeout;
+ gf->history_expiration = history_expiration;
+ gf->purse_account_limit = purse_account_limit;
+ gf->master_sig = *master_sig;
+ GNUNET_CONTAINER_DLL_insert (ksh->gf_head,
+ ksh->gf_tail,
+ gf);
+ GNUNET_assert (
+ 0 ==
+ json_array_append_new (
+ ksh->global_fees,
+ GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("start_date",
+ start_date),
+ GNUNET_JSON_pack_timestamp ("end_date",
+ end_date),
+ TALER_JSON_PACK_GLOBAL_FEES (fees),
+ GNUNET_JSON_pack_time_rel ("history_expiration",
+ history_expiration),
+ GNUNET_JSON_pack_time_rel ("account_kyc_timeout",
+ kyc_timeout),
+ GNUNET_JSON_pack_time_rel ("purse_timeout",
+ purse_timeout),
+ GNUNET_JSON_pack_uint64 ("purse_account_limit",
+ purse_account_limit),
+ GNUNET_JSON_pack_data_auto ("master_sig",
+ master_sig))));
+}
+
+
+/**
* Create a key state.
*
* @param[in] hs helper state to (re)use, NULL if not available
@@ -2246,6 +2364,20 @@ build_key_state (struct HelperState *hs,
/* NOTE: fetches master-signed signkeys, but ALSO those that were revoked! */
GNUNET_break (GNUNET_OK ==
TEH_plugin->preflight (TEH_plugin->cls));
+ if (NULL != ksh->global_fees)
+ json_decref (ksh->global_fees);
+ ksh->global_fees = json_array ();
+ qs = TEH_plugin->get_global_fees (TEH_plugin->cls,
+ &global_fee_info_cb,
+ ksh);
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+ destroy_key_state (ksh,
+ true);
+ return NULL;
+ }
qs = TEH_plugin->iterate_denominations (TEH_plugin->cls,
&denomination_info_cb,
ksh);
diff --git a/src/exchange/taler-exchange-httpd_management_extensions.c b/src/exchange/taler-exchange-httpd_management_extensions.c
index 109e863d3..ce151e2e5 100644
--- a/src/exchange/taler-exchange-httpd_management_extensions.c
+++ b/src/exchange/taler-exchange-httpd_management_extensions.c
@@ -126,6 +126,11 @@ set_extensions (void *cls,
.size = htons (sizeof (ev)),
.type = htons (TALER_DBEVENT_EXCHANGE_EXTENSIONS_UPDATED)
};
+
+ // FIXME-Oec: bug: convert type to NBO first!
+ // FIXME-Oec: bug: sizeof enum is ill-defined...
+ // FIXME-Oec: bug: don't see /keys listening to the event
+ // FIXME-Oec: why is TEH_keys_update_states (); not enough?
TEH_plugin->event_notify (TEH_plugin->cls,
&ev,
type,
@@ -294,7 +299,6 @@ TEH_handler_management_post_extensions (
NULL,
0);
-
CLEANUP:
for (unsigned int i = 0; i < sec.num_extensions; i++)
{
diff --git a/src/exchange/taler-exchange-httpd_management_global_fees.c b/src/exchange/taler-exchange-httpd_management_global_fees.c
index eb3aa5995..37bb40d90 100644
--- a/src/exchange/taler-exchange-httpd_management_global_fees.c
+++ b/src/exchange/taler-exchange-httpd_management_global_fees.c
@@ -27,6 +27,7 @@
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#include "taler_signatures.h"
+#include "taler-exchange-httpd_keys.h"
#include "taler-exchange-httpd_management.h"
#include "taler-exchange-httpd_responses.h"
@@ -256,7 +257,7 @@ TEH_handler_management_post_global_fees (
if (GNUNET_SYSERR == res)
return ret;
}
- // TEH_global_update_state (); // FIXME: trigger!
+ TEH_keys_update_states ();
return TALER_MHD_reply_static (
connection,
MHD_HTTP_NO_CONTENT,
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 2a955e15f..bb6f46f59 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1385,6 +1385,28 @@ prepare_statements (struct PostgresClosure *pg)
" WHERE start_date <= $1"
" AND end_date > $1;",
1),
+ /* Used in #postgres_get_global_fees() */
+ GNUNET_PQ_make_prepare (
+ "get_global_fees",
+ "SELECT "
+ " start_date"
+ ",end_date"
+ ",history_fee_val"
+ ",history_fee_frac"
+ ",kyc_fee_val"
+ ",kyc_fee_frac"
+ ",account_fee_val"
+ ",account_fee_frac"
+ ",purse_fee_val"
+ ",purse_fee_frac"
+ ",purse_timeout"
+ ",kyc_timeout"
+ ",history_expiration"
+ ",purse_account_limit"
+ ",master_sig"
+ " FROM global_fee"
+ " WHERE start_date >= $1",
+ 1),
/* Used in #postgres_insert_wire_fee */
GNUNET_PQ_make_prepare (
"insert_wire_fee",
@@ -7819,6 +7841,142 @@ postgres_get_global_fee (void *cls,
/**
+ * Closure for #global_fees_cb().
+ */
+struct GlobalFeeContext
+{
+ /**
+ * Function to call for each global fee block.
+ */
+ TALER_EXCHANGEDB_GlobalFeeCallback cb;
+
+ /**
+ * Closure to give to @e rec.
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+
+ /**
+ * Set to #GNUNET_SYSERR on error.
+ */
+ enum GNUNET_GenericReturnValue status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+global_fees_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct GlobalFeeContext *gctx = cls;
+ struct PostgresClosure *pg = gctx->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct TALER_GlobalFeeSet fees;
+ struct GNUNET_TIME_Relative purse_timeout;
+ struct GNUNET_TIME_Relative kyc_timeout;
+ struct GNUNET_TIME_Relative history_expiration;
+ uint32_t purse_account_limit;
+ struct GNUNET_TIME_Timestamp start_date;
+ struct GNUNET_TIME_Timestamp end_date;
+ struct TALER_MasterSignatureP master_sig;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_timestamp ("start_date",
+ &start_date),
+ GNUNET_PQ_result_spec_timestamp ("end_date",
+ &end_date),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("history_fee",
+ &fees.history),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("kyc_fee",
+ &fees.kyc),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("account_fee",
+ &fees.account),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("purse_fee",
+ &fees.purse),
+ GNUNET_PQ_result_spec_relative_time ("purse_timeout",
+ &purse_timeout),
+ GNUNET_PQ_result_spec_relative_time ("kyc_timeout",
+ &kyc_timeout),
+ GNUNET_PQ_result_spec_relative_time ("history_expiration",
+ &history_expiration),
+ GNUNET_PQ_result_spec_uint32 ("purse_account_limit",
+ &purse_account_limit),
+ GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+ &master_sig),
+ GNUNET_PQ_result_spec_end
+ };
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ gctx->status = GNUNET_SYSERR;
+ break;
+ }
+ gctx->cb (gctx->cb_cls,
+ &fees,
+ purse_timeout,
+ kyc_timeout,
+ history_expiration,
+ purse_account_limit,
+ start_date,
+ end_date,
+ &master_sig);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+/**
+ * Obtain global fees from database.
+ *
+ * @param cls closure
+ * @param cb function to call on each fee entry
+ * @param cb_cls closure for @a cb
+ * @return status of the transaction
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_global_fees (void *cls,
+ TALER_EXCHANGEDB_GlobalFeeCallback cb,
+ void *cb_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_TIME_Timestamp date
+ = GNUNET_TIME_timestamp_get ();
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_timestamp (&date),
+ GNUNET_PQ_query_param_end
+ };
+ struct GlobalFeeContext gctx = {
+ .cb = cb,
+ .cb_cls = cb_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
+
+ return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "get_global_fees",
+ params,
+ &global_fees_cb,
+ &gctx);
+}
+
+
+/**
* Insert wire transfer fee into database.
*
* @param cls closure
@@ -8034,7 +8192,7 @@ struct ExpiredReserveContext
/**
* Set to #GNUNET_SYSERR on error.
*/
- int status;
+ enum GNUNET_GenericReturnValue status;
};
@@ -8053,7 +8211,7 @@ reserve_expired_cb (void *cls,
{
struct ExpiredReserveContext *erc = cls;
struct PostgresClosure *pg = erc->pg;
- int ret;
+ enum GNUNET_GenericReturnValue ret;
ret = GNUNET_OK;
for (unsigned int i = 0; i<num_results; i++)
@@ -8117,13 +8275,14 @@ postgres_get_expired_reserves (void *cls,
GNUNET_PQ_query_param_timestamp (&now),
GNUNET_PQ_query_param_end
};
- struct ExpiredReserveContext ectx;
+ struct ExpiredReserveContext ectx = {
+ .rec = rec,
+ .rec_cls = rec_cls,
+ .pg = pg,
+ .status = GNUNET_OK
+ };
enum GNUNET_DB_QueryStatus qs;
- ectx.rec = rec;
- ectx.rec_cls = rec_cls;
- ectx.pg = pg;
- ectx.status = GNUNET_OK;
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
"get_expired_reserves",
params,
@@ -12371,6 +12530,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->insert_global_fee = &postgres_insert_global_fee;
plugin->get_wire_fee = &postgres_get_wire_fee;
plugin->get_global_fee = &postgres_get_global_fee;
+ plugin->get_global_fees = &postgres_get_global_fees;
plugin->get_expired_reserves = &postgres_get_expired_reserves;
plugin->insert_reserve_closed = &postgres_insert_reserve_closed;
plugin->wire_prepare_data_insert = &postgres_wire_prepare_data_insert;
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 48272a6b8..56940669d 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -204,6 +204,55 @@ struct TALER_EXCHANGE_AuditorInformation
/**
+ * Global fees and options of an exchange for a given time period.
+ */
+struct TALER_EXCHANGE_GlobalFee
+{
+
+ /**
+ * Signature affirming all of the data.
+ */
+ struct TALER_MasterSignatureP master_sig;
+
+ /**
+ * Starting time of the validity period (inclusive).
+ */
+ struct GNUNET_TIME_Timestamp start_date;
+
+ /**
+ * End time of the validity period (exclusive).
+ */
+ struct GNUNET_TIME_Timestamp end_date;
+
+ /**
+ * Unmerged purses will be timed out after at most this time.
+ */
+ struct GNUNET_TIME_Relative purse_timeout;
+
+ /**
+ * Accounts without KYC will be closed after this time.
+ */
+ struct GNUNET_TIME_Relative kyc_timeout;
+
+ /**
+ * Account history is limited to this timeframe.
+ */
+ struct GNUNET_TIME_Relative history_expiration;
+
+ /**
+ * Fees that apply globally, independent of denomination
+ * and wire method.
+ */
+ struct TALER_GlobalFeeSet fees;
+
+ /**
+ * Number of free purses per account.
+ */
+ uint32_t purse_account_limit;
+};
+
+
+/**
* @brief Information about keys from the exchange.
*/
struct TALER_EXCHANGE_Keys
@@ -230,6 +279,11 @@ struct TALER_EXCHANGE_Keys
struct TALER_EXCHANGE_AuditorInformation *auditors;
/**
+ * Array with the global fees of the exchange.
+ */
+ struct TALER_EXCHANGE_GlobalFee *global_fees;
+
+ /**
* Supported Taler protocol version by the exchange.
* String in the format current:revision:age using the
* semantics of GNU libtool. See
@@ -273,6 +327,11 @@ struct TALER_EXCHANGE_Keys
struct TALER_AgeMask age_mask;
/**
+ * Length of the @e global_fees array.
+ */
+ unsigned int num_global_fees;
+
+ /**
* Length of the @e sign_keys array (number of valid entries).
*/
unsigned int num_sign_keys;
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 7696b607d..fc909a1ba 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -3968,7 +3968,7 @@ struct TALER_EXCHANGEDB_Plugin
* Obtain information about the global fee structure of the exchange.
*
* @param cls closure
- * @param cb function to call on each account
+ * @param cb function to call on each fee entry
* @param cb_cls closure for @a cb
* @return transaction status code
*/
diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h
index 6238c07d3..b4f999001 100644
--- a/src/include/taler_json_lib.h
+++ b/src/include/taler_json_lib.h
@@ -280,6 +280,30 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
/**
+ * Generate specification to parse all global fees.
+ *
+ * @param currency which currency to expect
+ * @param[out] gfs a `struct TALER_GlobalFeeSet` to initialize
+ */
+#define TALER_JSON_SPEC_GLOBAL_FEES(currency,gfs) \
+ TALER_JSON_spec_amount ("kyc_fee", (currency), &(gfs)->kyc), \
+ TALER_JSON_spec_amount ("history_fee", (currency), &(gfs)->history), \
+ TALER_JSON_spec_amount ("account_fee", (currency), &(gfs)->account), \
+ TALER_JSON_spec_amount ("purse_fee", (currency), &(gfs)->purse)
+
+/**
+ * Macro to pack all of the global fees.
+ *
+ * @param gfs a `struct TALER_GlobalFeeSet` to pack
+ */
+#define TALER_JSON_PACK_GLOBAL_FEES(gfs) \
+ TALER_JSON_pack_amount ("kyc_fee", &(gfs)->kyc), \
+ TALER_JSON_pack_amount ("history_fee", &(gfs)->history), \
+ TALER_JSON_pack_amount ("account_fee", &(gfs)->account), \
+ TALER_JSON_pack_amount ("purse_fee", &(gfs)->purse)
+
+
+/**
* Generate line in parser specification for denomination public key.
*
* @param field name of the field
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index a9713a45a..f7e877913 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2021 Taler Systems SA
+ Copyright (C) 2014-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published
@@ -40,7 +40,7 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
-#define EXCHANGE_PROTOCOL_CURRENT 12
+#define EXCHANGE_PROTOCOL_CURRENT 13
/**
* How many versions are we backwards compatible with?
@@ -255,7 +255,7 @@ free_keys_request (struct KeysRequest *kr)
*/
static enum GNUNET_GenericReturnValue
parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
- int check_sigs,
+ bool check_sigs,
json_t *sign_key_obj,
const struct TALER_MasterPublicKeyP *master_key)
{
@@ -317,7 +317,7 @@ parse_json_signkey (struct TALER_EXCHANGE_SigningPublicKey *sign_key,
static enum GNUNET_GenericReturnValue
parse_json_denomkey (const char *currency,
struct TALER_EXCHANGE_DenomPublicKey *denom_key,
- int check_sigs,
+ bool check_sigs,
json_t *denom_key_obj,
struct TALER_MasterPublicKeyP *master_key,
struct GNUNET_HashContext *hash_context)
@@ -394,7 +394,7 @@ EXITIF_exit:
*/
static enum GNUNET_GenericReturnValue
parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
- int check_sigs,
+ bool check_sigs,
json_t *auditor_obj,
const struct TALER_EXCHANGE_Keys *key_data)
{
@@ -505,6 +505,79 @@ parse_json_auditor (struct TALER_EXCHANGE_AuditorInformation *auditor,
/**
+ * Parse a exchange's global fee information encoded in JSON.
+ *
+ * @param[out] gf where to return the result
+ * @param check_sigs should we check signatures
+ * @param[in] fee_obj json to parse
+ * @param key_data already parsed information about the exchange
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ * invalid or the json malformed.
+ */
+static enum GNUNET_GenericReturnValue
+parse_global_fee (struct TALER_EXCHANGE_GlobalFee *gf,
+ bool check_sigs,
+ json_t *fee_obj,
+ const struct TALER_EXCHANGE_Keys *key_data)
+{
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("start_time",
+ &gf->start_date),
+ GNUNET_JSON_spec_timestamp ("end_time",
+ &gf->end_date),
+ GNUNET_JSON_spec_relative_time ("purse_timeout",
+ &gf->purse_timeout),
+ GNUNET_JSON_spec_relative_time ("kyc_timeout",
+ &gf->kyc_timeout),
+ GNUNET_JSON_spec_relative_time ("history_expiration",
+ &gf->history_expiration),
+ GNUNET_JSON_spec_uint32 ("purse_account_limit",
+ &gf->purse_account_limit),
+ TALER_JSON_SPEC_GLOBAL_FEES (key_data->currency,
+ &gf->fees),
+ GNUNET_JSON_spec_fixed_auto ("master_sig",
+ &gf->master_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_JSON_parse (fee_obj,
+ spec,
+ NULL, NULL))
+ {
+ GNUNET_break_op (0);
+#if DEBUG
+ json_dumpf (fee_obj,
+ stderr,
+ JSON_INDENT (2));
+#endif
+ return GNUNET_SYSERR;
+ }
+ if (check_sigs)
+ {
+ if (GNUNET_OK !=
+ TALER_exchange_offline_global_fee_verify (
+ gf->start_date,
+ gf->end_date,
+ &gf->fees,
+ gf->purse_timeout,
+ gf->kyc_timeout,
+ gf->history_expiration,
+ gf->purse_account_limit,
+ &key_data->master_pub,
+ &gf->master_sig))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_SYSERR;
+ }
+ }
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
* Function called with information about the auditor. Marks an
* auditor as 'up'.
*
@@ -691,7 +764,7 @@ decode_keys_json (const json_t *resp_obj,
stderr,
JSON_INDENT (2));
#endif
- /* check the version */
+ /* check the version first */
{
const char *ver;
unsigned int age;
@@ -762,6 +835,32 @@ decode_keys_json (const json_t *resp_obj,
hash_context_restricted = GNUNET_CRYPTO_hash_context_start ();
}
+ /* parse the global fees */
+ {
+ json_t *global_fees;
+ json_t *global_fee;
+ unsigned int index;
+
+ EXITIF (NULL == (global_fees =
+ json_object_get (resp_obj,
+ "global_fees")));
+ EXITIF (! json_is_array (global_fees));
+ if (0 != (key_data->num_global_fees =
+ json_array_size (global_fees)))
+ {
+ key_data->global_fees
+ = GNUNET_new_array (key_data->num_global_fees,
+ struct TALER_EXCHANGE_GlobalFee);
+ json_array_foreach (global_fees, index, global_fee) {
+ EXITIF (GNUNET_SYSERR ==
+ parse_global_fee (&key_data->global_fees[index],
+ check_sig,
+ global_fee,
+ key_data));
+ }
+ }
+ }
+
/* parse the signing keys */
{
json_t *sign_keys_array;
diff --git a/src/lib/exchange_api_reserves_history.c b/src/lib/exchange_api_reserves_history.c
new file mode 100644
index 000000000..f7191b2ad
--- /dev/null
+++ b/src/lib/exchange_api_reserves_history.c
@@ -0,0 +1,368 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published 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 lib/exchange_api_reserves_history.c
+ * @brief Implementation of the POST /reserves/$RESERVE_PUB/history requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP history codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/$RID/history Handle
+ */
+struct TALER_EXCHANGE_ReservesHistoryHandle
+{
+
+ /**
+ * The connection to exchange this request handle will use
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Context for #TEH_curl_easy_post(). Keeps the data that must
+ * persist for Curl to make the upload.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_EXCHANGE_ReservesHistoryCallback cb;
+
+ /**
+ * Public key of the reserve we are querying.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK history code. Handle the JSON
+ * response.
+ *
+ * @param rgh 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,
+ 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 GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("balance",
+ &balance),
+ GNUNET_JSON_spec_bool ("kyc_passed",
+ &kyc_ok),
+ GNUNET_JSON_spec_bool ("kyc_required",
+ &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,
+ spec,
+ NULL,
+ NULL))
+ {
+ 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);
+ GNUNET_JSON_parse_free (spec);
+ 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);
+ GNUNET_JSON_parse_free (spec);
+ 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);
+ }
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RID/history request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesHistoryHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_history_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_EXCHANGE_ReservesHistoryHandle *rgh = cls;
+ const json_t *j = response;
+ struct TALER_EXCHANGE_HttpResponse hr = {
+ .reply = j,
+ .http_history = (unsigned int) response_code
+ };
+
+ rgh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ handle_reserves_history_ok (rgh,
+ j))
+ {
+ hr.http_history = 0;
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for reserves history\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ break;
+ }
+ if (NULL != rgh->cb)
+ {
+ rgh->cb (rgh->cb_cls,
+ &hr,
+ NULL,
+ 0,
+ NULL);
+ rgh->cb = NULL;
+ }
+ TALER_EXCHANGE_reserves_history_cancel (rgh);
+}
+
+
+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)
+{
+ struct TALER_EXCHANGE_ReservesHistoryHandle *rgh;
+ 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 GNUNET_TIME_Timestamp ts
+ = GNUNET_TIME_timestamp_get ();
+
+ if (GNUNET_YES !=
+ TEAH_handle_is_ready (exchange))
+ {
+ 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;
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &rgh->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),
+ pub_str,
+ sizeof (pub_str));
+ *end = '\0';
+ GNUNET_snprintf (arg_str,
+ sizeof (arg_str),
+ "/reserves/%s/history",
+ pub_str);
+ }
+ rgh->url = TEAH_path_to_url (exchange,
+ arg_str);
+ if (NULL == rgh->url)
+ {
+ GNUNET_free (rgh);
+ return NULL;
+ }
+ eh = TALER_EXCHANGE_curl_easy_history_ (rgh->url);
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ GNUNET_free (rgh->url);
+ GNUNET_free (rgh);
+ return NULL;
+ }
+ TALER_wallet_reserve_history_sign (ts,
+ history_fee,
+ reserve_priv,
+ &reserve_sig);
+ {
+ json_t *history_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("request_timestamp",
+ &ts),
+ GNUNET_JSON_pack_data_auto ("reserve_sig",
+ &reserve_sig));
+
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&rgh->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;
+ }
+ json_decref (history_obj);
+ }
+ ctx = TEAH_handle_to_context (exchange);
+ rgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_reserves_history_finished,
+ rgh);
+ return rgh;
+}
+
+
+void
+TALER_EXCHANGE_reserves_history_cancel (
+ struct TALER_EXCHANGE_ReservesHistoryHandle *rgh)
+{
+ if (NULL != rgh->job)
+ {
+ GNUNET_CURL_job_cancel (rgh->job);
+ rgh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&rgh->post_ctx);
+ GNUNET_free (rgh->url);
+ GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserves_history.c */
diff --git a/src/lib/exchange_api_reserves_status.c b/src/lib/exchange_api_reserves_status.c
new file mode 100644
index 000000000..2758a3a28
--- /dev/null
+++ b/src/lib/exchange_api_reserves_status.c
@@ -0,0 +1,364 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published 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 lib/exchange_api_reserves_status.c
+ * @brief Implementation of the POST /reserves/$RESERVE_PUB/status requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/$RID/status Handle
+ */
+struct TALER_EXCHANGE_ReservesStatusHandle
+{
+
+ /**
+ * The connection to exchange this request handle will use
+ */
+ struct TALER_EXCHANGE_Handle *exchange;
+
+ /**
+ * The url for this request.
+ */
+ char *url;
+
+ /**
+ * Handle for the request.
+ */
+ struct GNUNET_CURL_Job *job;
+
+ /**
+ * Context for #TEH_curl_easy_post(). Keeps the data that must
+ * persist for Curl to make the upload.
+ */
+ struct TALER_CURL_PostContext post_ctx;
+
+ /**
+ * Function to call with the result.
+ */
+ TALER_EXCHANGE_ReservesStatusCallback cb;
+
+ /**
+ * Public key of the reserve we are querying.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Closure for @a cb.
+ */
+ void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK status code. Handle the JSON
+ * response.
+ *
+ * @param rgh 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,
+ 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 GNUNET_JSON_Specification spec[] = {
+ TALER_JSON_spec_amount_any ("balance",
+ &balance),
+ GNUNET_JSON_spec_bool ("kyc_passed",
+ &kyc_ok),
+ GNUNET_JSON_spec_bool ("kyc_required",
+ &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,
+ spec,
+ NULL,
+ NULL))
+ {
+ 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);
+ 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))
+ {
+ /* 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;
+ }
+ TALER_EXCHANGE_free_reserve_history (rhistory,
+ len);
+ }
+ GNUNET_JSON_parse_free (spec);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RID/status request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesStatusHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_status_finished (void *cls,
+ long response_code,
+ const void *response)
+{
+ struct TALER_EXCHANGE_ReservesStatusHandle *rgh = cls;
+ const json_t *j = response;
+ struct TALER_EXCHANGE_HttpResponse hr = {
+ .reply = j,
+ .http_status = (unsigned int) response_code
+ };
+
+ rgh->job = NULL;
+ switch (response_code)
+ {
+ case 0:
+ hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ break;
+ case MHD_HTTP_OK:
+ if (GNUNET_OK !=
+ handle_reserves_status_ok (rgh,
+ j))
+ {
+ hr.http_status = 0;
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unexpected response code %u/%d for reserves status\n",
+ (unsigned int) response_code,
+ (int) hr.ec);
+ break;
+ }
+ if (NULL != rgh->cb)
+ {
+ rgh->cb (rgh->cb_cls,
+ &hr,
+ NULL,
+ 0,
+ NULL);
+ rgh->cb = NULL;
+ }
+ TALER_EXCHANGE_reserves_status_cancel (rgh);
+}
+
+
+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)
+{
+ struct TALER_EXCHANGE_ReservesStatusHandle *rgh;
+ struct GNUNET_CURL_Context *ctx;
+ CURL *eh;
+ char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+ struct GNUNET_TIME_Timestamp ts
+ = GNUNET_TIME_timestamp_get ();
+
+ if (GNUNET_YES !=
+ TEAH_handle_is_ready (exchange))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
+ rgh->exchange = exchange;
+ rgh->cb = cb;
+ rgh->cb_cls = cb_cls;
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &rgh->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),
+ pub_str,
+ sizeof (pub_str));
+ *end = '\0';
+ GNUNET_snprintf (arg_str,
+ sizeof (arg_str),
+ "/reserves/%s/status",
+ pub_str);
+ }
+ rgh->url = TEAH_path_to_url (exchange,
+ arg_str);
+ if (NULL == rgh->url)
+ {
+ GNUNET_free (rgh);
+ return NULL;
+ }
+ eh = TALER_EXCHANGE_curl_easy_status_ (rgh->url);
+ if (NULL == eh)
+ {
+ GNUNET_break (0);
+ GNUNET_free (rgh->url);
+ GNUNET_free (rgh);
+ return NULL;
+ }
+ TALER_wallet_reserve_status_sign (ts,
+ reserve_priv,
+ &reserve_sig);
+ {
+ json_t *status_obj = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_timestamp ("request_timestamp",
+ &ts),
+ GNUNET_JSON_pack_data_auto ("reserve_sig",
+ &reserve_sig));
+
+ if (GNUNET_OK !=
+ TALER_curl_easy_post (&rgh->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;
+ }
+ json_decref (status_obj);
+ }
+ ctx = TEAH_handle_to_context (exchange);
+ rgh->job = GNUNET_CURL_job_add (ctx,
+ eh,
+ &handle_reserves_status_finished,
+ rgh);
+ return rgh;
+}
+
+
+void
+TALER_EXCHANGE_reserves_status_cancel (
+ struct TALER_EXCHANGE_ReservesStatusHandle *rgh)
+{
+ if (NULL != rgh->job)
+ {
+ GNUNET_CURL_job_cancel (rgh->job);
+ rgh->job = NULL;
+ }
+ TALER_curl_easy_post_finished (&rgh->post_ctx);
+ GNUNET_free (rgh->url);
+ GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserves_status.c */