return new global fees from /keys
This commit is contained in:
parent
1bb5a77c8d
commit
dee45bf022
@ -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
|
||||
{
|
||||
|
||||
@ -296,12 +321,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;
|
||||
@ -2201,6 +2255,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.
|
||||
*
|
||||
@ -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);
|
||||
|
@ -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++)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
@ -7818,6 +7840,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.
|
||||
*
|
||||
@ -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;
|
||||
|
@ -203,6 +203,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.
|
||||
*/
|
||||
@ -229,6 +278,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
|
||||
@ -272,6 +326,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).
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -279,6 +279,30 @@ TALER_JSON_spec_amount_any_nbo (const char *name,
|
||||
TALER_JSON_pack_amount (pfx "_refund", &(dfs)->refund)
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -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)
|
||||
{
|
||||
@ -504,6 +504,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;
|
||||
|
368
src/lib/exchange_api_reserves_history.c
Normal file
368
src/lib/exchange_api_reserves_history.c
Normal file
@ -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 */
|
364
src/lib/exchange_api_reserves_status.c
Normal file
364
src/lib/exchange_api_reserves_status.c
Normal file
@ -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 */
|
Loading…
Reference in New Issue
Block a user