towards support for new reserve history/status APIs
This commit is contained in:
parent
dee45bf022
commit
427417b835
@ -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
|
struct TEH_KeyStateHandle
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -324,12 +299,12 @@ struct TEH_KeyStateHandle
|
|||||||
/**
|
/**
|
||||||
* Head of DLL of our global fees.
|
* Head of DLL of our global fees.
|
||||||
*/
|
*/
|
||||||
struct GlobalFee *gf_head;
|
struct TEH_GlobalFee *gf_head;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tail of DLL of our global fees.
|
* 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
|
* json array with the auditors of this exchange. Contains exactly
|
||||||
@ -1215,7 +1190,7 @@ static void
|
|||||||
destroy_key_state (struct TEH_KeyStateHandle *ksh,
|
destroy_key_state (struct TEH_KeyStateHandle *ksh,
|
||||||
bool free_helper)
|
bool free_helper)
|
||||||
{
|
{
|
||||||
struct GlobalFee *gf;
|
struct TEH_GlobalFee *gf;
|
||||||
|
|
||||||
clear_response_cache (ksh);
|
clear_response_cache (ksh);
|
||||||
while (NULL != (gf = ksh->gf_head))
|
while (NULL != (gf = ksh->gf_head))
|
||||||
@ -2282,9 +2257,9 @@ global_fee_info_cb (
|
|||||||
const struct TALER_MasterSignatureP *master_sig)
|
const struct TALER_MasterSignatureP *master_sig)
|
||||||
{
|
{
|
||||||
struct TEH_KeyStateHandle *ksh = cls;
|
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->start_date = start_date;
|
||||||
gf->end_date = end_date;
|
gf->end_date = end_date;
|
||||||
gf->fees = *fees;
|
gf->fees = *fees;
|
||||||
@ -2517,9 +2492,30 @@ 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 *
|
struct TEH_DenominationKey *
|
||||||
TEH_keys_denomination_by_hash (const struct
|
TEH_keys_denomination_by_hash (
|
||||||
TALER_DenominationHashP *h_denom_pub,
|
const struct TALER_DenominationHashP *h_denom_pub,
|
||||||
struct MHD_Connection *conn,
|
struct MHD_Connection *conn,
|
||||||
MHD_RESULT *mret)
|
MHD_RESULT *mret)
|
||||||
{
|
{
|
||||||
|
@ -82,6 +82,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
|
* Snapshot of the (coin and signing) keys (including private keys) of
|
||||||
* the exchange. There can be multiple instances of this struct, as it is
|
* the exchange. There can be multiple instances of this struct, as it is
|
||||||
@ -129,6 +187,20 @@ void
|
|||||||
TEH_keys_update_states (void);
|
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
|
* 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
|
* must only be used in this thread and only until another key or
|
||||||
@ -141,8 +213,8 @@ TEH_keys_update_states (void);
|
|||||||
* or NULL if @a h_denom_pub could not be found
|
* or NULL if @a h_denom_pub could not be found
|
||||||
*/
|
*/
|
||||||
struct TEH_DenominationKey *
|
struct TEH_DenominationKey *
|
||||||
TEH_keys_denomination_by_hash (const struct
|
TEH_keys_denomination_by_hash (
|
||||||
TALER_DenominationHashP *h_denom_pub,
|
const struct TALER_DenominationHashP *h_denom_pub,
|
||||||
struct MHD_Connection *conn,
|
struct MHD_Connection *conn,
|
||||||
MHD_RESULT *mret);
|
MHD_RESULT *mret);
|
||||||
|
|
||||||
|
@ -636,6 +636,19 @@ TALER_EXCHANGE_get_denomination_key (
|
|||||||
const struct TALER_DenominationPublicKey *pk);
|
const struct TALER_DenominationPublicKey *pk);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Create a copy of a denomination public key.
|
||||||
*
|
*
|
||||||
@ -1281,13 +1294,6 @@ TALER_EXCHANGE_csr_withdraw_cancel (
|
|||||||
|
|
||||||
/* ********************* GET /reserves/$RESERVE_PUB *********************** */
|
/* ********************* GET /reserves/$RESERVE_PUB *********************** */
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief A /reserves/ GET Handle
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGE_ReservesGetHandle;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ways how a reserve's balance may change.
|
* Ways how a reserve's balance may change.
|
||||||
*/
|
*/
|
||||||
@ -1320,7 +1326,7 @@ enum TALER_EXCHANGE_ReserveTransactionType
|
|||||||
/**
|
/**
|
||||||
* @brief Entry in the reserve's transaction history.
|
* @brief Entry in the reserve's transaction history.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_ReserveHistory
|
struct TALER_EXCHANGE_ReserveHistoryEntry
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1453,23 +1459,59 @@ 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
|
* Callbacks of this type are used to serve the result of submitting a
|
||||||
* reserve status request to a exchange.
|
* reserve status request to a exchange.
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param hr HTTP response data
|
* @param rs 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
|
|
||||||
*/
|
*/
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_EXCHANGE_ReservesGetCallback) (
|
(*TALER_EXCHANGE_ReservesGetCallback) (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_ReserveSummary *rs);
|
||||||
const struct TALER_Amount *balance,
|
|
||||||
unsigned int history_length,
|
|
||||||
const struct TALER_EXCHANGE_ReserveHistory *history);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1510,6 +1552,214 @@ TALER_EXCHANGE_reserves_get_cancel (
|
|||||||
struct TALER_EXCHANGE_ReservesGetHandle *rgh);
|
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 *********************** */
|
/* ********************* POST /reserves/$RESERVE_PUB/withdraw *********************** */
|
||||||
|
|
||||||
|
|
||||||
@ -2451,7 +2701,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
const char *currency,
|
const char *currency,
|
||||||
struct TALER_Amount *balance,
|
struct TALER_Amount *balance,
|
||||||
unsigned int history_length,
|
unsigned int history_length,
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory);
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2462,7 +2712,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_free_reserve_history (
|
TALER_EXCHANGE_free_reserve_history (
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory,
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory,
|
||||||
unsigned int len);
|
unsigned int len);
|
||||||
|
|
||||||
|
|
||||||
|
@ -1382,15 +1382,38 @@ TALER_TESTING_cmd_status (const char *label,
|
|||||||
const char *expected_balance,
|
const char *expected_balance,
|
||||||
unsigned int expected_response_code);
|
unsigned int expected_response_code);
|
||||||
|
|
||||||
/**
|
|
||||||
* Index of the deposit value trait of a deposit command.
|
|
||||||
*/
|
|
||||||
#define TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE 0
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index of the deposit fee 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_FEE 1
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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.
|
* 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 (contract_terms, const json_t) \
|
||||||
op (wire_details, const json_t) \
|
op (wire_details, const json_t) \
|
||||||
op (exchange_keys, 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_url, const char *) \
|
||||||
op (exchange_bank_account_url, const char *) \
|
op (exchange_bank_account_url, const char *) \
|
||||||
op (taler_uri, const char *) \
|
op (taler_uri, const char *) \
|
||||||
|
@ -18,7 +18,7 @@ lib_LTLIBRARIES = \
|
|||||||
libtalerexchange.la
|
libtalerexchange.la
|
||||||
|
|
||||||
libtalerexchange_la_LDFLAGS = \
|
libtalerexchange_la_LDFLAGS = \
|
||||||
-version-info 4:0:0 \
|
-version-info 5:0:0 \
|
||||||
-no-undefined
|
-no-undefined
|
||||||
libtalerexchange_la_SOURCES = \
|
libtalerexchange_la_SOURCES = \
|
||||||
exchange_api_auditor_add_denomination.c \
|
exchange_api_auditor_add_denomination.c \
|
||||||
@ -51,6 +51,8 @@ libtalerexchange_la_SOURCES = \
|
|||||||
exchange_api_refreshes_reveal.c \
|
exchange_api_refreshes_reveal.c \
|
||||||
exchange_api_refund.c \
|
exchange_api_refund.c \
|
||||||
exchange_api_reserves_get.c \
|
exchange_api_reserves_get.c \
|
||||||
|
exchange_api_reserves_history.c \
|
||||||
|
exchange_api_reserves_status.c \
|
||||||
exchange_api_transfers_get.c \
|
exchange_api_transfers_get.c \
|
||||||
exchange_api_withdraw.c \
|
exchange_api_withdraw.c \
|
||||||
exchange_api_withdraw2.c \
|
exchange_api_withdraw2.c \
|
||||||
|
@ -34,7 +34,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
const char *currency,
|
const char *currency,
|
||||||
struct TALER_Amount *balance,
|
struct TALER_Amount *balance,
|
||||||
unsigned int history_length,
|
unsigned int history_length,
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory)
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory)
|
||||||
{
|
{
|
||||||
struct GNUNET_HashCode uuid[history_length];
|
struct GNUNET_HashCode uuid[history_length];
|
||||||
unsigned int uuid_off;
|
unsigned int uuid_off;
|
||||||
@ -50,7 +50,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
uuid_off = 0;
|
uuid_off = 0;
|
||||||
for (unsigned int off = 0; off<history_length; off++)
|
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;
|
json_t *transaction;
|
||||||
struct TALER_Amount amount;
|
struct TALER_Amount amount;
|
||||||
const char *type;
|
const char *type;
|
||||||
@ -368,6 +368,11 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check balance = total_in - total_out < withdraw-amount */
|
/* check balance = total_in - total_out < withdraw-amount */
|
||||||
|
if (NULL != balance)
|
||||||
|
{
|
||||||
|
/* 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 >
|
if (0 >
|
||||||
TALER_amount_subtract (balance,
|
TALER_amount_subtract (balance,
|
||||||
&total_in,
|
&total_in,
|
||||||
@ -377,13 +382,14 @@ TALER_EXCHANGE_parse_reserve_history (
|
|||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_free_reserve_history (
|
TALER_EXCHANGE_free_reserve_history (
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory,
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory,
|
||||||
unsigned int len)
|
unsigned int len)
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i<len; i++)
|
for (unsigned int i = 0; i<len; i++)
|
||||||
|
@ -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 *
|
struct TALER_EXCHANGE_DenomPublicKey *
|
||||||
TALER_EXCHANGE_copy_denomination_key (
|
TALER_EXCHANGE_copy_denomination_key (
|
||||||
const struct TALER_EXCHANGE_DenomPublicKey *key)
|
const struct TALER_EXCHANGE_DenomPublicKey *key)
|
||||||
|
@ -83,19 +83,15 @@ static enum GNUNET_GenericReturnValue
|
|||||||
handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
||||||
const json_t *j)
|
const json_t *j)
|
||||||
{
|
{
|
||||||
json_t *history;
|
struct TALER_EXCHANGE_ReserveSummary rs = {
|
||||||
unsigned int len;
|
.hr.reply = j,
|
||||||
struct TALER_Amount balance;
|
.hr.http_status = MHD_HTTP_OK
|
||||||
struct TALER_Amount balance_from_history;
|
};
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
TALER_JSON_spec_amount_any ("balance",
|
TALER_JSON_spec_amount_any ("balance",
|
||||||
&balance),
|
&rs.details.ok.balance),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
|
||||||
.reply = j,
|
|
||||||
.http_status = MHD_HTTP_OK
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_JSON_parse (j,
|
GNUNET_JSON_parse (j,
|
||||||
@ -106,55 +102,9 @@ handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
|||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return GNUNET_SYSERR;
|
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,
|
rgh->cb (rgh->cb_cls,
|
||||||
&hr,
|
&rs);
|
||||||
&balance,
|
|
||||||
len,
|
|
||||||
rhistory);
|
|
||||||
rgh->cb = NULL;
|
rgh->cb = NULL;
|
||||||
}
|
|
||||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
|
||||||
len);
|
|
||||||
}
|
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,61 +124,59 @@ handle_reserves_get_finished (void *cls,
|
|||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
|
struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
struct TALER_EXCHANGE_ReserveSummary rs = {
|
||||||
.reply = j,
|
.hr.reply = j,
|
||||||
.http_status = (unsigned int) response_code
|
.hr.http_status = (unsigned int) response_code
|
||||||
};
|
};
|
||||||
|
|
||||||
rgh->job = NULL;
|
rgh->job = NULL;
|
||||||
switch (response_code)
|
switch (response_code)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
handle_reserves_get_ok (rgh,
|
handle_reserves_get_ok (rgh,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
hr.http_status = 0;
|
rs.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
/* Nothing really to verify, this should never
|
/* Nothing really to verify, this should never
|
||||||
happen, we should pass the JSON reply to the application */
|
happen, we should pass the JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
/* Server had an internal issue; we should retry, but this API
|
/* Server had an internal issue; we should retry, but this API
|
||||||
leaves this to the application */
|
leaves this to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* unexpected response code */
|
/* unexpected response code */
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected response code %u/%d for reserves get\n",
|
"Unexpected response code %u/%d for reserves get\n",
|
||||||
(unsigned int) response_code,
|
(unsigned int) response_code,
|
||||||
(int) hr.ec);
|
(int) rs.hr.ec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL != rgh->cb)
|
if (NULL != rgh->cb)
|
||||||
{
|
{
|
||||||
rgh->cb (rgh->cb_cls,
|
rgh->cb (rgh->cb_cls,
|
||||||
&hr,
|
&rs);
|
||||||
NULL,
|
|
||||||
0, NULL);
|
|
||||||
rgh->cb = NULL;
|
rgh->cb = NULL;
|
||||||
}
|
}
|
||||||
TALER_EXCHANGE_reserves_get_cancel (rgh);
|
TALER_EXCHANGE_reserves_get_cancel (rgh);
|
||||||
|
@ -81,35 +81,32 @@ struct TALER_EXCHANGE_ReservesHistoryHandle
|
|||||||
* We received an #MHD_HTTP_OK history code. Handle the JSON
|
* We received an #MHD_HTTP_OK history code. Handle the JSON
|
||||||
* response.
|
* response.
|
||||||
*
|
*
|
||||||
* @param rgh handle of the request
|
* @param rsh handle of the request
|
||||||
* @param j JSON response
|
* @param j JSON response
|
||||||
* @return #GNUNET_OK on success
|
* @return #GNUNET_OK on success
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_GenericReturnValue
|
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)
|
const json_t *j)
|
||||||
{
|
{
|
||||||
json_t *history;
|
json_t *history;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
bool kyc_ok;
|
struct TALER_Amount history_balance;
|
||||||
bool kyc_required;
|
struct TALER_EXCHANGE_ReserveHistory rs = {
|
||||||
struct TALER_Amount balance;
|
.hr.reply = j,
|
||||||
struct TALER_Amount balance_from_history;
|
.hr.http_status = MHD_HTTP_OK
|
||||||
|
};
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
TALER_JSON_spec_amount_any ("balance",
|
TALER_JSON_spec_amount_any ("balance",
|
||||||
&balance),
|
&rs.details.ok.balance),
|
||||||
GNUNET_JSON_spec_bool ("kyc_passed",
|
GNUNET_JSON_spec_bool ("kyc_passed",
|
||||||
&kyc_ok),
|
&rs.details.ok.kyc_ok),
|
||||||
GNUNET_JSON_spec_bool ("kyc_required",
|
GNUNET_JSON_spec_bool ("kyc_required",
|
||||||
&kyc_required),
|
&rs.details.ok.kyc_required),
|
||||||
GNUNET_JSON_spec_json ("history",
|
GNUNET_JSON_spec_json ("history",
|
||||||
&history),
|
&history),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
|
||||||
.reply = j,
|
|
||||||
.http_history = MHD_HTTP_OK
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_JSON_parse (j,
|
GNUNET_JSON_parse (j,
|
||||||
@ -122,16 +119,19 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
|
|||||||
}
|
}
|
||||||
len = json_array_size (history);
|
len = json_array_size (history);
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory;
|
||||||
|
|
||||||
rhistory = GNUNET_new_array (len,
|
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 !=
|
if (GNUNET_OK !=
|
||||||
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
TALER_EXCHANGE_parse_reserve_history (rsh->exchange,
|
||||||
history,
|
history,
|
||||||
&rgh->reserve_pub,
|
&rsh->reserve_pub,
|
||||||
balance.currency,
|
rs.details.ok.balance.currency,
|
||||||
&balance_from_history,
|
&history_balance,
|
||||||
len,
|
len,
|
||||||
rhistory))
|
rhistory))
|
||||||
{
|
{
|
||||||
@ -141,25 +141,13 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
|
|||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
if (0 !=
|
if (NULL != rsh->cb)
|
||||||
TALER_amount_cmp (&balance_from_history,
|
|
||||||
&balance))
|
|
||||||
{
|
{
|
||||||
/* exchange cannot add up balances!? */
|
rs.details.ok.history = rhistory;
|
||||||
GNUNET_break_op (0);
|
rs.details.ok.history_len = len;
|
||||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
rsh->cb (rsh->cb_cls,
|
||||||
len);
|
&rs);
|
||||||
GNUNET_JSON_parse_free (spec);
|
rsh->cb = NULL;
|
||||||
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,
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
len);
|
len);
|
||||||
@ -182,75 +170,72 @@ handle_reserves_history_finished (void *cls,
|
|||||||
long response_code,
|
long response_code,
|
||||||
const void *response)
|
const void *response)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rgh = cls;
|
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
struct TALER_EXCHANGE_ReserveHistory rs = {
|
||||||
.reply = j,
|
.hr.reply = j,
|
||||||
.http_history = (unsigned int) response_code
|
.hr.http_status = (unsigned int) response_code
|
||||||
};
|
};
|
||||||
|
|
||||||
rgh->job = NULL;
|
rsh->job = NULL;
|
||||||
switch (response_code)
|
switch (response_code)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
handle_reserves_history_ok (rgh,
|
handle_reserves_history_ok (rsh,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
hr.http_history = 0;
|
rs.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
hr.ec = TALER_JSON_history_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_history_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_FORBIDDEN:
|
case MHD_HTTP_FORBIDDEN:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
hr.ec = TALER_JSON_history_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_history_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
/* Nothing really to verify, this should never
|
/* Nothing really to verify, this should never
|
||||||
happen, we should pass the JSON reply to the application */
|
happen, we should pass the JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_history_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_history_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
/* Server had an internal issue; we should retry, but this API
|
/* Server had an internal issue; we should retry, but this API
|
||||||
leaves this to the application */
|
leaves this to the application */
|
||||||
hr.ec = TALER_JSON_history_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_history_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* unexpected response code */
|
/* unexpected response code */
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.ec = TALER_JSON_history_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_history_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected response code %u/%d for reserves history\n",
|
"Unexpected response code %u/%d for reserves history\n",
|
||||||
(unsigned int) response_code,
|
(unsigned int) response_code,
|
||||||
(int) hr.ec);
|
(int) rs.hr.ec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL != rgh->cb)
|
if (NULL != rsh->cb)
|
||||||
{
|
{
|
||||||
rgh->cb (rgh->cb_cls,
|
rsh->cb (rsh->cb_cls,
|
||||||
&hr,
|
&rs);
|
||||||
NULL,
|
rsh->cb = NULL;
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
rgh->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,
|
TALER_EXCHANGE_ReservesHistoryCallback cb,
|
||||||
void *cb_cls)
|
void *cb_cls)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rgh;
|
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh;
|
||||||
struct GNUNET_CURL_Context *ctx;
|
struct GNUNET_CURL_Context *ctx;
|
||||||
CURL *eh;
|
CURL *eh;
|
||||||
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
||||||
const struct TALER_Amount *history_fee;
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
const struct TALER_EXCHANGE_Keys *keys;
|
|
||||||
struct GNUNET_TIME_Timestamp ts
|
struct GNUNET_TIME_Timestamp ts
|
||||||
= GNUNET_TIME_timestamp_get ();
|
= GNUNET_TIME_timestamp_get ();
|
||||||
|
|
||||||
@ -276,22 +260,19 @@ TALER_EXCHANGE_reserves_history (
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
keys = TALER_EXCHANGE_get_keys (exchange);
|
rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle);
|
||||||
// FIXME: extract history_fee from keys!
|
rsh->exchange = exchange;
|
||||||
history_fee = FIXME;
|
rsh->cb = cb;
|
||||||
rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle);
|
rsh->cb_cls = cb_cls;
|
||||||
rgh->exchange = exchange;
|
|
||||||
rgh->cb = cb;
|
|
||||||
rgh->cb_cls = cb_cls;
|
|
||||||
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
|
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 pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||||
char *end;
|
char *end;
|
||||||
|
|
||||||
end = GNUNET_STRINGS_data_to_string (
|
end = GNUNET_STRINGS_data_to_string (
|
||||||
&rgh->reserve_pub,
|
&rsh->reserve_pub,
|
||||||
sizeof (rgh->reserve_pub),
|
sizeof (rsh->reserve_pub),
|
||||||
pub_str,
|
pub_str,
|
||||||
sizeof (pub_str));
|
sizeof (pub_str));
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
@ -300,68 +281,67 @@ TALER_EXCHANGE_reserves_history (
|
|||||||
"/reserves/%s/history",
|
"/reserves/%s/history",
|
||||||
pub_str);
|
pub_str);
|
||||||
}
|
}
|
||||||
rgh->url = TEAH_path_to_url (exchange,
|
rsh->url = TEAH_path_to_url (exchange,
|
||||||
arg_str);
|
arg_str);
|
||||||
if (NULL == rgh->url)
|
if (NULL == rsh->url)
|
||||||
{
|
{
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
eh = TALER_EXCHANGE_curl_easy_history_ (rgh->url);
|
eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url);
|
||||||
if (NULL == eh)
|
if (NULL == eh)
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
TALER_wallet_reserve_history_sign (ts,
|
TALER_wallet_reserve_history_sign (ts,
|
||||||
history_fee,
|
NULL, /* FIXME: fee! */
|
||||||
reserve_priv,
|
reserve_priv,
|
||||||
&reserve_sig);
|
&reserve_sig);
|
||||||
{
|
{
|
||||||
json_t *history_obj = GNUNET_JSON_PACK (
|
json_t *history_obj = GNUNET_JSON_PACK (
|
||||||
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
||||||
&ts),
|
ts),
|
||||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
||||||
&reserve_sig));
|
&reserve_sig));
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_curl_easy_post (&rgh->post_ctx,
|
TALER_curl_easy_post (&rsh->post_ctx,
|
||||||
eh,
|
eh,
|
||||||
history_obj))
|
history_obj))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
curl_easy_cleanup (eh);
|
curl_easy_cleanup (eh);
|
||||||
json_decref (history_obj);
|
json_decref (history_obj);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
json_decref (history_obj);
|
json_decref (history_obj);
|
||||||
}
|
}
|
||||||
ctx = TEAH_handle_to_context (exchange);
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
rgh->job = GNUNET_CURL_job_add (ctx,
|
rsh->job = GNUNET_CURL_job_add (ctx,
|
||||||
eh,
|
eh,
|
||||||
&handle_reserves_history_finished,
|
&handle_reserves_history_finished,
|
||||||
rgh);
|
rsh);
|
||||||
return rgh;
|
return rsh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_reserves_history_cancel (
|
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);
|
GNUNET_CURL_job_cancel (rsh->job);
|
||||||
rgh->job = NULL;
|
rsh->job = NULL;
|
||||||
}
|
}
|
||||||
TALER_curl_easy_post_finished (&rgh->post_ctx);
|
TALER_curl_easy_post_finished (&rsh->post_ctx);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,35 +81,31 @@ struct TALER_EXCHANGE_ReservesStatusHandle
|
|||||||
* We received an #MHD_HTTP_OK status code. Handle the JSON
|
* We received an #MHD_HTTP_OK status code. Handle the JSON
|
||||||
* response.
|
* response.
|
||||||
*
|
*
|
||||||
* @param rgh handle of the request
|
* @param rsh handle of the request
|
||||||
* @param j JSON response
|
* @param j JSON response
|
||||||
* @return #GNUNET_OK on success
|
* @return #GNUNET_OK on success
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_GenericReturnValue
|
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)
|
const json_t *j)
|
||||||
{
|
{
|
||||||
json_t *history;
|
json_t *history;
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
bool kyc_ok;
|
struct TALER_EXCHANGE_ReserveStatus rs = {
|
||||||
bool kyc_required;
|
.hr.reply = j,
|
||||||
struct TALER_Amount balance;
|
.hr.http_status = MHD_HTTP_OK
|
||||||
struct TALER_Amount balance_from_history;
|
};
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
TALER_JSON_spec_amount_any ("balance",
|
TALER_JSON_spec_amount_any ("balance",
|
||||||
&balance),
|
&rs.details.ok.balance),
|
||||||
GNUNET_JSON_spec_bool ("kyc_passed",
|
GNUNET_JSON_spec_bool ("kyc_passed",
|
||||||
&kyc_ok),
|
&rs.details.ok.kyc_ok),
|
||||||
GNUNET_JSON_spec_bool ("kyc_required",
|
GNUNET_JSON_spec_bool ("kyc_required",
|
||||||
&kyc_required),
|
&rs.details.ok.kyc_required),
|
||||||
GNUNET_JSON_spec_json ("history",
|
GNUNET_JSON_spec_json ("history",
|
||||||
&history),
|
&history),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
|
||||||
.reply = j,
|
|
||||||
.http_status = MHD_HTTP_OK
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_JSON_parse (j,
|
GNUNET_JSON_parse (j,
|
||||||
@ -122,16 +118,16 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
|
|||||||
}
|
}
|
||||||
len = json_array_size (history);
|
len = json_array_size (history);
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory;
|
||||||
|
|
||||||
rhistory = GNUNET_new_array (len,
|
rhistory = GNUNET_new_array (len,
|
||||||
struct TALER_EXCHANGE_ReserveHistory);
|
struct TALER_EXCHANGE_ReserveHistoryEntry);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
TALER_EXCHANGE_parse_reserve_history (rsh->exchange,
|
||||||
history,
|
history,
|
||||||
&rgh->reserve_pub,
|
&rsh->reserve_pub,
|
||||||
balance.currency,
|
rs.details.ok.balance.currency,
|
||||||
&balance_from_history,
|
NULL,
|
||||||
len,
|
len,
|
||||||
rhistory))
|
rhistory))
|
||||||
{
|
{
|
||||||
@ -141,27 +137,13 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
|
|||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
// FIXME: status history is allowed to be
|
if (NULL != rsh->cb)
|
||||||
// partial, so this is NOT ok...
|
|
||||||
if (0 !=
|
|
||||||
TALER_amount_cmp (&balance_from_history,
|
|
||||||
&balance))
|
|
||||||
{
|
{
|
||||||
/* exchange cannot add up balances!? */
|
rs.details.ok.history = rhistory;
|
||||||
GNUNET_break_op (0);
|
rs.details.ok.history_len = len;
|
||||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
rsh->cb (rsh->cb_cls,
|
||||||
len);
|
&rs);
|
||||||
GNUNET_JSON_parse_free (spec);
|
rsh->cb = NULL;
|
||||||
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,
|
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||||
len);
|
len);
|
||||||
@ -184,75 +166,72 @@ handle_reserves_status_finished (void *cls,
|
|||||||
long response_code,
|
long response_code,
|
||||||
const void *response)
|
const void *response)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReservesStatusHandle *rgh = cls;
|
struct TALER_EXCHANGE_ReservesStatusHandle *rsh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
struct TALER_EXCHANGE_ReserveStatus rs = {
|
||||||
.reply = j,
|
.hr.reply = j,
|
||||||
.http_status = (unsigned int) response_code
|
.hr.http_status = (unsigned int) response_code
|
||||||
};
|
};
|
||||||
|
|
||||||
rgh->job = NULL;
|
rsh->job = NULL;
|
||||||
switch (response_code)
|
switch (response_code)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
handle_reserves_status_ok (rgh,
|
handle_reserves_status_ok (rsh,
|
||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
hr.http_status = 0;
|
rs.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
hr.ec = TALER_JSON_status_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_status_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_FORBIDDEN:
|
case MHD_HTTP_FORBIDDEN:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
hr.ec = TALER_JSON_status_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_status_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
/* Nothing really to verify, this should never
|
/* Nothing really to verify, this should never
|
||||||
happen, we should pass the JSON reply to the application */
|
happen, we should pass the JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_status_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_status_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
/* Server had an internal issue; we should retry, but this API
|
/* Server had an internal issue; we should retry, but this API
|
||||||
leaves this to the application */
|
leaves this to the application */
|
||||||
hr.ec = TALER_JSON_status_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_status_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* unexpected response code */
|
/* unexpected response code */
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.ec = TALER_JSON_status_error_code (j);
|
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_status_error_hint (j);
|
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected response code %u/%d for reserves status\n",
|
"Unexpected response code %u/%d for reserves status\n",
|
||||||
(unsigned int) response_code,
|
(unsigned int) response_code,
|
||||||
(int) hr.ec);
|
(int) rs.hr.ec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL != rgh->cb)
|
if (NULL != rsh->cb)
|
||||||
{
|
{
|
||||||
rgh->cb (rgh->cb_cls,
|
rsh->cb (rsh->cb_cls,
|
||||||
&hr,
|
&rs);
|
||||||
NULL,
|
rsh->cb = NULL;
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
rgh->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,
|
TALER_EXCHANGE_ReservesStatusCallback cb,
|
||||||
void *cb_cls)
|
void *cb_cls)
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_ReservesStatusHandle *rgh;
|
struct TALER_EXCHANGE_ReservesStatusHandle *rsh;
|
||||||
struct GNUNET_CURL_Context *ctx;
|
struct GNUNET_CURL_Context *ctx;
|
||||||
CURL *eh;
|
CURL *eh;
|
||||||
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
||||||
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
struct GNUNET_TIME_Timestamp ts
|
struct GNUNET_TIME_Timestamp ts
|
||||||
= GNUNET_TIME_timestamp_get ();
|
= GNUNET_TIME_timestamp_get ();
|
||||||
|
|
||||||
@ -276,19 +256,19 @@ TALER_EXCHANGE_reserves_status (
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
|
rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
|
||||||
rgh->exchange = exchange;
|
rsh->exchange = exchange;
|
||||||
rgh->cb = cb;
|
rsh->cb = cb;
|
||||||
rgh->cb_cls = cb_cls;
|
rsh->cb_cls = cb_cls;
|
||||||
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
|
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 pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||||
char *end;
|
char *end;
|
||||||
|
|
||||||
end = GNUNET_STRINGS_data_to_string (
|
end = GNUNET_STRINGS_data_to_string (
|
||||||
&rgh->reserve_pub,
|
&rsh->reserve_pub,
|
||||||
sizeof (rgh->reserve_pub),
|
sizeof (rsh->reserve_pub),
|
||||||
pub_str,
|
pub_str,
|
||||||
sizeof (pub_str));
|
sizeof (pub_str));
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
@ -297,19 +277,19 @@ TALER_EXCHANGE_reserves_status (
|
|||||||
"/reserves/%s/status",
|
"/reserves/%s/status",
|
||||||
pub_str);
|
pub_str);
|
||||||
}
|
}
|
||||||
rgh->url = TEAH_path_to_url (exchange,
|
rsh->url = TEAH_path_to_url (exchange,
|
||||||
arg_str);
|
arg_str);
|
||||||
if (NULL == rgh->url)
|
if (NULL == rsh->url)
|
||||||
{
|
{
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
eh = TALER_EXCHANGE_curl_easy_status_ (rgh->url);
|
eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url);
|
||||||
if (NULL == eh)
|
if (NULL == eh)
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
TALER_wallet_reserve_status_sign (ts,
|
TALER_wallet_reserve_status_sign (ts,
|
||||||
@ -318,46 +298,45 @@ TALER_EXCHANGE_reserves_status (
|
|||||||
{
|
{
|
||||||
json_t *status_obj = GNUNET_JSON_PACK (
|
json_t *status_obj = GNUNET_JSON_PACK (
|
||||||
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
||||||
&ts),
|
ts),
|
||||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
||||||
&reserve_sig));
|
&reserve_sig));
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_curl_easy_post (&rgh->post_ctx,
|
TALER_curl_easy_post (&rsh->post_ctx,
|
||||||
eh,
|
eh,
|
||||||
status_obj))
|
status_obj))
|
||||||
)
|
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
curl_easy_cleanup (eh);
|
curl_easy_cleanup (eh);
|
||||||
json_decref (status_obj);
|
json_decref (status_obj);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
json_decref (status_obj);
|
json_decref (status_obj);
|
||||||
}
|
}
|
||||||
ctx = TEAH_handle_to_context (exchange);
|
ctx = TEAH_handle_to_context (exchange);
|
||||||
rgh->job = GNUNET_CURL_job_add (ctx,
|
rsh->job = GNUNET_CURL_job_add (ctx,
|
||||||
eh,
|
eh,
|
||||||
&handle_reserves_status_finished,
|
&handle_reserves_status_finished,
|
||||||
rgh);
|
rsh);
|
||||||
return rgh;
|
return rsh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TALER_EXCHANGE_reserves_status_cancel (
|
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);
|
GNUNET_CURL_job_cancel (rsh->job);
|
||||||
rgh->job = NULL;
|
rsh->job = NULL;
|
||||||
}
|
}
|
||||||
TALER_curl_easy_post_finished (&rgh->post_ctx);
|
TALER_curl_easy_post_finished (&rsh->post_ctx);
|
||||||
GNUNET_free (rgh->url);
|
GNUNET_free (rsh->url);
|
||||||
GNUNET_free (rgh);
|
GNUNET_free (rsh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,14 +177,14 @@ reserve_withdraw_payment_required (
|
|||||||
total incoming and outgoing amounts */
|
total incoming and outgoing amounts */
|
||||||
len = json_array_size (history);
|
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
|
/* 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
|
not fit on the stack. Use "GNUNET_malloc_large" as a malicious
|
||||||
exchange may theoretically try to crash us by giving a history
|
exchange may theoretically try to crash us by giving a history
|
||||||
that does not fit into our memory. */
|
that does not fit into our memory. */
|
||||||
rhistory = GNUNET_malloc_large (
|
rhistory = GNUNET_malloc_large (
|
||||||
sizeof (struct TALER_EXCHANGE_ReserveHistory)
|
sizeof (struct TALER_EXCHANGE_ReserveHistoryEntry)
|
||||||
* len);
|
* len);
|
||||||
if (NULL == rhistory)
|
if (NULL == rhistory)
|
||||||
{
|
{
|
||||||
|
@ -74,6 +74,8 @@ libtalertesting_la_SOURCES = \
|
|||||||
testing_api_cmd_recoup_refresh.c \
|
testing_api_cmd_recoup_refresh.c \
|
||||||
testing_api_cmd_refund.c \
|
testing_api_cmd_refund.c \
|
||||||
testing_api_cmd_refresh.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.c \
|
||||||
testing_api_cmd_revoke_denom_key.c \
|
testing_api_cmd_revoke_denom_key.c \
|
||||||
testing_api_cmd_revoke_sign_key.c \
|
testing_api_cmd_revoke_sign_key.c \
|
||||||
|
@ -107,7 +107,7 @@ struct AdminAddIncomingState
|
|||||||
* the "sender_url" field is set to a 'const char *' and
|
* the "sender_url" field is set to a 'const char *' and
|
||||||
* MUST NOT be free()'ed.
|
* 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.
|
* Set to the wire transfer's unique ID.
|
||||||
|
@ -49,7 +49,7 @@ struct CloserState
|
|||||||
* expect_close is true. Will be of type
|
* expect_close is true. Will be of type
|
||||||
* #TALER_EXCHANGE_RTT_RESERVE_CLOSED.
|
* #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
|
* If the closer filled a reserve (@e expect_close is set), this is set to
|
||||||
|
@ -62,7 +62,7 @@ struct RecoupState
|
|||||||
* Reserve history entry, set if this recoup actually filled up a reserve.
|
* Reserve history entry, set if this recoup actually filled up a reserve.
|
||||||
* Otherwise `reserve_history.type` will be zero.
|
* Otherwise `reserve_history.type` will be zero.
|
||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_ReserveHistory reserve_history;
|
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
413
src/testing/testing_api_cmd_reserve_history.c
Normal file
413
src/testing/testing_api_cmd_reserve_history.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
413
src/testing/testing_api_cmd_reserve_status.c
Normal file
413
src/testing/testing_api_cmd_reserve_status.c
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file testing/testing_api_cmd_status.c
|
* @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
|
* @author Marcello Stanisci
|
||||||
*/
|
*/
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
@ -65,252 +65,52 @@ 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
|
* Check that the reserve balance and HTTP response code are
|
||||||
* both acceptable.
|
* both acceptable.
|
||||||
*
|
*
|
||||||
* @param cls closure.
|
* @param cls closure.
|
||||||
* @param hr HTTP response details
|
* @param rs 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.
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
reserve_status_cb (void *cls,
|
reserve_status_cb (void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_ReserveSummary *rs)
|
||||||
const struct TALER_Amount *balance,
|
|
||||||
unsigned int history_length,
|
|
||||||
const struct TALER_EXCHANGE_ReserveHistory *history)
|
|
||||||
{
|
{
|
||||||
struct StatusState *ss = cls;
|
struct StatusState *ss = cls;
|
||||||
struct TALER_TESTING_Interpreter *is = ss->is;
|
struct TALER_TESTING_Interpreter *is = ss->is;
|
||||||
struct TALER_Amount eb;
|
struct TALER_Amount eb;
|
||||||
|
|
||||||
ss->rsh = NULL;
|
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,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected HTTP response code: %d in %s:%u\n",
|
"Unexpected HTTP response code: %d in %s:%u\n",
|
||||||
hr->http_status,
|
rs->hr.http_status,
|
||||||
__FILE__,
|
__FILE__,
|
||||||
__LINE__);
|
__LINE__);
|
||||||
json_dumpf (hr->reply,
|
json_dumpf (rs->hr.reply,
|
||||||
stderr,
|
stderr,
|
||||||
0);
|
0);
|
||||||
TALER_TESTING_interpreter_fail (ss->is);
|
TALER_TESTING_interpreter_fail (ss->is);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (MHD_HTTP_OK != ss->expected_response_code)
|
||||||
|
{
|
||||||
|
TALER_TESTING_interpreter_next (is);
|
||||||
|
return;
|
||||||
|
}
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_string_to_amount (ss->expected_balance,
|
TALER_string_to_amount (ss->expected_balance,
|
||||||
&eb));
|
&eb));
|
||||||
|
|
||||||
if (0 != TALER_amount_cmp (&eb,
|
if (0 != TALER_amount_cmp (&eb,
|
||||||
balance))
|
&rs->details.ok.balance))
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected amount in reserve: %s\n",
|
"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);
|
TALER_TESTING_interpreter_fail (ss->is);
|
||||||
return;
|
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);
|
TALER_TESTING_interpreter_next (is);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ struct WithdrawState
|
|||||||
* Reserve history entry that corresponds to this operation.
|
* Reserve history entry that corresponds to this operation.
|
||||||
* Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
|
* 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).
|
* Withdraw handle (while operation is running).
|
||||||
|
Loading…
Reference in New Issue
Block a user