exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test_kyc_targets.c (24130B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file exchangedb/test_kyc_targets.c
     18  * @brief tests for the exchangedb functions whose primary table is
     19  *        `kyc_targets`
     20  * @author Christian Grothoff
     21  *
     22  * Covers #TALER_EXCHANGEDB_get_aml_file_number(),
     23  * #TALER_EXCHANGEDB_get_h_payto_by_access_token(),
     24  * #TALER_EXCHANGEDB_update_to_aml_locked(),
     25  * #TALER_EXCHANGEDB_update_to_aml_unlocked() and
     26  * #TALER_EXCHANGEDB_iterate_kyc_accounts().
     27  *
     28  * `kyc_targets` is the exchange's list of accounts it knows in a KYC
     29  * sense, keyed by the hash of the *normalized* payto URI.  No exported
     30  * function creates a row -- the deposit and credit paths do, as a side
     31  * effect -- so the checks use TDB_account() for that.  The access token
     32  * is generated by the database itself, so the checks read it back out.
     33  */
     34 #include "test_common.h"
     35 #include "exchange-database/get_aml_file_number.h"
     36 #include "exchange-database/get_h_payto_by_access_token.h"
     37 #include "exchange-database/iterate_kyc_accounts.h"
     38 #include "exchange-database/update_to_aml_locked.h"
     39 #include "exchange-database/update_to_aml_unlocked.h"
     40 
     41 
     42 /**
     43  * Accounts the checks work on.
     44  */
     45 static struct TDB_Account account;
     46 
     47 /**
     48  * A second account.
     49  */
     50 static struct TDB_Account other;
     51 
     52 
     53 /**
     54  * Set the AML file lifecycle timestamps for an account.
     55  *
     56  * @param pg database context
     57  * @param acc account to update
     58  * @param opened true to set an opening timestamp
     59  * @param closed true to set a closing timestamp
     60  */
     61 static void
     62 set_file_lifecycle (struct TALER_EXCHANGEDB_PostgresContext *pg,
     63                     const struct TDB_Account *acc,
     64                     bool opened,
     65                     bool closed)
     66 {
     67   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
     68   struct GNUNET_PQ_QueryParam params[] = {
     69     GNUNET_PQ_query_param_auto_from_type (&acc->h_normalized),
     70     opened
     71     ? GNUNET_PQ_query_param_timestamp (&now)
     72     : GNUNET_PQ_query_param_null (),
     73     closed
     74     ? GNUNET_PQ_query_param_timestamp (&now)
     75     : GNUNET_PQ_query_param_null (),
     76     GNUNET_PQ_query_param_end
     77   };
     78 
     79   GNUNET_assert (GNUNET_OK ==
     80                  GNUNET_PQ_prepare_anon (pg->conn,
     81                                          "UPDATE kyc_targets"
     82                                          " SET open_time=$2, close_time=$3"
     83                                          " WHERE h_normalized_payto=$1;"));
     84   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
     85                  GNUNET_PQ_eval_prepared_non_select (pg->conn,
     86                                                      "",
     87                                                      params));
     88 }
     89 
     90 
     91 /**
     92  * Read the access token the database generated for an account.
     93  *
     94  * @param pg the database context
     95  * @param acc account to look up
     96  * @param[out] access_token set to the account's access token
     97  */
     98 static void
     99 get_access_token (struct TALER_EXCHANGEDB_PostgresContext *pg,
    100                   const struct TDB_Account *acc,
    101                   struct TALER_AccountAccessTokenP *access_token)
    102 {
    103   struct GNUNET_PQ_QueryParam params[] = {
    104     GNUNET_PQ_query_param_auto_from_type (&acc->h_normalized),
    105     GNUNET_PQ_query_param_end
    106   };
    107   struct GNUNET_PQ_ResultSpec rs[] = {
    108     GNUNET_PQ_result_spec_auto_from_type ("access_token",
    109                                           access_token),
    110     GNUNET_PQ_result_spec_end
    111   };
    112 
    113   GNUNET_assert (GNUNET_OK ==
    114                  GNUNET_PQ_prepare_anon (pg->conn,
    115                                          "SELECT access_token"
    116                                          " FROM kyc_targets"
    117                                          " WHERE h_normalized_payto=$1;"));
    118   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    119                  GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    120                                                            "",
    121                                                            params,
    122                                                            rs));
    123 }
    124 
    125 
    126 /**
    127  * Closure for #account_cb().
    128  */
    129 struct AccountContext
    130 {
    131   /**
    132    * How many accounts did the callback see?
    133    */
    134   unsigned int total;
    135 
    136   /**
    137    * Account we are looking for, NULL to match nothing.
    138    */
    139   const struct TALER_NormalizedPaytoHashP *h_payto;
    140 
    141   /**
    142    * How many times did we see it?
    143    */
    144   unsigned int matched;
    145 
    146   /**
    147    * Row of the first account seen.
    148    */
    149   uint64_t first_row;
    150 
    151   /**
    152    * Was the matching account flagged for investigation?
    153    */
    154   bool to_investigate;
    155 
    156   /**
    157    * Was the matching account flagged high risk?
    158    */
    159   bool high_risk;
    160 
    161   /**
    162    * Was a payto URI reported for it?
    163    */
    164   bool have_payto;
    165 };
    166 
    167 
    168 /**
    169  * Callback for #TALER_EXCHANGEDB_iterate_kyc_accounts().
    170  *
    171  * @param cls a `struct AccountContext *`
    172  * @param row_id row of the account
    173  * @param h_payto the account
    174  * @param open_time when the account was opened
    175  * @param close_time when it was closed
    176  * @param comments file note on the account
    177  * @param customer_label human-readable customer label
    178  * @param high_risk whether it is a high-risk relationship
    179  * @param to_investigate whether it is flagged for investigation
    180  * @param payto the account's payto URI
    181  */
    182 static void
    183 account_cb (void *cls,
    184             uint64_t row_id,
    185             const struct TALER_NormalizedPaytoHashP *h_payto,
    186             struct GNUNET_TIME_Timestamp open_time,
    187             struct GNUNET_TIME_Timestamp close_time,
    188             const char *comments,
    189             const char *customer_label,
    190             bool high_risk,
    191             bool to_investigate,
    192             struct TALER_FullPayto payto)
    193 {
    194   struct AccountContext *ctx = cls;
    195 
    196   (void) open_time;
    197   (void) close_time;
    198   (void) comments;
    199   (void) customer_label;
    200   if (0 == ctx->total++)
    201     ctx->first_row = row_id;
    202   if ( (NULL != ctx->h_payto) &&
    203        (0 == GNUNET_memcmp (h_payto,
    204                             ctx->h_payto)) )
    205   {
    206     ctx->matched++;
    207     ctx->to_investigate = to_investigate;
    208     ctx->high_risk = high_risk;
    209     ctx->have_payto = (NULL != payto.full_payto);
    210   }
    211 }
    212 
    213 
    214 /**
    215  * Nothing is known about an account the exchange never saw.
    216  *
    217  * @param pg the database context
    218  * @return 0 on success
    219  */
    220 static int
    221 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg)
    222 {
    223   struct TALER_NormalizedPaytoHashP h_payto;
    224   struct TALER_AccountAccessTokenP access_token;
    225   struct GNUNET_TIME_Absolute existing_lock;
    226   struct AccountContext ctx = { 0 };
    227   uint64_t kyc_target_row;
    228   bool is_wallet;
    229 
    230   TDB_FILL (h_payto,
    231             1);
    232   TDB_FILL (access_token,
    233             1);
    234   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    235           TALER_EXCHANGEDB_get_aml_file_number (pg,
    236                                                 &h_payto,
    237                                                 &kyc_target_row,
    238                                                 &is_wallet));
    239   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    240           TALER_EXCHANGEDB_get_h_payto_by_access_token (pg,
    241                                                         &access_token,
    242                                                         &h_payto,
    243                                                         &is_wallet));
    244   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    245           TALER_EXCHANGEDB_update_to_aml_locked (pg,
    246                                                  &h_payto,
    247                                                  GNUNET_TIME_UNIT_HOURS,
    248                                                  &existing_lock));
    249   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    250           TALER_EXCHANGEDB_update_to_aml_unlocked (pg,
    251                                                    &h_payto));
    252   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    253           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    254                                                  NULL,
    255                                                  TALER_EXCHANGE_YNA_ALL,
    256                                                  TALER_EXCHANGE_YNA_ALL,
    257                                                  TALER_EXCHANGE_YNA_ALL,
    258                                                  0,
    259                                                  10,
    260                                                  &account_cb,
    261                                                  &ctx));
    262   FAILIF (0 != ctx.total);
    263   return 0;
    264 }
    265 
    266 
    267 /**
    268  * A known account is found by its hash and by its access token.
    269  *
    270  * @param pg the database context
    271  * @return 0 on success
    272  */
    273 static int
    274 check_lookup (struct TALER_EXCHANGEDB_PostgresContext *pg)
    275 {
    276   struct TALER_AccountAccessTokenP access_token;
    277   struct TALER_NormalizedPaytoHashP h_payto;
    278   uint64_t kyc_target_row = 0;
    279   bool is_wallet = true;
    280 
    281   TDB_account (pg,
    282                10,
    283                &account);
    284   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    285           TALER_EXCHANGEDB_get_aml_file_number (pg,
    286                                                 &account.h_normalized,
    287                                                 &kyc_target_row,
    288                                                 &is_wallet));
    289   FAILIF (0 == kyc_target_row);
    290   /* TDB_account() creates bank accounts, not wallet accounts */
    291   FAILIF (is_wallet);
    292 
    293   get_access_token (pg,
    294                     &account,
    295                     &access_token);
    296   is_wallet = true;
    297   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    298           TALER_EXCHANGEDB_get_h_payto_by_access_token (pg,
    299                                                         &access_token,
    300                                                         &h_payto,
    301                                                         &is_wallet));
    302   FAILIF (0 != GNUNET_memcmp (&h_payto,
    303                               &account.h_normalized));
    304   FAILIF (is_wallet);
    305 
    306   /* an access token nobody was given finds nothing */
    307   {
    308     struct TALER_AccountAccessTokenP other_token;
    309 
    310     TDB_FILL (other_token,
    311               99);
    312     FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    313             TALER_EXCHANGEDB_get_h_payto_by_access_token (pg,
    314                                                           &other_token,
    315                                                           &h_payto,
    316                                                           &is_wallet));
    317   }
    318   return 0;
    319 }
    320 
    321 
    322 /**
    323  * Locking an account reports the lock to whoever tries next, and
    324  * unlocking clears it.
    325  *
    326  * @param pg the database context
    327  * @return 0 on success
    328  */
    329 static int
    330 check_lock (struct TALER_EXCHANGEDB_PostgresContext *pg)
    331 {
    332   struct GNUNET_TIME_Absolute existing_lock;
    333 
    334   /* no lock yet */
    335   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    336           TALER_EXCHANGEDB_update_to_aml_locked (pg,
    337                                                  &account.h_normalized,
    338                                                  GNUNET_TIME_UNIT_HOURS,
    339                                                  &existing_lock));
    340   FAILIF (! GNUNET_TIME_absolute_is_zero (existing_lock));
    341 
    342   /* a second attempt is told when the first lock runs out */
    343   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    344           TALER_EXCHANGEDB_update_to_aml_locked (pg,
    345                                                  &account.h_normalized,
    346                                                  GNUNET_TIME_UNIT_HOURS,
    347                                                  &existing_lock));
    348   FAILIF (GNUNET_TIME_absolute_is_zero (existing_lock));
    349   FAILIF (GNUNET_TIME_absolute_is_past (existing_lock));
    350 
    351   /* unlocking clears it */
    352   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    353           TALER_EXCHANGEDB_update_to_aml_unlocked (pg,
    354                                                    &account.h_normalized));
    355   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    356           TALER_EXCHANGEDB_update_to_aml_locked (pg,
    357                                                  &account.h_normalized,
    358                                                  GNUNET_TIME_UNIT_HOURS,
    359                                                  &existing_lock));
    360   FAILIF (! GNUNET_TIME_absolute_is_zero (existing_lock));
    361   /* unlocking an account that is not locked is fine */
    362   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    363           TALER_EXCHANGEDB_update_to_aml_unlocked (pg,
    364                                                    &account.h_normalized));
    365   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    366           TALER_EXCHANGEDB_update_to_aml_unlocked (pg,
    367                                                    &account.h_normalized));
    368   return 0;
    369 }
    370 
    371 
    372 /**
    373  * The account listing reports the accounts, with the filters applied.
    374  *
    375  * @param pg the database context
    376  * @return 0 on success
    377  */
    378 static int
    379 check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg)
    380 {
    381   struct AccountContext ctx;
    382   struct TALER_NormalizedPaytoHashP unknown;
    383   uint64_t lowest;
    384 
    385   TDB_account (pg,
    386                11,
    387                &other);
    388   memset (&ctx,
    389           0,
    390           sizeof (ctx));
    391   ctx.h_payto = &account.h_normalized;
    392   FAILIF (2 !=
    393           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    394                                                  NULL,
    395                                                  TALER_EXCHANGE_YNA_ALL,
    396                                                  TALER_EXCHANGE_YNA_ALL,
    397                                                  TALER_EXCHANGE_YNA_ALL,
    398                                                  0,
    399                                                  10,
    400                                                  &account_cb,
    401                                                  &ctx));
    402   FAILIF (1 != ctx.matched);
    403   FAILIF (! ctx.have_payto);
    404   /* no AML decision has been taken, so neither flag is set */
    405   FAILIF (ctx.to_investigate);
    406   FAILIF (ctx.high_risk);
    407   lowest = ctx.first_row;
    408 
    409   /* An exact account filter returns only the requested account. */
    410   memset (&ctx,
    411           0,
    412           sizeof (ctx));
    413   ctx.h_payto = &account.h_normalized;
    414   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    415           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    416                                                  &account.h_normalized,
    417                                                  TALER_EXCHANGE_YNA_ALL,
    418                                                  TALER_EXCHANGE_YNA_ALL,
    419                                                  TALER_EXCHANGE_YNA_ALL,
    420                                                  0,
    421                                                  10,
    422                                                  &account_cb,
    423                                                  &ctx));
    424   FAILIF (1 != ctx.total);
    425   FAILIF (1 != ctx.matched);
    426 
    427   /* An unknown exact account filter returns no result. */
    428   TDB_FILL (unknown,
    429             99);
    430   memset (&ctx,
    431           0,
    432           sizeof (ctx));
    433   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    434           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    435                                                  &unknown,
    436                                                  TALER_EXCHANGE_YNA_ALL,
    437                                                  TALER_EXCHANGE_YNA_ALL,
    438                                                  TALER_EXCHANGE_YNA_ALL,
    439                                                  0,
    440                                                  10,
    441                                                  &account_cb,
    442                                                  &ctx));
    443   FAILIF (0 != ctx.total);
    444 
    445   /* the limit caps the result set */
    446   memset (&ctx,
    447           0,
    448           sizeof (ctx));
    449   FAILIF (1 !=
    450           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    451                                                  NULL,
    452                                                  TALER_EXCHANGE_YNA_ALL,
    453                                                  TALER_EXCHANGE_YNA_ALL,
    454                                                  TALER_EXCHANGE_YNA_ALL,
    455                                                  0,
    456                                                  1,
    457                                                  &account_cb,
    458                                                  &ctx));
    459   FAILIF (lowest != ctx.first_row);
    460 
    461   /* a negative limit walks the list the other way */
    462   memset (&ctx,
    463           0,
    464           sizeof (ctx));
    465   FAILIF (2 !=
    466           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    467                                                  NULL,
    468                                                  TALER_EXCHANGE_YNA_ALL,
    469                                                  TALER_EXCHANGE_YNA_ALL,
    470                                                  TALER_EXCHANGE_YNA_ALL,
    471                                                  1000,
    472                                                  -10,
    473                                                  &account_cb,
    474                                                  &ctx));
    475   FAILIF (lowest == ctx.first_row);
    476 
    477   /* nothing is under investigation... */
    478   memset (&ctx,
    479           0,
    480           sizeof (ctx));
    481   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    482           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    483                                                  NULL,
    484                                                  TALER_EXCHANGE_YNA_YES,
    485                                                  TALER_EXCHANGE_YNA_ALL,
    486                                                  TALER_EXCHANGE_YNA_ALL,
    487                                                  0,
    488                                                  10,
    489                                                  &account_cb,
    490                                                  &ctx));
    491   FAILIF (0 != ctx.total);
    492   /* ...so everything comes back under "not under investigation" */
    493   memset (&ctx,
    494           0,
    495           sizeof (ctx));
    496   FAILIF (2 !=
    497           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    498                                                  NULL,
    499                                                  TALER_EXCHANGE_YNA_NO,
    500                                                  TALER_EXCHANGE_YNA_ALL,
    501                                                  TALER_EXCHANGE_YNA_ALL,
    502                                                  0,
    503                                                  10,
    504                                                  &account_cb,
    505                                                  &ctx));
    506 
    507   /* nothing is high risk either */
    508   memset (&ctx,
    509           0,
    510           sizeof (ctx));
    511   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    512           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    513                                                  NULL,
    514                                                  TALER_EXCHANGE_YNA_ALL,
    515                                                  TALER_EXCHANGE_YNA_ALL,
    516                                                  TALER_EXCHANGE_YNA_YES,
    517                                                  0,
    518                                                  10,
    519                                                  &account_cb,
    520                                                  &ctx));
    521   FAILIF (0 != ctx.total);
    522 
    523   /* Neither AML file has been opened yet. */
    524   memset (&ctx,
    525           0,
    526           sizeof (ctx));
    527   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    528           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    529                                                  NULL,
    530                                                  TALER_EXCHANGE_YNA_ALL,
    531                                                  TALER_EXCHANGE_YNA_YES,
    532                                                  TALER_EXCHANGE_YNA_ALL,
    533                                                  0,
    534                                                  10,
    535                                                  &account_cb,
    536                                                  &ctx));
    537   memset (&ctx,
    538           0,
    539           sizeof (ctx));
    540   FAILIF (2 !=
    541           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    542                                                  NULL,
    543                                                  TALER_EXCHANGE_YNA_ALL,
    544                                                  TALER_EXCHANGE_YNA_NO,
    545                                                  TALER_EXCHANGE_YNA_ALL,
    546                                                  0,
    547                                                  10,
    548                                                  &account_cb,
    549                                                  &ctx));
    550 
    551   /* Opening one AML file splits the YES and NO result sets. */
    552   set_file_lifecycle (pg,
    553                       &account,
    554                       true,
    555                       false);
    556   memset (&ctx,
    557           0,
    558           sizeof (ctx));
    559   FAILIF (1 !=
    560           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    561                                                  NULL,
    562                                                  TALER_EXCHANGE_YNA_ALL,
    563                                                  TALER_EXCHANGE_YNA_YES,
    564                                                  TALER_EXCHANGE_YNA_ALL,
    565                                                  0,
    566                                                  10,
    567                                                  &account_cb,
    568                                                  &ctx));
    569   memset (&ctx,
    570           0,
    571           sizeof (ctx));
    572   FAILIF (1 !=
    573           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    574                                                  NULL,
    575                                                  TALER_EXCHANGE_YNA_ALL,
    576                                                  TALER_EXCHANGE_YNA_NO,
    577                                                  TALER_EXCHANGE_YNA_ALL,
    578                                                  0,
    579                                                  10,
    580                                                  &account_cb,
    581                                                  &ctx));
    582 
    583   /* Closing it moves the file back into the NO result set. */
    584   set_file_lifecycle (pg,
    585                       &account,
    586                       true,
    587                       true);
    588   memset (&ctx,
    589           0,
    590           sizeof (ctx));
    591   FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    592           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    593                                                  NULL,
    594                                                  TALER_EXCHANGE_YNA_ALL,
    595                                                  TALER_EXCHANGE_YNA_YES,
    596                                                  TALER_EXCHANGE_YNA_ALL,
    597                                                  0,
    598                                                  10,
    599                                                  &account_cb,
    600                                                  &ctx));
    601   memset (&ctx,
    602           0,
    603           sizeof (ctx));
    604   FAILIF (2 !=
    605           TALER_EXCHANGEDB_iterate_kyc_accounts (pg,
    606                                                  NULL,
    607                                                  TALER_EXCHANGE_YNA_ALL,
    608                                                  TALER_EXCHANGE_YNA_NO,
    609                                                  TALER_EXCHANGE_YNA_ALL,
    610                                                  0,
    611                                                  10,
    612                                                  &account_cb,
    613                                                  &ctx));
    614   return 0;
    615 }
    616 
    617 
    618 /**
    619  * The checks to run, in order.
    620  */
    621 static const struct TDB_Test tests[] = {
    622   { "kyc-targets-empty",
    623     &check_empty },
    624   { "kyc-targets-lookup",
    625     &check_lookup },
    626   { "kyc-targets-lock",
    627     &check_lock },
    628   { "kyc-targets-iterate",
    629     &check_iterate },
    630   { NULL, NULL }
    631 };
    632 
    633 
    634 int
    635 main (int argc,
    636       char *const *argv)
    637 {
    638   int ret;
    639 
    640   ret = TDB_main (argc,
    641                   argv,
    642                   "test-kyc-targets",
    643                   "Tests for the exchangedb `kyc_targets' table",
    644                   tests);
    645   TDB_account_free (&account);
    646   TDB_account_free (&other);
    647   return ret;
    648 }
    649 
    650 
    651 /* end of test_kyc_targets.c */