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
|
||||
{
|
||||
|
||||
@ -324,12 +299,12 @@ struct TEH_KeyStateHandle
|
||||
/**
|
||||
* Head of DLL of our global fees.
|
||||
*/
|
||||
struct GlobalFee *gf_head;
|
||||
struct TEH_GlobalFee *gf_head;
|
||||
|
||||
/**
|
||||
* Tail of DLL of our global fees.
|
||||
*/
|
||||
struct GlobalFee *gf_tail;
|
||||
struct TEH_GlobalFee *gf_tail;
|
||||
|
||||
/**
|
||||
* json array with the auditors of this exchange. Contains exactly
|
||||
@ -1215,7 +1190,7 @@ static void
|
||||
destroy_key_state (struct TEH_KeyStateHandle *ksh,
|
||||
bool free_helper)
|
||||
{
|
||||
struct GlobalFee *gf;
|
||||
struct TEH_GlobalFee *gf;
|
||||
|
||||
clear_response_cache (ksh);
|
||||
while (NULL != (gf = ksh->gf_head))
|
||||
@ -2282,9 +2257,9 @@ global_fee_info_cb (
|
||||
const struct TALER_MasterSignatureP *master_sig)
|
||||
{
|
||||
struct TEH_KeyStateHandle *ksh = cls;
|
||||
struct GlobalFee *gf;
|
||||
struct TEH_GlobalFee *gf;
|
||||
|
||||
gf = GNUNET_new (struct GlobalFee);
|
||||
gf = GNUNET_new (struct TEH_GlobalFee);
|
||||
gf->start_date = start_date;
|
||||
gf->end_date = end_date;
|
||||
gf->fees = *fees;
|
||||
@ -2517,11 +2492,32 @@ TEH_keys_get_state (void)
|
||||
}
|
||||
|
||||
|
||||
const struct TEH_GlobalFee *
|
||||
TEH_keys_global_fee_by_time (
|
||||
struct TEH_KeyStateHandle *ksh,
|
||||
struct GNUNET_TIME_Timestamp ts)
|
||||
{
|
||||
for (const struct TEH_GlobalFee *gf = ksh->gf_head;
|
||||
NULL != gf;
|
||||
gf = gf->next)
|
||||
{
|
||||
if (GNUNET_TIME_timestamp_cmp (ts,
|
||||
>=,
|
||||
gf->start_date) &&
|
||||
GNUNET_TIME_timestamp_cmp (ts,
|
||||
<,
|
||||
gf->end_date))
|
||||
return gf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct TEH_DenominationKey *
|
||||
TEH_keys_denomination_by_hash (const struct
|
||||
TALER_DenominationHashP *h_denom_pub,
|
||||
struct MHD_Connection *conn,
|
||||
MHD_RESULT *mret)
|
||||
TEH_keys_denomination_by_hash (
|
||||
const struct TALER_DenominationHashP *h_denom_pub,
|
||||
struct MHD_Connection *conn,
|
||||
MHD_RESULT *mret)
|
||||
{
|
||||
struct TEH_KeyStateHandle *ksh;
|
||||
|
||||
|
@ -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
|
||||
* the exchange. There can be multiple instances of this struct, as it is
|
||||
@ -129,6 +187,20 @@ 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
|
||||
* must only be used in this thread and only until another key or
|
||||
@ -141,10 +213,10 @@ TEH_keys_update_states (void);
|
||||
* or NULL if @a h_denom_pub could not be found
|
||||
*/
|
||||
struct TEH_DenominationKey *
|
||||
TEH_keys_denomination_by_hash (const struct
|
||||
TALER_DenominationHashP *h_denom_pub,
|
||||
struct MHD_Connection *conn,
|
||||
MHD_RESULT *mret);
|
||||
TEH_keys_denomination_by_hash (
|
||||
const struct TALER_DenominationHashP *h_denom_pub,
|
||||
struct MHD_Connection *conn,
|
||||
MHD_RESULT *mret);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -636,6 +636,19 @@ TALER_EXCHANGE_get_denomination_key (
|
||||
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.
|
||||
*
|
||||
@ -1281,13 +1294,6 @@ TALER_EXCHANGE_csr_withdraw_cancel (
|
||||
|
||||
/* ********************* GET /reserves/$RESERVE_PUB *********************** */
|
||||
|
||||
|
||||
/**
|
||||
* @brief A /reserves/ GET Handle
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReservesGetHandle;
|
||||
|
||||
|
||||
/**
|
||||
* Ways how a reserve's balance may change.
|
||||
*/
|
||||
@ -1320,7 +1326,7 @@ enum TALER_EXCHANGE_ReserveTransactionType
|
||||
/**
|
||||
* @brief Entry in the reserve's transaction history.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry
|
||||
{
|
||||
|
||||
/**
|
||||
@ -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
|
||||
* reserve status request to a exchange.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param hr HTTP response data
|
||||
* @param balance current balance in the reserve, NULL on error
|
||||
* @param history_length number of entries in the transaction history, 0 on error
|
||||
* @param history detailed transaction history, NULL on error
|
||||
* @param rs HTTP response data
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_EXCHANGE_ReservesGetCallback) (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_Amount *balance,
|
||||
unsigned int history_length,
|
||||
const struct TALER_EXCHANGE_ReserveHistory *history);
|
||||
const struct TALER_EXCHANGE_ReserveSummary *rs);
|
||||
|
||||
|
||||
/**
|
||||
@ -1510,6 +1552,214 @@ TALER_EXCHANGE_reserves_get_cancel (
|
||||
struct TALER_EXCHANGE_ReservesGetHandle *rgh);
|
||||
|
||||
|
||||
/**
|
||||
* @brief A /reserves/$RID/status Handle
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reserve status details.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveStatus
|
||||
{
|
||||
|
||||
/**
|
||||
* High-level HTTP response details.
|
||||
*/
|
||||
struct TALER_EXCHANGE_HttpResponse hr;
|
||||
|
||||
/**
|
||||
* Details depending on @e hr.http_status.
|
||||
*/
|
||||
union
|
||||
{
|
||||
|
||||
/**
|
||||
* Information returned on success, if
|
||||
* @e hr.http_status is #MHD_HTTP_OK
|
||||
*/
|
||||
struct
|
||||
{
|
||||
|
||||
/**
|
||||
* Reserve balance.
|
||||
*/
|
||||
struct TALER_Amount balance;
|
||||
|
||||
/**
|
||||
* Reserve history.
|
||||
*/
|
||||
const struct TALER_EXCHANGE_ReserveHistoryEntry *history;
|
||||
|
||||
/**
|
||||
* Length of the @e history array.
|
||||
*/
|
||||
unsigned int history_len;
|
||||
|
||||
/**
|
||||
* KYC passed?
|
||||
*/
|
||||
bool kyc_ok;
|
||||
|
||||
/**
|
||||
* KYC required to withdraw?
|
||||
*/
|
||||
bool kyc_required;
|
||||
|
||||
} ok;
|
||||
|
||||
} details;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callbacks of this type are used to serve the result of submitting a
|
||||
* reserve status request to a exchange.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param rs HTTP response data
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_EXCHANGE_ReservesStatusCallback) (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_ReserveStatus *rs);
|
||||
|
||||
|
||||
/**
|
||||
* Submit a request to obtain the reserve status.
|
||||
*
|
||||
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||
* @param reserve_priv private key of the reserve to inspect
|
||||
* @param cb the callback to call when a reply for this request is available
|
||||
* @param cb_cls closure for the above callback
|
||||
* @return a handle for this request; NULL if the inputs are invalid (i.e.
|
||||
* signatures fail to verify). In this case, the callback is not called.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *
|
||||
TALER_EXCHANGE_reserves_status (
|
||||
struct TALER_EXCHANGE_Handle *exchange,
|
||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||
TALER_EXCHANGE_ReservesStatusCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a reserve status request. This function cannot be used
|
||||
* on a request handle if a response is already served for it.
|
||||
*
|
||||
* @param rsh the reserve request handle
|
||||
*/
|
||||
void
|
||||
TALER_EXCHANGE_reserves_status_cancel (
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rsh);
|
||||
|
||||
|
||||
/**
|
||||
* @brief A /reserves/$RID/history Handle
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Reserve history details.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory
|
||||
{
|
||||
|
||||
/**
|
||||
* High-level HTTP response details.
|
||||
*/
|
||||
struct TALER_EXCHANGE_HttpResponse hr;
|
||||
|
||||
/**
|
||||
* Details depending on @e hr.http_status.
|
||||
*/
|
||||
union
|
||||
{
|
||||
|
||||
/**
|
||||
* Information returned on success, if
|
||||
* @e hr.http_status is #MHD_HTTP_OK
|
||||
*/
|
||||
struct
|
||||
{
|
||||
|
||||
/**
|
||||
* Reserve balance.
|
||||
*/
|
||||
struct TALER_Amount balance;
|
||||
|
||||
/**
|
||||
* Reserve history.
|
||||
*/
|
||||
const struct TALER_EXCHANGE_ReserveHistoryEntry *history;
|
||||
|
||||
/**
|
||||
* Length of the @e history array.
|
||||
*/
|
||||
unsigned int history_len;
|
||||
|
||||
/**
|
||||
* KYC passed?
|
||||
*/
|
||||
bool kyc_ok;
|
||||
|
||||
/**
|
||||
* KYC required to withdraw?
|
||||
*/
|
||||
bool kyc_required;
|
||||
|
||||
} ok;
|
||||
|
||||
} details;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Callbacks of this type are used to serve the result of submitting a
|
||||
* reserve history request to a exchange.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param rs HTTP response data
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_EXCHANGE_ReservesHistoryCallback) (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_ReserveHistory *rs);
|
||||
|
||||
|
||||
/**
|
||||
* Submit a request to obtain the reserve history.
|
||||
*
|
||||
* @param exchange the exchange handle; the exchange must be ready to operate
|
||||
* @param reserve_priv private key of the reserve to inspect
|
||||
* @param cb the callback to call when a reply for this request is available
|
||||
* @param cb_cls closure for the above callback
|
||||
* @return a handle for this request; NULL if the inputs are invalid (i.e.
|
||||
* signatures fail to verify). In this case, the callback is not called.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *
|
||||
TALER_EXCHANGE_reserves_history (
|
||||
struct TALER_EXCHANGE_Handle *exchange,
|
||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||
TALER_EXCHANGE_ReservesHistoryCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a reserve history request. This function cannot be used
|
||||
* on a request handle if a response is already served for it.
|
||||
*
|
||||
* @param rsh the reserve request handle
|
||||
*/
|
||||
void
|
||||
TALER_EXCHANGE_reserves_history_cancel (
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh);
|
||||
|
||||
|
||||
/* ********************* POST /reserves/$RESERVE_PUB/withdraw *********************** */
|
||||
|
||||
|
||||
@ -2451,7 +2701,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
const char *currency,
|
||||
struct TALER_Amount *balance,
|
||||
unsigned int history_length,
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory);
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory);
|
||||
|
||||
|
||||
/**
|
||||
@ -2462,7 +2712,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
*/
|
||||
void
|
||||
TALER_EXCHANGE_free_reserve_history (
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory,
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory,
|
||||
unsigned int len);
|
||||
|
||||
|
||||
|
@ -1382,15 +1382,38 @@ TALER_TESTING_cmd_status (const char *label,
|
||||
const char *expected_balance,
|
||||
unsigned int expected_response_code);
|
||||
|
||||
/**
|
||||
* Index of the deposit value trait of a deposit command.
|
||||
*/
|
||||
#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.
|
||||
@ -2455,7 +2478,7 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
|
||||
op (contract_terms, const json_t) \
|
||||
op (wire_details, const json_t) \
|
||||
op (exchange_keys, const json_t) \
|
||||
op (reserve_history, const struct TALER_EXCHANGE_ReserveHistory) \
|
||||
op (reserve_history, const struct TALER_EXCHANGE_ReserveHistoryEntry) \
|
||||
op (exchange_url, const char *) \
|
||||
op (exchange_bank_account_url, const char *) \
|
||||
op (taler_uri, const char *) \
|
||||
|
@ -18,7 +18,7 @@ lib_LTLIBRARIES = \
|
||||
libtalerexchange.la
|
||||
|
||||
libtalerexchange_la_LDFLAGS = \
|
||||
-version-info 4:0:0 \
|
||||
-version-info 5:0:0 \
|
||||
-no-undefined
|
||||
libtalerexchange_la_SOURCES = \
|
||||
exchange_api_auditor_add_denomination.c \
|
||||
@ -51,6 +51,8 @@ libtalerexchange_la_SOURCES = \
|
||||
exchange_api_refreshes_reveal.c \
|
||||
exchange_api_refund.c \
|
||||
exchange_api_reserves_get.c \
|
||||
exchange_api_reserves_history.c \
|
||||
exchange_api_reserves_status.c \
|
||||
exchange_api_transfers_get.c \
|
||||
exchange_api_withdraw.c \
|
||||
exchange_api_withdraw2.c \
|
||||
|
@ -34,7 +34,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
const char *currency,
|
||||
struct TALER_Amount *balance,
|
||||
unsigned int history_length,
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory)
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory)
|
||||
{
|
||||
struct GNUNET_HashCode uuid[history_length];
|
||||
unsigned int uuid_off;
|
||||
@ -50,7 +50,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
uuid_off = 0;
|
||||
for (unsigned int off = 0; off<history_length; off++)
|
||||
{
|
||||
struct TALER_EXCHANGE_ReserveHistory *rh = &rhistory[off];
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rh = &rhistory[off];
|
||||
json_t *transaction;
|
||||
struct TALER_Amount amount;
|
||||
const char *type;
|
||||
@ -368,14 +368,20 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
}
|
||||
|
||||
/* check balance = total_in - total_out < withdraw-amount */
|
||||
if (0 >
|
||||
TALER_amount_subtract (balance,
|
||||
&total_in,
|
||||
&total_out))
|
||||
if (NULL != balance)
|
||||
{
|
||||
/* total_in < total_out, why did the exchange ever allow this!? */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
/* if balance is NULL, we may have a partial history
|
||||
in which case the subtraction may fail, so we do
|
||||
not even check that invariant in this case. */
|
||||
if (0 >
|
||||
TALER_amount_subtract (balance,
|
||||
&total_in,
|
||||
&total_out))
|
||||
{
|
||||
/* total_in < total_out, why did the exchange ever allow this!? */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
return GNUNET_OK;
|
||||
}
|
||||
@ -383,7 +389,7 @@ TALER_EXCHANGE_parse_reserve_history (
|
||||
|
||||
void
|
||||
TALER_EXCHANGE_free_reserve_history (
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory,
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory,
|
||||
unsigned int len)
|
||||
{
|
||||
for (unsigned int i = 0; i<len; i++)
|
||||
|
@ -2181,6 +2181,27 @@ TALER_EXCHANGE_get_denomination_key (
|
||||
}
|
||||
|
||||
|
||||
const struct TALER_EXCHANGE_GlobalFee *
|
||||
TALER_EXCHANGE_get_global_fee (
|
||||
const struct TALER_EXCHANGE_Keys *keys,
|
||||
struct GNUNET_TIME_Timestamp ts)
|
||||
{
|
||||
for (unsigned int i = 0; i<keys->num_global_fees; i++)
|
||||
{
|
||||
const struct TALER_EXCHANGE_GlobalFee *gf = &keys->global_fees[i];
|
||||
|
||||
if (GNUNET_TIME_timestamp_cmp (ts,
|
||||
>=,
|
||||
gf->start_date) &&
|
||||
GNUNET_TIME_timestamp_cmp (ts,
|
||||
<,
|
||||
gf->end_date))
|
||||
return gf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct TALER_EXCHANGE_DenomPublicKey *
|
||||
TALER_EXCHANGE_copy_denomination_key (
|
||||
const struct TALER_EXCHANGE_DenomPublicKey *key)
|
||||
|
@ -83,19 +83,15 @@ static enum GNUNET_GenericReturnValue
|
||||
handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
||||
const json_t *j)
|
||||
{
|
||||
json_t *history;
|
||||
unsigned int len;
|
||||
struct TALER_Amount balance;
|
||||
struct TALER_Amount balance_from_history;
|
||||
struct TALER_EXCHANGE_ReserveSummary rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = MHD_HTTP_OK
|
||||
};
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount_any ("balance",
|
||||
&balance),
|
||||
&rs.details.ok.balance),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = MHD_HTTP_OK
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
@ -106,55 +102,9 @@ handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
history = json_object_get (j,
|
||||
"history");
|
||||
if (NULL == history)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
len = json_array_size (history);
|
||||
{
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||
|
||||
rhistory = GNUNET_new_array (len,
|
||||
struct TALER_EXCHANGE_ReserveHistory);
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
||||
history,
|
||||
&rgh->reserve_pub,
|
||||
balance.currency,
|
||||
&balance_from_history,
|
||||
len,
|
||||
rhistory))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (0 !=
|
||||
TALER_amount_cmp (&balance_from_history,
|
||||
&balance))
|
||||
{
|
||||
/* exchange cannot add up balances!? */
|
||||
GNUNET_break_op (0);
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
&balance,
|
||||
len,
|
||||
rhistory);
|
||||
rgh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
}
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&rs);
|
||||
rgh->cb = NULL;
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
@ -174,61 +124,59 @@ handle_reserves_get_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
|
||||
const json_t *j = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = (unsigned int) response_code
|
||||
struct TALER_EXCHANGE_ReserveSummary rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = (unsigned int) response_code
|
||||
};
|
||||
|
||||
rgh->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
if (GNUNET_OK !=
|
||||
handle_reserves_get_ok (rgh,
|
||||
j))
|
||||
{
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
rs.hr.http_status = 0;
|
||||
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, this should never
|
||||
happen, we should pass the JSON reply to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for reserves get\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) rs.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
0, NULL);
|
||||
&rs);
|
||||
rgh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_reserves_get_cancel (rgh);
|
||||
|
@ -81,35 +81,32 @@ struct TALER_EXCHANGE_ReservesHistoryHandle
|
||||
* We received an #MHD_HTTP_OK history code. Handle the JSON
|
||||
* response.
|
||||
*
|
||||
* @param rgh handle of the request
|
||||
* @param rsh handle of the request
|
||||
* @param j JSON response
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
|
||||
handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rsh,
|
||||
const json_t *j)
|
||||
{
|
||||
json_t *history;
|
||||
unsigned int len;
|
||||
bool kyc_ok;
|
||||
bool kyc_required;
|
||||
struct TALER_Amount balance;
|
||||
struct TALER_Amount balance_from_history;
|
||||
struct TALER_Amount history_balance;
|
||||
struct TALER_EXCHANGE_ReserveHistory rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = MHD_HTTP_OK
|
||||
};
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount_any ("balance",
|
||||
&balance),
|
||||
&rs.details.ok.balance),
|
||||
GNUNET_JSON_spec_bool ("kyc_passed",
|
||||
&kyc_ok),
|
||||
&rs.details.ok.kyc_ok),
|
||||
GNUNET_JSON_spec_bool ("kyc_required",
|
||||
&kyc_required),
|
||||
&rs.details.ok.kyc_required),
|
||||
GNUNET_JSON_spec_json ("history",
|
||||
&history),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_history = MHD_HTTP_OK
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
@ -122,16 +119,19 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
|
||||
}
|
||||
len = json_array_size (history);
|
||||
{
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory;
|
||||
|
||||
rhistory = GNUNET_new_array (len,
|
||||
struct TALER_EXCHANGE_ReserveHistory);
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry);
|
||||
// FIXME: even this history could be partial
|
||||
// (if the reserve is too old!); update API
|
||||
// and return incoming & outgoing totals separately?
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
||||
TALER_EXCHANGE_parse_reserve_history (rsh->exchange,
|
||||
history,
|
||||
&rgh->reserve_pub,
|
||||
balance.currency,
|
||||
&balance_from_history,
|
||||
&rsh->reserve_pub,
|
||||
rs.details.ok.balance.currency,
|
||||
&history_balance,
|
||||
len,
|
||||
rhistory))
|
||||
{
|
||||
@ -141,25 +141,13 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rgh,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (0 !=
|
||||
TALER_amount_cmp (&balance_from_history,
|
||||
&balance))
|
||||
if (NULL != rsh->cb)
|
||||
{
|
||||
/* exchange cannot add up balances!? */
|
||||
GNUNET_break_op (0);
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
&balance,
|
||||
len,
|
||||
rhistory);
|
||||
rgh->cb = NULL;
|
||||
rs.details.ok.history = rhistory;
|
||||
rs.details.ok.history_len = len;
|
||||
rsh->cb (rsh->cb_cls,
|
||||
&rs);
|
||||
rsh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
@ -182,75 +170,72 @@ handle_reserves_history_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rgh = cls;
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh = cls;
|
||||
const json_t *j = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_history = (unsigned int) response_code
|
||||
struct TALER_EXCHANGE_ReserveHistory rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = (unsigned int) response_code
|
||||
};
|
||||
|
||||
rgh->job = NULL;
|
||||
rsh->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
if (GNUNET_OK !=
|
||||
handle_reserves_history_ok (rgh,
|
||||
handle_reserves_history_ok (rsh,
|
||||
j))
|
||||
{
|
||||
hr.http_history = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
rs.hr.http_status = 0;
|
||||
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
GNUNET_break (0);
|
||||
hr.ec = TALER_JSON_history_error_code (j);
|
||||
hr.hint = TALER_JSON_history_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
GNUNET_break (0);
|
||||
hr.ec = TALER_JSON_history_error_code (j);
|
||||
hr.hint = TALER_JSON_history_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, this should never
|
||||
happen, we should pass the JSON reply to the application */
|
||||
hr.ec = TALER_JSON_history_error_code (j);
|
||||
hr.hint = TALER_JSON_history_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
hr.ec = TALER_JSON_history_error_code (j);
|
||||
hr.hint = TALER_JSON_history_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_history_error_code (j);
|
||||
hr.hint = TALER_JSON_history_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for reserves history\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) rs.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
if (NULL != rsh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
rgh->cb = NULL;
|
||||
rsh->cb (rsh->cb_cls,
|
||||
&rs);
|
||||
rsh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_reserves_history_cancel (rgh);
|
||||
TALER_EXCHANGE_reserves_history_cancel (rsh);
|
||||
}
|
||||
|
||||
|
||||
@ -261,12 +246,11 @@ TALER_EXCHANGE_reserves_history (
|
||||
TALER_EXCHANGE_ReservesHistoryCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rgh;
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh;
|
||||
struct GNUNET_CURL_Context *ctx;
|
||||
CURL *eh;
|
||||
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
||||
const struct TALER_Amount *history_fee;
|
||||
const struct TALER_EXCHANGE_Keys *keys;
|
||||
struct TALER_ReserveSignatureP reserve_sig;
|
||||
struct GNUNET_TIME_Timestamp ts
|
||||
= GNUNET_TIME_timestamp_get ();
|
||||
|
||||
@ -276,22 +260,19 @@ TALER_EXCHANGE_reserves_history (
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
keys = TALER_EXCHANGE_get_keys (exchange);
|
||||
// FIXME: extract history_fee from keys!
|
||||
history_fee = FIXME;
|
||||
rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle);
|
||||
rgh->exchange = exchange;
|
||||
rgh->cb = cb;
|
||||
rgh->cb_cls = cb_cls;
|
||||
rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesHistoryHandle);
|
||||
rsh->exchange = exchange;
|
||||
rsh->cb = cb;
|
||||
rsh->cb_cls = cb_cls;
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
|
||||
&rgh->reserve_pub.eddsa_pub);
|
||||
&rsh->reserve_pub.eddsa_pub);
|
||||
{
|
||||
char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||
char *end;
|
||||
|
||||
end = GNUNET_STRINGS_data_to_string (
|
||||
&rgh->reserve_pub,
|
||||
sizeof (rgh->reserve_pub),
|
||||
&rsh->reserve_pub,
|
||||
sizeof (rsh->reserve_pub),
|
||||
pub_str,
|
||||
sizeof (pub_str));
|
||||
*end = '\0';
|
||||
@ -300,68 +281,67 @@ TALER_EXCHANGE_reserves_history (
|
||||
"/reserves/%s/history",
|
||||
pub_str);
|
||||
}
|
||||
rgh->url = TEAH_path_to_url (exchange,
|
||||
rsh->url = TEAH_path_to_url (exchange,
|
||||
arg_str);
|
||||
if (NULL == rgh->url)
|
||||
if (NULL == rsh->url)
|
||||
{
|
||||
GNUNET_free (rgh);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
eh = TALER_EXCHANGE_curl_easy_history_ (rgh->url);
|
||||
eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url);
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
TALER_wallet_reserve_history_sign (ts,
|
||||
history_fee,
|
||||
NULL, /* FIXME: fee! */
|
||||
reserve_priv,
|
||||
&reserve_sig);
|
||||
{
|
||||
json_t *history_obj = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
||||
&ts),
|
||||
ts),
|
||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
||||
&reserve_sig));
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_curl_easy_post (&rgh->post_ctx,
|
||||
TALER_curl_easy_post (&rsh->post_ctx,
|
||||
eh,
|
||||
history_obj))
|
||||
)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
curl_easy_cleanup (eh);
|
||||
json_decref (history_obj);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
GNUNET_break (0);
|
||||
curl_easy_cleanup (eh);
|
||||
json_decref (history_obj);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
json_decref (history_obj);
|
||||
}
|
||||
ctx = TEAH_handle_to_context (exchange);
|
||||
rgh->job = GNUNET_CURL_job_add (ctx,
|
||||
rsh->job = GNUNET_CURL_job_add (ctx,
|
||||
eh,
|
||||
&handle_reserves_history_finished,
|
||||
rgh);
|
||||
return rgh;
|
||||
rsh);
|
||||
return rsh;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TALER_EXCHANGE_reserves_history_cancel (
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rgh)
|
||||
struct TALER_EXCHANGE_ReservesHistoryHandle *rsh)
|
||||
{
|
||||
if (NULL != rgh->job)
|
||||
if (NULL != rsh->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (rgh->job);
|
||||
rgh->job = NULL;
|
||||
GNUNET_CURL_job_cancel (rsh->job);
|
||||
rsh->job = NULL;
|
||||
}
|
||||
TALER_curl_easy_post_finished (&rgh->post_ctx);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
TALER_curl_easy_post_finished (&rsh->post_ctx);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,35 +81,31 @@ struct TALER_EXCHANGE_ReservesStatusHandle
|
||||
* We received an #MHD_HTTP_OK status code. Handle the JSON
|
||||
* response.
|
||||
*
|
||||
* @param rgh handle of the request
|
||||
* @param rsh handle of the request
|
||||
* @param j JSON response
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
|
||||
handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rsh,
|
||||
const json_t *j)
|
||||
{
|
||||
json_t *history;
|
||||
unsigned int len;
|
||||
bool kyc_ok;
|
||||
bool kyc_required;
|
||||
struct TALER_Amount balance;
|
||||
struct TALER_Amount balance_from_history;
|
||||
struct TALER_EXCHANGE_ReserveStatus rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = MHD_HTTP_OK
|
||||
};
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount_any ("balance",
|
||||
&balance),
|
||||
&rs.details.ok.balance),
|
||||
GNUNET_JSON_spec_bool ("kyc_passed",
|
||||
&kyc_ok),
|
||||
&rs.details.ok.kyc_ok),
|
||||
GNUNET_JSON_spec_bool ("kyc_required",
|
||||
&kyc_required),
|
||||
&rs.details.ok.kyc_required),
|
||||
GNUNET_JSON_spec_json ("history",
|
||||
&history),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = MHD_HTTP_OK
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
@ -122,16 +118,16 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
|
||||
}
|
||||
len = json_array_size (history);
|
||||
{
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory;
|
||||
|
||||
rhistory = GNUNET_new_array (len,
|
||||
struct TALER_EXCHANGE_ReserveHistory);
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry);
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
|
||||
TALER_EXCHANGE_parse_reserve_history (rsh->exchange,
|
||||
history,
|
||||
&rgh->reserve_pub,
|
||||
balance.currency,
|
||||
&balance_from_history,
|
||||
&rsh->reserve_pub,
|
||||
rs.details.ok.balance.currency,
|
||||
NULL,
|
||||
len,
|
||||
rhistory))
|
||||
{
|
||||
@ -141,27 +137,13 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rgh,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
// FIXME: status history is allowed to be
|
||||
// partial, so this is NOT ok...
|
||||
if (0 !=
|
||||
TALER_amount_cmp (&balance_from_history,
|
||||
&balance))
|
||||
if (NULL != rsh->cb)
|
||||
{
|
||||
/* exchange cannot add up balances!? */
|
||||
GNUNET_break_op (0);
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
&balance,
|
||||
len,
|
||||
rhistory);
|
||||
rgh->cb = NULL;
|
||||
rs.details.ok.history = rhistory;
|
||||
rs.details.ok.history_len = len;
|
||||
rsh->cb (rsh->cb_cls,
|
||||
&rs);
|
||||
rsh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_free_reserve_history (rhistory,
|
||||
len);
|
||||
@ -184,75 +166,72 @@ handle_reserves_status_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rgh = cls;
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rsh = cls;
|
||||
const json_t *j = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = (unsigned int) response_code
|
||||
struct TALER_EXCHANGE_ReserveStatus rs = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = (unsigned int) response_code
|
||||
};
|
||||
|
||||
rgh->job = NULL;
|
||||
rsh->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
rs.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
if (GNUNET_OK !=
|
||||
handle_reserves_status_ok (rgh,
|
||||
handle_reserves_status_ok (rsh,
|
||||
j))
|
||||
{
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
rs.hr.http_status = 0;
|
||||
rs.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
GNUNET_break (0);
|
||||
hr.ec = TALER_JSON_status_error_code (j);
|
||||
hr.hint = TALER_JSON_status_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
GNUNET_break (0);
|
||||
hr.ec = TALER_JSON_status_error_code (j);
|
||||
hr.hint = TALER_JSON_status_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, this should never
|
||||
happen, we should pass the JSON reply to the application */
|
||||
hr.ec = TALER_JSON_status_error_code (j);
|
||||
hr.hint = TALER_JSON_status_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
hr.ec = TALER_JSON_status_error_code (j);
|
||||
hr.hint = TALER_JSON_status_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_status_error_code (j);
|
||||
hr.hint = TALER_JSON_status_error_hint (j);
|
||||
rs.hr.ec = TALER_JSON_get_error_code (j);
|
||||
rs.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for reserves status\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) rs.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != rgh->cb)
|
||||
if (NULL != rsh->cb)
|
||||
{
|
||||
rgh->cb (rgh->cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
rgh->cb = NULL;
|
||||
rsh->cb (rsh->cb_cls,
|
||||
&rs);
|
||||
rsh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_reserves_status_cancel (rgh);
|
||||
TALER_EXCHANGE_reserves_status_cancel (rsh);
|
||||
}
|
||||
|
||||
|
||||
@ -263,10 +242,11 @@ TALER_EXCHANGE_reserves_status (
|
||||
TALER_EXCHANGE_ReservesStatusCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rgh;
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rsh;
|
||||
struct GNUNET_CURL_Context *ctx;
|
||||
CURL *eh;
|
||||
char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
|
||||
struct TALER_ReserveSignatureP reserve_sig;
|
||||
struct GNUNET_TIME_Timestamp ts
|
||||
= GNUNET_TIME_timestamp_get ();
|
||||
|
||||
@ -276,19 +256,19 @@ TALER_EXCHANGE_reserves_status (
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
|
||||
rgh->exchange = exchange;
|
||||
rgh->cb = cb;
|
||||
rgh->cb_cls = cb_cls;
|
||||
rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesStatusHandle);
|
||||
rsh->exchange = exchange;
|
||||
rsh->cb = cb;
|
||||
rsh->cb_cls = cb_cls;
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
|
||||
&rgh->reserve_pub.eddsa_pub);
|
||||
&rsh->reserve_pub.eddsa_pub);
|
||||
{
|
||||
char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
|
||||
char *end;
|
||||
|
||||
end = GNUNET_STRINGS_data_to_string (
|
||||
&rgh->reserve_pub,
|
||||
sizeof (rgh->reserve_pub),
|
||||
&rsh->reserve_pub,
|
||||
sizeof (rsh->reserve_pub),
|
||||
pub_str,
|
||||
sizeof (pub_str));
|
||||
*end = '\0';
|
||||
@ -297,19 +277,19 @@ TALER_EXCHANGE_reserves_status (
|
||||
"/reserves/%s/status",
|
||||
pub_str);
|
||||
}
|
||||
rgh->url = TEAH_path_to_url (exchange,
|
||||
rsh->url = TEAH_path_to_url (exchange,
|
||||
arg_str);
|
||||
if (NULL == rgh->url)
|
||||
if (NULL == rsh->url)
|
||||
{
|
||||
GNUNET_free (rgh);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
eh = TALER_EXCHANGE_curl_easy_status_ (rgh->url);
|
||||
eh = TALER_EXCHANGE_curl_easy_get_ (rsh->url);
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
TALER_wallet_reserve_status_sign (ts,
|
||||
@ -318,46 +298,45 @@ TALER_EXCHANGE_reserves_status (
|
||||
{
|
||||
json_t *status_obj = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_timestamp ("request_timestamp",
|
||||
&ts),
|
||||
ts),
|
||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
||||
&reserve_sig));
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_curl_easy_post (&rgh->post_ctx,
|
||||
TALER_curl_easy_post (&rsh->post_ctx,
|
||||
eh,
|
||||
status_obj))
|
||||
)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
curl_easy_cleanup (eh);
|
||||
json_decref (status_obj);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
GNUNET_break (0);
|
||||
curl_easy_cleanup (eh);
|
||||
json_decref (status_obj);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
return NULL;
|
||||
}
|
||||
json_decref (status_obj);
|
||||
}
|
||||
ctx = TEAH_handle_to_context (exchange);
|
||||
rgh->job = GNUNET_CURL_job_add (ctx,
|
||||
rsh->job = GNUNET_CURL_job_add (ctx,
|
||||
eh,
|
||||
&handle_reserves_status_finished,
|
||||
rgh);
|
||||
return rgh;
|
||||
rsh);
|
||||
return rsh;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TALER_EXCHANGE_reserves_status_cancel (
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rgh)
|
||||
struct TALER_EXCHANGE_ReservesStatusHandle *rsh)
|
||||
{
|
||||
if (NULL != rgh->job)
|
||||
if (NULL != rsh->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (rgh->job);
|
||||
rgh->job = NULL;
|
||||
GNUNET_CURL_job_cancel (rsh->job);
|
||||
rsh->job = NULL;
|
||||
}
|
||||
TALER_curl_easy_post_finished (&rgh->post_ctx);
|
||||
GNUNET_free (rgh->url);
|
||||
GNUNET_free (rgh);
|
||||
TALER_curl_easy_post_finished (&rsh->post_ctx);
|
||||
GNUNET_free (rsh->url);
|
||||
GNUNET_free (rsh);
|
||||
}
|
||||
|
||||
|
||||
|
@ -177,14 +177,14 @@ reserve_withdraw_payment_required (
|
||||
total incoming and outgoing amounts */
|
||||
len = json_array_size (history);
|
||||
{
|
||||
struct TALER_EXCHANGE_ReserveHistory *rhistory;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry *rhistory;
|
||||
|
||||
/* Use heap allocation as "len" may be very big and thus this may
|
||||
not fit on the stack. Use "GNUNET_malloc_large" as a malicious
|
||||
exchange may theoretically try to crash us by giving a history
|
||||
that does not fit into our memory. */
|
||||
rhistory = GNUNET_malloc_large (
|
||||
sizeof (struct TALER_EXCHANGE_ReserveHistory)
|
||||
sizeof (struct TALER_EXCHANGE_ReserveHistoryEntry)
|
||||
* len);
|
||||
if (NULL == rhistory)
|
||||
{
|
||||
|
@ -74,6 +74,8 @@ libtalertesting_la_SOURCES = \
|
||||
testing_api_cmd_recoup_refresh.c \
|
||||
testing_api_cmd_refund.c \
|
||||
testing_api_cmd_refresh.c \
|
||||
testing_api_cmd_reserve_history.c \
|
||||
testing_api_cmd_reserve_status.c \
|
||||
testing_api_cmd_revoke.c \
|
||||
testing_api_cmd_revoke_denom_key.c \
|
||||
testing_api_cmd_revoke_sign_key.c \
|
||||
|
@ -107,7 +107,7 @@ struct AdminAddIncomingState
|
||||
* the "sender_url" field is set to a 'const char *' and
|
||||
* MUST NOT be free()'ed.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory reserve_history;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
|
||||
|
||||
/**
|
||||
* Set to the wire transfer's unique ID.
|
||||
|
@ -49,7 +49,7 @@ struct CloserState
|
||||
* expect_close is true. Will be of type
|
||||
* #TALER_EXCHANGE_RTT_RESERVE_CLOSED.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory reserve_history;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
|
||||
|
||||
/**
|
||||
* If the closer filled a reserve (@e expect_close is set), this is set to
|
||||
|
@ -62,7 +62,7 @@ struct RecoupState
|
||||
* Reserve history entry, set if this recoup actually filled up a reserve.
|
||||
* Otherwise `reserve_history.type` will be zero.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory reserve_history;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
|
||||
|
||||
};
|
||||
|
||||
|
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
|
||||
* @brief Implement the /reserve/status test command.
|
||||
* @brief Implement the GET /reserve/$RID test command.
|
||||
* @author Marcello Stanisci
|
||||
*/
|
||||
#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
|
||||
* both acceptable.
|
||||
*
|
||||
* @param cls closure.
|
||||
* @param hr HTTP response details
|
||||
* @param balance current balance in the reserve, NULL on error.
|
||||
* @param history_length number of entries in the transaction
|
||||
* history, 0 on error.
|
||||
* @param history detailed transaction history, NULL on error.
|
||||
* @param rs HTTP response details
|
||||
*/
|
||||
static void
|
||||
reserve_status_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_Amount *balance,
|
||||
unsigned int history_length,
|
||||
const struct TALER_EXCHANGE_ReserveHistory *history)
|
||||
const struct TALER_EXCHANGE_ReserveSummary *rs)
|
||||
{
|
||||
struct StatusState *ss = cls;
|
||||
struct TALER_TESTING_Interpreter *is = ss->is;
|
||||
struct TALER_Amount eb;
|
||||
|
||||
ss->rsh = NULL;
|
||||
if (ss->expected_response_code != hr->http_status)
|
||||
if (ss->expected_response_code != rs->hr.http_status)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected HTTP response code: %d in %s:%u\n",
|
||||
hr->http_status,
|
||||
rs->hr.http_status,
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
json_dumpf (hr->reply,
|
||||
json_dumpf (rs->hr.reply,
|
||||
stderr,
|
||||
0);
|
||||
TALER_TESTING_interpreter_fail (ss->is);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MHD_HTTP_OK != ss->expected_response_code)
|
||||
{
|
||||
TALER_TESTING_interpreter_next (is);
|
||||
return;
|
||||
}
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_string_to_amount (ss->expected_balance,
|
||||
&eb));
|
||||
|
||||
if (0 != TALER_amount_cmp (&eb,
|
||||
balance))
|
||||
&rs->details.ok.balance))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected amount in reserve: %s\n",
|
||||
TALER_amount_to_string (balance));
|
||||
TALER_amount_to_string (&rs->details.ok.balance));
|
||||
TALER_TESTING_interpreter_fail (ss->is);
|
||||
return;
|
||||
}
|
||||
{
|
||||
int found[history_length];
|
||||
|
||||
memset (found,
|
||||
0,
|
||||
sizeof (found));
|
||||
for (unsigned int i = 0; i<= (unsigned int) is->ip; i++)
|
||||
{
|
||||
struct TALER_TESTING_Command *cmd = &is->commands[i];
|
||||
|
||||
if (GNUNET_OK !=
|
||||
analyze_command (ss->reserve_pubp,
|
||||
cmd,
|
||||
history_length,
|
||||
history,
|
||||
found))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Entry for command `%s' missing in history\n",
|
||||
cmd->label);
|
||||
TALER_TESTING_interpreter_fail (ss->is);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i<history_length; i++)
|
||||
if (! found[i])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"History entry at index %u of type %d not justified by command history\n",
|
||||
i,
|
||||
history[i].type);
|
||||
TALER_TESTING_interpreter_fail (ss->is);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TALER_TESTING_interpreter_next (is);
|
||||
}
|
||||
|
||||
|
@ -148,7 +148,7 @@ struct WithdrawState
|
||||
* Reserve history entry that corresponds to this operation.
|
||||
* Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
|
||||
*/
|
||||
struct TALER_EXCHANGE_ReserveHistory reserve_history;
|
||||
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
|
||||
|
||||
/**
|
||||
* Withdraw handle (while operation is running).
|
||||
|
Loading…
Reference in New Issue
Block a user