-towards reserve history testing

This commit is contained in:
Christian Grothoff 2022-05-23 11:15:05 +02:00
parent b3844e4923
commit f1a58b0fd8
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
5 changed files with 232 additions and 53 deletions

View File

@ -1767,6 +1767,18 @@ struct TALER_EXCHANGE_ReserveHistory
*/ */
struct TALER_EXCHANGE_HttpResponse hr; struct TALER_EXCHANGE_HttpResponse hr;
/**
* Timestamp of when we made the history request
* (client-side).
*/
struct GNUNET_TIME_Timestamp ts;
/**
* Reserve signature affirming the history request
* (generated as part of the request).
*/
const struct TALER_ReserveSignatureP *reserve_sig;
/** /**
* Details depending on @e hr.http_status. * Details depending on @e hr.http_status.
*/ */
@ -4388,6 +4400,11 @@ struct TALER_EXCHANGE_AccountMergeResponse
*/ */
struct TALER_EXCHANGE_HttpResponse hr; struct TALER_EXCHANGE_HttpResponse hr;
/**
* Reserve signature affirming the merge.
*/
const struct TALER_ReserveSignatureP *reserve_sig;
/** /**
* Details depending on the HTTP status. * Details depending on the HTTP status.
*/ */
@ -4398,6 +4415,20 @@ struct TALER_EXCHANGE_AccountMergeResponse
*/ */
struct struct
{ {
/**
* Signature by the exchange affirming the merge.
*/
struct TALER_ExchangeSignatureP exchange_sig;
/**
* Online signing key used by the exchange.
*/
struct TALER_ExchangePublicKeyP exchange_pub;
/**
* Timestamp of the exchange for @e exchange_sig.
*/
struct GNUNET_TIME_Timestamp etime;
} success; } success;

View File

