LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_check_keys.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 64.7 % 218 141
Test Date: 2026-10-17 15:46:49 Functions: 100.0 % 14 14

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2026 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify
       6              :   it under the terms of the GNU General Public License as
       7              :   published by the Free Software Foundation; either version 3, or
       8              :   (at your option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful, but
      11              :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13              :   GNU General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not, see
      17              :   <http://www.gnu.org/licenses/>
      18              : */
      19              : /**
      20              :  * @file testing/testing_api_cmd_check_keys.c
      21              :  * @brief command to compare the /keys of successive "get exchange" commands
      22              :  * @author Christian Grothoff
      23              :  */
      24              : #include "taler/taler_json_lib.h"
      25              : #include <gnunet/gnunet_curl_lib.h>
      26              : #include "taler/taler_testing_lib.h"
      27              : 
      28              : 
      29              : /**
      30              :  * State for a "check keys" CMD.
      31              :  */
      32              : struct CheckKeysState
      33              : {
      34              :   /**
      35              :    * Label of the "get exchange" command with the earlier /keys.
      36              :    */
      37              :   const char *earlier_ref;
      38              : 
      39              :   /**
      40              :    * Label of the "get exchange" command with the later /keys.
      41              :    */
      42              :   const char *later_ref;
      43              : 
      44              :   /**
      45              :    * Label of a "get exchange" command that downloaded the full /keys
      46              :    * after @e later_ref, or NULL.
      47              :    */
      48              :   const char *full_ref;
      49              : };
      50              : 
      51              : 
      52              : /**
      53              :  * /keys of a "get exchange" command and when they were requested.
      54              :  */
      55              : struct KeysSnapshot
      56              : {
      57              :   /**
      58              :    * Label of the command.
      59              :    */
      60              :   const char *label;
      61              : 
      62              :   /**
      63              :    * The keys.
      64              :    */
      65              :   const struct TALER_EXCHANGE_Keys *keys;
      66              : 
      67              :   /**
      68              :    * When the request for @e keys was sent.
      69              :    */
      70              :   struct GNUNET_TIME_Absolute request_time;
      71              : 
      72              :   /**
      73              :    * When the reply with @e keys was received.
      74              :    */
      75              :   struct GNUNET_TIME_Absolute reply_time;
      76              : };
      77              : 
      78              : 
      79              : /**
      80              :  * Obtain the /keys of the "get exchange" command @a label.
      81              :  *
      82              :  * @param is interpreter state
      83              :  * @param label label of the command
      84              :  * @param[out] ks set to the keys of the command
      85              :  * @return #GNUNET_OK on success
      86              :  */
      87              : static enum GNUNET_GenericReturnValue
      88           18 : get_snapshot (struct TALER_TESTING_Interpreter *is,
      89              :               const char *label,
      90              :               struct KeysSnapshot *ks)
      91              : {
      92              :   const struct TALER_TESTING_Command *cmd;
      93              :   struct TALER_EXCHANGE_Keys *keys;
      94              :   const struct GNUNET_TIME_Absolute *request_time;
      95              :   const struct GNUNET_TIME_Absolute *reply_time;
      96              : 
      97           18 :   cmd = TALER_TESTING_interpreter_lookup_command (is,
      98              :                                                   label);
      99           18 :   if (NULL == cmd)
     100              :   {
     101            0 :     GNUNET_break (0);
     102            0 :     return GNUNET_SYSERR;
     103              :   }
     104           18 :   if ( (GNUNET_OK !=
     105           18 :         TALER_TESTING_get_trait_keys (cmd,
     106           18 :                                       &keys)) ||
     107           36 :        (NULL == keys) ||
     108              :        (GNUNET_OK !=
     109           18 :         TALER_TESTING_get_trait_absolute_time (cmd,
     110              :                                                0,
     111           18 :                                                &request_time)) ||
     112              :        (GNUNET_OK !=
     113           18 :         TALER_TESTING_get_trait_absolute_time (cmd,
     114              :                                                1,
     115              :                                                &reply_time)) )
     116              :   {
     117            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     118              :                 "Command `%s' did not download /keys\n",
     119              :                 label);
     120            0 :     return GNUNET_SYSERR;
     121              :   }
     122           18 :   ks->label = label;
     123           18 :   ks->keys = keys;
     124           18 :   ks->request_time = *request_time;
     125           18 :   ks->reply_time = *reply_time;
     126           18 :   return GNUNET_OK;
     127              : }
     128              : 
     129              : 
     130              : /**
     131              :  * Find the denomination @a dk in @a keys.
     132              :  *
     133              :  * @param keys keys to search
     134              :  * @param dk denomination to find (by its hash)
     135              :  * @return NULL if @a keys does not contain @a dk
     136              :  */
     137              : static const struct TALER_EXCHANGE_DenomPublicKey *
     138           66 : find_denom (const struct TALER_EXCHANGE_Keys *keys,
     139              :             const struct TALER_EXCHANGE_DenomPublicKey *dk)
     140              : {
     141          162 :   for (unsigned int i = 0; i<keys->num_denom_keys; i++)
     142          162 :     if (0 ==
     143          162 :         GNUNET_memcmp (&dk->h_key,
     144              :                        &keys->denom_keys[i].h_key))
     145           66 :       return &keys->denom_keys[i];
     146            0 :   return NULL;
     147              : }
     148              : 
     149              : 
     150              : /**
     151              :  * Check if @a a and @a b are the same amount.
     152              :  *
     153              :  * @param a an amount
     154              :  * @param b another amount
     155              :  * @return true if they are equal
     156              :  */
     157              : static bool
     158          330 : amount_equal (const struct TALER_Amount *a,
     159              :               const struct TALER_Amount *b)
     160              : {
     161              :   return (GNUNET_YES ==
     162          330 :           TALER_amount_cmp_currency (a,
     163          660 :                                      b)) &&
     164          330 :          (0 == TALER_amount_cmp (a,
     165              :                                  b));
     166              : }
     167              : 
     168              : 
     169              : /**
     170              :  * Check if @a a and @a b describe the same denomination with the
     171              :  * same meta data.
     172              :  *
     173              :  * @param a a denomination
     174              :  * @param b another denomination
     175              :  * @return true if they are the same
     176              :  */
     177              : static bool
     178           66 : denom_equal (const struct TALER_EXCHANGE_DenomPublicKey *a,
     179              :              const struct TALER_EXCHANGE_DenomPublicKey *b)
     180              : {
     181           66 :   return (0 == GNUNET_memcmp (&a->h_key,
     182           66 :                               &b->h_key)) &&
     183           66 :          (0 == TALER_denom_pub_cmp (&a->key,
     184           66 :                                     &b->key)) &&
     185           66 :          (0 == GNUNET_memcmp (&a->master_sig,
     186           66 :                               &b->master_sig)) &&
     187           66 :          (GNUNET_TIME_timestamp_cmp (a->valid_from,
     188              :                                      ==,
     189           66 :                                      b->valid_from)) &&
     190           66 :          (GNUNET_TIME_timestamp_cmp (a->withdraw_valid_until,
     191              :                                      ==,
     192           66 :                                      b->withdraw_valid_until)) &&
     193           66 :          (GNUNET_TIME_timestamp_cmp (a->expire_deposit,
     194              :                                      ==,
     195           66 :                                      b->expire_deposit)) &&
     196           66 :          (GNUNET_TIME_timestamp_cmp (a->expire_legal,
     197              :                                      ==,
     198           66 :                                      b->expire_legal)) &&
     199           66 :          amount_equal (&a->value,
     200           66 :                        &b->value) &&
     201           66 :          amount_equal (&a->fees.withdraw,
     202           66 :                        &b->fees.withdraw) &&
     203           66 :          amount_equal (&a->fees.deposit,
     204           66 :                        &b->fees.deposit) &&
     205           66 :          amount_equal (&a->fees.refresh,
     206           66 :                        &b->fees.refresh) &&
     207           66 :          amount_equal (&a->fees.refund,
     208          132 :                        &b->fees.refund) &&
     209           66 :          (a->revoked == b->revoked);
     210              : }
     211              : 
     212              : 
     213              : /**
     214              :  * Check that every denomination of @a a that is still valid after
     215              :  * @a valid_after is also in @a b, with the same meta data.
     216              :  *
     217              :  * @param a keys with the denominations to look for
     218              :  * @param b keys to search
     219              :  * @param valid_after denominations of @a a that expire before this
     220              :  *        time are not checked
     221              :  * @return true if all of them were found
     222              :  */
     223              : static bool
     224           18 : denoms_included (const struct KeysSnapshot *a,
     225              :                  const struct KeysSnapshot *b,
     226              :                  struct GNUNET_TIME_Absolute valid_after)
     227              : {
     228           18 :   bool ok = true;
     229              : 
     230           86 :   for (unsigned int i = 0; i<a->keys->num_denom_keys; i++)
     231              :   {
     232           68 :     const struct TALER_EXCHANGE_DenomPublicKey *dk
     233           68 :       = &a->keys->denom_keys[i];
     234              :     const struct TALER_EXCHANGE_DenomPublicKey *dkb;
     235              : 
     236           68 :     if (GNUNET_TIME_absolute_cmp (dk->expire_deposit.abs_time,
     237              :                                   <=,
     238              :                                   valid_after))
     239            2 :       continue;
     240           66 :     dkb = find_denom (b->keys,
     241              :                       dk);
     242           66 :     if (NULL == dkb)
     243              :     {
     244            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     245              :                   "Denomination %s (valid until %s) of `%s' missing in `%s'\n",
     246              :                   GNUNET_h2s (&dk->h_key.hash),
     247              :                   GNUNET_TIME_timestamp2s (dk->expire_deposit),
     248              :                   a->label,
     249              :                   b->label);
     250            0 :       ok = false;
     251            0 :       continue;
     252              :     }
     253           66 :     if (! denom_equal (dk,
     254              :                        dkb))
     255              :     {
     256            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     257              :                   "Denomination %s differs between `%s' and `%s'\n",
     258              :                   GNUNET_h2s (&dk->h_key.hash),
     259              :                   a->label,
     260              :                   b->label);
     261            0 :       ok = false;
     262              :     }
     263              :   }
     264           18 :   return ok;
     265              : }
     266              : 
     267              : 
     268              : /**
     269              :  * Check that @a ks lists no denomination that had expired
     270              :  * when its /keys were requested.
     271              :  *
     272              :  * @param ks keys to check
     273              :  * @return true if no expired denomination is listed
     274              :  */
     275              : static bool
     276           12 : no_expired_denoms (const struct KeysSnapshot *ks)
     277              : {
     278           12 :   bool ok = true;
     279              : 
     280           56 :   for (unsigned int i = 0; i<ks->keys->num_denom_keys; i++)
     281              :   {
     282           44 :     const struct TALER_EXCHANGE_DenomPublicKey *dk
     283           44 :       = &ks->keys->denom_keys[i];
     284              : 
     285           44 :     if (GNUNET_TIME_absolute_cmp (dk->expire_deposit.abs_time,
     286              :                                   <,
     287              :                                   ks->request_time))
     288              :     {
     289            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     290              :                   "`%s' lists denomination %s that expired at %s\n",
     291              :                   ks->label,
     292              :                   GNUNET_h2s (&dk->h_key.hash),
     293              :                   GNUNET_TIME_timestamp2s (dk->expire_deposit));
     294            0 :       ok = false;
     295              :     }
     296              :   }
     297           12 :   return ok;
     298              : }
     299              : 
     300              : 
     301              : /**
     302              :  * Check that @a ks lists each denomination only once.
     303              :  *
     304              :  * @param ks keys to check
     305              :  * @return true if there are no duplicates
     306              :  */
     307              : static bool
     308           12 : no_duplicate_denoms (const struct KeysSnapshot *ks)
     309              : {
     310           56 :   for (unsigned int i = 0; i<ks->keys->num_denom_keys; i++)
     311          108 :     for (unsigned int j = i + 1; j<ks->keys->num_denom_keys; j++)
     312           64 :       if (0 ==
     313           64 :           GNUNET_memcmp (&ks->keys->denom_keys[i].h_key,
     314              :                          &ks->keys->denom_keys[j].h_key))
     315              :       {
     316            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     317              :                     "`%s' lists denomination %s twice\n",
     318              :                     ks->label,
     319              :                     GNUNET_h2s (&ks->keys->denom_keys[i].h_key.hash));
     320            0 :         return false;
     321              :       }
     322           12 :   return true;
     323              : }
     324              : 
     325              : 
     326              : /**
     327              :  * Check that @a later is a valid successor of @a earlier: same master
     328              :  * public key and currency, all denominations of @a earlier that are
     329              :  * still valid are kept, none that expired, and none twice.
     330              :  *
     331              :  * @param earlier earlier /keys
     332              :  * @param later later /keys
     333              :  * @return true if @a later is a valid successor of @a earlier
     334              :  */
     335              : static bool
     336            6 : check_successor (const struct KeysSnapshot *earlier,
     337              :                  const struct KeysSnapshot *later)
     338              : {
     339            6 :   bool ok = true;
     340              : 
     341            6 :   if (0 !=
     342            6 :       GNUNET_memcmp (&earlier->keys->master_pub,
     343              :                      &later->keys->master_pub))
     344              :   {
     345            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     346              :                 "Master public key changed between `%s' and `%s'\n",
     347              :                 earlier->label,
     348              :                 later->label);
     349            0 :     ok = false;
     350              :   }
     351            6 :   if (0 != strcmp (earlier->keys->currency,
     352            6 :                    later->keys->currency))
     353              :   {
     354            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     355              :                 "Currency changed between `%s' and `%s'\n",
     356              :                 earlier->label,
     357              :                 later->label);
     358            0 :     ok = false;
     359              :   }
     360              :   /* Denominations that were still valid when the later reply was
     361              :      received must have been kept. */
     362            6 :   if (! denoms_included (earlier,
     363              :                          later,
     364              :                          later->reply_time))
     365            0 :     ok = false;
     366            6 :   if (! no_expired_denoms (later))
     367            0 :     ok = false;
     368            6 :   if (! no_duplicate_denoms (later))
     369            0 :     ok = false;
     370            6 :   return ok;
     371              : }
     372              : 
     373              : 
     374              : /**
     375              :  * Check that the auditors of @a a are also auditors in @a b, auditing
     376              :  * at least the same denominations, ignoring denominations that expire
     377              :  * before @a valid_after.
     378              :  *
     379              :  * @param a keys with the auditors to look for
     380              :  * @param b keys to search
     381              :  * @param valid_after ignore denominations that expire before this time
     382              :  * @return true if all of them were found
     383              :  */
     384              : static bool
     385           12 : auditors_included (const struct KeysSnapshot *a,
     386              :                    const struct KeysSnapshot *b,
     387              :                    struct GNUNET_TIME_Absolute valid_after)
     388              : {
     389           12 :   for (unsigned int i = 0; i<a->keys->num_auditors; i++)
     390              :   {
     391            0 :     const struct TALER_EXCHANGE_AuditorInformation *ai
     392            0 :       = &a->keys->auditors[i];
     393            0 :     const struct TALER_EXCHANGE_AuditorInformation *bi = NULL;
     394              : 
     395            0 :     for (unsigned int j = 0; j<b->keys->num_auditors; j++)
     396            0 :       if ( (0 ==
     397            0 :             GNUNET_memcmp (&ai->auditor_pub,
     398            0 :                            &b->keys->auditors[j].auditor_pub)) &&
     399            0 :            (0 == strcmp (ai->auditor_url,
     400            0 :                          b->keys->auditors[j].auditor_url)) )
     401            0 :         bi = &b->keys->auditors[j];
     402            0 :     if (NULL == bi)
     403              :     {
     404            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     405              :                   "Auditor %s of `%s' missing in `%s'\n",
     406              :                   ai->auditor_url,
     407              :                   a->label,
     408              :                   b->label);
     409            0 :       return false;
     410              :     }
     411            0 :     for (unsigned int k = 0; k<ai->num_denom_keys; k++)
     412              :     {
     413            0 :       const struct TALER_EXCHANGE_DenomPublicKey *dk
     414            0 :         = &a->keys->denom_keys[ai->denom_keys[k].denom_key_offset];
     415            0 :       bool found = false;
     416              : 
     417            0 :       if (GNUNET_TIME_absolute_cmp (dk->expire_deposit.abs_time,
     418              :                                     <=,
     419              :                                     valid_after))
     420            0 :         continue;
     421            0 :       for (unsigned int l = 0; l<bi->num_denom_keys; l++)
     422            0 :         if (0 ==
     423            0 :             GNUNET_memcmp (
     424              :               &dk->h_key,
     425              :               &b->keys->denom_keys[bi->denom_keys[l].denom_key_offset].h_key))
     426            0 :           found = true;
     427            0 :       if (! found)
     428              :       {
     429            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     430              :                     "Auditor %s does not audit denomination %s in `%s'\n",
     431              :                     ai->auditor_url,
     432              :                     GNUNET_h2s (&dk->h_key.hash),
     433              :                     b->label);
     434            0 :         return false;
     435              :       }
     436              :     }
     437              :   }
     438           12 :   return true;
     439              : }
     440              : 
     441              : 
     442              : /**
     443              :  * Check that the signing keys of @a a are also in @a b.
     444              :  *
     445              :  * @param a keys with the signing keys to look for
     446              :  * @param b keys to search
     447              :  * @return true if all of them were found
     448              :  */
     449              : static bool
     450           12 : signkeys_included (const struct KeysSnapshot *a,
     451              :                    const struct KeysSnapshot *b)
     452              : {
     453           24 :   for (unsigned int i = 0; i<a->keys->num_sign_keys; i++)
     454              :   {
     455           12 :     const struct TALER_EXCHANGE_SigningPublicKey *sk
     456           12 :       = &a->keys->sign_keys[i];
     457           12 :     bool found = false;
     458              : 
     459           24 :     for (unsigned int j = 0; j<b->keys->num_sign_keys; j++)
     460           12 :       if ( (0 ==
     461           12 :             GNUNET_memcmp (&sk->key,
     462           12 :                            &b->keys->sign_keys[j].key)) &&
     463              :            (0 ==
     464           12 :             GNUNET_memcmp (&sk->master_sig,
     465              :                            &b->keys->sign_keys[j].master_sig)) )
     466           12 :         found = true;
     467           12 :     if (! found)
     468              :     {
     469            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     470              :                   "Signing key %s of `%s' missing in `%s'\n",
     471              :                   TALER_B2S (&sk->key),
     472              :                   a->label,
     473              :                   b->label);
     474            0 :       return false;
     475              :     }
     476              :   }
     477           12 :   return true;
     478              : }
     479              : 
     480              : 
     481              : /**
     482              :  * Check that @a later (cherry-picked and merged by the client) equals
     483              :  * @a full, downloaded in full right after it.  Denominations that
     484              :  * expire between the two downloads may legitimately differ and are
     485              :  * ignored.
     486              :  *
     487              :  * @param later cherry-picked /keys
     488              :  * @param full full /keys
     489              :  * @return true if they are the same
     490              :  */
     491              : static bool
     492            6 : check_equal (const struct KeysSnapshot *later,
     493              :              const struct KeysSnapshot *full)
     494              : {
     495            6 :   bool ok = true;
     496              : 
     497            6 :   if (0 !=
     498            6 :       GNUNET_memcmp (&later->keys->master_pub,
     499              :                      &full->keys->master_pub))
     500              :   {
     501            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     502              :                 "Master public key differs between `%s' and `%s'\n",
     503              :                 later->label,
     504              :                 full->label);
     505            0 :     ok = false;
     506              :   }
     507            6 :   if (0 != strcmp (later->keys->currency,
     508            6 :                    full->keys->currency))
     509              :   {
     510            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     511              :                 "Currency differs between `%s' and `%s'\n",
     512              :                 later->label,
     513              :                 full->label);
     514            0 :     ok = false;
     515              :   }
     516            6 :   if (! denoms_included (later,
     517              :                          full,
     518              :                          full->reply_time))
     519            0 :     ok = false;
     520            6 :   if (! denoms_included (full,
     521              :                          later,
     522              :                          full->reply_time))
     523            0 :     ok = false;
     524            6 :   if (! auditors_included (later,
     525              :                            full,
     526              :                            full->reply_time))
     527            0 :     ok = false;
     528            6 :   if (! auditors_included (full,
     529              :                            later,
     530              :                            full->reply_time))
     531            0 :     ok = false;
     532            6 :   if (! signkeys_included (later,
     533              :                            full))
     534            0 :     ok = false;
     535            6 :   if (! signkeys_included (full,
     536              :                            later))
     537            0 :     ok = false;
     538            6 :   if (! no_expired_denoms (full))
     539            0 :     ok = false;
     540            6 :   if (! no_duplicate_denoms (full))
     541            0 :     ok = false;
     542            6 :   return ok;
     543              : }
     544              : 
     545              : 
     546              : /**
     547              :  * Run the "check keys" command.
     548              :  *
     549              :  * @param cls closure.
     550              :  * @param cmd the command currently being executed.
     551              :  * @param is the interpreter state.
     552              :  */
     553              : static void
     554            6 : check_keys_run (void *cls,
     555              :                 const struct TALER_TESTING_Command *cmd,
     556              :                 struct TALER_TESTING_Interpreter *is)
     557              : {
     558            6 :   struct CheckKeysState *cks = cls;
     559              :   struct KeysSnapshot earlier;
     560              :   struct KeysSnapshot later;
     561              : 
     562              :   (void) cmd;
     563            6 :   if ( (GNUNET_OK !=
     564            6 :         get_snapshot (is,
     565              :                       cks->earlier_ref,
     566            6 :                       &earlier)) ||
     567              :        (GNUNET_OK !=
     568            6 :         get_snapshot (is,
     569              :                       cks->later_ref,
     570              :                       &later)) )
     571              :   {
     572            0 :     GNUNET_break (0);
     573            0 :     TALER_TESTING_interpreter_fail (is);
     574            0 :     return;
     575              :   }
     576            6 :   if (! check_successor (&earlier,
     577              :                          &later))
     578              :   {
     579            0 :     GNUNET_break (0);
     580            0 :     TALER_TESTING_interpreter_fail (is);
     581            0 :     return;
     582              :   }
     583            6 :   if (NULL != cks->full_ref)
     584              :   {
     585              :     struct KeysSnapshot full;
     586              : 
     587            6 :     if (GNUNET_OK !=
     588            6 :         get_snapshot (is,
     589              :                       cks->full_ref,
     590              :                       &full))
     591              :     {
     592            0 :       GNUNET_break (0);
     593            0 :       TALER_TESTING_interpreter_fail (is);
     594            0 :       return;
     595              :     }
     596            6 :     if (GNUNET_TIME_absolute_cmp (full.request_time,
     597              :                                   <,
     598              :                                   later.reply_time))
     599              :     {
     600            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     601              :                   "`%s' must download /keys after `%s'\n",
     602              :                   cks->full_ref,
     603              :                   cks->later_ref);
     604            0 :       GNUNET_break (0);
     605            0 :       TALER_TESTING_interpreter_fail (is);
     606            0 :       return;
     607              :     }
     608            6 :     if (! check_equal (&later,
     609              :                        &full))
     610              :     {
     611            0 :       GNUNET_break (0);
     612            0 :       TALER_TESTING_interpreter_fail (is);
     613            0 :       return;
     614              :     }
     615              :   }
     616            6 :   TALER_TESTING_interpreter_next (is);
     617              : }
     618              : 
     619              : 
     620              : /**
     621              :  * Cleanup the state.
     622              :  *
     623              :  * @param cls closure.
     624              :  * @param cmd the command which is being cleaned up.
     625              :  */
     626              : static void
     627            6 : check_keys_cleanup (void *cls,
     628              :                     const struct TALER_TESTING_Command *cmd)
     629              : {
     630            6 :   struct CheckKeysState *cks = cls;
     631              : 
     632              :   (void) cmd;
     633            6 :   GNUNET_free (cks);
     634            6 : }
     635              : 
     636              : 
     637              : struct TALER_TESTING_Command
     638            6 : TALER_TESTING_cmd_check_keys (const char *label,
     639              :                               const char *earlier_ref,
     640              :                               const char *later_ref,
     641              :                               const char *full_ref)
     642              : {
     643              :   struct CheckKeysState *cks;
     644              : 
     645            6 :   GNUNET_assert (NULL != earlier_ref);
     646            6 :   GNUNET_assert (NULL != later_ref);
     647            6 :   cks = GNUNET_new (struct CheckKeysState);
     648            6 :   cks->earlier_ref = earlier_ref;
     649            6 :   cks->later_ref = later_ref;
     650            6 :   cks->full_ref = full_ref;
     651              :   {
     652            6 :     struct TALER_TESTING_Command cmd = {
     653              :       .cls = cks,
     654              :       .label = label,
     655              :       .run = &check_keys_run,
     656              :       .cleanup = &check_keys_cleanup
     657              :     };
     658              : 
     659            6 :     return cmd;
     660              :   }
     661              : }
     662              : 
     663              : 
     664              : /* end of testing_api_cmd_check_keys.c */
        

Generated by: LCOV version 2.0-1