From d5351641f8124040c02ced06690fda36535bedcf Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 28 Mar 2020 20:45:53 +0100 Subject: implement #6049 --- src/testing/testing_api_cmd_status.c | 230 ++++++++++++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 20 deletions(-) (limited to 'src/testing/testing_api_cmd_status.c') diff --git a/src/testing/testing_api_cmd_status.c b/src/testing/testing_api_cmd_status.c index bb1326456..71714d3ff 100644 --- a/src/testing/testing_api_cmd_status.c +++ b/src/testing/testing_api_cmd_status.c @@ -48,6 +48,11 @@ struct StatusState */ const char *expected_balance; + /** + * Public key of the reserve being analyzed. + */ + const struct TALER_ReservePublicKeyP *reserve_pubp; + /** * Expected HTTP response code. */ @@ -60,6 +65,175 @@ 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)) && + (h1->details.in_details.wire_reference_size == + h2->details.in_details.wire_reference_size) && + (0 == strcasecmp (h1->details.in_details.sender_url, + h2->details.in_details.sender_url)) && + (0 == memcmp (h1->details.in_details.wire_reference, + h2->details.in_details.wire_reference, + h1->details.in_details.wire_reference_size)) && + (h1->details.in_details.timestamp.abs_value_us == + h2->details.in_details.timestamp.abs_value_us) ) + 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 int +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)) + { +#define BATCH_INDEX 1 + 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_cmd (cmd, + BATCH_INDEX, + &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, + 0, + &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, + 0, + &he)) + { + /* NOTE: good for debugging for now, might later reduce debug + level in case there are commands that legitimately don't + impact the reserve history but have the public key trait */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "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; ilabel); + return GNUNET_SYSERR; + } +} + + /** * Check that the reserve balance and HTTP response code are * both acceptable. @@ -83,6 +257,7 @@ reserve_status_cb (void *cls, const struct TALER_EXCHANGE_ReserveHistory *history) { struct StatusState *ss = cls; + struct TALER_TESTING_Interpreter *is = ss->is; struct TALER_Amount eb; ss->rsh = NULL; @@ -111,24 +286,40 @@ reserve_status_cb (void *cls, TALER_TESTING_interpreter_fail (ss->is); return; } + { + int found[history_length]; - /** - * TODO (#6049): We should check that reserve history is consistent. Every - * command which relates to reserve 'x' should be added in a linked list of - * all commands that relate to the same reserve 'x'. - * - * API-wise, any command that relates to a reserve should offer a - * method called e.g. "compare_with_history" that takes an element - * of the array returned by "/reserve/status" and checks if that - * element correspond to itself (= the command exposing the check- - * method). - * - * IDEA: Maybe realize this via another trait, some kind of - * "reserve history update trait" which returns information about - * how the command changes the history (provided only by commands - * that change reserve balances)? - */// - TALER_TESTING_interpreter_next (ss->is); + memset (found, 0, sizeof (found)); + for (unsigned int i = 0; i<=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; iis); + return; + } + } + TALER_TESTING_interpreter_next (is); } @@ -146,7 +337,6 @@ status_run (void *cls, { struct StatusState *ss = cls; const struct TALER_TESTING_Command *create_reserve; - const struct TALER_ReservePublicKeyP *reserve_pubp; ss->is = is; create_reserve @@ -162,7 +352,7 @@ status_run (void *cls, if (GNUNET_OK != TALER_TESTING_get_trait_reserve_pub (create_reserve, 0, - &reserve_pubp)) + &ss->reserve_pubp)) { GNUNET_break (0); TALER_LOG_ERROR ("Failed to find reserve_pub for status query\n"); @@ -170,7 +360,7 @@ status_run (void *cls, return; } ss->rsh = TALER_EXCHANGE_reserves_get (is->exchange, - reserve_pubp, + ss->reserve_pubp, &reserve_status_cb, ss); } -- cgit v1.2.3