@ -104,6 +104,12 @@ struct TALER_EXCHANGE_AccountMergeHandle
* Our merge key. * Our merge key.
*/ */
struct TALER_PurseMergePrivateKeyP merge_priv; struct TALER_PurseMergePrivateKeyP merge_priv;
/**
* Reserve signature affirming the merge.
*/
struct TALER_ReserveSignatureP reserve_sig;
}; };
@ -168,7 +174,8 @@ handle_purse_merge_finished (void *cls,
const json_t *j = response; const json_t *j = response;
struct TALER_EXCHANGE_AccountMergeResponse dr = { struct TALER_EXCHANGE_AccountMergeResponse dr = {
.hr.reply = j, .hr.reply = j,
.hr.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code,
.reserve_sig = &pch->reserve_sig
}; };
pch->job = NULL; pch->job = NULL;
@ -180,17 +187,14 @@ handle_purse_merge_finished (void *cls,
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
const struct TALER_EXCHANGE_Keys *key_state; const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_TIME_Timestamp etime;
struct TALER_Amount total_deposited; struct TALER_Amount total_deposited;
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_ExchangePublicKeyP exchange_pub;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig", GNUNET_JSON_spec_fixed_auto ("exchange_sig",
&exchange_sig), &dr.details.success.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&exchange_pub), &dr.details.success.exchange_pub),
GNUNET_JSON_spec_timestamp ("exchange_timestamp", GNUNET_JSON_spec_timestamp ("exchange_timestamp",
&etime), &dr.details.success.etime),
TALER_JSON_spec_amount ("merge_amount", TALER_JSON_spec_amount ("merge_amount",
pch->purse_value_after_fees.currency, pch->purse_value_after_fees.currency,
&total_deposited), &total_deposited),
@ -210,7 +214,7 @@ handle_purse_merge_finished (void *cls,
key_state = TALER_EXCHANGE_get_keys (pch->exchange); key_state = TALER_EXCHANGE_get_keys (pch->exchange);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state, TALER_EXCHANGE_test_signing_key (key_state,
&exchange_pub)) &dr.details.success.exchange_pub))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
@ -219,15 +223,15 @@ handle_purse_merge_finished (void *cls,
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_exchange_online_purse_merged_verify ( TALER_exchange_online_purse_merged_verify (
etime, dr.details.success.etime,
pch->purse_expiration, pch->purse_expiration,
&pch->purse_value_after_fees, &pch->purse_value_after_fees,
&pch->purse_pub, &pch->purse_pub,
&pch->h_contract_terms, &pch->h_contract_terms,
&pch->reserve_pub, &pch->reserve_pub,
pch->provider_url, pch->provider_url,
&exchange_pub, &dr.details.success.exchange_pub,
&exchange_sig)) &dr.details.success.exchange_sig))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
dr.hr.http_status = 0; dr.hr.http_status = 0;
@ -367,7 +371,6 @@ TALER_EXCHANGE_account_merge (
json_t *merge_obj; json_t *merge_obj;
CURL *eh; CURL *eh;
struct TALER_PurseMergeSignatureP merge_sig; struct TALER_PurseMergeSignatureP merge_sig;
struct TALER_ReserveSignatureP reserve_sig;
char arg_str[sizeof (pch->purse_pub) * 2 + 32]; char arg_str[sizeof (pch->purse_pub) * 2 + 32];
char *reserve_url; char *reserve_url;
@ -442,7 +445,7 @@ TALER_EXCHANGE_account_merge (
min_age, min_age,
TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE, TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE,
reserve_priv, reserve_priv,
&reserve_sig); &pch->reserve_sig);
} }
merge_obj = GNUNET_JSON_PACK ( merge_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_string ("payto_uri", GNUNET_JSON_pack_string ("payto_uri",
@ -450,7 +453,7 @@ TALER_EXCHANGE_account_merge (
GNUNET_JSON_pack_data_auto ("merge_sig", GNUNET_JSON_pack_data_auto ("merge_sig",
&merge_sig), &merge_sig),
GNUNET_JSON_pack_data_auto ("reserve_sig", GNUNET_JSON_pack_data_auto ("reserve_sig",
&reserve_sig), &pch->reserve_sig),
GNUNET_JSON_pack_timestamp ("merge_timestamp", GNUNET_JSON_pack_timestamp ("merge_timestamp",
merge_timestamp)); merge_timestamp));
GNUNET_assert (NULL != merge_obj); GNUNET_assert (NULL != merge_obj);

View File

@ -64,15 +64,25 @@ struct TALER_EXCHANGE_ReservesHistoryHandle
*/ */
TALER_EXCHANGE_ReservesHistoryCallback cb; TALER_EXCHANGE_ReservesHistoryCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/** /**
* Public key of the reserve we are querying. * Public key of the reserve we are querying.
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_ReservePublicKeyP reserve_pub;
/** /**
* Closure for @a cb. * Our signature.
*/ */
void *cb_cls; struct TALER_ReserveSignatureP reserve_sig;
/**
* When did we make the request.
*/
struct GNUNET_TIME_Timestamp ts;
}; };
@ -93,7 +103,9 @@ handle_reserves_history_ok (struct TALER_EXCHANGE_ReservesHistoryHandle *rsh,
unsigned int len; unsigned int len;
struct TALER_EXCHANGE_ReserveHistory rs = { struct TALER_EXCHANGE_ReserveHistory rs = {
.hr.reply = j, .hr.reply = j,
.hr.http_status = MHD_HTTP_OK .hr.http_status = MHD_HTTP_OK,
.ts = rsh->ts,
.reserve_sig = &rsh->reserve_sig
}; };
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("balance", TALER_JSON_spec_amount_any ("balance",
@ -247,9 +259,8 @@ TALER_EXCHANGE_reserves_history (
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; const struct TALER_EXCHANGE_Keys *keys;
struct GNUNET_TIME_Timestamp ts const struct TALER_EXCHANGE_GlobalFee *gf;
= GNUNET_TIME_timestamp_get ();
if (GNUNET_YES != if (GNUNET_YES !=
TEAH_handle_is_ready (exchange)) TEAH_handle_is_ready (exchange))
@ -261,6 +272,7 @@ TALER_EXCHANGE_reserves_history (
rsh->exchange = exchange; rsh->exchange = exchange;
rsh->cb = cb; rsh->cb = cb;
rsh->cb_cls = cb_cls; rsh->cb_cls = cb_cls;
rsh->ts = GNUNET_TIME_timestamp_get ();
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&rsh->reserve_pub.eddsa_pub); &rsh->reserve_pub.eddsa_pub);
{ {
@ -293,16 +305,21 @@ TALER_EXCHANGE_reserves_history (
GNUNET_free (rsh); GNUNET_free (rsh);
return NULL; return NULL;
} }
TALER_wallet_reserve_history_sign (ts, keys = TALER_EXCHANGE_get_keys (exchange);
NULL, /* FIXME: fee! */ GNUNET_assert (NULL != keys);
gf = TALER_EXCHANGE_get_global_fee (keys,
rsh->ts);
GNUNET_assert (NULL != gf);
TALER_wallet_reserve_history_sign (rsh->ts,
&gf->fees.history,
reserve_priv, reserve_priv,
&reserve_sig); &rsh->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), rsh->ts),
GNUNET_JSON_pack_data_auto ("reserve_sig", GNUNET_JSON_pack_data_auto ("reserve_sig",
&reserve_sig)); &rsh->reserve_sig));
if (GNUNET_OK != if (GNUNET_OK !=
TALER_curl_easy_post (&rsh->post_ctx, TALER_curl_easy_post (&rsh->post_ctx,

View File

@ -71,6 +71,42 @@ struct PurseMergeState
*/ */
struct TALER_TESTING_Interpreter *is; struct TALER_TESTING_Interpreter *is;
/**
* Reserve history entry that corresponds to this operation.
* Will be of type #TALER_EXCHANGE_RTT_MERGE.
*/
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
/**
* Public key of the purse.
*/
struct TALER_PurseContractPublicKeyP purse_pub;
/**
* Public key of the merge capability.
*/
struct TALER_PurseMergePublicKeyP merge_pub;
/**
* Contract value.
*/
struct TALER_Amount value_after_fees;
/**
* Hash of the contract.
*/
struct TALER_PrivateContractHashP h_contract_terms;
/**
* When does the purse expire.
*/
struct GNUNET_TIME_Timestamp purse_expiration;
/**
* Minimum age of deposits into the purse.
*/
uint32_t min_age;
/** /**
* Expected HTTP response code. * Expected HTTP response code.
*/ */
@ -93,6 +129,36 @@ merge_cb (void *cls,
struct PurseMergeState *ds = cls; struct PurseMergeState *ds = cls;
ds->dh = NULL; ds->dh = NULL;
if (MHD_HTTP_OK == dr->hr.http_status)
{
const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_EXCHANGE_GlobalFee *gf;
ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE;
keys = TALER_EXCHANGE_get_keys (ds->is->exchange);
GNUNET_assert (NULL != keys);
gf = TALER_EXCHANGE_get_global_fee (keys,
ds->merge_timestamp);
GNUNET_assert (NULL != gf);
ds->reserve_history.amount = gf->fees.purse;
ds->reserve_history.details.merge_details.purse_fee = gf->fees.purse;
ds->reserve_history.details.merge_details.h_contract_terms
= ds->h_contract_terms;
ds->reserve_history.details.merge_details.merge_pub
= ds->merge_pub;
ds->reserve_history.details.merge_details.reserve_sig
= *dr->reserve_sig;
ds->reserve_history.details.merge_details.merge_timestamp
= ds->merge_timestamp;
ds->reserve_history.details.merge_details.purse_expiration
= ds->purse_expiration;
ds->reserve_history.details.merge_details.min_age
= ds->min_age;
ds->reserve_history.details.merge_details.flags
= TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE;
}
if (ds->expected_response_code != dr->hr.http_status) if (ds->expected_response_code != dr->hr.http_status)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -124,13 +190,8 @@ merge_run (void *cls,
struct TALER_TESTING_Interpreter *is) struct TALER_TESTING_Interpreter *is)
{ {
struct PurseMergeState *ds = cls; struct PurseMergeState *ds = cls;
const struct TALER_PurseContractPublicKeyP *purse_pub;
const struct TALER_PurseMergePrivateKeyP *merge_priv; const struct TALER_PurseMergePrivateKeyP *merge_priv;
const json_t *ct; const json_t *ct;
struct TALER_PrivateContractHashP h_contract_terms;
uint32_t min_age = 0;
struct TALER_Amount value_after_fees;
struct GNUNET_TIME_Timestamp purse_expiration;
const struct TALER_TESTING_Command *ref; const struct TALER_TESTING_Command *ref;
(void) cmd; (void) cmd;
@ -146,14 +207,20 @@ merge_run (void *cls,
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
if (GNUNET_OK !=
TALER_TESTING_get_trait_purse_pub (ref,
&purse_pub))
{ {
GNUNET_break (0); const struct TALER_PurseContractPublicKeyP *purse_pub;
TALER_TESTING_interpreter_fail (ds->is);
return; if (GNUNET_OK !=
TALER_TESTING_get_trait_purse_pub (ref,
&purse_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is);
return;
}
ds->purse_pub = *purse_pub;
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_contract_terms (ref, TALER_TESTING_get_trait_contract_terms (ref,
&ct)) &ct))
@ -164,7 +231,7 @@ merge_run (void *cls,
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_JSON_contract_hash (ct, TALER_JSON_contract_hash (ct,
&h_contract_terms)) &ds->h_contract_terms))
{ {
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
@ -173,12 +240,12 @@ merge_run (void *cls,
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_timestamp ("pay_deadline", GNUNET_JSON_spec_timestamp ("pay_deadline",
&purse_expiration), &ds->purse_expiration),
TALER_JSON_spec_amount_any ("amount", TALER_JSON_spec_amount_any ("amount",
&value_after_fees), &ds->value_after_fees),
GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint32 ("minimum_age", GNUNET_JSON_spec_uint32 ("minimum_age",
&min_age), &ds->min_age),
NULL), NULL),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
@ -217,17 +284,19 @@ merge_run (void *cls,
} }
GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv,
&ds->reserve_pub.eddsa_pub); &ds->reserve_pub.eddsa_pub);
GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
&ds->merge_pub.eddsa_pub);
ds->merge_timestamp = GNUNET_TIME_timestamp_get (); ds->merge_timestamp = GNUNET_TIME_timestamp_get ();
ds->dh = TALER_EXCHANGE_account_merge ( ds->dh = TALER_EXCHANGE_account_merge (
is->exchange, is->exchange,
NULL, /* no wad */ NULL, /* no wad */
&ds->reserve_priv, &ds->reserve_priv,
purse_pub, &ds->purse_pub,
merge_priv, merge_priv,
&h_contract_terms, &ds->h_contract_terms,
min_age, ds->min_age,
&value_after_fees, &ds->value_after_fees,
purse_expiration, ds->purse_expiration,
ds->merge_timestamp, ds->merge_timestamp,
&merge_cb, &merge_cb,
ds); ds);
@ -285,12 +354,16 @@ merge_traits (void *cls,
{ {
struct PurseMergeState *ds = cls; struct PurseMergeState *ds = cls;
struct TALER_TESTING_Trait traits[] = { struct TALER_TESTING_Trait traits[] = {
/* history entry MUST be first due to response code logic below! */
TALER_TESTING_make_trait_reserve_history (&ds->reserve_history),
TALER_TESTING_make_trait_timestamp (0, TALER_TESTING_make_trait_timestamp (0,
&ds->merge_timestamp), &ds->merge_timestamp),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };
return TALER_TESTING_get_trait (traits, return TALER_TESTING_get_trait ((ds->expected_response_code == MHD_HTTP_OK)
? &traits[0] /* we have reserve history */
: &traits[1], /* skip reserve history */
ret, ret,
trait, trait,
index); index);

View File

@ -32,6 +32,12 @@
*/ */
struct HistoryState struct HistoryState
{ {
/**
* Public key of the reserve being analyzed.
*/
struct TALER_ReservePublicKeyP reserve_pub;
/** /**
* Label to the command which created the reserve to check, * Label to the command which created the reserve to check,
* needed to resort the reserve key. * needed to resort the reserve key.
@ -54,19 +60,21 @@ struct HistoryState
const struct TALER_ReservePrivateKeyP *reserve_priv; const struct TALER_ReservePrivateKeyP *reserve_priv;
/** /**
* Public key of the reserve being analyzed. * Interpreter state.
*/ */
struct TALER_ReservePublicKeyP reserve_pub; struct TALER_TESTING_Interpreter *is;
/**
* Reserve history entry that corresponds to this operation.
* Will be of type #TALER_EXCHANGE_RTT_HISTORY.
*/
struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
/** /**
* Expected HTTP response code. * Expected HTTP response code.
*/ */
unsigned int expected_response_code; unsigned int expected_response_code;
/**
* Interpreter state.
*/
struct TALER_TESTING_Interpreter *is;
}; };
@ -177,6 +185,21 @@ reserve_history_cb (void *cls,
struct TALER_Amount eb; struct TALER_Amount eb;
ss->rsh = NULL; ss->rsh = NULL;
if (MHD_HTTP_OK == rs->hr.http_status)
{
const struct TALER_EXCHANGE_Keys *keys;
const struct TALER_EXCHANGE_GlobalFee *gf;
ss->reserve_history.type = TALER_EXCHANGE_RTT_HISTORY;
keys = TALER_EXCHANGE_get_keys (ss->is->exchange);
GNUNET_assert (NULL != keys);
gf = TALER_EXCHANGE_get_global_fee (keys,
rs->ts);
GNUNET_assert (NULL != gf);
ss->reserve_history.amount = gf->fees.history;
ss->reserve_history.details.history_details.request_timestamp = rs->ts;
ss->reserve_history.details.history_details.reserve_sig = *rs->reserve_sig;
}
if (ss->expected_response_code != rs->hr.http_status) if (ss->expected_response_code != rs->hr.http_status)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -290,6 +313,37 @@ history_run (void *cls,
} }
/**
* Offer internal data from a "history" CMD, to other commands.
*
* @param cls closure.
* @param[out] ret result.
* @param trait name of the trait.
* @param index index number of the object to offer.
* @return #GNUNET_OK on success.
*/
static enum GNUNET_GenericReturnValue
history_traits (void *cls,
const void **ret,
const char *trait,
unsigned int index)
{
struct HistoryState *hs = cls;
struct TALER_TESTING_Trait traits[] = {
/* history entry MUST be first due to response code logic below! */
TALER_TESTING_make_trait_reserve_history (&hs->reserve_history),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait ((hs->expected_response_code == MHD_HTTP_OK)
? &traits[0] /* we have reserve history */
: &traits[1], /* skip reserve history */
ret,
trait,
index);
}
/** /**
* Cleanup the state from a "reserve history" CMD, and possibly * Cleanup the state from a "reserve history" CMD, and possibly
* cancel a pending operation thereof. * cancel a pending operation thereof.
@ -334,7 +388,8 @@ TALER_TESTING_cmd_reserve_history (const char *label,
.cls = ss, .cls = ss,
.label = label, .label = label,
.run = &history_run, .run = &history_run,
.cleanup = &history_cleanup .cleanup = &history_cleanup,
.traits = &history_traits
}; };
return cmd; return cmd;