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 */