frosix

Multiparty signature service (experimental)
Log | Files | Refs | README | LICENSE

plugin_frosix_postgres.c (45869B)


      1 /*
      2   This file is part of Frosix
      3   Copyright (C) 2020, 2021, 2022 Anastasis SARL
      4 
      5   Frosix is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Frosix 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Frosix; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file frosixdb/plugin_frosix_postgres.c
     18  * @brief database helper functions for postgres used by Frosix
     19  * @author Christian Grothoff
     20  * @author Marcello Stanisci
     21  */
     22 #include "platform.h"
     23 #include "frosix_database_plugin.h"
     24 #include "frosix_database_lib.h"
     25 #include "keygen.h"
     26 #include "frosix-httpd_dkg.h"
     27 #include <taler/taler_pq_lib.h>
     28 #include <gnunet/gnunet_pq_lib.h>
     29 
     30 /**
     31  * How long do we keep transient accounts open (those that have
     32  * not been paid at all, but are awaiting payment). This puts
     33  * a cap on how long users have to make a payment after a payment
     34  * request was generated.
     35  */
     36 #define TRANSIENT_LIFETIME GNUNET_TIME_UNIT_WEEKS
     37 
     38 /**
     39  * How often do we re-try if we run into a DB serialization error?
     40  */
     41 #define MAX_RETRIES 3
     42 
     43 
     44 /**
     45  * Type of the "cls" argument given to each of the functions in
     46  * our API.
     47  */
     48 struct PostgresClosure
     49 {
     50 
     51   /**
     52    * Postgres connection handle.
     53    */
     54   struct GNUNET_PQ_Context *conn;
     55 
     56   /**
     57    * Underlying configuration.
     58    */
     59   const struct GNUNET_CONFIGURATION_Handle *cfg;
     60 
     61   /**
     62    * Name of the currently active transaction, NULL if none is active.
     63    */
     64   const char *transaction_name;
     65 
     66   /**
     67    * Currency we accept payments in.
     68    */
     69   char *currency;
     70 
     71   /**
     72    * Prepared statements have been initialized.
     73    */
     74   bool init;
     75 };
     76 
     77 
     78 /**
     79  * Drop anastasis tables
     80  *
     81  * @param cls closure our `struct Plugin`
     82  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
     83  */
     84 static enum GNUNET_GenericReturnValue
     85 postgres_drop_tables (void *cls)
     86 {
     87   struct PostgresClosure *pg = cls;
     88   struct GNUNET_PQ_Context *conn;
     89   enum GNUNET_GenericReturnValue ret;
     90 
     91   conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
     92                                      "frosixdb-postgres",
     93                                      NULL,
     94                                      NULL,
     95                                      NULL);
     96   if (NULL == conn)
     97     return GNUNET_SYSERR;
     98   ret = GNUNET_PQ_exec_sql (conn,
     99                             "drop");
    100   GNUNET_PQ_disconnect (conn);
    101   return ret;
    102 }
    103 
    104 
    105 /**
    106  * Initialize tables.
    107  *
    108  * @param cls the `struct PostgresClosure` with the plugin-specific state
    109  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
    110  */
    111 static enum GNUNET_GenericReturnValue
    112 postgres_create_tables (void *cls)
    113 {
    114   struct PostgresClosure *pc = cls;
    115   struct GNUNET_PQ_Context *conn;
    116   struct GNUNET_PQ_ExecuteStatement es[] = {
    117     GNUNET_PQ_make_execute ("SET search_path TO frosix;"),
    118     GNUNET_PQ_EXECUTE_STATEMENT_END
    119   };
    120 
    121   conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
    122                                      "frosixdb-postgres",
    123                                      "frosix-",
    124                                      es,
    125                                      NULL);
    126   if (NULL == conn)
    127     return GNUNET_SYSERR;
    128   GNUNET_PQ_disconnect (conn);
    129   return GNUNET_OK;
    130 }
    131 
    132 
    133 /**
    134  * Establish connection to the database.
    135  *
    136  * @param cls plugin context
    137  * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
    138  */
    139 static enum GNUNET_GenericReturnValue
    140 prepare_statements (void *cls)
    141 {
    142   struct PostgresClosure *pg = cls;
    143   struct GNUNET_PQ_PreparedStatement ps[] = {
    144     GNUNET_PQ_make_prepare ("do_commit",
    145                             "COMMIT"),
    146     GNUNET_PQ_make_prepare ("dkg_commitment_insert",
    147                             "INSERT INTO frosix_public_commitments "
    148                             "(dkg_id"
    149                             ",time_stamp"
    150                             ",commitments"
    151                             ") VALUES "
    152                             "($1, $2, $3);"),
    153     GNUNET_PQ_make_prepare ("dkg_commitments_select",
    154                             "SELECT"
    155                             " commitments"
    156                             " FROM frosix_public_commitments"
    157                             " WHERE dkg_id=$1;"),
    158     GNUNET_PQ_make_prepare ("dkg_commitments_lookup",
    159                             "SELECT"
    160                             " dkg_id"
    161                             " FROM frosix_public_commitments"
    162                             " WHERE dkg_id=$1;"),
    163     GNUNET_PQ_make_prepare ("dkg_key_insert",
    164                             "INSERT INTO frosix_key "
    165                             "(key_id"
    166                             ",identifier"
    167                             ",expiration"
    168                             ",encryption_nonce"
    169                             ",enc_key_data"
    170                             ",challenge_data"
    171                             ") VALUES "
    172                             "($1, $2, $3, $4, $5, $6);"),
    173     GNUNET_PQ_make_prepare ("key_lookup",
    174                             "SELECT"
    175                             " key_id"
    176                             " FROM frosix_key"
    177                             " WHERE key_id=$1;"),
    178     GNUNET_PQ_make_prepare ("key_data_select",
    179                             "SELECT"
    180                             " identifier"
    181                             ",encryption_nonce"
    182                             ",enc_key_data"
    183                             " FROM frosix_key"
    184                             " WHERE key_id=$1;"),
    185     GNUNET_PQ_make_prepare ("key_delete",
    186                             "DELETE"
    187                             " FROM frosix_key"
    188                             " WHERE key_id=$1;"),
    189     GNUNET_PQ_make_prepare ("auth_hash_select",
    190                             "SELECT"
    191                             " challenge_data"
    192                             " FROM frosix_key"
    193                             " WHERE key_id=$1;"),
    194     GNUNET_PQ_make_prepare ("seed_insert",
    195                             "INSERT INTO frosix_seed "
    196                             "(seed_id"
    197                             ",seed"
    198                             ",time_stamp"
    199                             ") VALUES "
    200                             "($1, $2, $3);"),
    201     GNUNET_PQ_make_prepare ("seed_select",
    202                             "SELECT"
    203                             " seed"
    204                             " FROM frosix_seed"
    205                             " WHERE seed_id=$1;"),
    206     GNUNET_PQ_make_prepare ("seed_delete",
    207                             "DELETE"
    208                             " FROM frosix_seed "
    209                             " WHERE seed_id=$1;"),
    210     GNUNET_PQ_make_prepare ("challengecode_insert",
    211                             "INSERT INTO frosix_challengecode "
    212                             "(challenge_id"
    213                             ",code"
    214                             ",creation_date"
    215                             ",expiration_date"
    216                             ",retry_counter"
    217                             ") VALUES "
    218                             "($1, $2, $3, $4, $5);"),
    219     GNUNET_PQ_make_prepare ("challengecode_select",
    220                             "SELECT "
    221                             " code"
    222                             ",satisfied"
    223                             " FROM frosix_challengecode"
    224                             " WHERE challenge_id=$1"
    225                             "   AND retry_counter != 0;"),
    226     GNUNET_PQ_make_prepare ("challengecode_select_meta",
    227                             "SELECT "
    228                             " code"
    229                             ",retry_counter"
    230                             ",retransmission_date"
    231                             " FROM frosix_challengecode"
    232                             " WHERE challenge_id=$1"
    233                             "   AND expiration_date > $2"
    234                             "   AND creation_date > $3"
    235                             " ORDER BY creation_date DESC"
    236                             " LIMIT 1;"),
    237     GNUNET_PQ_make_prepare ("challengecode_update_retry",
    238                             "UPDATE frosix_challengecode"
    239                             " SET retry_counter=retry_counter - 1"
    240                             " WHERE challenge_id=$1"
    241                             "   AND code=$2"
    242                             "   AND retry_counter != 0;"),
    243     GNUNET_PQ_make_prepare ("challengecode_mark_sent",
    244                             "UPDATE frosix_challengecode"
    245                             " SET retransmission_date=$3"
    246                             " WHERE challenge_id=$1"
    247                             "   AND code=$2"
    248                             "   AND creation_date IN"
    249                             " (SELECT creation_date"
    250                             "    FROM frosix_challengecode"
    251                             "   WHERE challenge_id=$1"
    252                             "     AND code=$2"
    253                             "    ORDER BY creation_date DESC"
    254                             "     LIMIT 1);"),
    255     GNUNET_PQ_make_prepare ("gc_challengecodes",
    256                             "DELETE FROM frosix_challengecode "
    257                             "WHERE "
    258                             "expiration_date < $1;"),
    259     GNUNET_PQ_PREPARED_STATEMENT_END
    260   };
    261 
    262   {
    263     enum GNUNET_GenericReturnValue ret;
    264 
    265     ret = GNUNET_PQ_prepare_statements (pg->conn,
    266                                         ps);
    267     if (GNUNET_OK != ret)
    268       return ret;
    269     pg->init = true;
    270     return GNUNET_OK;
    271   }
    272 }
    273 
    274 
    275 /**
    276  * Check that the database connection is still up.
    277  *
    278  * @param cls a `struct PostgresClosure` with connection to check
    279  */
    280 static void
    281 check_connection (void *cls)
    282 {
    283   struct PostgresClosure *pg = cls;
    284 
    285   GNUNET_PQ_reconnect_if_down (pg->conn);
    286 }
    287 
    288 
    289 /**
    290  * Connect to the database if the connection does not exist yet.
    291  *
    292  * @param pg the plugin-specific state
    293  * @param skip_prepare true if we should skip prepared statement setup
    294  * @return #GNUNET_OK on success
    295  */
    296 static enum GNUNET_GenericReturnValue
    297 internal_setup (struct PostgresClosure *pg,
    298                 bool skip_prepare)
    299 {
    300   if (NULL == pg->conn)
    301   {
    302 #if AUTO_EXPLAIN
    303     /* Enable verbose logging to see where queries do not
    304        properly use indices */
    305     struct GNUNET_PQ_ExecuteStatement es[] = {
    306       GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"),
    307       GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"),
    308       GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"),
    309       GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"),
    310       /* https://wiki.postgresql.org/wiki/Serializable suggests to really
    311          force the default to 'serializable' if SSI is to be used. */
    312       GNUNET_PQ_make_try_execute (
    313         "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"),
    314       GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"),
    315       GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"),
    316       GNUNET_PQ_make_execute ("SET search_path TO anastasis;"),
    317       GNUNET_PQ_EXECUTE_STATEMENT_END
    318     };
    319 #else
    320     struct GNUNET_PQ_ExecuteStatement es[] = {
    321       GNUNET_PQ_make_execute ("SET search_path TO frosix;"),
    322       GNUNET_PQ_EXECUTE_STATEMENT_END
    323     };
    324 #endif
    325     struct GNUNET_PQ_Context *db_conn;
    326 
    327     db_conn = GNUNET_PQ_connect_with_cfg (pg->cfg,
    328                                           "frosixdb-postgres",
    329                                           NULL,
    330                                           es,
    331                                           NULL);
    332     if (NULL == db_conn)
    333       return GNUNET_SYSERR;
    334     pg->conn = db_conn;
    335   }
    336   if (NULL == pg->transaction_name)
    337     GNUNET_PQ_reconnect_if_down (pg->conn);
    338   if (pg->init)
    339     return GNUNET_OK;
    340   if (skip_prepare)
    341     return GNUNET_OK;
    342   return prepare_statements (pg);
    343 }
    344 
    345 
    346 /**
    347  * Do a pre-flight check that we are not in an uncommitted transaction.
    348  * If we are, try to commit the previous transaction and output a warning.
    349  * Does not return anything, as we will continue regardless of the outcome.
    350  *
    351  * @param cls the `struct PostgresClosure` with the plugin-specific state
    352  * @return #GNUNET_OK if everything is fine
    353  *         #GNUNET_NO if a transaction was rolled back
    354  *         #GNUNET_SYSERR on hard errors
    355  */
    356 static enum GNUNET_GenericReturnValue
    357 postgres_preflight (void *cls)
    358 {
    359   struct PostgresClosure *pg = cls;
    360   struct GNUNET_PQ_ExecuteStatement es[] = {
    361     GNUNET_PQ_make_execute ("ROLLBACK"),
    362     GNUNET_PQ_EXECUTE_STATEMENT_END
    363   };
    364 
    365   if (! pg->init)
    366   {
    367     if (GNUNET_OK !=
    368         internal_setup (pg,
    369                         false))
    370       return GNUNET_SYSERR;
    371   }
    372   if (NULL == pg->transaction_name)
    373     return GNUNET_OK;  /* all good */
    374   if (GNUNET_OK ==
    375       GNUNET_PQ_exec_statements (pg->conn,
    376                                  es))
    377   {
    378     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    379                 "BUG: Preflight check rolled back transaction `%s'!\n",
    380                 pg->transaction_name);
    381   }
    382   else
    383   {
    384     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    385                 "BUG: Preflight check failed to rollback transaction `%s'!\n",
    386                 pg->transaction_name);
    387   }
    388   pg->transaction_name = NULL;
    389   return GNUNET_NO;
    390 }
    391 
    392 
    393 /**
    394  * Start a transaction.
    395  *
    396  * @param cls the `struct PostgresClosure` with the plugin-specific state
    397  * @param name unique name identifying the transaction (for debugging),
    398  *             must point to a constant
    399  * @return #GNUNET_OK on success
    400  */
    401 static enum GNUNET_GenericReturnValue
    402 begin_transaction (void *cls,
    403                    const char *name)
    404 {
    405   struct PostgresClosure *pg = cls;
    406   struct GNUNET_PQ_ExecuteStatement es[] = {
    407     GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
    408     GNUNET_PQ_EXECUTE_STATEMENT_END
    409   };
    410 
    411   check_connection (pg);
    412   GNUNET_break (GNUNET_OK ==
    413                 postgres_preflight (pg));
    414   pg->transaction_name = name;
    415   if (GNUNET_OK !=
    416       GNUNET_PQ_exec_statements (pg->conn,
    417                                  es))
    418   {
    419     TALER_LOG_ERROR ("Failed to start transaction\n");
    420     GNUNET_break (0);
    421     return GNUNET_SYSERR;
    422   }
    423   return GNUNET_OK;
    424 }
    425 
    426 
    427 /**
    428 * Roll back the current transaction of a database connection.
    429 *
    430 * @param cls the `struct PostgresClosure` with the plugin-specific state
    431 * @return #GNUNET_OK on success
    432 */
    433 static void
    434 rollback (void *cls)
    435 {
    436   struct PostgresClosure *pg = cls;
    437   struct GNUNET_PQ_ExecuteStatement es[] = {
    438     GNUNET_PQ_make_execute ("ROLLBACK"),
    439     GNUNET_PQ_EXECUTE_STATEMENT_END
    440   };
    441 
    442   if (GNUNET_OK !=
    443       GNUNET_PQ_exec_statements (pg->conn,
    444                                  es))
    445   {
    446     TALER_LOG_ERROR ("Failed to rollback transaction\n");
    447     GNUNET_break (0);
    448   }
    449   pg->transaction_name = NULL;
    450 }
    451 
    452 
    453 /**
    454  * Commit the current transaction of a database connection.
    455  *
    456  * @param cls the `struct PostgresClosure` with the plugin-specific state
    457  * @return transaction status code
    458  */
    459 static enum GNUNET_DB_QueryStatus
    460 commit_transaction (void *cls)
    461 {
    462   struct PostgresClosure *pg = cls;
    463   enum GNUNET_DB_QueryStatus qs;
    464   struct GNUNET_PQ_QueryParam no_params[] = {
    465     GNUNET_PQ_query_param_end
    466   };
    467 
    468   qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    469                                            "do_commit",
    470                                            no_params);
    471   pg->transaction_name = NULL;
    472   return qs;
    473 }
    474 
    475 
    476 /**
    477  * Register callback to be invoked on events of type @a es.
    478  *
    479  * @param cls database context to use
    480  * @param es specification of the event to listen for
    481  * @param timeout how long to wait for the event
    482  * @param cb function to call when the event happens, possibly
    483  *         multiple times (until cancel is invoked)
    484  * @param cb_cls closure for @a cb
    485  * @return handle useful to cancel the listener
    486  */
    487 static struct GNUNET_DB_EventHandler *
    488 postgres_event_listen (void *cls,
    489                        const struct GNUNET_DB_EventHeaderP *es,
    490                        struct GNUNET_TIME_Relative timeout,
    491                        GNUNET_DB_EventCallback cb,
    492                        void *cb_cls)
    493 {
    494   struct PostgresClosure *pg = cls;
    495 
    496   return GNUNET_PQ_event_listen (pg->conn,
    497                                  es,
    498                                  timeout,
    499                                  cb,
    500                                  cb_cls);
    501 }
    502 
    503 
    504 /**
    505  * Stop notifications.
    506  *
    507  * @param eh handle to unregister.
    508  */
    509 static void
    510 postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
    511 {
    512   GNUNET_PQ_event_listen_cancel (eh);
    513 }
    514 
    515 
    516 /**
    517  * Notify all that listen on @a es of an event.
    518  *
    519  * @param cls database context to use
    520  * @param es specification of the event to generate
    521  * @param extra additional event data provided
    522  * @param extra_size number of bytes in @a extra
    523  */
    524 static void
    525 postgres_event_notify (void *cls,
    526                        const struct GNUNET_DB_EventHeaderP *es,
    527                        const void *extra,
    528                        size_t extra_size)
    529 {
    530   struct PostgresClosure *pg = cls;
    531 
    532   return GNUNET_PQ_event_notify (pg->conn,
    533                                  es,
    534                                  extra,
    535                                  extra_size);
    536 }
    537 
    538 /**
    539  * FIXME
    540 */
    541 static enum FROSIX_DB_StoreStatus
    542 postgres_store_dkg_commitment (
    543   void *cls,
    544   const struct FROSIX_DkgRequestIdP *dkg_id,
    545   const struct FROSIX_DkgCommitmentsRaw *dkg_commits)
    546 {
    547   struct PostgresClosure *pg = cls;
    548   enum GNUNET_DB_QueryStatus qs;
    549 
    550   check_connection (pg);
    551   GNUNET_break (GNUNET_OK ==
    552                 postgres_preflight (pg));
    553   for (unsigned int retry = 0; retry < MAX_RETRIES; retry++)
    554   {
    555     if (GNUNET_OK !=
    556         begin_transaction (pg,
    557                            "dkg_commitment_insert"))
    558     {
    559       GNUNET_break (0);
    560       return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    561     }
    562 
    563     {
    564 
    565       // FIXME: do some checks
    566       struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    567       struct GNUNET_PQ_QueryParam params[] = {
    568         GNUNET_PQ_query_param_auto_from_type (dkg_id),
    569         GNUNET_PQ_query_param_timestamp (&now),
    570         GNUNET_PQ_query_param_fixed_size (dkg_commits->ptr_commits,
    571                                           dkg_commits->length),
    572         GNUNET_PQ_query_param_end
    573       };
    574 
    575       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    576                                                "dkg_commitment_insert",
    577                                                params);
    578 
    579       switch (qs)
    580       {
    581       case GNUNET_DB_STATUS_HARD_ERROR:
    582         rollback (pg);
    583         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    584       case GNUNET_DB_STATUS_SOFT_ERROR:
    585         goto retry;
    586       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    587         GNUNET_break (0);
    588         rollback (pg);
    589         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    590       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    591         qs = commit_transaction (pg);
    592         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    593           goto retry;
    594         if (qs < 0)
    595           return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    596         return FROSIX_DB_STORE_STATUS_SUCCESS;
    597       }
    598     }
    599     retry:
    600     rollback (pg);
    601   }
    602   return FROSIX_DB_STORE_STATUS_SOFT_ERROR;
    603 }
    604 
    605 
    606 
    607 /**
    608  * FIXME
    609 */
    610 static enum GNUNET_DB_QueryStatus
    611 postgres_get_dkg_commitment (
    612   void *cls,
    613   const struct FROSIX_DkgRequestIdP *dkg_id,
    614   struct FROSIX_DkgCommitmentsRaw *dkg_commits)
    615 {
    616   struct PostgresClosure *pg = cls;
    617   struct GNUNET_PQ_QueryParam params[] = {
    618     GNUNET_PQ_query_param_auto_from_type (dkg_id),
    619     GNUNET_PQ_query_param_end
    620   };
    621   struct GNUNET_PQ_ResultSpec rs[] = {
    622     GNUNET_PQ_result_spec_variable_size ("commitments",
    623                                          &dkg_commits->ptr_commits,
    624                                          &dkg_commits->length),
    625     GNUNET_PQ_result_spec_end
    626   };
    627 
    628   check_connection (pg);
    629   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    630 
    631   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    632                                                    "dkg_commitments_select",
    633                                                    params,
    634                                                    rs);
    635 }
    636 
    637 
    638 /**
    639  * FIXME
    640 */
    641 static enum FROSIX_DB_CommitmentStatus
    642 postgres_lookup_dkg_commitment (
    643   void *cls,
    644   const struct FROSIX_DkgRequestIdP *dkg_id)
    645 {
    646   enum GNUNET_DB_QueryStatus qs;
    647 
    648   struct PostgresClosure *pg = cls;
    649   check_connection (pg);
    650   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    651 
    652   struct GNUNET_PQ_QueryParam params[] = {
    653     GNUNET_PQ_query_param_auto_from_type (dkg_id),
    654     GNUNET_PQ_query_param_end
    655   };
    656 
    657   struct FROSIX_DkgRequestIdP id;
    658   struct GNUNET_PQ_ResultSpec rs[] = {
    659     GNUNET_PQ_result_spec_auto_from_type ("dkg_id",
    660                                           &id),
    661     GNUNET_PQ_result_spec_end
    662   };
    663 
    664   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    665                                                  "dkg_commitments_lookup",
    666                                                  params,
    667                                                  rs);
    668 
    669   switch (qs)
    670   {
    671   case GNUNET_DB_STATUS_HARD_ERROR:
    672     return FROSIX_DB_COMMITMENT_STATUS_HARD_ERROR;
    673   case GNUNET_DB_STATUS_SOFT_ERROR:
    674     GNUNET_break (0);
    675     return FROSIX_DB_COMMITMENT_STATUS_SOFT_ERROR;
    676   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    677     return FROSIX_DB_COMMITMENT_STATUS_NO_RESULTS;
    678   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    679     return FROSIX_DB_COMMITMENT_STATUS_ONE_RESULT;
    680   default:
    681     GNUNET_break (0);
    682     return FROSIX_DB_COMMITMENT_STATUS_HARD_ERROR;
    683   }
    684 }
    685 
    686 
    687 /**
    688  * FIXME
    689 */
    690 static enum FROSIX_DB_StoreStatus
    691 postgres_store_key (
    692   void *cls,
    693   const struct FROST_HashCode *id,
    694   const struct FROSIX_EncryptionNonceP *nonce,
    695   const struct FROSIX_KeyDataEncrypted *key_data,
    696   const struct FROSIX_ChallengeHashP *challenge_hash,
    697   uint32_t expiration,
    698   uint8_t identifier)
    699 {
    700   struct PostgresClosure *pg = cls;
    701   enum GNUNET_DB_QueryStatus qs;
    702 
    703   check_connection (pg);
    704   GNUNET_break (GNUNET_OK ==
    705                 postgres_preflight (pg));
    706   for (unsigned int retry = 0; retry < MAX_RETRIES; retry++)
    707   {
    708     if (GNUNET_OK !=
    709         begin_transaction (pg,
    710                            "dkg_key_insert"))
    711     {
    712       GNUNET_break (0);
    713       return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    714     }
    715 
    716     {
    717       const uint32_t id_32 = identifier;
    718       const uint32_t expiration_32 = expiration;
    719       // FIXME: do some checks?
    720       struct GNUNET_PQ_QueryParam params[] = {
    721         GNUNET_PQ_query_param_auto_from_type (id),
    722         GNUNET_PQ_query_param_uint32 (&id_32),
    723         GNUNET_PQ_query_param_uint32 (&expiration_32),
    724         GNUNET_PQ_query_param_auto_from_type (nonce),
    725         GNUNET_PQ_query_param_auto_from_type (key_data),
    726         GNUNET_PQ_query_param_auto_from_type (challenge_hash),
    727         GNUNET_PQ_query_param_end
    728       };
    729       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    730                                                "dkg_key_insert",
    731                                                params);
    732 
    733       switch (qs)
    734       {
    735       case GNUNET_DB_STATUS_HARD_ERROR:
    736         rollback (pg);
    737         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    738       case GNUNET_DB_STATUS_SOFT_ERROR:
    739         goto retry;
    740       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    741         GNUNET_break (0);
    742         rollback (pg);
    743         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    744       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    745         qs = commit_transaction (pg);
    746         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    747           goto retry;
    748         if (qs < 0)
    749           return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    750         return FROSIX_DB_STORE_STATUS_SUCCESS;
    751       }
    752     }
    753     retry:
    754     rollback (pg);
    755   }
    756   return FROSIX_DB_STORE_STATUS_SOFT_ERROR;
    757 }
    758 
    759 
    760 /**
    761  * FIXME
    762 */
    763 static enum FROSIX_DB_KeyStatus
    764 postgres_lookup_key (
    765   void *cls,
    766   const struct FROST_HashCode *id)
    767 {
    768   enum GNUNET_DB_QueryStatus qs;
    769 
    770   struct PostgresClosure *pg = cls;
    771   check_connection (pg);
    772   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    773 
    774   struct GNUNET_PQ_QueryParam params[] = {
    775     GNUNET_PQ_query_param_auto_from_type (id),
    776     GNUNET_PQ_query_param_end
    777   };
    778 
    779   struct FROST_HashCode id_db;
    780   struct GNUNET_PQ_ResultSpec rs[] = {
    781     GNUNET_PQ_result_spec_auto_from_type ("key_id",
    782                                           &id_db),
    783     GNUNET_PQ_result_spec_end
    784   };
    785 
    786   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    787                                                  "key_lookup",
    788                                                  params,
    789                                                  rs);
    790 
    791   switch (qs)
    792   {
    793   case GNUNET_DB_STATUS_HARD_ERROR:
    794     return FROSIX_DB_KEY_STATUS_HARD_ERROR;
    795   case GNUNET_DB_STATUS_SOFT_ERROR:
    796     GNUNET_break (0);
    797     return FROSIX_DB_KEY_STATUS_SOFT_ERROR;
    798   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    799     return FROSIX_DB_KEY_STATUS_NO_RESULTS;
    800   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    801     return FROSIX_DB_KEY_STATUS_ONE_RESULT;
    802   default:
    803     GNUNET_break (0);
    804     return FROSIX_DB_KEY_STATUS_HARD_ERROR;
    805   }
    806 }
    807 
    808 
    809 /**
    810  * FIXME
    811 */
    812 static enum GNUNET_DB_QueryStatus
    813 postgres_get_auth_hash (
    814   void *cls,
    815   const struct FROST_HashCode *db_id,
    816   struct FROSIX_ChallengeHashP *challenge_hash)
    817 {
    818   struct PostgresClosure *pg = cls;
    819   struct GNUNET_PQ_QueryParam params[] = {
    820     GNUNET_PQ_query_param_auto_from_type (db_id),
    821     GNUNET_PQ_query_param_end
    822   };
    823 
    824   struct GNUNET_PQ_ResultSpec rs[] = {
    825     GNUNET_PQ_result_spec_fixed_size ("challenge_data",
    826                                       challenge_hash,
    827                                       sizeof (*challenge_hash)),
    828     GNUNET_PQ_result_spec_end
    829   };
    830 
    831   check_connection (pg);
    832   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    833 
    834   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    835                                                    "auth_hash_select",
    836                                                    params,
    837                                                    rs);
    838 }
    839 
    840 
    841 /**
    842  * FIXME
    843 */
    844 static enum GNUNET_DB_QueryStatus
    845 postgres_get_key_data (
    846   void *cls,
    847   const struct FROST_HashCode *db_id,
    848   uint32_t *identifier,
    849   struct FROSIX_EncryptionNonceP *nonce,
    850   struct FROSIX_KeyDataEncrypted *enc_key_data)
    851 {
    852   struct PostgresClosure *pg = cls;
    853   struct GNUNET_PQ_QueryParam params[] = {
    854     GNUNET_PQ_query_param_auto_from_type (db_id),
    855     GNUNET_PQ_query_param_end
    856   };
    857 
    858   struct GNUNET_PQ_ResultSpec rs[] = {
    859     GNUNET_PQ_result_spec_uint32 ("identifier",
    860                                   identifier),
    861     GNUNET_PQ_result_spec_fixed_size ("encryption_nonce",
    862                                       nonce,
    863                                       sizeof (*nonce)),
    864     GNUNET_PQ_result_spec_fixed_size ("enc_key_data",
    865                                       enc_key_data,
    866                                       sizeof (*enc_key_data)),
    867     GNUNET_PQ_result_spec_end
    868   };
    869 
    870   check_connection (pg);
    871   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    872 
    873   return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    874                                                    "key_data_select",
    875                                                    params,
    876                                                    rs);
    877 }
    878 
    879 
    880 /**
    881  * FIXME
    882 */
    883 static enum GNUNET_DB_QueryStatus
    884 postgres_delete_key_data (
    885   void *cls,
    886   const struct FROST_HashCode *db_id)
    887 {
    888   struct PostgresClosure *pg = cls;
    889   struct GNUNET_PQ_QueryParam params[] = {
    890     GNUNET_PQ_query_param_auto_from_type (db_id),
    891     GNUNET_PQ_query_param_end
    892   };
    893 
    894   check_connection (pg);
    895   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    896 
    897   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
    898                                              "key_delete",
    899                                              params);
    900 }
    901 
    902 
    903 /**
    904  * FIXME
    905 */
    906 static enum GNUNET_DB_QueryStatus
    907 postgres_store_commitment_seed (
    908   void *cls,
    909   const struct GNUNET_HashCode *db_id,
    910   const struct FROST_CommitmentSeed *seed)
    911 {
    912   struct PostgresClosure *pg = cls;
    913   enum GNUNET_DB_QueryStatus qs;
    914   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
    915 
    916   check_connection (pg);
    917   GNUNET_break (GNUNET_OK ==
    918                 postgres_preflight (pg));
    919   for (unsigned int retry = 0; retry < MAX_RETRIES; retry++)
    920   {
    921     if (GNUNET_OK !=
    922         begin_transaction (pg,
    923                            "seed_insert"))
    924     {
    925       GNUNET_break (0);
    926       return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    927     }
    928 
    929     {
    930       struct GNUNET_PQ_QueryParam params[] = {
    931         GNUNET_PQ_query_param_auto_from_type (db_id),
    932         GNUNET_PQ_query_param_auto_from_type (seed),
    933         GNUNET_PQ_query_param_timestamp (&now),
    934         GNUNET_PQ_query_param_end
    935       };
    936       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    937                                                "seed_insert",
    938                                                params);
    939 
    940       switch (qs)
    941       {
    942       case GNUNET_DB_STATUS_HARD_ERROR:
    943         rollback (pg);
    944         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    945       case GNUNET_DB_STATUS_SOFT_ERROR:
    946         goto retry;
    947       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    948         GNUNET_break (0);
    949         rollback (pg);
    950         return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    951       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    952         qs = commit_transaction (pg);
    953         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    954           goto retry;
    955         if (qs < 0)
    956           return FROSIX_DB_STORE_STATUS_HARD_ERROR;
    957         return FROSIX_DB_STORE_STATUS_SUCCESS;
    958       }
    959     }
    960     retry:
    961     rollback (pg);
    962   }
    963   return FROSIX_DB_STORE_STATUS_SOFT_ERROR;
    964 }
    965 
    966 
    967 /**
    968  * FIXME
    969 */
    970 static enum GNUNET_DB_QueryStatus
    971 postgres_get_and_delete_commitment_seed (
    972   void *cls,
    973   const struct GNUNET_HashCode *db_id,
    974   struct FROST_CommitmentSeed *seed)
    975 {
    976   struct PostgresClosure *pg = cls;
    977   struct GNUNET_PQ_QueryParam params[] = {
    978     GNUNET_PQ_query_param_auto_from_type (db_id),
    979     GNUNET_PQ_query_param_end
    980   };
    981 
    982   struct GNUNET_PQ_ResultSpec rs[] = {
    983     GNUNET_PQ_result_spec_auto_from_type ("seed",
    984                                           seed),
    985     GNUNET_PQ_result_spec_end
    986   };
    987 
    988   check_connection (pg);
    989   GNUNET_break (GNUNET_OK == postgres_preflight (pg));
    990 
    991   enum GNUNET_DB_QueryStatus qs;
    992 
    993   qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    994                                                  "seed_select",
    995                                                  params,
    996                                                  rs);
    997 
    998   switch (qs)
    999   {
   1000   case GNUNET_DB_STATUS_HARD_ERROR:
   1001     return FROSIX_DB_STORE_STATUS_HARD_ERROR;
   1002   case GNUNET_DB_STATUS_SOFT_ERROR:
   1003     return FROSIX_DB_STORE_STATUS_SOFT_ERROR;
   1004   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1005     return FROSIX_DB_STORE_STATUS_NO_RESULTS;
   1006   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1007     break;
   1008   }
   1009 
   1010   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1011                                              "seed_delete",
   1012                                              params);
   1013 }
   1014 
   1015 
   1016 /**
   1017  * Create a new challenge code for a given challenge identified by the challenge
   1018  * public key. The function will first check if there is already a valid code
   1019  * for this challenge present and won't insert a new one in this case.
   1020  *
   1021  * @param cls closure
   1022  * @param truth_uuid the identifier for the challenge
   1023  * @param rotation_period for how long is the code available
   1024  * @param validity_period for how long is the code available
   1025  * @param retry_counter amount of retries allowed
   1026  * @param[out] retransmission_date when to next retransmit
   1027  * @param[out] code set to the code which will be checked for later
   1028  * @return transaction status,
   1029  *        #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries,
   1030  *        #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB
   1031  */
   1032 enum GNUNET_DB_QueryStatus
   1033 postgres_create_challenge_code (
   1034   void *cls,
   1035   const struct FROSIX_ChallengeIdP *truth_uuid,
   1036   struct GNUNET_TIME_Relative rotation_period,
   1037   struct GNUNET_TIME_Relative validity_period,
   1038   uint32_t retry_counter,
   1039   struct GNUNET_TIME_Timestamp *retransmission_date,
   1040   uint64_t *code)
   1041 {
   1042   struct PostgresClosure *pg = cls;
   1043   enum GNUNET_DB_QueryStatus qs;
   1044   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
   1045   struct GNUNET_TIME_Timestamp expiration_date;
   1046   struct GNUNET_TIME_Absolute ex_rot;
   1047 
   1048   check_connection (pg);
   1049   expiration_date = GNUNET_TIME_relative_to_timestamp (validity_period);
   1050   ex_rot = GNUNET_TIME_absolute_subtract (now.abs_time,
   1051                                           rotation_period);
   1052   for (unsigned int retries = 0; retries<MAX_RETRIES; retries++)
   1053   {
   1054     if (GNUNET_OK !=
   1055         begin_transaction (pg,
   1056                            "create_challenge_code"))
   1057     {
   1058       GNUNET_break (0);
   1059       return GNUNET_DB_STATUS_HARD_ERROR;
   1060     }
   1061 
   1062     {
   1063       uint32_t old_retry_counter;
   1064       struct GNUNET_PQ_QueryParam params[] = {
   1065         GNUNET_PQ_query_param_auto_from_type (truth_uuid),
   1066         GNUNET_PQ_query_param_timestamp (&now),
   1067         GNUNET_PQ_query_param_absolute_time (&ex_rot),
   1068         GNUNET_PQ_query_param_end
   1069       };
   1070       struct GNUNET_PQ_ResultSpec rs[] = {
   1071         GNUNET_PQ_result_spec_uint64 ("code",
   1072                                       code),
   1073         GNUNET_PQ_result_spec_uint32 ("retry_counter",
   1074                                       &old_retry_counter),
   1075         GNUNET_PQ_result_spec_timestamp ("retransmission_date",
   1076                                          retransmission_date),
   1077         GNUNET_PQ_result_spec_end
   1078       };
   1079       enum GNUNET_DB_QueryStatus qs;
   1080 
   1081       qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
   1082                                                      "challengecode_select_meta",
   1083                                                      params,
   1084                                                      rs);
   1085       switch (qs)
   1086       {
   1087       case GNUNET_DB_STATUS_HARD_ERROR:
   1088         GNUNET_break (0);
   1089         rollback (pg);
   1090         return qs;
   1091       case GNUNET_DB_STATUS_SOFT_ERROR:
   1092         goto retry;
   1093       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1094         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1095                     "No active challenge found, creating a fresh one\n");
   1096         break;
   1097       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1098         if (0 == old_retry_counter)
   1099         {
   1100           rollback (pg);
   1101           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1102                       "Active challenge %llu has zero tries left, refusing to create another one\n",
   1103                       (unsigned long long) *code);
   1104           return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   1105         }
   1106         rollback (pg);
   1107         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1108                     "Active challenge has %u tries left, returning old challenge %llu\n",
   1109                     (unsigned int) old_retry_counter,
   1110                     (unsigned long long) *code);
   1111         return qs;
   1112       }
   1113     }
   1114 
   1115     *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE,
   1116                                       FROSIX_PIN_MAX_VALUE);
   1117     *retransmission_date = GNUNET_TIME_UNIT_ZERO_TS;
   1118     {
   1119       struct GNUNET_PQ_QueryParam params[] = {
   1120         GNUNET_PQ_query_param_auto_from_type (truth_uuid),
   1121         GNUNET_PQ_query_param_uint64 (code),
   1122         GNUNET_PQ_query_param_timestamp (&now),
   1123         GNUNET_PQ_query_param_timestamp (&expiration_date),
   1124         GNUNET_PQ_query_param_uint32 (&retry_counter),
   1125         GNUNET_PQ_query_param_end
   1126       };
   1127 
   1128       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1129                                                "challengecode_insert",
   1130                                                params);
   1131       switch (qs)
   1132       {
   1133       case GNUNET_DB_STATUS_HARD_ERROR:
   1134         rollback (pg);
   1135         return GNUNET_DB_STATUS_HARD_ERROR;
   1136       case GNUNET_DB_STATUS_SOFT_ERROR:
   1137         goto retry;
   1138       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1139         GNUNET_break (0);
   1140         rollback (pg);
   1141         return GNUNET_DB_STATUS_HARD_ERROR;
   1142       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1143         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1144                     "Created fresh challenge with %u tries left\n",
   1145                     (unsigned int) retry_counter);
   1146         break;
   1147       }
   1148     }
   1149     qs = commit_transaction (pg);
   1150     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
   1151       goto retry;
   1152     if (qs < 0)
   1153       return qs;
   1154     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   1155     retry:
   1156     rollback (pg);
   1157   }
   1158   return GNUNET_DB_STATUS_SOFT_ERROR;
   1159 }
   1160 
   1161 
   1162 /**
   1163  * Closure for check_valid_code().
   1164  */
   1165 struct CheckValidityContext
   1166 {
   1167   /**
   1168    * Code to check for.
   1169    */
   1170   const struct GNUNET_HashCode *hashed_code;
   1171 
   1172   /**
   1173    * Truth we are processing.
   1174    */
   1175   const struct FROSIX_ChallengeIdP *truth_uuid;
   1176 
   1177   /**
   1178    * Database context.
   1179    */
   1180   struct PostgresClosure *pg;
   1181 
   1182   /**
   1183    * Set to the matching challenge code (if @e valid).
   1184    */
   1185   uint64_t code;
   1186 
   1187   /**
   1188    * Set to true if a code matching @e hashed_code was found.
   1189    */
   1190   bool valid;
   1191 
   1192   /**
   1193    * Set to true if a code matching @e hashed_code was set to 'satisfied' by the plugin.
   1194    */
   1195   bool satisfied;
   1196 
   1197   /**
   1198    * Set to true if we had a database failure.
   1199    */
   1200   bool db_failure;
   1201 
   1202 };
   1203 
   1204 
   1205 /**
   1206  * Helper function for #postgres_verify_challenge_code().
   1207  * To be called with the results of a SELECT statement
   1208  * that has returned @a num_results results.
   1209  *
   1210  * @param cls closure of type `struct CheckValidityContext *`
   1211  * @param result the postgres result
   1212  * @param num_results the number of results in @a result
   1213  */
   1214 static void
   1215 check_valid_code (void *cls,
   1216                   PGresult *result,
   1217                   unsigned int num_results)
   1218 {
   1219   struct CheckValidityContext *cvc = cls;
   1220   struct PostgresClosure *pg = cvc->pg;
   1221 
   1222   for (unsigned int i = 0; i < num_results; i++)
   1223   {
   1224     uint64_t server_code;
   1225     uint8_t sat;
   1226     struct GNUNET_PQ_ResultSpec rs[] = {
   1227       GNUNET_PQ_result_spec_uint64 ("code",
   1228                                     &server_code),
   1229       GNUNET_PQ_result_spec_auto_from_type ("satisfied",
   1230                                             &sat),
   1231       GNUNET_PQ_result_spec_end
   1232     };
   1233 
   1234     if (GNUNET_OK !=
   1235         GNUNET_PQ_extract_result (result,
   1236                                   rs,
   1237                                   i))
   1238     {
   1239       GNUNET_break (0);
   1240       cvc->db_failure = true;
   1241       return;
   1242     }
   1243     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1244                 "Found issued challenge %llu (client: %s)\n",
   1245                 (unsigned long long) server_code,
   1246                 GNUNET_h2s (cvc->hashed_code));
   1247     {
   1248       struct GNUNET_HashCode shashed_code;
   1249 
   1250       FROSIX_hash_answer (server_code,
   1251                           &shashed_code);
   1252       if (0 ==
   1253           GNUNET_memcmp (&shashed_code,
   1254                          cvc->hashed_code))
   1255       {
   1256         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1257                     "Challenge is valid challenge (%s)\n",
   1258                     (0 != sat) ? "satisfied" : "not satisfied");
   1259         cvc->valid = true;
   1260         cvc->code = server_code;
   1261         cvc->satisfied = (0 != sat);
   1262       }
   1263       else
   1264       {
   1265         /* count failures to prevent brute-force attacks */
   1266         struct GNUNET_PQ_QueryParam params[] = {
   1267           GNUNET_PQ_query_param_auto_from_type (cvc->truth_uuid),
   1268           GNUNET_PQ_query_param_uint64 (&server_code),
   1269           GNUNET_PQ_query_param_end
   1270         };
   1271         enum GNUNET_DB_QueryStatus qs;
   1272 
   1273         qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1274                                                  "challengecode_update_retry",
   1275                                                  params);
   1276         if (qs <= 0)
   1277         {
   1278           GNUNET_break (0);
   1279           cvc->db_failure = true;
   1280         }
   1281       }
   1282     }
   1283   }
   1284 }
   1285 
   1286 
   1287 /**
   1288  * Verify the provided code with the code on the server.
   1289  * If the code matches the function will return with success, if the code
   1290  * does not match, the retry counter will be decreased by one.
   1291  *
   1292  * @param cls closure
   1293  * @param truth_uuid identification of the challenge which the code corresponds to
   1294  * @param hashed_code code which the user provided and wants to verify
   1295  * @param[out] code set to the original numeric code
   1296  * @param[out] satisfied set to true if the challenge is set to satisfied
   1297  * @return code validity status
   1298  */
   1299 enum FROSIX_DB_CodeStatus
   1300 postgres_verify_challenge_code (
   1301   void *cls,
   1302   const struct FROSIX_ChallengeIdP *truth_uuid,
   1303   const struct GNUNET_HashCode *hashed_code,
   1304   uint64_t *code,
   1305   bool *satisfied)
   1306 {
   1307   struct PostgresClosure *pg = cls;
   1308   struct CheckValidityContext cvc = {
   1309     .truth_uuid = truth_uuid,
   1310     .hashed_code = hashed_code,
   1311     .pg = pg
   1312   };
   1313   struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get ();
   1314   struct GNUNET_PQ_QueryParam params[] = {
   1315     GNUNET_PQ_query_param_auto_from_type (truth_uuid),
   1316     // GNUNET_PQ_query_param_timestamp (&now),
   1317     GNUNET_PQ_query_param_end
   1318   };
   1319   enum GNUNET_DB_QueryStatus qs;
   1320 
   1321   *satisfied = false;
   1322   check_connection (pg);
   1323   qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
   1324                                              "challengecode_select",
   1325                                              params,
   1326                                              &check_valid_code,
   1327                                              &cvc);
   1328   if ( (qs < 0) ||
   1329        (cvc.db_failure) )
   1330     return FROSIX_DB_CODE_STATUS_HARD_ERROR;
   1331   *code = cvc.code;
   1332   if (cvc.valid)
   1333   {
   1334     *satisfied = cvc.satisfied;
   1335     return FROSIX_DB_CODE_STATUS_VALID_CODE_STORED;
   1336   }
   1337   if (0 == qs)
   1338     return FROSIX_DB_CODE_STATUS_NO_RESULTS;
   1339   return FROSIX_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH;
   1340 }
   1341 
   1342 
   1343 /**
   1344  * Remember in the database that we successfully sent a challenge.
   1345  *
   1346  * @param cls closure
   1347  * @param truth_uuid the identifier for the challenge
   1348  * @param code the challenge that was sent
   1349  */
   1350 static enum GNUNET_DB_QueryStatus
   1351 postgres_mark_challenge_sent (
   1352   void *cls,
   1353   const struct FROSIX_ChallengeIdP *truth_uuid,
   1354   uint64_t code)
   1355 {
   1356   struct PostgresClosure *pg = cls;
   1357   enum GNUNET_DB_QueryStatus qs;
   1358 
   1359   check_connection (pg);
   1360   {
   1361     struct GNUNET_TIME_Timestamp now;
   1362     struct GNUNET_PQ_QueryParam params[] = {
   1363       GNUNET_PQ_query_param_auto_from_type (truth_uuid),
   1364       GNUNET_PQ_query_param_uint64 (&code),
   1365       GNUNET_PQ_query_param_timestamp (&now),
   1366       GNUNET_PQ_query_param_end
   1367     };
   1368 
   1369     now = GNUNET_TIME_timestamp_get ();
   1370     qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1371                                              "challengecode_mark_sent",
   1372                                              params);
   1373     if (qs <= 0)
   1374       return qs;
   1375   }
   1376   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1377               "Marking challenge %llu as issued\n",
   1378               (unsigned long long) code);
   1379   return qs;
   1380 }
   1381 
   1382 
   1383 /**
   1384  * Function called to remove all expired codes from the database.
   1385  *
   1386  * @return transaction status
   1387  */
   1388 enum GNUNET_DB_QueryStatus
   1389 postgres_challenge_gc (void *cls)
   1390 {
   1391   struct PostgresClosure *pg = cls;
   1392   struct GNUNET_TIME_Timestamp time_now = GNUNET_TIME_timestamp_get ();
   1393   struct GNUNET_PQ_QueryParam params[] = {
   1394     GNUNET_PQ_query_param_timestamp (&time_now),
   1395     GNUNET_PQ_query_param_end
   1396   };
   1397 
   1398   check_connection (pg);
   1399   GNUNET_break (GNUNET_OK ==
   1400                 postgres_preflight (pg));
   1401   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
   1402                                              "gc_challengecodes",
   1403                                              params);
   1404 }
   1405 
   1406 
   1407 /**
   1408  * Initialize Postgres database subsystem.
   1409  *
   1410  * @param cls a configuration instance
   1411  * @return NULL on error, otherwise a `struct FROSIX_DatabasePlugin`
   1412  */
   1413 void *
   1414 libfrosix_plugin_db_postgres_init (void *cls)
   1415 {
   1416   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
   1417   struct PostgresClosure *pg;
   1418   struct FROSIX_DatabasePlugin *plugin;
   1419 
   1420   pg = GNUNET_new (struct PostgresClosure);
   1421   pg->cfg = cfg;
   1422 
   1423   if (GNUNET_OK !=
   1424       GNUNET_CONFIGURATION_get_value_string (cfg,
   1425                                              "taler",
   1426                                              "CURRENCY",
   1427                                              &pg->currency))
   1428   {
   1429     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1430                                "taler",
   1431                                "CURRENCY");
   1432     GNUNET_PQ_disconnect (pg->conn);
   1433     GNUNET_free (pg);
   1434     return NULL;
   1435   }
   1436   plugin = GNUNET_new (struct FROSIX_DatabasePlugin);
   1437   plugin->cls = pg;
   1438   /* FIXME: Should this be the same? */
   1439   plugin->connect = &postgres_preflight;
   1440   plugin->create_tables = &postgres_create_tables;
   1441   plugin->drop_tables = &postgres_drop_tables;
   1442   // plugin->gc = &postgres_gc;
   1443   plugin->preflight = &postgres_preflight;
   1444   plugin->rollback = &rollback;
   1445   plugin->start = &begin_transaction;
   1446   plugin->commit = &commit_transaction;
   1447   plugin->event_listen = &postgres_event_listen;
   1448   plugin->event_listen_cancel = &postgres_event_listen_cancel;
   1449   plugin->event_notify = &postgres_event_notify;
   1450   plugin->store_dkg_commitment = &postgres_store_dkg_commitment;
   1451   plugin->get_dkg_commitment = &postgres_get_dkg_commitment;
   1452   plugin->lookup_dkg_commitment = &postgres_lookup_dkg_commitment;
   1453   plugin->store_key = &postgres_store_key;
   1454   plugin->lookup_key = &postgres_lookup_key;
   1455   plugin->get_key_data = &postgres_get_key_data;
   1456   plugin->delete_key_data = &postgres_delete_key_data;
   1457   plugin->get_auth_hash = &postgres_get_auth_hash;
   1458   plugin->store_commitment_seed = &postgres_store_commitment_seed;
   1459   plugin->get_and_delete_commitment_seed =
   1460     &postgres_get_and_delete_commitment_seed;
   1461   plugin->create_challenge_code = &postgres_create_challenge_code;
   1462   plugin->verify_challenge_code = &postgres_verify_challenge_code;
   1463   plugin->mark_challenge_sent = &postgres_mark_challenge_sent;
   1464   plugin->challenge_gc = &postgres_challenge_gc;
   1465   return plugin;
   1466 }
   1467 
   1468 
   1469 /**
   1470  * Shutdown Postgres database subsystem.
   1471  *
   1472  * @param cls a `struct FROSIX_DB_STATUS_Plugin`
   1473  * @return NULL (always)
   1474  */
   1475 void *
   1476 libfrosix_plugin_db_postgres_done (void *cls)
   1477 {
   1478   struct FROSIX_DatabasePlugin *plugin = cls;
   1479   struct PostgresClosure *pg = plugin->cls;
   1480 
   1481   GNUNET_PQ_disconnect (pg->conn);
   1482   GNUNET_free (pg->currency);
   1483   GNUNET_free (pg);
   1484   GNUNET_free (plugin);
   1485   return NULL;
   1486 }
   1487 
   1488 
   1489 /* end of plugin_frosix_postgres.c */