anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

commit 82af0bc97507eb3a7e89b4d7f66ba968419fea30
parent 21d9a6dbfc8a1a8be0cc7997dc84e50a57dc5c9a
Author: Christian Grothoff <christian@grothoff.org>
Date:   Thu,  4 Jun 2026 23:00:56 +0200

stasis: replace database plugin with directly-linked libanastasisdb
  and modernize libgnunetpq API usage

- Removes plugin_anastasis_postgres.c, anastasis_db_plugin.c (the dlopen
  loader) and anastasis_database_plugin.h.
- Splits the monolithic plugin into one implementation file per database
  operation (src/stasis/anastasis-db_*.c) plus shared connection,
  transaction and event helpers in anastasis-db_pg.{c,h}.
- Adds one header per operation under
  include/anastasis/anastasis-database/, so each compilation unit only
  includes the database headers it actually uses. anastasis_database_lib.h
  becomes the umbrella header and declares ANASTASIS_DB_init/_fini.
- Updates all callers (backend httpd, authorization plugins, IBAN helper,
  anastasis-dbinit, test) to call ANASTASIS_DB_* directly instead of
  db->func(db->cls, ...). Drop the now-unused db handle from the
  authorization plugin context and loader.
- Update meson build accordingly.
- new GNUNET_PQ_init() with proper DB connection loss handling
- PREPARE macro use to prepare on-demand

Not yet pretty ('pq' global without proper prefix, some tests still
failing), but major progress.

Diffstat:
Msrc/authorization/anastasis-helper-authorization-iban.c | 51+++++++++++++++++++++------------------------------
Msrc/authorization/anastasis_authorization_plugin.c | 2--
Msrc/authorization/anastasis_authorization_plugin_email.c | 6+++---
Msrc/authorization/anastasis_authorization_plugin_file.c | 8++++----
Msrc/authorization/anastasis_authorization_plugin_iban.c | 34+++++++++++++---------------------
Msrc/authorization/anastasis_authorization_plugin_post.c | 6+++---
Msrc/authorization/anastasis_authorization_plugin_sms.c | 6+++---
Msrc/backend/anastasis-httpd.c | 20++------------------
Msrc/backend/anastasis-httpd.h | 5-----
Msrc/backend/anastasis-httpd_config.c | 1-
Msrc/backend/anastasis-httpd_policy-meta.c | 20++++++++++----------
Msrc/backend/anastasis-httpd_policy-upload.c | 76++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/backend/anastasis-httpd_policy.c | 38+++++++++++++++++++-------------------
Msrc/backend/anastasis-httpd_truth-challenge.c | 77++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/backend/anastasis-httpd_truth-solve.c | 81+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/backend/anastasis-httpd_truth-upload.c | 40+++++++++++++++++++---------------------
Asrc/include/anastasis/anastasis-database/challenge_gc.h | 37+++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/check_challenge_payment.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/check_payment_identifier.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/check_truth_upload_paid.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/common.h | 180+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/create_challenge_code.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/create_tables.h | 34++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/drop_tables.h | 34++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/event.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/gc.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_escrow_challenge.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_key_share.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_last_auth_iban_payment_row.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_latest_recovery_document.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_recovery_document.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/get_recovery_meta_data.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/increment_lifetime.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/lookup_account.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/lookup_challenge_payment.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/mark_challenge_code_satisfied.h | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/mark_challenge_sent.h | 42++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/preflight.h | 38++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/record_auth_iban_payment.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/record_challenge_payment.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/record_challenge_refund.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/record_recdoc_payment.h | 47+++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/record_truth_upload_payment.h | 43+++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/store_recovery_document.h | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/store_truth.h | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/test_auth_iban_payment.h | 39+++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/test_challenge_code_satisfied.h | 45+++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/transaction.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/update_challenge_payment.h | 41+++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/update_lifetime.h | 44++++++++++++++++++++++++++++++++++++++++++++
Asrc/include/anastasis/anastasis-database/verify_challenge_code.h | 39+++++++++++++++++++++++++++++++++++++++
Msrc/include/anastasis_authorization_lib.h | 2--
Msrc/include/anastasis_authorization_plugin.h | 5-----
Msrc/include/anastasis_database_lib.h | 61++++++++++++++++++++++++++++++++++++++++++++++---------------
Dsrc/include/anastasis_database_plugin.h | 884-------------------------------------------------------------------------------
Msrc/include/meson.build | 40+++++++++++++++++++++++++++++++++++++++-
Asrc/stasis/anastasis-db_challenge_gc.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_check_challenge_payment.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_check_payment_identifier.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_check_truth_upload_paid.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_create_challenge_code.c | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_create_tables.c | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_drop_tables.c | 34++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_gc.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_escrow_challenge.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_key_share.c | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_last_auth_iban_payment_row.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_latest_recovery_document.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_recovery_document.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_get_recovery_meta_data.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_increment_lifetime.c | 245+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_lookup_account.c | 136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_lookup_challenge_payment.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_mark_challenge_code_satisfied.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_mark_challenge_sent.c | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_pg.c | 220+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_pg.h | 115+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_preflight.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_record_auth_iban_payment.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_record_challenge_payment.c | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_record_challenge_refund.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_record_recdoc_payment.c | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_record_truth_upload_payment.c | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_store_recovery_document.c | 312+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_store_truth.c | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_test_auth_iban_payment.c | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_test_challenge_code_satisfied.c | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_update_challenge_payment.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_update_lifetime.c | 201+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/stasis/anastasis-db_verify_challenge_code.c | 211+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/stasis/anastasis-dbinit.c | 26++++++++++++--------------
Dsrc/stasis/anastasis_db_plugin.c | 87-------------------------------------------------------------------------------
Msrc/stasis/meson.build | 66++++++++++++++++++++++++++++++++++++++++++++++--------------------
Dsrc/stasis/plugin_anastasis_postgres.c | 2990-------------------------------------------------------------------------------
Msrc/stasis/test_anastasis_db.c | 218++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/util/meson.build | 9++++++---
96 files changed, 5853 insertions(+), 4393 deletions(-)

diff --git a/src/authorization/anastasis-helper-authorization-iban.c b/src/authorization/anastasis-helper-authorization-iban.c @@ -76,11 +76,6 @@ static struct GNUNET_CURL_RescheduleContext *rc; static const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Our DB plugin. - */ -static struct ANASTASIS_DatabasePlugin *db_plugin; - -/** * How long should we sleep when idle before trying to find more work? * Useful in case bank does not support long polling. */ @@ -173,10 +168,10 @@ notify (const char *sender_account_uri, &ev.debit_iban_hash); GNUNET_free (iban); as = TALER_amount2s (amount); - db_plugin->event_notify (db_plugin->cls, - &ev.header, - as, - strlen (as)); + ANASTASIS_DB_event_notify ( + &ev.header, + as, + strlen (as)); } @@ -209,8 +204,7 @@ shutdown_task (void *cls) GNUNET_SCHEDULER_cancel (task); task = NULL; } - ANASTASIS_DB_plugin_unload (db_plugin); - db_plugin = NULL; + ANASTASIS_DB_fini (); ANASTASIS_EUFIN_auth_free (&auth); cfg = NULL; } @@ -286,13 +280,13 @@ history_cb (void *cls, char *dcanon = payto_get_iban (details->debit_account_uri); char *ccanon = payto_get_iban (details->credit_account_uri); - qs = db_plugin->record_auth_iban_payment (db_plugin->cls, - serial_id, - details->wire_subject, - &details->amount, - dcanon, - ccanon, - details->execution_date); + qs = ANASTASIS_DB_record_auth_iban_payment ( + serial_id, + details->wire_subject, + &details->amount, + dcanon, + ccanon, + details->execution_date); GNUNET_free (ccanon); GNUNET_free (dcanon); } @@ -380,10 +374,10 @@ run (void *cls, (void) cls; (void) args; (void) cfgfile; + cfg = c; - if (NULL == - (db_plugin = ANASTASIS_DB_plugin_load (cfg, - false))) + if (GNUNET_OK != + ANASTASIS_DB_init (cfg)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database not set up. Did you run anastasis-dbinit?\n"); @@ -400,8 +394,7 @@ run (void *cls, "authorization-iban", "CREDIT_IBAN"); global_ret = EXIT_NOTCONFIGURED; - ANASTASIS_DB_plugin_unload (db_plugin); - db_plugin = NULL; + ANASTASIS_DB_fini (); return; } @@ -412,23 +405,21 @@ run (void *cls, { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to load bank access configuration data\n"); - ANASTASIS_DB_plugin_unload (db_plugin); - db_plugin = NULL; + ANASTASIS_DB_fini (); global_ret = EXIT_NOTCONFIGURED; return; } { enum GNUNET_DB_QueryStatus qs; - qs = db_plugin->get_last_auth_iban_payment_row (db_plugin->cls, - authorization_iban, - &latest_row_off); + qs = ANASTASIS_DB_get_last_auth_iban_payment_row ( + authorization_iban, + &latest_row_off); if (qs < 0) { GNUNET_break (0); ANASTASIS_EUFIN_auth_free (&auth); - ANASTASIS_DB_plugin_unload (db_plugin); - db_plugin = NULL; + ANASTASIS_DB_fini (); return; } } diff --git a/src/authorization/anastasis_authorization_plugin.c b/src/authorization/anastasis_authorization_plugin.c @@ -77,7 +77,6 @@ struct AuthPlugin struct ANASTASIS_AuthorizationPlugin * ANASTASIS_authorization_plugin_load ( const char *method, - struct ANASTASIS_DatabasePlugin *db, const struct GNUNET_CONFIGURATION_Handle *AH_cfg) { struct ANASTASIS_AuthorizationPlugin *authorization; @@ -103,7 +102,6 @@ ANASTASIS_authorization_plugin_load ( return NULL; } ap = GNUNET_new (struct AuthPlugin); - ap->ac.db = db; ap->ac.cfg = AH_cfg; if (GNUNET_OK != TALER_config_get_amount (AH_cfg, diff --git a/src/authorization/anastasis_authorization_plugin_email.c b/src/authorization/anastasis_authorization_plugin_email.c @@ -262,9 +262,9 @@ email_start (void *cls, plugin is already happy (no additional requirements), so mark this challenge as already satisfied from the start. */ - qs = ctx->ac->db->mark_challenge_code_satisfied (ctx->ac->db->cls, - truth_uuid, - code); + qs = ANASTASIS_DB_mark_challenge_code_satisfied ( + truth_uuid, + code); if (qs <= 0) { GNUNET_break (0); diff --git a/src/authorization/anastasis_authorization_plugin_file.c b/src/authorization/anastasis_authorization_plugin_file.c @@ -130,17 +130,17 @@ file_start (void *cls, const void *data, size_t data_length) { - const struct ANASTASIS_AuthorizationContext *ac = cls; struct ANASTASIS_AUTHORIZATION_State *as; enum GNUNET_DB_QueryStatus qs; + (void) cls; /* If the user can show this challenge code, this plugin is already happy (no additional requirements), so mark this challenge as already satisfied from the start. */ - qs = ac->db->mark_challenge_code_satisfied (ac->db->cls, - truth_uuid, - code); + qs = ANASTASIS_DB_mark_challenge_code_satisfied ( + truth_uuid, + code); if (qs <= 0) { GNUNET_break (0); diff --git a/src/authorization/anastasis_authorization_plugin_iban.c b/src/authorization/anastasis_authorization_plugin_iban.c @@ -365,8 +365,6 @@ static enum } test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as) { - struct IBAN_Context *ctx = as->ctx; - struct ANASTASIS_DatabasePlugin *db = ctx->ac->db; enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Absolute now; struct GNUNET_TIME_Timestamp limit; @@ -377,8 +375,7 @@ test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as) limit = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_absolute_subtract (now, CODE_VALIDITY_PERIOD)); - qs = db->test_auth_iban_payment ( - db->cls, + qs = ANASTASIS_DB_test_auth_iban_payment ( as->iban_number, limit, &check_payment_ok, @@ -399,8 +396,7 @@ test_wire_transfers (struct ANASTASIS_AUTHORIZATION_State *as) case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } - qs = db->mark_challenge_code_satisfied ( - db->cls, + qs = ANASTASIS_DB_mark_challenge_code_satisfied ( &as->truth_uuid, as->code); GNUNET_break (qs > 0); @@ -522,8 +518,6 @@ iban_solve (struct ANASTASIS_AUTHORIZATION_State *as, const struct GNUNET_HashCode *challenge_response, struct MHD_Connection *connection) { - struct IBAN_Context *ctx = as->ctx; - struct ANASTASIS_DatabasePlugin *db = ctx->ac->db; enum MHD_Result mres; enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); @@ -540,20 +534,20 @@ iban_solve (struct ANASTASIS_AUTHORIZATION_State *as, GNUNET_CRYPTO_hash (as->iban_number, strlen (as->iban_number), &espec.debit_iban_hash); - as->eh = db->event_listen (db->cls, - &espec.header, - GNUNET_TIME_absolute_get_remaining ( - timeout), - &bank_event_cb, - as); + as->eh = ANASTASIS_DB_event_listen ( + &espec.header, + GNUNET_TIME_absolute_get_remaining ( + timeout), + &bank_event_cb, + as); } after = GNUNET_TIME_absolute_to_timestamp ( GNUNET_TIME_absolute_subtract (now, CODE_VALIDITY_PERIOD)); - qs = db->test_challenge_code_satisfied (db->cls, - &as->truth_uuid, - as->code, - after); + qs = ANASTASIS_DB_test_challenge_code_satisfied ( + &as->truth_uuid, + as->code, + after); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -617,11 +611,9 @@ iban_solve (struct ANASTASIS_AUTHORIZATION_State *as, static void iban_cleanup (struct ANASTASIS_AUTHORIZATION_State *as) { - struct IBAN_Context *ctx = as->ctx; - if (NULL != as->eh) { - ctx->ac->db->event_listen_cancel (as->eh); + ANASTASIS_DB_event_listen_cancel (as->eh); as->eh = NULL; } GNUNET_free (as->iban_number); diff --git a/src/authorization/anastasis_authorization_plugin_post.c b/src/authorization/anastasis_authorization_plugin_post.c @@ -292,9 +292,9 @@ post_start (void *cls, plugin is already happy (no additional requirements), so mark this challenge as already satisfied from the start. */ - qs = ctx->ac->db->mark_challenge_code_satisfied (ctx->ac->db->cls, - truth_uuid, - code); + qs = ANASTASIS_DB_mark_challenge_code_satisfied ( + truth_uuid, + code); if (qs <= 0) { GNUNET_break (0); diff --git a/src/authorization/anastasis_authorization_plugin_sms.c b/src/authorization/anastasis_authorization_plugin_sms.c @@ -261,9 +261,9 @@ sms_start (void *cls, plugin is already happy (no additional requirements), so mark this challenge as already satisfied from the start. */ - qs = ctx->ac->db->mark_challenge_code_satisfied (ctx->ac->db->cls, - truth_uuid, - code); + qs = ANASTASIS_DB_mark_challenge_code_satisfied ( + truth_uuid, + code); if (qs <= 0) { GNUNET_break (0); diff --git a/src/backend/anastasis-httpd.c b/src/backend/anastasis-httpd.c @@ -113,11 +113,6 @@ struct GNUNET_CONTAINER_Heap *AH_to_heap; static int global_result; /** - * Connection handle to the our database - */ -struct ANASTASIS_DatabasePlugin *db; - -/** * Reschedule context for #AH_ctx. */ static struct GNUNET_CURL_RescheduleContext *rc; @@ -482,11 +477,7 @@ do_shutdown (void *cls) GNUNET_CURL_gnunet_rc_destroy (rc); rc = NULL; } - if (NULL != db) - { - ANASTASIS_DB_plugin_unload (db); - db = NULL; - } + ANASTASIS_DB_fini (); if (NULL != AH_to_heap) { GNUNET_CONTAINER_heap_destroy (AH_to_heap); @@ -819,15 +810,8 @@ run (void *cls, GNUNET_free (auth_header); } - if (NULL == - (db = ANASTASIS_DB_plugin_load (config, - false))) - { - GNUNET_SCHEDULER_shutdown (); - return; - } if (GNUNET_OK != - db->connect (db->cls)) + ANASTASIS_DB_init (config)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Database not setup. Did you run anastasis-dbinit?\n"); diff --git a/src/backend/anastasis-httpd.h b/src/backend/anastasis-httpd.h @@ -140,11 +140,6 @@ struct TM_HandlerContext }; /** - * Handle to the database backend. - */ -extern struct ANASTASIS_DatabasePlugin *db; - -/** * Upload limit to the service, in megabytes. */ extern unsigned long long AH_upload_limit_mb; diff --git a/src/backend/anastasis-httpd_config.c b/src/backend/anastasis-httpd_config.c @@ -53,7 +53,6 @@ add_methods (void *cls, return; section += strlen ("authorization-"); p = ANASTASIS_authorization_plugin_load (section, - db, AH_cfg); if (NULL == p) { diff --git a/src/backend/anastasis-httpd_policy-meta.c b/src/backend/anastasis-httpd_policy-meta.c @@ -114,11 +114,11 @@ return_policy_meta ( } result = json_object (); GNUNET_assert (NULL != result); - qs = db->get_recovery_meta_data (db->cls, - account_pub, - max_version, - &build_meta_result, - result); + qs = ANASTASIS_DB_get_recovery_meta_data ( + account_pub, + max_version, + &build_meta_result, + result); switch (qs) { @@ -160,11 +160,11 @@ AH_policy_meta_get ( uint32_t version; struct GNUNET_TIME_Timestamp expiration; - as = db->lookup_account (db->cls, - account_pub, - &expiration, - &recovery_data_hash, - &version); + as = ANASTASIS_DB_lookup_account ( + account_pub, + &expiration, + &recovery_data_hash, + &version); switch (as) { case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: diff --git a/src/backend/anastasis-httpd_policy-upload.c b/src/backend/anastasis-httpd_policy-upload.c @@ -364,11 +364,11 @@ proposal_cb (struct PolicyUploadContext *puc, "Storing payment request for order `%s'\n", por->details.ok.order_id); - qs = db->record_recdoc_payment (db->cls, - &puc->account, - (uint32_t) AH_post_counter, - &puc->payment_identifier, - &AH_annual_fee); + qs = ANASTASIS_DB_record_recdoc_payment ( + &puc->account, + (uint32_t) AH_post_counter, + &puc->payment_identifier, + &AH_annual_fee); if (0 >= qs) { GNUNET_break (0); @@ -474,11 +474,11 @@ check_payment_cb (struct PolicyUploadContext *puc, paid_until = GNUNET_TIME_relative_add (paid_until, GNUNET_TIME_UNIT_WEEKS); - qs = db->increment_lifetime (db->cls, - &puc->account, - &puc->payment_identifier, - paid_until, - &puc->paid_until); + qs = ANASTASIS_DB_increment_lifetime ( + &puc->account, + &puc->payment_identifier, + paid_until, + &puc->paid_until); if (0 <= qs) return; /* continue as planned */ GNUNET_break (0); @@ -871,10 +871,10 @@ AH_handler_policy_post ( bool valid_counter; enum GNUNET_DB_QueryStatus qs; - qs = db->check_payment_identifier (db->cls, - &puc->payment_identifier, - &paid, - &valid_counter); + qs = ANASTASIS_DB_check_payment_identifier ( + &puc->payment_identifier, + &paid, + &valid_counter); if (qs < 0) return TALER_MHD_reply_with_error (puc->con, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -919,11 +919,11 @@ AH_handler_policy_post ( /* Cost is zero, fake "zero" payment having happened */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Policy upload is free, allowing upload without payment\n"); - qs = db->record_recdoc_payment (db->cls, - account_pub, - AH_post_counter, - &puc->payment_identifier, - &AH_annual_fee); + qs = ANASTASIS_DB_record_recdoc_payment ( + account_pub, + AH_post_counter, + &puc->payment_identifier, + &AH_annual_fee); if (qs <= 0) return TALER_MHD_reply_with_error (puc->con, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -938,10 +938,10 @@ AH_handler_policy_post ( true), ANASTASIS_MAX_YEARS_STORAGE); puc->paid_until = GNUNET_TIME_relative_to_timestamp (rel); - qs = db->update_lifetime (db->cls, - account_pub, - &puc->payment_identifier, - puc->paid_until); + qs = ANASTASIS_DB_update_lifetime ( + account_pub, + &puc->payment_identifier, + puc->paid_until); if (qs <= 0) { GNUNET_break (0); @@ -961,11 +961,11 @@ AH_handler_policy_post ( struct GNUNET_TIME_Timestamp now; struct GNUNET_TIME_Relative rem; - as = db->lookup_account (db->cls, - account_pub, - &puc->paid_until, - &hash, - &version); + as = ANASTASIS_DB_lookup_account ( + account_pub, + &puc->paid_until, + &hash, + &version); now = GNUNET_TIME_timestamp_get (); if (GNUNET_TIME_timestamp_cmp (puc->paid_until, <, @@ -1106,16 +1106,16 @@ AH_handler_policy_post ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Uploading recovery document\n"); - ss = db->store_recovery_document (db->cls, - &puc->account, - &puc->account_sig, - &puc->new_policy_upload_hash, - puc->upload, - puc->upload_size, - puc->meta_data, - puc->meta_data_size, - &puc->payment_identifier, - &version); + ss = ANASTASIS_DB_store_recovery_document ( + &puc->account, + &puc->account_sig, + &puc->new_policy_upload_hash, + puc->upload, + puc->upload_size, + puc->meta_data, + puc->meta_data_size, + &puc->payment_identifier, + &version); GNUNET_snprintf (version_s, sizeof (version_s), "%u", diff --git a/src/backend/anastasis-httpd_policy.c b/src/backend/anastasis-httpd_policy.c @@ -69,23 +69,23 @@ return_policy (struct MHD_Connection *connection, TALER_EC_GENERIC_PARAMETER_MALFORMED, "version"); } - qs = db->get_recovery_document (db->cls, - account_pub, - version, - &account_sig, - &recovery_data_hash, - &res_recovery_data_size, - &res_recovery_data); + qs = ANASTASIS_DB_get_recovery_document ( + account_pub, + version, + &account_sig, + &recovery_data_hash, + &res_recovery_data_size, + &res_recovery_data); } else { - qs = db->get_latest_recovery_document (db->cls, - account_pub, - &account_sig, - &recovery_data_hash, - &res_recovery_data_size, - &res_recovery_data, - &version); + qs = ANASTASIS_DB_get_latest_recovery_document ( + account_pub, + &account_sig, + &recovery_data_hash, + &res_recovery_data_size, + &res_recovery_data, + &version); GNUNET_snprintf (version_b, sizeof (version_b), "%u", @@ -170,11 +170,11 @@ AH_policy_get (struct MHD_Connection *connection, uint32_t version; struct GNUNET_TIME_Timestamp expiration; - as = db->lookup_account (db->cls, - account_pub, - &expiration, - &recovery_data_hash, - &version); + as = ANASTASIS_DB_lookup_account ( + account_pub, + &expiration, + &recovery_data_hash, + &version); switch (as) { case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: diff --git a/src/backend/anastasis-httpd_truth-challenge.c b/src/backend/anastasis-httpd_truth-challenge.c @@ -388,9 +388,9 @@ refund_cb ( GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Refund `%s' succeeded\n", re->order_id); - qs = db->record_challenge_refund (db->cls, - &re->truth_uuid, - &re->payment_identifier); + qs = ANASTASIS_DB_record_challenge_refund ( + &re->truth_uuid, + &re->payment_identifier); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -640,10 +640,10 @@ proposal_cb (struct ChallengeContext *gc, gc->response_code = MHD_HTTP_BAD_GATEWAY; return; } - qs = db->record_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier, - &gc->challenge_cost); + qs = ANASTASIS_DB_record_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier, + &gc->challenge_cost); if (0 >= qs) { GNUNET_break (0); @@ -730,9 +730,9 @@ check_payment_cb (struct ChallengeContext *gc, { enum GNUNET_DB_QueryStatus qs; - qs = db->update_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier); + qs = ANASTASIS_DB_update_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier); if (0 <= qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -773,9 +773,9 @@ begin_payment (struct ChallengeContext *gc) enum GNUNET_DB_QueryStatus qs; char *order_id; - qs = db->lookup_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier); + qs = ANASTASIS_DB_lookup_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier); if (qs < 0) { GNUNET_break (0); @@ -938,10 +938,10 @@ run_authorization_process (struct MHD_Connection *connection, "Authorization request %llu for %s sent successfully\n", (unsigned long long) gc->code, TALER_B2S (&gc->truth_uuid)); - qs = db->mark_challenge_sent (db->cls, - &gc->payment_identifier, - &gc->truth_uuid, - gc->code); + qs = ANASTASIS_DB_mark_challenge_sent ( + &gc->payment_identifier, + &gc->truth_uuid, + gc->code); GNUNET_break (0 < qs); gc->authorization->cleanup (gc->as); gc->as = NULL; @@ -960,10 +960,10 @@ run_authorization_process (struct MHD_Connection *connection, return MHD_YES; case ANASTASIS_AUTHORIZATION_CRES_SUCCESS_REPLY_FAILED: /* Challenge sent successfully */ - qs = db->mark_challenge_sent (db->cls, - &gc->payment_identifier, - &gc->truth_uuid, - gc->code); + qs = ANASTASIS_DB_mark_challenge_sent ( + &gc->payment_identifier, + &gc->truth_uuid, + gc->code); GNUNET_break (0 < qs); gc->authorization->cleanup (gc->as); gc->as = NULL; @@ -1099,12 +1099,12 @@ AH_handler_truth_challenge ( enum GNUNET_DB_QueryStatus qs; char *method; - qs = db->get_escrow_challenge (db->cls, - &gc->truth_uuid, - &encrypted_truth, - &encrypted_truth_size, - &truth_mime, - &method); + qs = ANASTASIS_DB_get_escrow_challenge ( + &gc->truth_uuid, + &encrypted_truth, + &encrypted_truth_size, + &truth_mime, + &method); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1137,7 +1137,6 @@ AH_handler_truth_challenge ( gc->authorization = ANASTASIS_authorization_plugin_load (method, - db, AH_cfg); if (NULL == gc->authorization) { @@ -1189,10 +1188,10 @@ AH_handler_truth_challenge ( "Beginning payment, client did not provide payment identifier\n"); return begin_payment (gc); } - qs = db->check_challenge_payment (db->cls, - &gc->payment_identifier, - &gc->truth_uuid, - &paid); + qs = ANASTASIS_DB_check_challenge_payment ( + &gc->payment_identifier, + &gc->truth_uuid, + &paid); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1285,13 +1284,13 @@ AH_handler_truth_challenge ( struct GNUNET_TIME_Timestamp transmission_date; enum GNUNET_DB_QueryStatus qs; - qs = db->create_challenge_code (db->cls, - &gc->truth_uuid, - gc->authorization->code_rotation_period, - gc->authorization->code_validity_period, - gc->authorization->retry_counter, - &transmission_date, - &gc->code); + qs = ANASTASIS_DB_create_challenge_code ( + &gc->truth_uuid, + gc->authorization->code_rotation_period, + gc->authorization->code_validity_period, + gc->authorization->retry_counter, + &transmission_date, + &gc->code); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: diff --git a/src/backend/anastasis-httpd_truth-solve.c b/src/backend/anastasis-httpd_truth-solve.c @@ -475,10 +475,10 @@ proposal_cb (struct SolveContext *gc, gc->response_code = MHD_HTTP_BAD_GATEWAY; return; } - qs = db->record_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier, - &gc->challenge_cost); + qs = ANASTASIS_DB_record_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier, + &gc->challenge_cost); if (0 >= qs) { GNUNET_break (0); @@ -565,9 +565,9 @@ check_payment_cb (struct SolveContext *gc, { enum GNUNET_DB_QueryStatus qs; - qs = db->update_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier); + qs = ANASTASIS_DB_update_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier); if (0 <= qs) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -608,9 +608,9 @@ begin_payment (struct SolveContext *gc) enum GNUNET_DB_QueryStatus qs; char *order_id; - qs = db->lookup_challenge_payment (db->cls, - &gc->truth_uuid, - &gc->payment_identifier); + qs = ANASTASIS_DB_lookup_challenge_payment ( + &gc->truth_uuid, + &gc->payment_identifier); if (qs < 0) { GNUNET_break (0); @@ -726,9 +726,9 @@ return_key_share ( { enum GNUNET_DB_QueryStatus qs; - qs = db->get_key_share (db->cls, - truth_uuid, - &encrypted_keyshare); + qs = ANASTASIS_DB_get_key_share ( + truth_uuid, + &encrypted_keyshare); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -888,13 +888,13 @@ rate_limit (struct SolveContext *gc) uint64_t dummy; rt = GNUNET_TIME_UNIT_FOREVER_TS; - qs = db->create_challenge_code (db->cls, - &gc->truth_uuid, - MAX_QUESTION_FREQ, - GNUNET_TIME_UNIT_HOURS, - INITIAL_RETRY_COUNTER, - &rt, - &code); + qs = ANASTASIS_DB_create_challenge_code ( + &gc->truth_uuid, + MAX_QUESTION_FREQ, + GNUNET_TIME_UNIT_HOURS, + INITIAL_RETRY_COUNTER, + &rt, + &code); if (0 > qs) { GNUNET_break (0 < qs); @@ -918,11 +918,11 @@ rate_limit (struct SolveContext *gc) /* decrement trial counter */ ANASTASIS_hash_answer (code + 1, /* always use wrong answer */ &hc); - cs = db->verify_challenge_code (db->cls, - &gc->truth_uuid, - &hc, - &dummy, - &satisfied); + cs = ANASTASIS_DB_verify_challenge_code ( + &gc->truth_uuid, + &hc, + &dummy, + &satisfied); switch (cs) { case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: @@ -1258,12 +1258,12 @@ AH_handler_truth_solve ( enum GNUNET_DB_QueryStatus qs; char *method; - qs = db->get_escrow_challenge (db->cls, - &gc->truth_uuid, - &encrypted_truth, - &encrypted_truth_size, - &truth_mime, - &method); + qs = ANASTASIS_DB_get_escrow_challenge ( + &gc->truth_uuid, + &encrypted_truth, + &encrypted_truth_size, + &truth_mime, + &method); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1287,7 +1287,6 @@ AH_handler_truth_solve ( { gc->authorization = ANASTASIS_authorization_plugin_load (method, - db, AH_cfg); if (NULL == gc->authorization) { @@ -1330,10 +1329,10 @@ AH_handler_truth_solve ( "Beginning payment, client did not provide payment identifier\n"); return begin_payment (gc); } - qs = db->check_challenge_payment (db->cls, - &gc->payment_identifier, - &gc->truth_uuid, - &paid); + qs = ANASTASIS_DB_check_challenge_payment ( + &gc->payment_identifier, + &gc->truth_uuid, + &paid); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -1435,11 +1434,11 @@ AH_handler_truth_solve ( } /* random code, check against database */ - cs = db->verify_challenge_code (db->cls, - &gc->truth_uuid, - &gc->challenge_response, - &code, - &satisfied); + cs = ANASTASIS_DB_verify_challenge_code ( + &gc->truth_uuid, + &gc->challenge_response, + &code, + &satisfied); switch (cs) { case ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH: diff --git a/src/backend/anastasis-httpd_truth-upload.c b/src/backend/anastasis-httpd_truth-upload.c @@ -357,8 +357,7 @@ check_payment_cb (struct TruthUploadContext *tuc, with 365 days. */ paid_until = GNUNET_TIME_relative_add (paid_until, GNUNET_TIME_UNIT_WEEKS); - qs = db->record_truth_upload_payment ( - db->cls, + qs = ANASTASIS_DB_record_truth_upload_payment ( &tuc->truth_uuid, &osr->details.ok.details.paid.deposit_total, paid_until); @@ -623,7 +622,6 @@ AH_handler_truth_post ( type)) && (NULL == ANASTASIS_authorization_plugin_load (type, - db, AH_cfg)) ) { GNUNET_JSON_parse_free (spec); @@ -653,9 +651,9 @@ AH_handler_truth_post ( = GNUNET_TIME_relative_to_timestamp ( GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, storage_years)); - qs = db->check_truth_upload_paid (db->cls, - truth_uuid, - &paid_until); + qs = ANASTASIS_DB_check_truth_upload_paid ( + truth_uuid, + &paid_until); if (qs < 0) return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, @@ -719,17 +717,17 @@ AH_handler_truth_post ( { enum GNUNET_DB_QueryStatus qs; - qs = db->store_truth (db->cls, - truth_uuid, - &key_share_data, - (NULL == truth_mime) + qs = ANASTASIS_DB_store_truth ( + truth_uuid, + &key_share_data, + (NULL == truth_mime) ? "" : truth_mime, - encrypted_truth, - encrypted_truth_size, - type, - GNUNET_TIME_absolute_get_remaining ( - paid_until.abs_time)); + encrypted_truth, + encrypted_truth_size, + type, + GNUNET_TIME_absolute_get_remaining ( + paid_until.abs_time)); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -749,12 +747,12 @@ AH_handler_truth_post ( bool ok = false; if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == - db->get_escrow_challenge (db->cls, - truth_uuid, - &xtruth, - &xtruth_size, - &xtruth_mime, - &xmethod)) + ANASTASIS_DB_get_escrow_challenge ( + truth_uuid, + &xtruth, + &xtruth_size, + &xtruth_mime, + &xmethod)) { ok = ( (xtruth_size == encrypted_truth_size) && (0 == strcmp (xmethod, diff --git a/src/include/anastasis/anastasis-database/challenge_gc.h b/src/include/anastasis/anastasis-database/challenge_gc.h @@ -0,0 +1,37 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/challenge_gc.h + * @brief Anastasis database: challenge gc + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CHALLENGE_GC_H +#define ANASTASIS_DATABASE_CHALLENGE_GC_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Function called to remove all expired codes from the database. + * + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_challenge_gc (void); + +#endif diff --git a/src/include/anastasis/anastasis-database/check_challenge_payment.h b/src/include/anastasis/anastasis-database/check_challenge_payment.h @@ -0,0 +1,44 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/check_challenge_payment.h + * @brief Anastasis database: check challenge payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CHECK_CHALLENGE_PAYMENT_H +#define ANASTASIS_DATABASE_CHECK_CHALLENGE_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Check payment identifier. Used to check if a payment identifier given by + * the user is valid (existing and paid). + * + * @param payment_secret payment secret which the user must provide with every upload + * @param truth_uuid which truth should we check the payment status of + * @param[out] paid bool value to show if payment is paid + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_challenge_payment ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + bool *paid); + +#endif diff --git a/src/include/anastasis/anastasis-database/check_payment_identifier.h b/src/include/anastasis/anastasis-database/check_payment_identifier.h @@ -0,0 +1,44 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/check_payment_identifier.h + * @brief Anastasis database: check payment identifier + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CHECK_PAYMENT_IDENTIFIER_H +#define ANASTASIS_DATABASE_CHECK_PAYMENT_IDENTIFIER_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Check payment identifier. Used to check if a payment identifier given by + * the user is valid (existing and paid). + * + * @param payment_secret payment secret which the user must provide with every upload + * @param[out] paid bool value to show if payment is paid + * @param[out] valid_counter bool value to show if post_counter is > 0 + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_payment_identifier ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + bool *paid, + bool *valid_counter); + +#endif diff --git a/src/include/anastasis/anastasis-database/check_truth_upload_paid.h b/src/include/anastasis/anastasis-database/check_truth_upload_paid.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/check_truth_upload_paid.h + * @brief Anastasis database: check truth upload paid + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CHECK_TRUTH_UPLOAD_PAID_H +#define ANASTASIS_DATABASE_CHECK_TRUTH_UPLOAD_PAID_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Inquire whether truth upload payment was made. + * + * @param uuid the truth's UUID + * @param[out] paid_until set for how long this truth is paid for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_truth_upload_paid ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + struct GNUNET_TIME_Timestamp *paid_until); + +#endif diff --git a/src/include/anastasis/anastasis-database/common.h b/src/include/anastasis/anastasis-database/common.h @@ -0,0 +1,180 @@ +/* + This file is part of Anastasis + Copyright (C) 2019-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/common.h + * @brief common definitions for the Anastasis database + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_COMMON_H +#define ANASTASIS_DATABASE_COMMON_H + +#include "anastasis_service.h" +#include <gnunet/gnunet_db_lib.h> + +/** + * How long is an offer for a challenge payment valid for payment? + */ +#define ANASTASIS_CHALLENGE_OFFER_LIFETIME GNUNET_TIME_UNIT_HOURS + +/** + * Return values for checking code validity. + */ +enum ANASTASIS_DB_CodeStatus +{ + /** + * Provided authentication code does not match database content. + */ + ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH = -3, + + /** + * Encountered hard error talking to DB. + */ + ANASTASIS_DB_CODE_STATUS_HARD_ERROR = -2, + + /** + * Encountered serialization error talking to DB. + */ + ANASTASIS_DB_CODE_STATUS_SOFT_ERROR = -1, + + /** + * We have no challenge in the database. + */ + ANASTASIS_DB_CODE_STATUS_NO_RESULTS = 0, + + /** + * The provided challenge matches what we have in the database. + */ + ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED = 1, +}; + + +/** + * Return values for checking account validity. + */ +enum ANASTASIS_DB_AccountStatus +{ + /** + * Account is unknown, user should pay to establish it. + */ + ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED = -3, + + /** + * Encountered hard error talking to DB. + */ + ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR = -2, + + /** + * Account is valid, but we have no policy stored yet. + */ + ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS = 0, + + /** + * Account is valid, and we have a policy stored. + */ + ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED = 1, +}; + + +/** + * Return values for storing data in database with payment. + */ +enum ANASTASIS_DB_StoreStatus +{ + /** + * The client has stored too many policies, should pay to store more. + */ + ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED = -4, + + /** + * The client needs to pay to store policies. + */ + ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED = -3, + + /** + * Encountered hard error talking to DB. + */ + ANASTASIS_DB_STORE_STATUS_HARD_ERROR = -2, + + /** + * Despite retrying, we encountered serialization errors. + */ + ANASTASIS_DB_STORE_STATUS_SOFT_ERROR = -1, + + /** + * Database did not need an update (document exists). + */ + ANASTASIS_DB_STORE_STATUS_NO_RESULTS = 0, + + /** + * We successfully stored the document. + */ + ANASTASIS_DB_STORE_STATUS_SUCCESS = 1, +}; + + +/** + * Function called on all pending payments for an account or challenge. + * + * @param cls closure + * @param timestamp for how long have we been waiting + * @param payment_secret payment secret / order id in the backend + * @param amount how much is the order for + */ +typedef void +(*ANASTASIS_DB_PaymentPendingIterator)( + void *cls, + struct GNUNET_TIME_Timestamp timestamp, + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct TALER_Amount *amount); + + +/** + * Function called to test if a given wire transfer + * satisfied the authentication requirement of the + * IBAN plugin. + * + * @param cls closure + * @param credit amount that was transferred + * @param wire_subject subject provided in the wire transfer + * @return true if this wire transfer satisfied the authentication check + */ +typedef bool +(*ANASTASIS_DB_AuthIbanTransfercheck)( + void *cls, + const struct TALER_Amount *credit, + const char *wire_subject); + + +/** + * Function called on matching meta data. Note that if the client did + * not provide meta data for @a version, the function will be called + * with @a recovery_meta_data being NULL. + * + * @param cls closure + * @param version the version of the recovery document + * @param ts timestamp when the document was uploaded + * @param recovery_meta_data contains meta data about the encrypted recovery document + * @param recovery_meta_data_size size of @a recovery_meta_data blob + * @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration + */ +typedef enum GNUNET_GenericReturnValue +(*ANASTASIS_DB_RecoveryMetaCallback)(void *cls, + uint32_t version, + struct GNUNET_TIME_Timestamp ts, + const void *recovery_meta_data, + size_t recovery_meta_data_size); + +#endif diff --git a/src/include/anastasis/anastasis-database/create_challenge_code.h b/src/include/anastasis/anastasis-database/create_challenge_code.h @@ -0,0 +1,53 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/create_challenge_code.h + * @brief Anastasis database: create challenge code + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CREATE_CHALLENGE_CODE_H +#define ANASTASIS_DATABASE_CREATE_CHALLENGE_CODE_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Create a new challenge code for a given challenge identified by the challenge + * public key. The function will first check if there is already a valid code + * for this challenge present and won't insert a new one in this case. + * + * @param truth_uuid the identifier for the challenge + * @param rotation_period for how long is the code available + * @param validity_period for how long is the code available + * @param retry_counter amount of retries allowed + * @param[out] retransmission_date when to next retransmit + * @param[out] code set to the code which will be checked for later + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries, + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_create_challenge_code ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct GNUNET_TIME_Relative rotation_period, + struct GNUNET_TIME_Relative validity_period, + uint32_t retry_counter, + struct GNUNET_TIME_Timestamp *retransmission_date, + uint64_t *code); + +#endif diff --git a/src/include/anastasis/anastasis-database/create_tables.h b/src/include/anastasis/anastasis-database/create_tables.h @@ -0,0 +1,34 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/create_tables.h + * @brief create the Anastasis database tables + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_CREATE_TABLES_H +#define ANASTASIS_DATABASE_CREATE_TABLES_H + +#include <gnunet/gnunet_util_lib.h> + +/** + * Initialize the database tables. + * + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +enum GNUNET_GenericReturnValue +ANASTASIS_DB_create_tables (void); + +#endif diff --git a/src/include/anastasis/anastasis-database/drop_tables.h b/src/include/anastasis/anastasis-database/drop_tables.h @@ -0,0 +1,34 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/drop_tables.h + * @brief drop the Anastasis database tables + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_DROP_TABLES_H +#define ANASTASIS_DATABASE_DROP_TABLES_H + +#include <gnunet/gnunet_util_lib.h> + +/** + * Drop the Anastasis tables. Used for testcases. + * + * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure + */ +enum GNUNET_GenericReturnValue +ANASTASIS_DB_drop_tables (void); + +#endif diff --git a/src/include/anastasis/anastasis-database/event.h b/src/include/anastasis/anastasis-database/event.h @@ -0,0 +1,65 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/event.h + * @brief database event (notification) support for Anastasis + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_EVENT_H +#define ANASTASIS_DATABASE_EVENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> + +/** + * Register callback to be invoked on events of type @a es. + * + * @param es specification of the event to listen for + * @param timeout how long to wait for the event + * @param cb function to call when the event happens, possibly + * multiple times (until cancel is invoked) + * @param cb_cls closure for @a cb + * @return handle useful to cancel the listener + */ +struct GNUNET_DB_EventHandler * +ANASTASIS_DB_event_listen (const struct GNUNET_DB_EventHeaderP *es, + struct GNUNET_TIME_Relative timeout, + GNUNET_DB_EventCallback cb, + void *cb_cls); + + +/** + * Stop notifications. + * + * @param eh handle to unregister. + */ +void +ANASTASIS_DB_event_listen_cancel (struct GNUNET_DB_EventHandler *eh); + + +/** + * Notify all that listen on @a es of an event. + * + * @param es specification of the event to generate + * @param extra additional event data provided + * @param extra_size number of bytes in @a extra + */ +void +ANASTASIS_DB_event_notify (const struct GNUNET_DB_EventHeaderP *es, + const void *extra, + size_t extra_size); + +#endif diff --git a/src/include/anastasis/anastasis-database/gc.h b/src/include/anastasis/anastasis-database/gc.h @@ -0,0 +1,43 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/gc.h + * @brief garbage collection for the Anastasis database + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GC_H +#define ANASTASIS_DATABASE_GC_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> + +/** + * Function called to perform "garbage collection" on the + * database, expiring records we no longer require. Deletes + * all user records that are not paid up (and by cascade deletes + * the associated recovery documents). Also deletes expired + * truth and financial records older than @a expire_pending_payments. + * + * @param expire_backups backups older than the given time stamp should be garbage collected + * @param expire_pending_payments payments still pending from since before + * this value should be garbage collected + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_gc (struct GNUNET_TIME_Absolute expire_backups, + struct GNUNET_TIME_Absolute expire_pending_payments); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_escrow_challenge.h b/src/include/anastasis/anastasis-database/get_escrow_challenge.h @@ -0,0 +1,47 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_escrow_challenge.h + * @brief Anastasis database: get escrow challenge + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_ESCROW_CHALLENGE_H +#define ANASTASIS_DATABASE_GET_ESCROW_CHALLENGE_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Get the encrypted truth to validate the challenge response + * + * @param truth_uuid the identifier for the Truth + * @param[out] truth contains the encrypted truth + * @param[out] truth_size size of the encrypted truth + * @param[out] truth_mime mime type of truth + * @param[out] method type of the challenge + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_escrow_challenge ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + void **truth, + size_t *truth_size, + char **truth_mime, + char **method); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_key_share.h b/src/include/anastasis/anastasis-database/get_key_share.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_key_share.h + * @brief Anastasis database: get key share + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_KEY_SHARE_H +#define ANASTASIS_DATABASE_GET_KEY_SHARE_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Lookup (encrypted) key share by @a truth_uuid. + * + * @param truth_uuid the identifier for the Truth + * @param[out] key_share contains the encrypted Keyshare + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_key_share ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_last_auth_iban_payment_row.h b/src/include/anastasis/anastasis-database/get_last_auth_iban_payment_row.h @@ -0,0 +1,45 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_last_auth_iban_payment_row.h + * @brief Anastasis database: get last auth iban payment row + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_LAST_AUTH_IBAN_PAYMENT_ROW_H +#define ANASTASIS_DATABASE_GET_LAST_AUTH_IBAN_PAYMENT_ROW_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Function to check the last known IBAN payment. + * + * @param credit_account which credit account to check + * @param[out] last_row set to the last known row + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb + * returned 'true' once + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no + * wire transfers existed for which @a cb returned true + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_last_auth_iban_payment_row ( + const char *credit_account, + uint64_t *last_row); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_latest_recovery_document.h b/src/include/anastasis/anastasis-database/get_latest_recovery_document.h @@ -0,0 +1,49 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_latest_recovery_document.h + * @brief Anastasis database: get latest recovery document + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_LATEST_RECOVERY_DOCUMENT_H +#define ANASTASIS_DATABASE_GET_LATEST_RECOVERY_DOCUMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Fetch latest recovery document for user. + * + * @param account_pub public key of the user's account + * @param account_sig signature + * @param recovery_data_hash hash of the current recovery data + * @param data_size size of data blob + * @param data blob which contains the recovery document + * @param[out] version set to the version number of the policy being returned + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_latest_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + struct ANASTASIS_AccountSignatureP *account_sig, + struct GNUNET_HashCode *recovery_data_hash, + size_t *data_size, + void **data, + uint32_t *version); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_recovery_document.h b/src/include/anastasis/anastasis-database/get_recovery_document.h @@ -0,0 +1,49 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_recovery_document.h + * @brief Anastasis database: get recovery document + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_RECOVERY_DOCUMENT_H +#define ANASTASIS_DATABASE_GET_RECOVERY_DOCUMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Fetch recovery document for user according given version. + * + * @param account_pub public key of the user's account + * @param version the version number of the policy the user requests + * @param[out] account_sig signature + * @param[out] recovery_data_hash hash of the current recovery data + * @param[out] data_size size of data blob + * @param[out] data blob which contains the recovery document + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t version, + struct ANASTASIS_AccountSignatureP *account_sig, + struct GNUNET_HashCode *recovery_data_hash, + size_t *data_size, + void **data); + +#endif diff --git a/src/include/anastasis/anastasis-database/get_recovery_meta_data.h b/src/include/anastasis/anastasis-database/get_recovery_meta_data.h @@ -0,0 +1,39 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/get_recovery_meta_data.h + * @brief Anastasis database: get recovery meta data + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_GET_RECOVERY_META_DATA_H +#define ANASTASIS_DATABASE_GET_RECOVERY_META_DATA_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Closure for meta_iterator(). + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_recovery_meta_data ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t max_version, + ANASTASIS_DB_RecoveryMetaCallback cb, + void *cb_cls); + +#endif diff --git a/src/include/anastasis/anastasis-database/increment_lifetime.h b/src/include/anastasis/anastasis-database/increment_lifetime.h @@ -0,0 +1,46 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/increment_lifetime.h + * @brief Anastasis database: increment lifetime + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_INCREMENT_LIFETIME_H +#define ANASTASIS_DATABASE_INCREMENT_LIFETIME_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Increment account lifetime based on payment having been received. + * Does nothing if the payment is not new. + * + * @param account_pub which account received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @param lifetime for how long is the account now paid (increment) + * @param[out] paid_until set to the end of the lifetime after the operation + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_increment_lifetime ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_PaymentSecretP *payment_identifier, + struct GNUNET_TIME_Relative lifetime, + struct GNUNET_TIME_Timestamp *paid_until); + +#endif diff --git a/src/include/anastasis/anastasis-database/lookup_account.h b/src/include/anastasis/anastasis-database/lookup_account.h @@ -0,0 +1,46 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/lookup_account.h + * @brief Anastasis database: lookup account + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_LOOKUP_ACCOUNT_H +#define ANASTASIS_DATABASE_LOOKUP_ACCOUNT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Check if an account exists, and if so, return the + * current @a recovery_document_hash. + * + * @param account_pub account identifier + * @param[out] paid_until until when is the account paid up? + * @param[out] recovery_data_hash set to hash of @a recovery document + * @param[out] version set to the recovery policy version + * @return transaction status + */ +enum ANASTASIS_DB_AccountStatus +ANASTASIS_DB_lookup_account ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + struct GNUNET_TIME_Timestamp *paid_until, + struct GNUNET_HashCode *recovery_data_hash, + uint32_t *version); + +#endif diff --git a/src/include/anastasis/anastasis-database/lookup_challenge_payment.h b/src/include/anastasis/anastasis-database/lookup_challenge_payment.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/lookup_challenge_payment.h + * @brief Anastasis database: lookup challenge payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_LOOKUP_CHALLENGE_PAYMENT_H +#define ANASTASIS_DATABASE_LOOKUP_CHALLENGE_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Lookup pending payment for a certain challenge. + * + * @param truth_uuid identification of the challenge + * @param[out] payment_secret set to the challenge payment secret + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_lookup_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct ANASTASIS_PaymentSecretP *payment_secret); + +#endif diff --git a/src/include/anastasis/anastasis-database/mark_challenge_code_satisfied.h b/src/include/anastasis/anastasis-database/mark_challenge_code_satisfied.h @@ -0,0 +1,42 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/mark_challenge_code_satisfied.h + * @brief Anastasis database: mark challenge code satisfied + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_MARK_CHALLENGE_CODE_SATISFIED_H +#define ANASTASIS_DATABASE_MARK_CHALLENGE_CODE_SATISFIED_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Set the 'satisfied' bit for the given challenge and code to + * 'true'. + * + * @param truth_uuid identification of the challenge which the code corresponds to + * @param code code which is now satisfied + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_mark_challenge_code_satisfied ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const uint64_t code); + +#endif diff --git a/src/include/anastasis/anastasis-database/mark_challenge_sent.h b/src/include/anastasis/anastasis-database/mark_challenge_sent.h @@ -0,0 +1,42 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/mark_challenge_sent.h + * @brief Anastasis database: mark challenge sent + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_MARK_CHALLENGE_SENT_H +#define ANASTASIS_DATABASE_MARK_CHALLENGE_SENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Remember in the database that we successfully sent a challenge. + * + * @param payment_secret payment secret which the user must provide with every upload + * @param truth_uuid the identifier for the challenge + * @param code the challenge that was sent + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_mark_challenge_sent ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + uint64_t code); + +#endif diff --git a/src/include/anastasis/anastasis-database/preflight.h b/src/include/anastasis/anastasis-database/preflight.h @@ -0,0 +1,38 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/preflight.h + * @brief preflight check for the Anastasis database + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_PREFLIGHT_H +#define ANASTASIS_DATABASE_PREFLIGHT_H + +#include <gnunet/gnunet_util_lib.h> + +/** + * Do a pre-flight check that we are not in an uncommitted transaction. + * If we are, try to commit the previous transaction and output a warning. + * Does not return anything, as we will continue regardless of the outcome. + * + * @return #GNUNET_OK if everything is fine + * #GNUNET_NO if a transaction was rolled back + * #GNUNET_SYSERR on hard errors + */ +enum GNUNET_GenericReturnValue +ANASTASIS_DB_preflight (void); + +#endif diff --git a/src/include/anastasis/anastasis-database/record_auth_iban_payment.h b/src/include/anastasis/anastasis-database/record_auth_iban_payment.h @@ -0,0 +1,49 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/record_auth_iban_payment.h + * @brief Anastasis database: record auth iban payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_RECORD_AUTH_IBAN_PAYMENT_H +#define ANASTASIS_DATABASE_RECORD_AUTH_IBAN_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Store inbound IBAN payment made for authentication. + * + * @param wire_reference unique identifier inside LibEuFin/Nexus + * @param wire_subject subject of the wire transfer + * @param amount how much was transferred + * @param debit_account account that was debited + * @param credit_account Anastasis operator account credited + * @param execution_date when was the transfer made + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_auth_iban_payment ( + uint64_t wire_reference, + const char *wire_subject, + const struct TALER_Amount *amount, + const char *debit_account, + const char *credit_account, + struct GNUNET_TIME_Timestamp execution_date); + +#endif diff --git a/src/include/anastasis/anastasis-database/record_challenge_payment.h b/src/include/anastasis/anastasis-database/record_challenge_payment.h @@ -0,0 +1,43 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/record_challenge_payment.h + * @brief Anastasis database: record challenge payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_RECORD_CHALLENGE_PAYMENT_H +#define ANASTASIS_DATABASE_RECORD_CHALLENGE_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Store payment for challenge. + * + * @param truth_uuid identifier of the challenge to pay + * @param payment_secret payment secret which the user must provide with every upload + * @param amount how much we asked for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct TALER_Amount *amount); + +#endif diff --git a/src/include/anastasis/anastasis-database/record_challenge_refund.h b/src/include/anastasis/anastasis-database/record_challenge_refund.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/record_challenge_refund.h + * @brief Anastasis database: record challenge refund + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_RECORD_CHALLENGE_REFUND_H +#define ANASTASIS_DATABASE_RECORD_CHALLENGE_REFUND_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Store refund granted for challenge. + * + * @param truth_uuid identifier of the challenge to refund + * @param payment_secret payment secret which the user must provide with every upload + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_challenge_refund ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_secret); + +#endif diff --git a/src/include/anastasis/anastasis-database/record_recdoc_payment.h b/src/include/anastasis/anastasis-database/record_recdoc_payment.h @@ -0,0 +1,47 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/record_recdoc_payment.h + * @brief Anastasis database: record recdoc payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_RECORD_RECDOC_PAYMENT_H +#define ANASTASIS_DATABASE_RECORD_RECDOC_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Store payment. Used to begin a payment, not indicative + * that the payment actually was made. (That is done + * when we increment the account's lifetime.) + * + * @param account_pub anastasis's public key + * @param post_counter how many uploads does @a amount pay for + * @param payment_secret payment secret which the user must provide with every upload + * @param amount how much we asked for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_recdoc_payment ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t post_counter, + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct TALER_Amount *amount); + +#endif diff --git a/src/include/anastasis/anastasis-database/record_truth_upload_payment.h b/src/include/anastasis/anastasis-database/record_truth_upload_payment.h @@ -0,0 +1,43 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/record_truth_upload_payment.h + * @brief Anastasis database: record truth upload payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_RECORD_TRUTH_UPLOAD_PAYMENT_H +#define ANASTASIS_DATABASE_RECORD_TRUTH_UPLOAD_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Record truth upload payment was made. + * + * @param uuid the truth's UUID + * @param amount the amount that was paid + * @param duration how long is the truth paid for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_truth_upload_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Relative duration); + +#endif diff --git a/src/include/anastasis/anastasis-database/store_recovery_document.h b/src/include/anastasis/anastasis-database/store_recovery_document.h @@ -0,0 +1,54 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/store_recovery_document.h + * @brief Anastasis database: store recovery document + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_STORE_RECOVERY_DOCUMENT_H +#define ANASTASIS_DATABASE_STORE_RECOVERY_DOCUMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Store encrypted recovery document. + * + * @param account_pub public key of the user's account + * @param account_sig signature affirming storage request + * @param recovery_data_hash hash of @a data + * @param recovery_data contains encrypted_recovery_document + * @param recovery_data_size size of data blob + * @param payment_secret identifier for the payment, used to later charge on uploads + * @param[out] version set to the version assigned to the document by the database + * @return transaction status, 0 if upload could not be finished because @a payment_secret + * did not have enough upload left; HARD error if @a payment_secret is unknown, ... + */ +enum ANASTASIS_DB_StoreStatus +ANASTASIS_DB_store_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_AccountSignatureP *account_sig, + const struct GNUNET_HashCode *recovery_data_hash, + const void *recovery_data, + size_t recovery_data_size, + const void *recovery_meta_data, + size_t recovery_meta_data_size, + const struct ANASTASIS_PaymentSecretP *payment_secret, + uint32_t *version); + +#endif diff --git a/src/include/anastasis/anastasis-database/store_truth.h b/src/include/anastasis/anastasis-database/store_truth.h @@ -0,0 +1,51 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/store_truth.h + * @brief Anastasis database: store truth + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_STORE_TRUTH_H +#define ANASTASIS_DATABASE_STORE_TRUTH_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Upload Truth, which contains the Truth and the KeyShare. + * + * @param truth_uuid the identifier for the Truth + * @param key_share_data contains information of an EncryptedKeyShare + * @param mime_type presumed mime type of data in @a encrypted_truth + * @param encrypted_truth contains the encrypted Truth which includes the ground truth i.e. H(challenge answer), phonenumber, SMS + * @param encrypted_truth_size the size of the Truth + * @param method name of method + * @param truth_expiration time till the according data will be stored + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_store_truth ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share_data, + const char *mime_type, + const void *encrypted_truth, + size_t encrypted_truth_size, + const char *method, + struct GNUNET_TIME_Relative truth_expiration); + +#endif diff --git a/src/include/anastasis/anastasis-database/test_auth_iban_payment.h b/src/include/anastasis/anastasis-database/test_auth_iban_payment.h @@ -0,0 +1,39 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/test_auth_iban_payment.h + * @brief Anastasis database: test auth iban payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_TEST_AUTH_IBAN_PAYMENT_H +#define ANASTASIS_DATABASE_TEST_AUTH_IBAN_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Closure for #test_auth_cb. + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_test_auth_iban_payment ( + const char *debit_account, + struct GNUNET_TIME_Timestamp earliest_date, + ANASTASIS_DB_AuthIbanTransfercheck cb, + void *cb_cls); + +#endif diff --git a/src/include/anastasis/anastasis-database/test_challenge_code_satisfied.h b/src/include/anastasis/anastasis-database/test_challenge_code_satisfied.h @@ -0,0 +1,45 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/test_challenge_code_satisfied.h + * @brief Anastasis database: test challenge code satisfied + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_TEST_CHALLENGE_CODE_SATISFIED_H +#define ANASTASIS_DATABASE_TEST_CHALLENGE_CODE_SATISFIED_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Check if the 'satisfied' bit for the given challenge and code is + * 'true' and the challenge code is not yet expired. + * + * @param truth_uuid identification of the challenge which the code corresponds to + * @param code code which is now satisfied + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the challenge code is not satisfied or expired + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the challenge code has been marked as satisfied + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_test_challenge_code_satisfied ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const uint64_t code, + struct GNUNET_TIME_Timestamp after); + +#endif diff --git a/src/include/anastasis/anastasis-database/transaction.h b/src/include/anastasis/anastasis-database/transaction.h @@ -0,0 +1,60 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/transaction.h + * @brief transaction management for the Anastasis database + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_TRANSACTION_H +#define ANASTASIS_DATABASE_TRANSACTION_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> + +/** + * Check that the database connection is still up. + */ +void +ANASTASIS_DB_check_connection (void); + + +/** + * Start a transaction. + * + * @param name unique name identifying the transaction (for debugging), + * must point to a constant + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +ANASTASIS_DB_start (const char *name); + + +/** + * Roll back the current transaction of the database connection. + */ +void +ANASTASIS_DB_rollback (void); + + +/** + * Commit the current transaction of the database connection. + * + * @return transaction status code + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_commit (void); + +#endif diff --git a/src/include/anastasis/anastasis-database/update_challenge_payment.h b/src/include/anastasis/anastasis-database/update_challenge_payment.h @@ -0,0 +1,41 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/update_challenge_payment.h + * @brief Anastasis database: update challenge payment + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_UPDATE_CHALLENGE_PAYMENT_H +#define ANASTASIS_DATABASE_UPDATE_CHALLENGE_PAYMENT_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Update payment status of challenge + * + * @param truth_uuid which challenge received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_update_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_identifier); + +#endif diff --git a/src/include/anastasis/anastasis-database/update_lifetime.h b/src/include/anastasis/anastasis-database/update_lifetime.h @@ -0,0 +1,44 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/update_lifetime.h + * @brief Anastasis database: update lifetime + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_UPDATE_LIFETIME_H +#define ANASTASIS_DATABASE_UPDATE_LIFETIME_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Update account lifetime to the maximum of the current + * value and @a eol. + * + * @param account_pub which account received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @param eol for how long is the account now paid (absolute) + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_update_lifetime ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_PaymentSecretP *payment_identifier, + struct GNUNET_TIME_Timestamp eol); + +#endif diff --git a/src/include/anastasis/anastasis-database/verify_challenge_code.h b/src/include/anastasis/anastasis-database/verify_challenge_code.h @@ -0,0 +1,39 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file include/anastasis/anastasis-database/verify_challenge_code.h + * @brief Anastasis database: verify challenge code + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DATABASE_VERIFY_CHALLENGE_CODE_H +#define ANASTASIS_DATABASE_VERIFY_CHALLENGE_CODE_H + +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include "anastasis_service.h" +#include "anastasis/anastasis-database/common.h" + +/** + * Closure for check_valid_code(). + */ +enum ANASTASIS_DB_CodeStatus +ANASTASIS_DB_verify_challenge_code ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct GNUNET_HashCode *hashed_code, + uint64_t *code, + bool *satisfied); + +#endif diff --git a/src/include/anastasis_authorization_lib.h b/src/include/anastasis_authorization_lib.h @@ -29,14 +29,12 @@ * Load authorization plugin. * * @param method name of the method to load - * @param db database handle to use * @param AH_cfg configuration to use * @return plugin handle on success */ struct ANASTASIS_AuthorizationPlugin * ANASTASIS_authorization_plugin_load ( const char *method, - struct ANASTASIS_DatabasePlugin *db, const struct GNUNET_CONFIGURATION_Handle *AH_cfg); diff --git a/src/include/anastasis_authorization_plugin.h b/src/include/anastasis_authorization_plugin.h @@ -123,11 +123,6 @@ enum ANASTASIS_AUTHORIZATION_SolveResult struct ANASTASIS_AuthorizationContext { /** - * Database handle. - */ - struct ANASTASIS_DatabasePlugin *db; - - /** * Configuration to use. */ const struct GNUNET_CONFIGURATION_Handle *cfg; diff --git a/src/include/anastasis_database_lib.h b/src/include/anastasis_database_lib.h @@ -1,6 +1,6 @@ /* This file is part of Anastasis - Copyright (C) 2019 Anastasis SARL + Copyright (C) 2019-2022 Anastasis SARL Anastasis is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -15,7 +15,7 @@ */ /** * @file include/anastasis_database_lib.h - * @brief database plugin loader + * @brief Anastasis database library interface * @author Dominik Meister * @author Dennis Neufeld * @author Christian Grothoff @@ -23,29 +23,60 @@ #ifndef ANASTASIS_DB_LIB_H #define ANASTASIS_DB_LIB_H -#include "anastasis_database_plugin.h" +#include "anastasis_service.h" +#include <gnunet/gnunet_db_lib.h> +#include "anastasis/anastasis-database/common.h" +#include "anastasis/anastasis-database/preflight.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/event.h" +#include "anastasis/anastasis-database/create_tables.h" +#include "anastasis/anastasis-database/drop_tables.h" +#include "anastasis/anastasis-database/gc.h" +#include "anastasis/anastasis-database/store_recovery_document.h" +#include "anastasis/anastasis-database/get_recovery_meta_data.h" +#include "anastasis/anastasis-database/get_recovery_document.h" +#include "anastasis/anastasis-database/get_latest_recovery_document.h" +#include "anastasis/anastasis-database/store_truth.h" +#include "anastasis/anastasis-database/get_escrow_challenge.h" +#include "anastasis/anastasis-database/get_key_share.h" +#include "anastasis/anastasis-database/lookup_account.h" +#include "anastasis/anastasis-database/check_payment_identifier.h" +#include "anastasis/anastasis-database/check_challenge_payment.h" +#include "anastasis/anastasis-database/increment_lifetime.h" +#include "anastasis/anastasis-database/update_lifetime.h" +#include "anastasis/anastasis-database/record_recdoc_payment.h" +#include "anastasis/anastasis-database/record_truth_upload_payment.h" +#include "anastasis/anastasis-database/check_truth_upload_paid.h" +#include "anastasis/anastasis-database/verify_challenge_code.h" +#include "anastasis/anastasis-database/mark_challenge_code_satisfied.h" +#include "anastasis/anastasis-database/test_challenge_code_satisfied.h" +#include "anastasis/anastasis-database/create_challenge_code.h" +#include "anastasis/anastasis-database/mark_challenge_sent.h" +#include "anastasis/anastasis-database/challenge_gc.h" +#include "anastasis/anastasis-database/record_challenge_payment.h" +#include "anastasis/anastasis-database/record_challenge_refund.h" +#include "anastasis/anastasis-database/lookup_challenge_payment.h" +#include "anastasis/anastasis-database/update_challenge_payment.h" +#include "anastasis/anastasis-database/record_auth_iban_payment.h" +#include "anastasis/anastasis-database/test_auth_iban_payment.h" +#include "anastasis/anastasis-database/get_last_auth_iban_payment_row.h" + /** - * Initialize the plugin. + * Initialize the Anastasis database subsystem. * * @param cfg configuration to use - * @param skip_preflight true if we should skip the usual - * preflight check which assures us that the DB is actually - * operational; only anastasis-dbinit should use true here. - * @return NULL on failure + * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ -struct ANASTASIS_DatabasePlugin * -ANASTASIS_DB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight); +enum GNUNET_GenericReturnValue +ANASTASIS_DB_init (const struct GNUNET_CONFIGURATION_Handle *cfg); /** - * Shutdown the plugin. - * - * @param plugin plugin to unload + * Shutdown the Anastasis database subsystem. */ void -ANASTASIS_DB_plugin_unload (struct ANASTASIS_DatabasePlugin *plugin); +ANASTASIS_DB_fini (void); #endif /* ANASTASIS_DB_LIB_H */ diff --git a/src/include/anastasis_database_plugin.h b/src/include/anastasis_database_plugin.h @@ -1,884 +0,0 @@ -/* - This file is part of Anastasis - Copyright (C) 2019-2022 Anastasis SARL - - Anastasis is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file include/anastasis_database_plugin.h - * @brief database access for Anastasis - * @author Christian Grothoff - */ -#ifndef ANASTASIS_DATABASE_PLUGIN_H -#define ANASTASIS_DATABASE_PLUGIN_H - -#include "anastasis_service.h" -#include <gnunet/gnunet_db_lib.h> - -/** - * How long is an offer for a challenge payment valid for payment? - */ -#define ANASTASIS_CHALLENGE_OFFER_LIFETIME GNUNET_TIME_UNIT_HOURS - -/** - * Return values for checking code validity. - */ -enum ANASTASIS_DB_CodeStatus -{ - /** - * Provided authentication code does not match database content. - */ - ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH = -3, - - /** - * Encountered hard error talking to DB. - */ - ANASTASIS_DB_CODE_STATUS_HARD_ERROR = -2, - - /** - * Encountered serialization error talking to DB. - */ - ANASTASIS_DB_CODE_STATUS_SOFT_ERROR = -1, - - /** - * We have no challenge in the database. - */ - ANASTASIS_DB_CODE_STATUS_NO_RESULTS = 0, - - /** - * The provided challenge matches what we have in the database. - */ - ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED = 1, -}; - - -/** - * Return values for checking account validity. - */ -enum ANASTASIS_DB_AccountStatus -{ - /** - * Account is unknown, user should pay to establish it. - */ - ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED = -3, - - /** - * Encountered hard error talking to DB. - */ - ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR = -2, - - /** - * Account is valid, but we have no policy stored yet. - */ - ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS = 0, - - /** - * Account is valid, and we have a policy stored. - */ - ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED = 1, -}; - - -/** - * Return values for storing data in database with payment. - */ -enum ANASTASIS_DB_StoreStatus -{ - /** - * The client has stored too many policies, should pay to store more. - */ - ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED = -4, - - /** - * The client needs to pay to store policies. - */ - ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED = -3, - - /** - * Encountered hard error talking to DB. - */ - ANASTASIS_DB_STORE_STATUS_HARD_ERROR = -2, - - /** - * Despite retrying, we encountered serialization errors. - */ - ANASTASIS_DB_STORE_STATUS_SOFT_ERROR = -1, - - /** - * Database did not need an update (document exists). - */ - ANASTASIS_DB_STORE_STATUS_NO_RESULTS = 0, - - /** - * We successfully stored the document. - */ - ANASTASIS_DB_STORE_STATUS_SUCCESS = 1, -}; - - -/** - * Function called on all pending payments for an account or challenge. - * - * @param cls closure - * @param timestamp for how long have we been waiting - * @param payment_secret payment secret / order id in the backend - * @param amount how much is the order for - */ -typedef void -(*ANASTASIS_DB_PaymentPendingIterator)( - void *cls, - struct GNUNET_TIME_Timestamp timestamp, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct TALER_Amount *amount); - - -/** - * Function called to test if a given wire transfer - * satisfied the authentication requirement of the - * IBAN plugin. - * - * @param cls closure - * @param credit amount that was transferred - * @param wire_subject subject provided in the wire transfer - * @return true if this wire transfer satisfied the authentication check - */ -typedef bool -(*ANASTASIS_DB_AuthIbanTransfercheck)( - void *cls, - const struct TALER_Amount *credit, - const char *wire_subject); - - -/** - * Function called on matching meta data. Note that if the client did - * not provide meta data for @a version, the function will be called - * with @a recovery_meta_data being NULL. - * - * @param cls closure - * @param version the version of the recovery document - * @param ts timestamp when the document was uploaded - * @param recovery_meta_data contains meta data about the encrypted recovery document - * @param recovery_meta_data_size size of @a recovery_meta_data blob - * @return #GNUNET_OK to continue to iterate, #GNUNET_NO to abort iteration - */ -typedef enum GNUNET_GenericReturnValue -(*ANASTASIS_DB_RecoveryMetaCallback)(void *cls, - uint32_t version, - struct GNUNET_TIME_Timestamp ts, - const void *recovery_meta_data, - size_t recovery_meta_data_size); - - -/** - * Handle to interact with the database. - * - * Functions ending with "_TR" run their OWN transaction scope - * and MUST NOT be called from within a transaction setup by the - * caller. Functions ending with "_NT" require the caller to - * setup a transaction scope. Functions without a suffix are - * simple, single SQL queries that MAY be used either way. - */ -struct ANASTASIS_DatabasePlugin -{ - - /** - * Closure for all callbacks. - */ - void *cls; - - /** - * Name of the library which generated this plugin. Set by the - * plugin loader. - */ - char *library_name; - - /** - * Drop anastasis tables. Used for testcases. - * - * @param cls closure - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ - enum GNUNET_GenericReturnValue - (*drop_tables)(void *cls); - - /** - * Connect to the database. - * - * @param cls closure - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ - enum GNUNET_GenericReturnValue - (*connect)(void *cls); - - /** - * Initialize merchant tables - * - * @param cls closure - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ - enum GNUNET_GenericReturnValue - (*create_tables)(void *cls); - - /** - * Function called to perform "garbage collection" on the - * database, expiring records we no longer require. Deletes - * all user records that are not paid up (and by cascade deletes - * the associated recovery documents). Also deletes expired - * truth and financial records older than @a fin_expire. - * - * @param cls closure - * @param expire_backups backups older than the given time stamp should be garbage collected - * @param expire_pending_payments payments still pending from since before - * this value should be garbage collected - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*gc)(void *cls, - struct GNUNET_TIME_Absolute expire, - struct GNUNET_TIME_Absolute expire_pending_payments); - - /** - * Do a pre-flight check that we are not in an uncommitted transaction. - * If we are, try to commit the previous transaction and output a warning. - * Does not return anything, as we will continue regardless of the outcome. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_OK if everything is fine - * #GNUNET_NO if a transaction was rolled back - * #GNUNET_SYSERR on hard errors - */ - enum GNUNET_GenericReturnValue - (*preflight)(void *cls); - - /** - * Check that the database connection is still up. - * - * @param pg connection to check - */ - void - (*check_connection) (void *cls); - - /** - * Roll back the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_OK on success - */ - void - (*rollback) (void *cls); - - /** - * Start a transaction. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param name unique name identifying the transaction (for debugging), - * must point to a constant - * @return #GNUNET_OK on success - */ - int - (*start) (void *cls, - const char *name); - - /** - * Commit the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return transaction status code - */ - enum GNUNET_DB_QueryStatus - (*commit)(void *cls); - - - /** - * Register callback to be invoked on events of type @a es. - * - * @param cls database context to use - * @param es specification of the event to listen for - * @param timeout how long to wait for the event - * @param cb function to call when the event happens, possibly - * multiple times (until cancel is invoked) - * @param cb_cls closure for @a cb - * @return handle useful to cancel the listener - */ - struct GNUNET_DB_EventHandler * - (*event_listen)(void *cls, - const struct GNUNET_DB_EventHeaderP *es, - struct GNUNET_TIME_Relative timeout, - GNUNET_DB_EventCallback cb, - void *cb_cls); - - /** - * Stop notifications. - * - * @param eh handle to unregister. - */ - void - (*event_listen_cancel)(struct GNUNET_DB_EventHandler *eh); - - - /** - * Notify all that listen on @a es of an event. - * - * @param cls database context to use - * @param es specification of the event to generate - * @param extra additional event data provided - * @param extra_size number of bytes in @a extra - */ - void - (*event_notify)(void *cls, - const struct GNUNET_DB_EventHeaderP *es, - const void *extra, - size_t extra_size); - - - /** - * Store encrypted recovery document. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param account_sig signature affirming storage request - * @param recovery_data_hash hash of @a data - * @param recovery_data contains encrypted recovery document - * @param recovery_data_size size of @a recovery_data blob - * @param recovery_meta_data contains meta data about the encrypted recovery document - * @param recovery_meta_data_size size of @a recovery_meta_data blob - * @param payment_secret identifier for the payment, used to later charge on uploads - * @param[out] version set to the version assigned to the document by the database - * @return transaction status, 0 if upload could not be finished because @a payment_secret - * did not have enough upload left; HARD error if @a payment_secret is unknown, ... - */ - enum ANASTASIS_DB_StoreStatus - (*store_recovery_document)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_AccountSignatureP *account_sig, - const struct GNUNET_HashCode *recovery_data_hash, - const void *recovery_data, - size_t recovery_data_size, - const void *recovery_meta_data, - size_t recovery_meta_data_size, - const struct ANASTASIS_PaymentSecretP *payment_secret, - uint32_t *version); - - - /** - * Fetch recovery document meta data for user. Returns - * meta data in descending order from @a max_version. - * The size of the result set may be limited. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param max_version the maximum version number the user requests - * @param cb function to call on each result - * @param cb_cls closure for @a cb - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_recovery_meta_data)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t max_version, - ANASTASIS_DB_RecoveryMetaCallback cb, - void *cb_cls); - - - /** - * Fetch recovery document for user according given version. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param version the version number of the policy the user requests - * @param[out] account_sig signature - * @param[out] recovery_data_hash hash of the current recovery data - * @param[out] data_size size of data blob - * @param[out] data blob which contains the recovery document - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_recovery_document)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t version, - struct ANASTASIS_AccountSignatureP *account_sig, - struct GNUNET_HashCode *recovery_data_hash, - size_t *data_size, - void **data); - - - /** - * Fetch latest recovery document for user. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param account_sig signature - * @param recovery_data_hash hash of the current recovery data - * @param[out] data_size set to size of @a data blob - * @param[out] data set to blob which contains the recovery document - * @param[out] version set to the version number of the policy being returned - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_latest_recovery_document)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - struct ANASTASIS_AccountSignatureP *account_sig, - struct GNUNET_HashCode *recovery_data_hash, - size_t *data_size, - void **data, - uint32_t *version); - - - /** - * Upload Truth, which contains the Truth and the KeyShare. - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param key_share_data contains information of an EncryptedKeyShare - * @param mime_type presumed mime type of data in @a encrypted_truth - * @param encrypted_truth contains the encrypted Truth which includes the ground truth i.e. H(challenge answer), phonenumber, SMS - * @param encrypted_truth_size the size of the Truth - * @param method name of method - * @param truth_expiration time till the according data will be stored - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*store_truth)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share_data, - const char *mime_type, - const void *encrypted_truth, - size_t encrypted_truth_size, - const char *method, - struct GNUNET_TIME_Relative truth_expiration); - - - /** - * Get the encrypted truth to validate the challenge response - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param[out] truth contains the encrypted truth - * @param[out] truth_size size of the encrypted truth - * @param[out] truth_mime mime type of truth - * @param[out] method type of the challenge - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_escrow_challenge)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - void **truth, - size_t *truth_size, - char **truth_mime, - char **method); - - - /** - * Lookup (encrypted) key share by @a truth_uuid. - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param[out] key_share set to the encrypted Keyshare - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_key_share)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share); - - - /** - * Check if an account exists, and if so, return the - * current @a recovery_document_hash. - * - * @param cls closure - * @param account_pub account identifier - * @param[out] paid_until until when is the account paid up? - * @param[out] recovery_data_hash set to hash of @a recovery document - * @param[out] version set to the recovery policy version - * @return transaction status - */ - enum ANASTASIS_DB_AccountStatus - (*lookup_account)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - struct GNUNET_TIME_Timestamp *paid_until, - struct GNUNET_HashCode *recovery_data_hash, - uint32_t *version); - - - /** - * Check payment identifier. Used to check if a payment identifier given by - * the user is valid (existing and paid). - * - * @param cls closure - * @param payment_secret payment secret which the user must provide with every upload - * @param[out] paid bool value to show if payment is paid - * @param[out] valid_counter bool value to show if post_counter is > 0 - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*check_payment_identifier)( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - bool *paid, - bool *valid_counter); - - - /** - * Check payment identifier. Used to check if a payment identifier given by - * the user is valid (existing and paid). - * - * @param cls closure - * @param payment_secret payment secret which the user must provide with every upload - * @param truth_uuid unique identifier of the truth the user must satisfy the challenge - * @param[out] paid bool value to show if payment is paid - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*check_challenge_payment)( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - bool *paid); - - - /** - * Increment account lifetime by @a lifetime. - * - * @param cls closure - * @param account_pub which account received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @param lifetime for how long is the account now paid (increment) - * @param[out] paid_until set to the end of the lifetime after the operation - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*increment_lifetime)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_PaymentSecretP *payment_identifier, - struct GNUNET_TIME_Relative lifetime, - struct GNUNET_TIME_Timestamp *paid_until); - - - /** - * Update account lifetime to the maximum of the current - * value and @a eol. - * - * @param cls closure - * @param account_pub which account received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @param eol for how long is the account now paid (absolute) - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*update_lifetime)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_PaymentSecretP *payment_identifier, - struct GNUNET_TIME_Timestamp eol); - - - /** - * Store payment. Used to begin a payment, not indicative - * that the payment actually was made. (That is done - * when we increment the account's lifetime.) - * - * @param cls closure - * @param account_pub anastasis's public key - * @param post_counter how many uploads does @a amount pay for - * @param payment_secret payment secret which the user must provide with every upload - * @param amount how much we asked for - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*record_recdoc_payment)( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t post_counter, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct TALER_Amount *amount); - - - /** - * Record truth upload payment was made. - * - * @param cls closure - * @param uuid the truth's UUID - * @param amount the amount that was paid - * @param duration how long is the truth paid for - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*record_truth_upload_payment)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Relative duration); - - - /** - * Inquire whether truth upload payment was made. - * - * @param cls closure - * @param uuid the truth's UUID - * @param[out] paid_until set for how long this truth is paid for - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*check_truth_upload_paid)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, - struct GNUNET_TIME_Timestamp *paid_until); - - - /** - * Verify the provided code with the code on the server. - * If the code matches the function will return with success, if the code - * does not match, the retry counter will be decreased by one. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param hashed_code code which the user provided and wants to verify - * @param[out] code set to the original numeric code - * @param[out] satisfied set to true if the challenge is set to satisfied - * @return transaction status - */ - enum ANASTASIS_DB_CodeStatus - (*verify_challenge_code)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct GNUNET_HashCode *hashed_code, - uint64_t *code, - bool *satisfied); - - - /** - * Set the 'satisfied' bit for the given challenge and code to - * 'true'. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param code code which is now satisfied - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*mark_challenge_code_satisfied)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - uint64_t code); - - - /** - * Check if the 'satisfied' bit for the given challenge and code is - * 'true' and the challenge code is not yet expired. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param code code which is now satisfied - * @param after after what time must the challenge have been created - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the challenge code is not satisfied or expired - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the challenge code has been marked as satisfied - */ - enum GNUNET_DB_QueryStatus - (*test_challenge_code_satisfied)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const uint64_t code, - struct GNUNET_TIME_Timestamp after); - - - /** - * Insert a new challenge code for a given challenge identified by the challenge - * public key. The function will first check if there is already a valid code - * for this challenge present and won't insert a new one in this case. - * - * @param cls closure - * @param truth_uuid the identifier for the challenge - * @param rotation_period for how long is the code available - * @param validity_period for how long is the code available - * @param retry_counter amount of retries allowed - * @param[out] retransmission_date when to next retransmit - * @param[out] code set to the code which will be checked for later - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB - */ - enum GNUNET_DB_QueryStatus - (*create_challenge_code)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct GNUNET_TIME_Relative rotation_period, - struct GNUNET_TIME_Relative validity_period, - uint32_t retry_counter, - struct GNUNET_TIME_Timestamp *retransmission_date, - uint64_t *code); - - - /** - * Remember in the database that we successfully sent a challenge. - * - * @param cls closure - * @param truth_uuid the identifier for the challenge - * @param code the challenge that was sent - */ - enum GNUNET_DB_QueryStatus - (*mark_challenge_sent)( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - uint64_t code); - - - /** - * Store payment for challenge. - * - * @param cls closure - * @param truth_key identifier of the challenge to pay - * @param payment_secret payment secret which the user must provide with every upload - * @param amount how much we asked for - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*record_challenge_payment)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct TALER_Amount *amount); - - - /** - * Record refund for challenge. - * - * @param cls closure - * @param truth_uuid identifier of the challenge to refund - * @param payment_secret payment secret which the user must provide with every upload - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*record_challenge_refund)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_secret); - - - /** - * Lookup for a pending payment for a certain challenge - * - * @param cls closure - * @param truth_uuid identification of the challenge - * @param[out] payment_secret set to the challenge payment secret - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*lookup_challenge_payment)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct ANASTASIS_PaymentSecretP *payment_secret); - - - /** - * Update payment status of challenge - * - * @param cls closure - * @param truth_uuid which challenge received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*update_challenge_payment)( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_identifier); - - - /** - * Store inbound IBAN payment made for authentication. - * - * @param cls closure - * @param wire_reference unique identifier inside LibEuFin/Nexus - * @param wire_subject subject of the wire transfer - * @param amount how much was transferred - * @param debit_account account that was debited - * @param credit_account Anastasis operator account credited - * @param execution_date when was the transfer made - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*record_auth_iban_payment)( - void *cls, - uint64_t wire_reference, - const char *wire_subject, - const struct TALER_Amount *amount, - const char *debit_account, - const char *credit_account, - struct GNUNET_TIME_Timestamp execution_date); - - - /** - * Function to check if we are aware of a wire transfer - * that satisfies the IBAN plugin's authentication check. - * - * @param cls closure - * @param debit_account which debit account to check - * @param earliest_date earliest date to check - * @param cb function to call on all entries found - * @param cb_cls closure for @a cb - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb - * returned 'true' once - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no - * wire transfers existed for which @a cb returned true - */ - enum GNUNET_DB_QueryStatus - (*test_auth_iban_payment)( - void *cls, - const char *debit_account, - struct GNUNET_TIME_Timestamp earliest_date, - ANASTASIS_DB_AuthIbanTransfercheck cb, - void *cb_cls); - - - /** - * Function to check the last known IBAN payment. - * - * @param cls closure - * @param credit_account which credit account to check - * @param[out] last_row set to the last known row - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb - * returned 'true' once - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no - * wire transfers existed for which @a cb returned true - */ - enum GNUNET_DB_QueryStatus - (*get_last_auth_iban_payment_row)( - void *cls, - const char *credit_account, - uint64_t *last_row); - - - /** - * Function called to remove all expired codes from the database. - * - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*challenge_gc)(void *cls); - - -}; -#endif diff --git a/src/include/meson.build b/src/include/meson.build @@ -8,7 +8,6 @@ install_data( 'anastasis_authorization_plugin.h', 'anastasis_crypto_lib.h', 'anastasis_database_lib.h', - 'anastasis_database_plugin.h', 'anastasis_eufin_lib.h', 'anastasis_error_codes.h', 'anastasis_redux.h', @@ -18,3 +17,42 @@ install_data( 'anastasis.h', install_dir: anastasisincludedir, ) + +install_data( + 'anastasis/anastasis-database/common.h', + 'anastasis/anastasis-database/preflight.h', + 'anastasis/anastasis-database/transaction.h', + 'anastasis/anastasis-database/event.h', + 'anastasis/anastasis-database/create_tables.h', + 'anastasis/anastasis-database/drop_tables.h', + 'anastasis/anastasis-database/gc.h', + 'anastasis/anastasis-database/store_recovery_document.h', + 'anastasis/anastasis-database/get_recovery_meta_data.h', + 'anastasis/anastasis-database/get_recovery_document.h', + 'anastasis/anastasis-database/get_latest_recovery_document.h', + 'anastasis/anastasis-database/store_truth.h', + 'anastasis/anastasis-database/get_escrow_challenge.h', + 'anastasis/anastasis-database/get_key_share.h', + 'anastasis/anastasis-database/lookup_account.h', + 'anastasis/anastasis-database/check_payment_identifier.h', + 'anastasis/anastasis-database/check_challenge_payment.h', + 'anastasis/anastasis-database/increment_lifetime.h', + 'anastasis/anastasis-database/update_lifetime.h', + 'anastasis/anastasis-database/record_recdoc_payment.h', + 'anastasis/anastasis-database/record_truth_upload_payment.h', + 'anastasis/anastasis-database/check_truth_upload_paid.h', + 'anastasis/anastasis-database/verify_challenge_code.h', + 'anastasis/anastasis-database/mark_challenge_code_satisfied.h', + 'anastasis/anastasis-database/test_challenge_code_satisfied.h', + 'anastasis/anastasis-database/create_challenge_code.h', + 'anastasis/anastasis-database/mark_challenge_sent.h', + 'anastasis/anastasis-database/challenge_gc.h', + 'anastasis/anastasis-database/record_challenge_payment.h', + 'anastasis/anastasis-database/record_challenge_refund.h', + 'anastasis/anastasis-database/lookup_challenge_payment.h', + 'anastasis/anastasis-database/update_challenge_payment.h', + 'anastasis/anastasis-database/record_auth_iban_payment.h', + 'anastasis/anastasis-database/test_auth_iban_payment.h', + 'anastasis/anastasis-database/get_last_auth_iban_payment_row.h', + install_dir: anastasisincludedir / 'anastasis-database', +) diff --git a/src/stasis/anastasis-db_challenge_gc.c b/src/stasis/anastasis-db_challenge_gc.c @@ -0,0 +1,55 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_challenge_gc.c + * @brief Anastasis database: challenge gc + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/challenge_gc.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Function called to remove all expired codes from the database. + * + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_challenge_gc (void) +{ + struct GNUNET_TIME_Timestamp time_now = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_timestamp (&time_now), + GNUNET_PQ_query_param_end + }; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + PREPARE ("gc_challengecodes", + "DELETE FROM anastasis_challengecode " + "WHERE " + "expiration_date < $1;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "gc_challengecodes", + params); +} + + +/* end of anastasis-db_challenge_gc.c */ diff --git a/src/stasis/anastasis-db_check_challenge_payment.c b/src/stasis/anastasis-db_check_challenge_payment.c @@ -0,0 +1,71 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_check_challenge_payment.c + * @brief Anastasis database: check challenge payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/check_challenge_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Check payment identifier. Used to check if a payment identifier given by + * the user is valid (existing and paid). + * + * @param payment_secret payment secret which the user must provide with every upload + * @param truth_uuid which truth should we check the payment status of + * @param[out] paid bool value to show if payment is paid + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_challenge_payment ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + bool *paid) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("paid", + paid), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("challenge_payment_select", + "SELECT" + " paid" + " FROM anastasis_challenge_payment" + " WHERE payment_identifier=$1" + " AND truth_uuid=$2" + " AND refunded=FALSE" + " AND counter>0;"); + return GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "challenge_payment_select", + params, + rs); +} + + +/* end of anastasis-db_check_challenge_payment.c */ diff --git a/src/stasis/anastasis-db_check_payment_identifier.c b/src/stasis/anastasis-db_check_payment_identifier.c @@ -0,0 +1,71 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_check_payment_identifier.c + * @brief Anastasis database: check payment identifier + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/check_payment_identifier.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Check payment identifier. Used to check if a payment identifier given by + * the user is valid (existing and paid). + * + * @param payment_secret payment secret which the user must provide with every upload + * @param[out] paid bool value to show if payment is paid + * @param[out] valid_counter bool value to show if post_counter is > 0 + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_payment_identifier ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + bool *paid, + bool *valid_counter) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_bool ("paid", + paid), + GNUNET_PQ_result_spec_bool ("valid_counter", + valid_counter), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("recdoc_payment_select", + "SELECT" + " creation_date" + ",post_counter > 0 AS valid_counter" + ",amount" + ",paid" + " FROM anastasis_recdoc_payment" + " WHERE payment_identifier=$1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "recdoc_payment_select", + params, + rs); +} + + +/* end of anastasis-db_check_payment_identifier.c */ diff --git a/src/stasis/anastasis-db_check_truth_upload_paid.c b/src/stasis/anastasis-db_check_truth_upload_paid.c @@ -0,0 +1,66 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_check_truth_upload_paid.c + * @brief Anastasis database: check truth upload paid + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/check_truth_upload_paid.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Inquire whether truth upload payment was made. + * + * @param uuid the truth's UUID + * @param[out] paid_until set for how long this truth is paid for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_check_truth_upload_paid ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + struct GNUNET_TIME_Timestamp *paid_until) +{ + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (uuid), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration", + paid_until), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("truth_payment_select", + "SELECT" + " expiration" + " FROM anastasis_truth_payment" + " WHERE truth_uuid=$1" + " AND expiration>$2;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "truth_payment_select", + params, + rs); +} + + +/* end of anastasis-db_check_truth_upload_paid.c */ diff --git a/src/stasis/anastasis-db_create_challenge_code.c b/src/stasis/anastasis-db_create_challenge_code.c @@ -0,0 +1,196 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_create_challenge_code.c + * @brief Anastasis database: create challenge code + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/create_challenge_code.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Create a new challenge code for a given challenge identified by the challenge + * public key. The function will first check if there is already a valid code + * for this challenge present and won't insert a new one in this case. + * + * @param truth_uuid the identifier for the challenge + * @param rotation_period for how long is the code available + * @param validity_period for how long is the code available + * @param retry_counter amount of retries allowed + * @param[out] retransmission_date when to next retransmit + * @param[out] code set to the code which will be checked for later + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries, + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_create_challenge_code ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct GNUNET_TIME_Relative rotation_period, + struct GNUNET_TIME_Relative validity_period, + uint32_t retry_counter, + struct GNUNET_TIME_Timestamp *retransmission_date, + uint64_t *code) +{ + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + struct GNUNET_TIME_Timestamp expiration_date; + struct GNUNET_TIME_Absolute ex_rot; + + PREPARE ("challengecode_select_meta", + "SELECT " + " code" + ",retry_counter" + ",retransmission_date" + " FROM anastasis_challengecode" + " WHERE truth_uuid=$1" + " AND expiration_date > $2" + " AND creation_date > $3" + " ORDER BY creation_date DESC" + " LIMIT 1;"); + PREPARE ("challengecode_insert", + "INSERT INTO anastasis_challengecode " + "(truth_uuid" + ",code" + ",creation_date" + ",expiration_date" + ",retry_counter" + ") VALUES " + "($1, $2, $3, $4, $5);"); + expiration_date = GNUNET_TIME_relative_to_timestamp (validity_period); + ex_rot = GNUNET_TIME_absolute_subtract (now.abs_time, + rotation_period); + for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) + { + if (GNUNET_OK != + ANASTASIS_DB_start ( + "create_challenge_code")) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + { + uint32_t old_retry_counter; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_absolute_time (&ex_rot), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("code", + code), + GNUNET_PQ_result_spec_uint32 ("retry_counter", + &old_retry_counter), + GNUNET_PQ_result_spec_timestamp ("retransmission_date", + retransmission_date), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "challengecode_select_meta", + params, + rs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "No active challenge found, creating a fresh one\n"); + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + if (0 == old_retry_counter) + { + ANASTASIS_DB_rollback (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Active challenge %llu has zero tries left, refusing to create another one\n", + (unsigned long long) *code); + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + ANASTASIS_DB_rollback (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Active challenge has %u tries left, returning old challenge %llu\n", + (unsigned int) old_retry_counter, + (unsigned long long) *code); + return qs; + } + } + + *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, + ANASTASIS_PIN_MAX_VALUE); + *retransmission_date = GNUNET_TIME_UNIT_ZERO_TS; + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_uint64 (code), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_timestamp (&expiration_date), + GNUNET_PQ_query_param_uint32 (&retry_counter), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challengecode_insert", + params); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Created fresh challenge with %u tries left\n", + (unsigned int) retry_counter); + break; + } + } + + { + enum GNUNET_DB_QueryStatus qs; + + qs = ANASTASIS_DB_commit (); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + if (qs < 0) + return qs; + } + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +retry: + ANASTASIS_DB_rollback (); + } + return GNUNET_DB_STATUS_SOFT_ERROR; +} + + +/* end of anastasis-db_create_challenge_code.c */ diff --git a/src/stasis/anastasis-db_create_tables.c b/src/stasis/anastasis-db_create_tables.c @@ -0,0 +1,55 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_create_tables.c + * @brief create the Anastasis database tables + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/create_tables.h" + + +enum GNUNET_GenericReturnValue +ANASTASIS_DB_create_tables (void) +{ + if (GNUNET_SYSERR == + GNUNET_PQ_load_versioning (pg->conn)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_PQ_run_sql (pg->conn, + "stasis-")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } +#if 0 + if (GNUNET_OK != + GNUNET_PQ_exec_sql (pg->conn, + "procedures")) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } +#endif + return GNUNET_OK; +} + + +/* end of anastasis-db_create_tables.c */ diff --git a/src/stasis/anastasis-db_drop_tables.c b/src/stasis/anastasis-db_drop_tables.c @@ -0,0 +1,34 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_drop_tables.c + * @brief drop the Anastasis database tables + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/drop_tables.h" + + +enum GNUNET_GenericReturnValue +ANASTASIS_DB_drop_tables (void) +{ + return GNUNET_PQ_exec_sql (pg->conn, + "drop"); +} + + +/* end of anastasis-db_drop_tables.c */ diff --git a/src/stasis/anastasis-db_gc.c b/src/stasis/anastasis-db_gc.c @@ -0,0 +1,76 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_gc.c + * @brief garbage collection for the Anastasis database + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/gc.h" +#include "anastasis/anastasis-database/preflight.h" +#include "anastasis/anastasis-database/transaction.h" + + +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_gc (struct GNUNET_TIME_Absolute expire_backups, + struct GNUNET_TIME_Absolute expire_pending_payments) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_absolute_time (&expire_backups), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_QueryParam params2[] = { + GNUNET_PQ_query_param_absolute_time (&expire_pending_payments), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); +#if 0 + /* FIXME: should we do this? */ + PREPARE ("gc_challenge_pending_payments", + "DELETE FROM anastasis_challenge_payment " + "WHERE" + " (paid=FALSE" + " OR" + " refunded)" + " AND" + " creation_date < $1;"), +#endif + PREPARE ("gc_accounts", + "DELETE FROM anastasis_user " + "WHERE" + " expiration_date < $1;"); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "gc_accounts", + params); + if (qs < 0) + return qs; + PREPARE ("gc_recdoc_pending_payments", + "DELETE FROM anastasis_recdoc_payment " + "WHERE" + " paid=FALSE" + " AND" + " creation_date < $1;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "gc_recdoc_pending_payments", + params2); +} + + +/* end of anastasis-db_gc.c */ diff --git a/src/stasis/anastasis-db_get_escrow_challenge.c b/src/stasis/anastasis-db_get_escrow_challenge.c @@ -0,0 +1,76 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_escrow_challenge.c + * @brief Anastasis database: get escrow challenge + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_escrow_challenge.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Get the encrypted truth to validate the challenge response + * + * @param truth_uuid the identifier for the Truth + * @param[out] truth contains the encrypted truth + * @param[out] truth_size size of the encrypted truth + * @param[out] truth_mime mime type of truth + * @param[out] method type of the challenge + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_escrow_challenge ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + void **truth, + size_t *truth_size, + char **truth_mime, + char **method) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_variable_size ("encrypted_truth", + truth, + truth_size), + GNUNET_PQ_result_spec_string ("truth_mime", + truth_mime), + GNUNET_PQ_result_spec_string ("method_name", + method), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("truth_select", + "SELECT " + " method_name" + ",encrypted_truth" + ",truth_mime" + " FROM anastasis_truth" + " WHERE truth_uuid=$1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "truth_select", + params, + rs); +} + + +/* end of anastasis-db_get_escrow_challenge.c */ diff --git a/src/stasis/anastasis-db_get_key_share.c b/src/stasis/anastasis-db_get_key_share.c @@ -0,0 +1,64 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_key_share.c + * @brief Anastasis database: get key share + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_key_share.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Lookup (encrypted) key share by @a truth_uuid. + * + * @param truth_uuid the identifier for the Truth + * @param[out] key_share contains the encrypted Keyshare + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_key_share ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("key_share_data", + key_share), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("key_share_select", + "SELECT " + "key_share_data " + "FROM " + "anastasis_truth " + "WHERE truth_uuid =$1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "key_share_select", + params, + rs); +} + + +/* end of anastasis-db_get_key_share.c */ diff --git a/src/stasis/anastasis-db_get_last_auth_iban_payment_row.c b/src/stasis/anastasis-db_get_last_auth_iban_payment_row.c @@ -0,0 +1,69 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_last_auth_iban_payment_row.c + * @brief Anastasis database: get last auth iban payment row + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_last_auth_iban_payment_row.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Function to check the last known IBAN payment. + * + * @param credit_account which credit account to check + * @param[out] last_row set to the last known row + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb + * returned 'true' once + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no + * wire transfers existed for which @a cb returned true + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_last_auth_iban_payment_row ( + const char *credit_account, + uint64_t *last_row) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (credit_account), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("wire_reference", + last_row), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("get_last_auth_iban_payment", + "SELECT " + " wire_reference" + " FROM anastasis_auth_iban_in" + " WHERE credit_account_details=$1" + " ORDER BY wire_reference DESC" + " LIMIT 1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "get_last_auth_iban_payment", + params, + rs); +} + + +/* end of anastasis-db_get_last_auth_iban_payment_row.c */ diff --git a/src/stasis/anastasis-db_get_latest_recovery_document.c b/src/stasis/anastasis-db_get_latest_recovery_document.c @@ -0,0 +1,85 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_latest_recovery_document.c + * @brief Anastasis database: get latest recovery document + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_latest_recovery_document.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Fetch latest recovery document for user. + * + * @param account_pub public key of the user's account + * @param account_sig signature + * @param recovery_data_hash hash of the current recovery data + * @param data_size size of data blob + * @param data blob which contains the recovery document + * @param[out] version set to the version number of the policy being returned + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_latest_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + struct ANASTASIS_AccountSignatureP *account_sig, + struct GNUNET_HashCode *recovery_data_hash, + size_t *data_size, + void **data, + uint32_t *version) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("version", + version), + GNUNET_PQ_result_spec_auto_from_type ("account_sig", + account_sig), + GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", + recovery_data_hash), + GNUNET_PQ_result_spec_variable_size ("recovery_data", + data, + data_size), + GNUNET_PQ_result_spec_end + }; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + PREPARE ("latest_recoverydocument_select", + "SELECT " + " version" + ",account_sig" + ",recovery_data_hash" + ",recovery_data" + " FROM anastasis_recoverydocument" + " WHERE user_id=$1" + " ORDER BY version DESC" + " LIMIT 1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "latest_recoverydocument_select", + params, + rs); +} + + +/* end of anastasis-db_get_latest_recovery_document.c */ diff --git a/src/stasis/anastasis-db_get_recovery_document.c b/src/stasis/anastasis-db_get_recovery_document.c @@ -0,0 +1,80 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_recovery_document.c + * @brief Anastasis database: get recovery document + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_recovery_document.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Fetch recovery document for user according given version. + * + * @param account_pub public key of the user's account + * @param version the version number of the policy the user requests + * @param[out] account_sig signature + * @param[out] recovery_data_hash hash of the current recovery data + * @param[out] data_size size of data blob + * @param[out] data blob which contains the recovery document + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t version, + struct ANASTASIS_AccountSignatureP *account_sig, + struct GNUNET_HashCode *recovery_data_hash, + size_t *data_size, + void **data) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_uint32 (&version), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("account_sig", + account_sig), + GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", + recovery_data_hash), + GNUNET_PQ_result_spec_variable_size ("recovery_data", + data, + data_size), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("recoverydocument_select", + "SELECT " + " account_sig" + ",recovery_data_hash" + ",recovery_data" + " FROM anastasis_recoverydocument" + " WHERE user_id=$1" + " AND version=$2;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "recoverydocument_select", + params, + rs); +} + + +/* end of anastasis-db_get_recovery_document.c */ diff --git a/src/stasis/anastasis-db_get_recovery_meta_data.c b/src/stasis/anastasis-db_get_recovery_meta_data.c @@ -0,0 +1,156 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_get_recovery_meta_data.c + * @brief Anastasis database: get recovery meta data + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/get_recovery_meta_data.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +struct MetaIteratorContext +{ + /** + * Function to call on each result. + */ + ANASTASIS_DB_RecoveryMetaCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Set to true on database failure. + */ + bool db_failure; +}; + + +/** + * Helper function for #postgres_get_recovery_meta_data(). + * To be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct MetaIteratorContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +meta_iterator (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct MetaIteratorContext *ctx = cls; + + for (unsigned int i = 0; i<num_results; i++) + { + uint32_t version; + void *meta_data; + size_t meta_data_size; + struct GNUNET_TIME_Timestamp ts; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("version", + &version), + GNUNET_PQ_result_spec_timestamp ("creation_date", + &ts), + GNUNET_PQ_result_spec_variable_size ("recovery_meta_data", + &meta_data, + &meta_data_size), + GNUNET_PQ_result_spec_end + }; + enum GNUNET_GenericReturnValue ret; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ctx->db_failure = true; + return; + } + ret = ctx->cb (ctx->cb_cls, + version, + ts, + meta_data, + meta_data_size); + GNUNET_PQ_cleanup_result (rs); + if (GNUNET_OK != ret) + break; + } +} + + +/** + * Fetch recovery document meta data for user. Returns + * meta data in descending order from @a max_version. + * The size of the result set may be limited. + * + * @param cls closure + * @param account_pub public key of the user's account + * @param max_version the maximum version number the user requests + * @param cb function to call on each result + * @param cb_cls closure for @a cb + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_get_recovery_meta_data ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t max_version, + ANASTASIS_DB_RecoveryMetaCallback cb, + void *cb_cls) +{ + struct MetaIteratorContext ctx = { + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_uint32 (&max_version), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE ("recoverydocument_select_meta", + "SELECT " + " version" + ",creation_date" + ",recovery_meta_data" + " FROM anastasis_recoverydocument" + " WHERE user_id=$1" + " AND version < $2" + " ORDER BY version DESC" + " LIMIT 1000;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "recoverydocument_select_meta", + params, + &meta_iterator, + &ctx); + if (qs < 0) + return qs; + if (ctx.db_failure) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} + + +/* end of anastasis-db_get_recovery_meta_data.c */ diff --git a/src/stasis/anastasis-db_increment_lifetime.c b/src/stasis/anastasis-db_increment_lifetime.c @@ -0,0 +1,245 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_increment_lifetime.c + * @brief Anastasis database: increment lifetime + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/increment_lifetime.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Increment account lifetime based on payment having been received. + * Does nothing if the payment is not new. + * + * @param account_pub which account received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @param lifetime for how long is the account now paid (increment) + * @param[out] paid_until set to the end of the lifetime after the operation + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_increment_lifetime ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_PaymentSecretP *payment_identifier, + struct GNUNET_TIME_Relative lifetime, + struct GNUNET_TIME_Timestamp *paid_until) +{ + enum GNUNET_DB_QueryStatus qs; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incrementing lifetime of account %s based on payment by %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_relative2s (lifetime, + true)); + PREPARE ("user_insert", + "INSERT INTO anastasis_user " + "(user_id" + ",expiration_date" + ") VALUES " + "($1, $2);"); + PREPARE ("user_select", + "SELECT" + " expiration_date " + "FROM anastasis_user" + " WHERE user_id=$1" + " FOR UPDATE;"); + PREPARE ("user_update", + "UPDATE anastasis_user" + " SET " + " expiration_date=$1" + " WHERE user_id=$2;"); + PREPARE ("recdoc_payment_done", + "UPDATE anastasis_recdoc_payment " + "SET" + " paid=TRUE " + "WHERE" + " payment_identifier=$1" + " AND" + " user_id=$2" + " AND" + " paid=FALSE;"); + for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) + { + if (GNUNET_OK != + ANASTASIS_DB_start ( + "increment lifetime")) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_identifier), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "recdoc_payment_done", + params); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + *paid_until = GNUNET_TIME_UNIT_ZERO_TS; + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* Payment not new or payment request unknown. */ + /* continued below */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* Payment just now marked as 'paid' */ + /* continued below */ + break; + } + } + + { + enum GNUNET_DB_QueryStatus qs2; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_TIME_Timestamp expiration; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + &expiration), + GNUNET_PQ_result_spec_end + }; + + qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "user_select", + params, + rs); + switch (qs2) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return qs2; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* inconsistent, cannot have recdoc payment but no user!? */ + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return GNUNET_DB_STATUS_HARD_ERROR; + } + else + { + /* user does not exist, create new one */ + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_timestamp (&expiration), + GNUNET_PQ_query_param_end + }; + + expiration = GNUNET_TIME_relative_to_timestamp (lifetime); + GNUNET_break (! GNUNET_TIME_absolute_is_never (expiration.abs_time)); + *paid_until = expiration; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Creating new account %s with initial lifetime of %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_relative2s (lifetime, + true)); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "user_insert", + iparams); + } + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + { + /* existing rec doc payment (payment replay), return + existing expiration */ + *paid_until = expiration; + ANASTASIS_DB_rollback (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Payment existed, lifetime of account %s unchanged at %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_timestamp2s (*paid_until)); + return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + } + else + { + /* user exists, payment is new, update expiration_date */ + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_timestamp (&expiration), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incrementing lifetime of account %s by %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_relative2s (lifetime, + true)); + expiration + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_add (expiration.abs_time, + lifetime)); + GNUNET_break (! GNUNET_TIME_absolute_is_never ( + expiration.abs_time)); + *paid_until = expiration; + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "user_update", + iparams); + } + break; + } + } + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + qs = ANASTASIS_DB_commit (); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + if (qs < 0) + return GNUNET_DB_STATUS_HARD_ERROR; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Incremented lifetime of account %s to %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_timestamp2s (*paid_until)); + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +retry: + ANASTASIS_DB_rollback (); + } + return GNUNET_DB_STATUS_SOFT_ERROR; +} + + +/* end of anastasis-db_increment_lifetime.c */ diff --git a/src/stasis/anastasis-db_lookup_account.c b/src/stasis/anastasis-db_lookup_account.c @@ -0,0 +1,136 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_lookup_account.c + * @brief Anastasis database: lookup account + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/lookup_account.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Check if an account exists, and if so, return the + * current @a recovery_document_hash. + * + * @param account_pub account identifier + * @param[out] paid_until until when is the account paid up? + * @param[out] recovery_data_hash set to hash of @a recovery document + * @param[out] version set to the recovery policy version + * @return transaction status + */ +enum ANASTASIS_DB_AccountStatus +ANASTASIS_DB_lookup_account ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + struct GNUNET_TIME_Timestamp *paid_until, + struct GNUNET_HashCode *recovery_data_hash, + uint32_t *version) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE ("user_select2", + "SELECT" + " expiration_date " + "FROM anastasis_user" + " WHERE user_id=$1" + " FOR UPDATE;"); + PREPARE ("latest_recovery_version_select", + "SELECT" + " version" + ",recovery_data_hash" + ",expiration_date" + " FROM anastasis_recoverydocument" + " JOIN anastasis_user USING (user_id)" + " WHERE user_id=$1" + " ORDER BY version DESC" + " LIMIT 1;"); + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + paid_until), + GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", + recovery_data_hash), + GNUNET_PQ_result_spec_uint32 ("version", + version), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "latest_recovery_version_select", + params, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + break; /* handle interesting case below */ + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + return ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED; + } + + /* check if account exists */ + { + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + paid_until), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "user_select2", + params, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* indicates: no account */ + return ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* indicates: no backup */ + *version = UINT32_MAX; + memset (recovery_data_hash, + 0, + sizeof (*recovery_data_hash)); + return ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS; + default: + GNUNET_break (0); + return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; + } +} + + +/* end of anastasis-db_lookup_account.c */ diff --git a/src/stasis/anastasis-db_lookup_challenge_payment.c b/src/stasis/anastasis-db_lookup_challenge_payment.c @@ -0,0 +1,78 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_lookup_challenge_payment.c + * @brief Anastasis database: lookup challenge payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/lookup_challenge_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Lookup pending payment for a certain challenge. + * + * @param truth_uuid identification of the challenge + * @param[out] payment_secret set to the challenge payment secret + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_lookup_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + struct ANASTASIS_PaymentSecretP *payment_secret) +{ + struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); + struct GNUNET_TIME_Timestamp recent + = GNUNET_TIME_absolute_to_timestamp ( + GNUNET_TIME_absolute_subtract (now, + ANASTASIS_CHALLENGE_OFFER_LIFETIME)); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_timestamp (&recent), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("payment_identifier", + payment_secret), + GNUNET_PQ_result_spec_end + }; + + PREPARE ("challenge_pending_payment_select", + "SELECT" + " creation_date" + ",payment_identifier" + ",amount" + " FROM anastasis_challenge_payment" + " WHERE" + " paid=FALSE" + " AND" + " refunded=FALSE" + " AND" + " truth_uuid=$1" + " AND" + " creation_date > $2;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "challenge_pending_payment_select", + params, + rs); +} + + +/* end of anastasis-db_lookup_challenge_payment.c */ diff --git a/src/stasis/anastasis-db_mark_challenge_code_satisfied.c b/src/stasis/anastasis-db_mark_challenge_code_satisfied.c @@ -0,0 +1,66 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_mark_challenge_code_satisfied.c + * @brief Anastasis database: mark challenge code satisfied + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/mark_challenge_code_satisfied.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Set the 'satisfied' bit for the given challenge and code to + * 'true'. + * + * @param truth_uuid identification of the challenge which the code corresponds to + * @param code code which is now satisfied + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_mark_challenge_code_satisfied ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const uint64_t code) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_uint64 (&code), + GNUNET_PQ_query_param_end + }; + + PREPARE ("challengecode_set_satisfied", + "UPDATE anastasis_challengecode" + " SET satisfied=TRUE" + " WHERE truth_uuid=$1" + " AND code=$2" + " AND creation_date IN" + " (SELECT creation_date" + " FROM anastasis_challengecode" + " WHERE truth_uuid=$1" + " AND code=$2" + " ORDER BY creation_date DESC" + " LIMIT 1);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challengecode_set_satisfied", + params); +} + + +/* end of anastasis-db_mark_challenge_code_satisfied.c */ diff --git a/src/stasis/anastasis-db_mark_challenge_sent.c b/src/stasis/anastasis-db_mark_challenge_sent.c @@ -0,0 +1,98 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_mark_challenge_sent.c + * @brief Anastasis database: mark challenge sent + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/mark_challenge_sent.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Remember in the database that we successfully sent a challenge. + * + * @param payment_secret payment secret which the user must provide with every upload + * @param truth_uuid the identifier for the challenge + * @param code the challenge that was sent + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_mark_challenge_sent ( + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + uint64_t code) +{ + enum GNUNET_DB_QueryStatus qs; + + PREPARE ("challengecode_mark_sent", + "UPDATE anastasis_challengecode" + " SET retransmission_date=$3" + " WHERE truth_uuid=$1" + " AND code=$2" + " AND creation_date IN" + " (SELECT creation_date" + " FROM anastasis_challengecode" + " WHERE truth_uuid=$1" + " AND code=$2" + " ORDER BY creation_date DESC" + " LIMIT 1);"); + PREPARE ("challengepayment_dec_counter", + "UPDATE anastasis_challenge_payment" + " SET counter=counter - 1" + " WHERE truth_uuid=$1" + " AND payment_identifier=$2" + " AND counter > 0;"); + { + struct GNUNET_TIME_Timestamp now; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_uint64 (&code), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + + now = GNUNET_TIME_timestamp_get (); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challengecode_mark_sent", + params); + if (qs <= 0) + return qs; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Marking challenge %llu as issued\n", + (unsigned long long) code); + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challengepayment_dec_counter", + params); + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* probably was free */ + return qs; + } +} + + +/* end of anastasis-db_mark_challenge_sent.c */ diff --git a/src/stasis/anastasis-db_pg.c b/src/stasis/anastasis-db_pg.c @@ -0,0 +1,220 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_pg.c + * @brief shared database state and helpers for the Anastasis postgres backend + * @author Christian Grothoff + * @author Marcello Stanisci + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/preflight.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/event.h" +#include <taler/taler_pq_lib.h> + + +/** + * Global database state. + */ +struct PostgresClosure *pg; + + +enum GNUNET_GenericReturnValue +ANASTASIS_DB_start (const char *name) +{ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + pg->transaction_name = name; + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + TALER_LOG_ERROR ("Failed to start transaction\n"); + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +void +ANASTASIS_DB_rollback (void) +{ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("ROLLBACK"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + TALER_LOG_ERROR ("Failed to rollback transaction\n"); + GNUNET_break (0); + } + pg->transaction_name = NULL; +} + + +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_commit (void) +{ + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam no_params[] = { + GNUNET_PQ_query_param_end + }; + + PREPARE ("do_commit", + "COMMIT"); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "do_commit", + no_params); + pg->transaction_name = NULL; + return qs; +} + + +struct GNUNET_DB_EventHandler * +ANASTASIS_DB_event_listen (const struct GNUNET_DB_EventHeaderP *es, + struct GNUNET_TIME_Relative timeout, + GNUNET_DB_EventCallback cb, + void *cb_cls) +{ + return GNUNET_PQ_event_listen (pg->conn, + es, + timeout, + cb, + cb_cls); +} + + +void +ANASTASIS_DB_event_listen_cancel (struct GNUNET_DB_EventHandler *eh) +{ + GNUNET_PQ_event_listen_cancel (eh); +} + + +void +ANASTASIS_DB_event_notify (const struct GNUNET_DB_EventHeaderP *es, + const void *extra, + size_t extra_size) +{ + GNUNET_PQ_event_notify (pg->conn, + es, + extra, + extra_size); +} + + +/** + * Function called each time we connect or reconnect to the + * database. Gives the application a chance to run some + * per-connection initialization logic. + * + * @param cls unused, NULL + * @param pq database connection handle + */ +static void +reconnect_cb (void *cls, + struct GNUNET_PQ_Context *pq) +{ +#if AUTO_EXPLAIN + /* Enable verbose logging to see where queries do not + properly use indices */ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), + GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), + /* https://wiki.postgresql.org/wiki/Serializable suggests to really + force the default to 'serializable' if SSI is to be used. */ + GNUNET_PQ_make_try_execute ( + "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), + GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), + GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), + GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; +#else + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; +#endif + + (void) cls; + if (GNUNET_OK != + GNUNET_PQ_exec_statements (pq, + es)) + { + GNUNET_break (0); + return; + } + pg->prep_gen++; +} + + +enum GNUNET_GenericReturnValue +ANASTASIS_DB_init (const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + pg = GNUNET_new (struct PostgresClosure); + pg->cfg = cfg; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "anastasis", + "CURRENCY", + &pg->currency)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "anastasis", + "CURRENCY"); + GNUNET_free (pg); + return GNUNET_SYSERR; + } + pg->conn = GNUNET_PQ_init (pg->cfg, + "stasis-postgres", + &reconnect_cb, + pg); + if (NULL == pg->conn) + { + GNUNET_break (0); + ANASTASIS_DB_fini (); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +void +ANASTASIS_DB_fini (void) +{ + if (NULL == pg) + return; + if (NULL != pg->conn) + GNUNET_PQ_disconnect (pg->conn); + GNUNET_free (pg->currency); + GNUNET_free (pg); +} + + +/* end of anastasis-db_pg.c */ diff --git a/src/stasis/anastasis-db_pg.h b/src/stasis/anastasis-db_pg.h @@ -0,0 +1,115 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_pg.h + * @brief internal shared state for the Anastasis postgres database + * @author Christian Grothoff + */ +#ifndef ANASTASIS_DB_PG_H +#define ANASTASIS_DB_PG_H + +#include "platform.h" +#include "anastasis_util_lib.h" +#include "anastasis_database_lib.h" +#include <gnunet/gnunet_util_lib.h> +#include <gnunet/gnunet_db_lib.h> +#include <gnunet/gnunet_pq_lib.h> +#include <taler/taler_pq_lib.h> + +/** + * How long do we keep transient accounts open (those that have + * not been paid at all, but are awaiting payment). This puts + * a cap on how long users have to make a payment after a payment + * request was generated. + */ +#define TRANSIENT_LIFETIME GNUNET_TIME_UNIT_WEEKS + +/** + * How often do we re-try if we run into a DB serialization error? + */ +#define MAX_RETRIES 3 + + +/** + * Shared database state for the Anastasis postgres backend. + */ +struct PostgresClosure +{ + + /** + * Postgres connection handle. + */ + struct GNUNET_PQ_Context *conn; + + /** + * Underlying configuration. + */ + const struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Name of the currently active transaction, NULL if none is active. + */ + const char *transaction_name; + + /** + * Currency we accept payments in. + */ + char *currency; + + /** + * Prepared statements have been initialized in this edition. + */ + uint64_t prep_gen; +}; + + +/** + * Global database state, initialized by ANASTASIS_DB_init(). + */ +extern struct PostgresClosure *pg; + +/** + * Prepares SQL statement @a sql under @a name for + * connection @a pg once. + * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure. + * + * @param name name to prepare the statement under + * @param sql actual SQL text + */ +#define PREPARE(name,sql) \ + do { \ + static unsigned long long gen; \ + \ + if (gen < pg->prep_gen) \ + { \ + struct GNUNET_PQ_PreparedStatement ps[] = { \ + GNUNET_PQ_make_prepare (name, sql), \ + GNUNET_PQ_PREPARED_STATEMENT_END \ + }; \ + \ + if (GNUNET_OK != \ + GNUNET_PQ_prepare_statements (pg->conn, \ + ps)) \ + { \ + GNUNET_break (0); \ + return GNUNET_DB_STATUS_HARD_ERROR; \ + } \ + gen = pg->prep_gen; \ + } \ + } while (0) + + +#endif diff --git a/src/stasis/anastasis-db_preflight.c b/src/stasis/anastasis-db_preflight.c @@ -0,0 +1,58 @@ +/* + This file is part of Anastasis + Copyright (C) 2020, 2021, 2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_preflight.c + * @brief preflight check for the Anastasis database + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/preflight.h" + + +enum GNUNET_GenericReturnValue +ANASTASIS_DB_preflight (void) +{ + struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("ROLLBACK"), + GNUNET_PQ_EXECUTE_STATEMENT_END + }; + + if (NULL == pg->transaction_name) + { + GNUNET_PQ_reconnect_if_down (pg->conn); + return GNUNET_OK; /* all good */ + } + if (GNUNET_OK == + GNUNET_PQ_exec_statements (pg->conn, + es)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BUG: Preflight check rolled back transaction `%s'!\n", + pg->transaction_name); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "BUG: Preflight check failed to rollback transaction `%s'!\n", + pg->transaction_name); + } + pg->transaction_name = NULL; + return GNUNET_NO; +} + + +/* end of anastasis-db_preflight.c */ diff --git a/src/stasis/anastasis-db_record_auth_iban_payment.c b/src/stasis/anastasis-db_record_auth_iban_payment.c @@ -0,0 +1,76 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_record_auth_iban_payment.c + * @brief Anastasis database: record auth iban payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/record_auth_iban_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Store inbound IBAN payment made for authentication. + * + * @param wire_reference unique identifier inside LibEuFin/Nexus + * @param wire_subject subject of the wire transfer + * @param amount how much was transferred + * @param debit_account account that was debited + * @param credit_account Anastasis operator account credited + * @param execution_date when was the transfer made + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_auth_iban_payment ( + uint64_t wire_reference, + const char *wire_subject, + const struct TALER_Amount *amount, + const char *debit_account, + const char *credit_account, + struct GNUNET_TIME_Timestamp execution_date) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint64 (&wire_reference), + GNUNET_PQ_query_param_string (wire_subject), + TALER_PQ_query_param_amount (pg->conn, + amount), + GNUNET_PQ_query_param_string (debit_account), + GNUNET_PQ_query_param_string (credit_account), + GNUNET_PQ_query_param_timestamp (&execution_date), + GNUNET_PQ_query_param_end + }; + + PREPARE ("store_auth_iban_payment_details", + "INSERT INTO anastasis_auth_iban_in " + "(wire_reference" + ",wire_subject" + ",credit" + ",debit_account_details" + ",credit_account_details" + ",execution_date" + ") VALUES " + "($1, $2, $3, $4, $5, $6);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "store_auth_iban_payment_details", + params); +} + + +/* end of anastasis-db_record_auth_iban_payment.c */ diff --git a/src/stasis/anastasis-db_record_challenge_payment.c b/src/stasis/anastasis-db_record_challenge_payment.c @@ -0,0 +1,67 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_record_challenge_payment.c + * @brief Anastasis database: record challenge payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/record_challenge_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Store payment for challenge. + * + * @param truth_uuid identifier of the challenge to pay + * @param payment_secret payment secret which the user must provide with every upload + * @param amount how much we asked for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct TALER_Amount *amount) +{ + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + TALER_PQ_query_param_amount (pg->conn, + amount), + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + + PREPARE ("challenge_payment_insert", + "INSERT INTO anastasis_challenge_payment " + "(truth_uuid" + ",amount" + ",payment_identifier" + ",creation_date" + ") VALUES " + "($1, $2, $3, $4);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challenge_payment_insert", + params); +} + + +/* end of anastasis-db_record_challenge_payment.c */ diff --git a/src/stasis/anastasis-db_record_challenge_refund.c b/src/stasis/anastasis-db_record_challenge_refund.c @@ -0,0 +1,63 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_record_challenge_refund.c + * @brief Anastasis database: record challenge refund + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/record_challenge_refund.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Store refund granted for challenge. + * + * @param truth_uuid identifier of the challenge to refund + * @param payment_secret payment secret which the user must provide with every upload + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_challenge_refund ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_secret) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_end + }; + + PREPARE ("challenge_refund_update", + "UPDATE anastasis_challenge_payment " + "SET" + " refunded=TRUE " + "WHERE" + " payment_identifier=$1" + " AND" + " paid=TRUE" + " AND" + " truth_uuid=$2;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challenge_refund_update", + params); +} + + +/* end of anastasis-db_record_challenge_refund.c */ diff --git a/src/stasis/anastasis-db_record_recdoc_payment.c b/src/stasis/anastasis-db_record_recdoc_payment.c @@ -0,0 +1,155 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_record_recdoc_payment.c + * @brief Anastasis database: record recdoc payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/record_recdoc_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Store payment. Used to begin a payment, not indicative + * that the payment actually was made. (That is done + * when we increment the account's lifetime.) + * + * @param account_pub anastasis's public key + * @param post_counter how many uploads does @a amount pay for + * @param payment_secret payment secret which the user must provide with every upload + * @param amount how much we asked for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_recdoc_payment ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + uint32_t post_counter, + const struct ANASTASIS_PaymentSecretP *payment_secret, + const struct TALER_Amount *amount) +{ + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + struct GNUNET_TIME_Timestamp expiration; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_uint32 (&post_counter), + TALER_PQ_query_param_amount (pg->conn, + amount), + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + PREPARE ("user_select3", + "SELECT" + " expiration_date " + "FROM anastasis_user" + " WHERE user_id=$1" + " FOR UPDATE;"); + PREPARE ("user_insert3", + "INSERT INTO anastasis_user " + "(user_id" + ",expiration_date" + ") VALUES " + "($1, $2);"); + PREPARE ("recdoc_payment_insert", + "INSERT INTO anastasis_recdoc_payment " + "(user_id" + ",post_counter" + ",amount" + ",payment_identifier" + ",creation_date" + ") VALUES " + "($1, $2, $3, $4, $5);"); + + /* because of constraint at user_id, first we have to verify + if user exists, and if not, create one */ + { + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + &expiration), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "user_select3", + iparams, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + { + /* create new user with short lifetime */ + struct GNUNET_TIME_Timestamp exp + = GNUNET_TIME_relative_to_timestamp (TRANSIENT_LIFETIME); + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_timestamp (&exp), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "user_insert3", + iparams); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* successful, continue below */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Created new account %s with transient life until %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_timestamp2s (exp)); + break; + } + } + /* continue below */ + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* handle case below */ + break; + } + + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "recdoc_payment_insert", + params); +} + + +/* end of anastasis-db_record_recdoc_payment.c */ diff --git a/src/stasis/anastasis-db_record_truth_upload_payment.c b/src/stasis/anastasis-db_record_truth_upload_payment.c @@ -0,0 +1,66 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_record_truth_upload_payment.c + * @brief Anastasis database: record truth upload payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/record_truth_upload_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Record truth upload payment was made. + * + * @param uuid the truth's UUID + * @param amount the amount that was paid + * @param duration how long is the truth paid for + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_record_truth_upload_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, + const struct TALER_Amount *amount, + struct GNUNET_TIME_Relative duration) +{ + struct GNUNET_TIME_Timestamp exp = GNUNET_TIME_relative_to_timestamp ( + duration); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (uuid), + TALER_PQ_query_param_amount (pg->conn, + amount), + GNUNET_PQ_query_param_timestamp (&exp), + GNUNET_PQ_query_param_end + }; + + PREPARE ("truth_payment_insert", + "INSERT INTO anastasis_truth_payment " + "(truth_uuid" + ",amount" + ",expiration" + ") VALUES " + "($1, $2, $3);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "truth_payment_insert", + params); +} + + +/* end of anastasis-db_record_truth_upload_payment.c */ diff --git a/src/stasis/anastasis-db_store_recovery_document.c b/src/stasis/anastasis-db_store_recovery_document.c @@ -0,0 +1,312 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_store_recovery_document.c + * @brief Anastasis database: store recovery document + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/store_recovery_document.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Store encrypted recovery document. + * + * @param account_pub public key of the user's account + * @param account_sig signature affirming storage request + * @param recovery_data_hash hash of @a data + * @param recovery_data contains encrypted_recovery_document + * @param recovery_data_size size of data blob + * @param payment_secret identifier for the payment, used to later charge on uploads + * @param[out] version set to the version assigned to the document by the database + * @return transaction status, 0 if upload could not be finished because @a payment_secret + * did not have enough upload left; HARD error if @a payment_secret is unknown, ... + */ +enum ANASTASIS_DB_StoreStatus +ANASTASIS_DB_store_recovery_document ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_AccountSignatureP *account_sig, + const struct GNUNET_HashCode *recovery_data_hash, + const void *recovery_data, + size_t recovery_data_size, + const void *recovery_meta_data, + size_t recovery_meta_data_size, + const struct ANASTASIS_PaymentSecretP *payment_secret, + uint32_t *version) +{ + enum GNUNET_DB_QueryStatus qs; + + GNUNET_break (GNUNET_OK == + ANASTASIS_DB_preflight ()); + + PREPARE ("user_select5", + "SELECT" + " expiration_date " + "FROM anastasis_user" + " WHERE user_id=$1" + " FOR UPDATE;"); + PREPARE ("latest_recovery_version_select2", + "SELECT" + " version" + ",recovery_data_hash" + ",expiration_date" + " FROM anastasis_recoverydocument" + " JOIN anastasis_user USING (user_id)" + " WHERE user_id=$1" + " ORDER BY version DESC" + " LIMIT 1;"); + PREPARE ("postcounter_select", + "SELECT" + " post_counter" + " FROM anastasis_recdoc_payment" + " WHERE user_id=$1" + " AND payment_identifier=$2;"); + PREPARE ("postcounter_update", + "UPDATE" + " anastasis_recdoc_payment" + " SET" + " post_counter=$1" + " WHERE user_id =$2" + " AND payment_identifier=$3;"); + PREPARE ("recovery_document_insert", + "INSERT INTO anastasis_recoverydocument " + "(user_id" + ",version" + ",account_sig" + ",recovery_data_hash" + ",recovery_data" + ",recovery_meta_data" + ",creation_date" + ") VALUES " + "($1, $2, $3, $4, $5, $6, $7);"); + for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) + { + if (GNUNET_OK != + ANASTASIS_DB_start ( + "store_recovery_document")) + { + GNUNET_break (0); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + } + /* get the current version and hash of the latest recovery document + for this account */ + { + struct GNUNET_HashCode dh; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("version", + version), + GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", + &dh), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select ( + pg->conn, + "latest_recovery_version_select2", + params, + rs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + *version = 1; + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* had an existing recovery_data, is it identical? */ + if (0 == GNUNET_memcmp (&dh, + recovery_data_hash)) + { + /* Yes. Previous identical recovery data exists */ + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_NO_RESULTS; + } + (*version)++; + break; + default: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + } + } + + /* First, check if account exists */ + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "user_select5", + params, + rs); + } + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + /* handle interesting case below */ + break; + } + + { + uint32_t postcounter; + + /* lookup if the user has enough uploads left and decrement */ + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("post_counter", + &postcounter), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "postcounter_select", + params, + rs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + } + + if (0 == postcounter) + { + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED; + } + /* Decrement the postcounter by one */ + postcounter--; + + /* Update the postcounter in the Database */ + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_uint32 (&postcounter), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_auto_from_type (payment_secret), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "postcounter_update", + params); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + default: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + } + } + } + + /* finally, actually insert the recovery document */ + { + struct GNUNET_TIME_Timestamp now + = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_uint32 (version), + GNUNET_PQ_query_param_auto_from_type (account_sig), + GNUNET_PQ_query_param_auto_from_type (recovery_data_hash), + GNUNET_PQ_query_param_fixed_size (recovery_data, + recovery_data_size), + GNUNET_PQ_query_param_fixed_size (recovery_meta_data, + recovery_meta_data_size), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "recovery_document_insert", + params); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + qs = ANASTASIS_DB_commit (); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + if (qs < 0) + return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; + return ANASTASIS_DB_STORE_STATUS_SUCCESS; + } + } +retry: + ANASTASIS_DB_rollback (); + } + return ANASTASIS_DB_STORE_STATUS_SOFT_ERROR; +} + + +/* end of anastasis-db_store_recovery_document.c */ diff --git a/src/stasis/anastasis-db_store_truth.c b/src/stasis/anastasis-db_store_truth.c @@ -0,0 +1,80 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_store_truth.c + * @brief Anastasis database: store truth + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/store_truth.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Upload Truth, which contains the Truth and the KeyShare. + * + * @param truth_uuid the identifier for the Truth + * @param key_share_data contains information of an EncryptedKeyShare + * @param mime_type presumed mime type of data in @a encrypted_truth + * @param encrypted_truth contains the encrypted Truth which includes the ground truth i.e. H(challenge answer), phonenumber, SMS + * @param encrypted_truth_size the size of the Truth + * @param method name of method + * @param truth_expiration time till the according data will be stored + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_store_truth ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share_data, + const char *mime_type, + const void *encrypted_truth, + size_t encrypted_truth_size, + const char *method, + struct GNUNET_TIME_Relative truth_expiration) +{ + struct GNUNET_TIME_Timestamp expiration; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_auto_from_type (key_share_data), + GNUNET_PQ_query_param_string (method), + GNUNET_PQ_query_param_fixed_size (encrypted_truth, + encrypted_truth_size), + GNUNET_PQ_query_param_string (mime_type), + GNUNET_PQ_query_param_timestamp (&expiration), + GNUNET_PQ_query_param_end + }; + + expiration = GNUNET_TIME_relative_to_timestamp (truth_expiration); + PREPARE ("truth_insert", + "INSERT INTO anastasis_truth " + "(truth_uuid" + ",key_share_data" + ",method_name" + ",encrypted_truth" + ",truth_mime" + ",expiration" + ") VALUES " + "($1, $2, $3, $4, $5, $6);"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "truth_insert", + params); +} + + +/* end of anastasis-db_store_truth.c */ diff --git a/src/stasis/anastasis-db_test_auth_iban_payment.c b/src/stasis/anastasis-db_test_auth_iban_payment.c @@ -0,0 +1,156 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_test_auth_iban_payment.c + * @brief Anastasis database: test auth iban payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/test_auth_iban_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +struct TestIbanContext +{ + + /** + * Function to call on each wire transfer found. + */ + ANASTASIS_DB_AuthIbanTransfercheck cb; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Value to return. + */ + enum GNUNET_DB_QueryStatus qs; +}; + + +/** + * Helper function for #postgres_test_auth_iban_payment(). + * To be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct TestIbanContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +test_auth_cb (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct TestIbanContext *tic = cls; + + for (unsigned int i = 0; i<num_results; i++) + { + struct TALER_Amount credit; + char *wire_subject; + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_amount ("credit", + pg->currency, + &credit), + GNUNET_PQ_result_spec_string ("wire_subject", + &wire_subject), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + tic->qs = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + if (tic->cb (tic->cb_cls, + &credit, + wire_subject)) + { + GNUNET_free (wire_subject); + tic->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; + return; + } + GNUNET_free (wire_subject); + } +} + + +/** + * Function to check if we are aware of a wire transfer + * that satisfies the IBAN plugin's authentication check. + * + * @param cls closure + * @param debit_account which debit account to check + * @param earliest_date earliest date to check + * @param cb function to call on all entries found + * @param cb_cls closure for @a cb + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb + * returned 'true' once + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no + * wire transfers existed for which @a cb returned true + */ + + +/** + * Closure for #test_auth_cb. + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_test_auth_iban_payment ( + const char *debit_account, + struct GNUNET_TIME_Timestamp earliest_date, + ANASTASIS_DB_AuthIbanTransfercheck cb, + void *cb_cls) +{ + struct TestIbanContext tic = { + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_string (debit_account), + GNUNET_PQ_query_param_timestamp (&earliest_date), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE ("test_auth_iban_payment", + "SELECT" + " credit" + ",wire_subject" + " FROM anastasis_auth_iban_in" + " WHERE debit_account_details=$1" + " AND execution_date>=$2;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "test_auth_iban_payment", + params, + &test_auth_cb, + &tic); + if (qs < 0) + return qs; + return tic.qs; +} + + +/* end of anastasis-db_test_auth_iban_payment.c */ diff --git a/src/stasis/anastasis-db_test_challenge_code_satisfied.c b/src/stasis/anastasis-db_test_challenge_code_satisfied.c @@ -0,0 +1,69 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_test_challenge_code_satisfied.c + * @brief Anastasis database: test challenge code satisfied + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/test_challenge_code_satisfied.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Check if the 'satisfied' bit for the given challenge and code is + * 'true' and the challenge code is not yet expired. + * + * @param truth_uuid identification of the challenge which the code corresponds to + * @param code code which is now satisfied + * @return transaction status, + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the challenge code is not satisfied or expired + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the challenge code has been marked as satisfied + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_test_challenge_code_satisfied ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const uint64_t code, + struct GNUNET_TIME_Timestamp after) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_uint64 (&code), + GNUNET_PQ_query_param_timestamp (&after), + GNUNET_PQ_query_param_end + }; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_end + }; + + PREPARE ("challengecode_test_satisfied", + "SELECT 1 FROM anastasis_challengecode" + " WHERE truth_uuid=$1" + " AND satisfied=TRUE" + " AND code=$2" + " AND creation_date >= $3" + " LIMIT 1;"); + return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "challengecode_test_satisfied", + params, + rs); +} + + +/* end of anastasis-db_test_challenge_code_satisfied.c */ diff --git a/src/stasis/anastasis-db_update_challenge_payment.c b/src/stasis/anastasis-db_update_challenge_payment.c @@ -0,0 +1,65 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_update_challenge_payment.c + * @brief Anastasis database: update challenge payment + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/update_challenge_payment.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Update payment status of challenge + * + * @param truth_uuid which challenge received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_update_challenge_payment ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct ANASTASIS_PaymentSecretP *payment_identifier) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_identifier), + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_end + }; + + PREPARE ("challenge_payment_done", + "UPDATE anastasis_challenge_payment " + "SET" + " paid=TRUE " + "WHERE" + " payment_identifier=$1" + " AND" + " refunded=FALSE" + " AND" + " truth_uuid=$2" + " AND" + " paid=FALSE;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challenge_payment_done", + params); +} + + +/* end of anastasis-db_update_challenge_payment.c */ diff --git a/src/stasis/anastasis-db_update_lifetime.c b/src/stasis/anastasis-db_update_lifetime.c @@ -0,0 +1,201 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_update_lifetime.c + * @brief Anastasis database: update lifetime + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/update_lifetime.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +/** + * Update account lifetime to the maximum of the current + * value and @a eol. + * + * @param account_pub which account received a payment + * @param payment_identifier proof of payment, must be unique and match pending payment + * @param eol for how long is the account now paid (absolute) + * @return transaction status + */ +// FIXME: what is the significant delta to increment_lifetime? +// Should we combine these? +enum GNUNET_DB_QueryStatus +ANASTASIS_DB_update_lifetime ( + const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, + const struct ANASTASIS_PaymentSecretP *payment_identifier, + struct GNUNET_TIME_Timestamp eol) +{ + enum GNUNET_DB_QueryStatus qs; + + PREPARE ("user_select4", + "SELECT" + " expiration_date " + "FROM anastasis_user" + " WHERE user_id=$1" + " FOR UPDATE;"); + PREPARE ("user_insert4", + "INSERT INTO anastasis_user " + "(user_id" + ",expiration_date" + ") VALUES " + "($1, $2);"); + PREPARE ("user_update4", + "UPDATE anastasis_user" + " SET " + " expiration_date=$1" + " WHERE user_id=$2;"); + PREPARE ("recdoc_payment_done2", + "UPDATE anastasis_recdoc_payment " + "SET" + " paid=TRUE " + "WHERE" + " payment_identifier=$1" + " AND" + " user_id=$2" + " AND" + " paid=FALSE;"); + for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) + { + if (GNUNET_OK != + ANASTASIS_DB_start ( + "update lifetime")) + { + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; + } + + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (payment_identifier), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "recdoc_payment_done2", + params); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + if (0 >= qs) + { + /* same payment made before, or unknown, or error + => no further action! */ + ANASTASIS_DB_rollback (); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Payment existed, lifetime of account %s unchanged\n", + TALER_B2S (account_pub)); + return qs; + } + } + + { + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + struct GNUNET_TIME_Timestamp expiration; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_timestamp ("expiration_date", + &expiration), + GNUNET_PQ_result_spec_end + }; + + qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, + "user_select4", + params, + rs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + { + /* user does not exist, create new one */ + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_timestamp (&eol), + GNUNET_PQ_query_param_end + }; + + GNUNET_break (! GNUNET_TIME_absolute_is_never (eol.abs_time)); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "user_insert4", + iparams); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Created new account %s with expiration %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_timestamp2s (eol)); + } + break; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + { + /* user exists, update expiration_date */ + struct GNUNET_PQ_QueryParam iparams[] = { + GNUNET_PQ_query_param_timestamp (&expiration), + GNUNET_PQ_query_param_auto_from_type (account_pub), + GNUNET_PQ_query_param_end + }; + + expiration = GNUNET_TIME_timestamp_max (expiration, + eol); + GNUNET_break (! GNUNET_TIME_absolute_is_never (expiration.abs_time)); + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "user_update4", + iparams); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Updated account %s to new expiration %s\n", + TALER_B2S (account_pub), + GNUNET_TIME_timestamp2s (expiration)); + } + break; + } + } + + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + ANASTASIS_DB_rollback (); + return qs; + case GNUNET_DB_STATUS_SOFT_ERROR: + goto retry; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + ANASTASIS_DB_rollback (); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + qs = ANASTASIS_DB_commit (); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + if (qs < 0) + return GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +retry: + ANASTASIS_DB_rollback (); + } + return GNUNET_DB_STATUS_SOFT_ERROR; +} + + +/* end of anastasis-db_update_lifetime.c */ diff --git a/src/stasis/anastasis-db_verify_challenge_code.c b/src/stasis/anastasis-db_verify_challenge_code.c @@ -0,0 +1,211 @@ +/* + This file is part of Anastasis + Copyright (C) 2020-2022 Anastasis SARL + + Anastasis is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file stasis/anastasis-db_verify_challenge_code.c + * @brief Anastasis database: verify challenge code + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-db_pg.h" +#include "anastasis/anastasis-database/verify_challenge_code.h" +#include "anastasis/anastasis-database/transaction.h" +#include "anastasis/anastasis-database/preflight.h" +#include <taler/taler_pq_lib.h> + + +struct CheckValidityContext +{ + /** + * Code to check for. + */ + const struct GNUNET_HashCode *hashed_code; + + /** + * Truth we are processing. + */ + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid; + + /** + * Set to the matching challenge code (if @e valid). + */ + uint64_t code; + + /** + * Set to true if a code matching @e hashed_code was found. + */ + bool valid; + + /** + * Set to true if a code matching @e hashed_code was set to 'satisfied' by the plugin. + */ + bool satisfied; + + /** + * Set to true if we had a database failure. + */ + bool db_failure; + +}; + + +/** + * Helper function for #postgres_verify_challenge_code(). + * To be called with the results of a SELECT statement + * that has returned @a num_results results. + * + * @param cls closure of type `struct CheckValidityContext *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +check_valid_code (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct CheckValidityContext *cvc = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + uint64_t server_code; + uint8_t sat; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint64 ("code", + &server_code), + GNUNET_PQ_result_spec_auto_from_type ("satisfied", + &sat), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + cvc->db_failure = true; + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Found issued challenge %llu (client: %s)\n", + (unsigned long long) server_code, + GNUNET_h2s (cvc->hashed_code)); + { + struct GNUNET_HashCode shashed_code; + + ANASTASIS_hash_answer (server_code, + &shashed_code); + if (0 == + GNUNET_memcmp (&shashed_code, + cvc->hashed_code)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Challenge is valid challenge (%s)\n", + (0 != sat) ? "satisfied" : "not satisfied"); + cvc->valid = true; + cvc->code = server_code; + cvc->satisfied = (0 != sat); + } + else + { + /* count failures to prevent brute-force attacks */ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (cvc->truth_uuid), + GNUNET_PQ_query_param_uint64 (&server_code), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, + "challengecode_update_retry", + params); + if (qs <= 0) + { + GNUNET_break (0); + cvc->db_failure = true; + } + } + } + } +} + + +/** + * Verify the provided code with the code on the server. + * If the code matches the function will return with success, if the code + * does not match, the retry counter will be decreased by one. + * + * @param cls closure + * @param truth_uuid identification of the challenge which the code corresponds to + * @param hashed_code code which the user provided and wants to verify + * @param[out] code set to the original numeric code + * @param[out] satisfied set to true if the challenge is set to satisfied + * @return code validity status + */ +enum ANASTASIS_DB_CodeStatus +ANASTASIS_DB_verify_challenge_code ( + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const struct GNUNET_HashCode *hashed_code, + uint64_t *code, + bool *satisfied) +{ + struct CheckValidityContext cvc = { + .truth_uuid = truth_uuid, + .hashed_code = hashed_code, + }; + struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (truth_uuid), + GNUNET_PQ_query_param_timestamp (&now), + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + *satisfied = false; + PREPARE ("challengecode_update_retry", + "UPDATE anastasis_challengecode" + " SET retry_counter=retry_counter - 1" + " WHERE truth_uuid=$1" + " AND code=$2" + " AND retry_counter != 0;"); + PREPARE ("challengecode_select", + "SELECT " + " code" + ",satisfied" + " FROM anastasis_challengecode" + " WHERE truth_uuid=$1" + " AND expiration_date > $2" + " AND retry_counter != 0;"); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "challengecode_select", + params, + &check_valid_code, + &cvc); + if ( (qs < 0) || + (cvc.db_failure) ) + return ANASTASIS_DB_CODE_STATUS_HARD_ERROR; + *code = cvc.code; + if (cvc.valid) + { + *satisfied = cvc.satisfied; + return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED; + } + if (0 == qs) + return ANASTASIS_DB_CODE_STATUS_NO_RESULTS; + return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH; +} + + +/* end of anastasis-db_verify_challenge_code.c */ diff --git a/src/stasis/anastasis-dbinit.c b/src/stasis/anastasis-dbinit.c @@ -1,6 +1,6 @@ /* This file is part of Anastasis - Copyright (C) 2019-2021 SARL + Copyright (C) 2019-2021, 2026 Anastasis SARL Anastasis is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -53,30 +53,28 @@ run (void *cls, const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg) { - struct ANASTASIS_DatabasePlugin *plugin; - - if (NULL == - (plugin = ANASTASIS_DB_plugin_load (cfg, - true))) + if (GNUNET_OK != + ANASTASIS_DB_init (cfg)) { fprintf (stderr, - "Failed to initialize database plugin.\n"); + "Failed to initialize database.\n"); global_ret = EXIT_NOTINSTALLED; return; } if (reset_db) { - if (GNUNET_OK != plugin->drop_tables (plugin->cls)) + if (GNUNET_OK != + ANASTASIS_DB_drop_tables ()) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not drop tables as requested. Either database was not yet initialized, or permission denied. Consult the logs. Will still try to create new tables.\n"); } } if (GNUNET_OK != - plugin->create_tables (plugin->cls)) + ANASTASIS_DB_create_tables ()) { global_ret = EXIT_FAILURE; - ANASTASIS_DB_plugin_unload (plugin); + ANASTASIS_DB_fini (); return; } if (gc_db) @@ -96,15 +94,15 @@ run (void *cls, GNUNET_TIME_relative_multiply ( GNUNET_TIME_UNIT_YEARS, 10)); - if (0 > plugin->gc (plugin->cls, - expire_backups, - expire_payments)) + if (0 > ANASTASIS_DB_gc ( + expire_backups, + expire_payments)) { fprintf (stderr, "Garbage collection failed!\n"); } } - ANASTASIS_DB_plugin_unload (plugin); + ANASTASIS_DB_fini (); } diff --git a/src/stasis/anastasis_db_plugin.c b/src/stasis/anastasis_db_plugin.c @@ -1,87 +0,0 @@ -/* - This file is part of Anastasis - Copyright (C) 2015, 2016 Anastasis SARL - - Anastasis is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file stasis/anastasis_db_plugin.c - * @brief Logic to load database plugin - * @author Christian Grothoff - * @author Sree Harsha Totakura <sreeharsha@totakura.in> - */ -#include "platform.h" -#include "anastasis_database_lib.h" -#include "anastasis_util_lib.h" -#include <ltdl.h> - - -struct ANASTASIS_DatabasePlugin * -ANASTASIS_DB_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - bool skip_preflight) -{ - char *plugin_name; - char *lib_name; - struct ANASTASIS_DatabasePlugin *plugin; - - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, - "anastasis", - "db", - &plugin_name)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "anastasis", - "db"); - return NULL; - } - (void) GNUNET_asprintf (&lib_name, - "libanastasis_plugin_db_%s", - plugin_name); - GNUNET_free (plugin_name); - plugin = GNUNET_PLUGIN_load (ANASTASIS_project_data (), - lib_name, - (void *) cfg); - if (NULL == plugin) - { - GNUNET_free (lib_name); - return NULL; - } - plugin->library_name = lib_name; - if ( (! skip_preflight) && - (GNUNET_OK != - plugin->preflight (plugin->cls)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Database not ready. Try running anastasis-dbinit!\n"); - ANASTASIS_DB_plugin_unload (plugin); - return NULL; - } - return plugin; -} - - -void -ANASTASIS_DB_plugin_unload (struct ANASTASIS_DatabasePlugin *plugin) -{ - char *lib_name; - - if (NULL == plugin) - return; - lib_name = plugin->library_name; - GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, - plugin)); - GNUNET_free (lib_name); -} - - -/* end of anastasis_db_plugin.c */ diff --git a/src/stasis/meson.build b/src/stasis/meson.build @@ -12,13 +12,56 @@ install_data( install_data('stasis-postgres.conf', install_dir: pkgcfgdir) +libanastasisdb_SOURCES = [ + 'anastasis-db_pg.c', + 'anastasis-db_preflight.c', + 'anastasis-db_create_tables.c', + 'anastasis-db_drop_tables.c', + 'anastasis-db_gc.c', + 'anastasis-db_challenge_gc.c', + 'anastasis-db_check_challenge_payment.c', + 'anastasis-db_check_payment_identifier.c', + 'anastasis-db_check_truth_upload_paid.c', + 'anastasis-db_create_challenge_code.c', + 'anastasis-db_get_escrow_challenge.c', + 'anastasis-db_get_key_share.c', + 'anastasis-db_get_last_auth_iban_payment_row.c', + 'anastasis-db_get_latest_recovery_document.c', + 'anastasis-db_get_recovery_document.c', + 'anastasis-db_get_recovery_meta_data.c', + 'anastasis-db_increment_lifetime.c', + 'anastasis-db_lookup_account.c', + 'anastasis-db_lookup_challenge_payment.c', + 'anastasis-db_mark_challenge_code_satisfied.c', + 'anastasis-db_mark_challenge_sent.c', + 'anastasis-db_record_auth_iban_payment.c', + 'anastasis-db_record_challenge_payment.c', + 'anastasis-db_record_challenge_refund.c', + 'anastasis-db_record_recdoc_payment.c', + 'anastasis-db_record_truth_upload_payment.c', + 'anastasis-db_store_recovery_document.c', + 'anastasis-db_store_truth.c', + 'anastasis-db_test_auth_iban_payment.c', + 'anastasis-db_test_challenge_code_satisfied.c', + 'anastasis-db_update_challenge_payment.c', + 'anastasis-db_update_lifetime.c', + 'anastasis-db_verify_challenge_code.c', +] + libanastasisdb = library( 'anastasisdb', - ['anastasis_db_plugin.c'], + libanastasisdb_SOURCES, soversion: solibversions['libanastasisdb']['soversion'], version: solibversions['libanastasisdb']['soversion'], install_rpath: rpath_option, - dependencies: [gnunetutil_dep, gnunetpq_dep, pq_dep, libanastasisutil_dep], + dependencies: [ + gnunetutil_dep, + gnunetpq_dep, + pq_dep, + talerpq_dep, + talerutil_dep, + libanastasisutil_dep, + ], include_directories: [incdir, configuration_inc], install: true, install_dir: get_option('libdir'), @@ -47,24 +90,6 @@ executable( install: true, ) -if pq_dep.found() - shared_module( - 'anastasis_plugin_db_postgres', - ['plugin_anastasis_postgres.c'], - dependencies: [ - libanastasisutil_dep, - gnunetutil_dep, - talerpq_dep, - talerutil_dep, - gnunetpq_dep, - pq_dep, - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: plugindir, - ) -endif - test_anastasis_db_postgres = executable( 'test_anastasis_db-postgres', ['test_anastasis_db.c'], @@ -75,6 +100,7 @@ test_anastasis_db_postgres = executable( talerutil_dep, talerpq_dep, gnunetpq_dep, + json_dep, ], include_directories: [incdir, configuration_inc], build_by_default: false, diff --git a/src/stasis/plugin_anastasis_postgres.c b/src/stasis/plugin_anastasis_postgres.c @@ -1,2990 +0,0 @@ -/* - This file is part of Anastasis - Copyright (C) 2020, 2021, 2022 Anastasis SARL - - Anastasis is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - Anastasis is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file stasis/plugin_anastasis_postgres.c - * @brief database helper functions for postgres used by GNU Anastasis - * @author Christian Grothoff - * @author Marcello Stanisci - */ -#include "platform.h" -#include "anastasis_database_plugin.h" -#include "anastasis_database_lib.h" -#include <taler/taler_pq_lib.h> - -/** - * How long do we keep transient accounts open (those that have - * not been paid at all, but are awaiting payment). This puts - * a cap on how long users have to make a payment after a payment - * request was generated. - */ -#define TRANSIENT_LIFETIME GNUNET_TIME_UNIT_WEEKS - -/** - * How often do we re-try if we run into a DB serialization error? - */ -#define MAX_RETRIES 3 - - -/** - * Type of the "cls" argument given to each of the functions in - * our API. - */ -struct PostgresClosure -{ - - /** - * Postgres connection handle. - */ - struct GNUNET_PQ_Context *conn; - - /** - * Underlying configuration. - */ - const struct GNUNET_CONFIGURATION_Handle *cfg; - - /** - * Name of the currently active transaction, NULL if none is active. - */ - const char *transaction_name; - - /** - * Currency we accept payments in. - */ - char *currency; - - /** - * Prepared statements have been initialized. - */ - bool init; -}; - - -/** - * Drop anastasis tables - * - * @param cls closure our `struct Plugin` - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static enum GNUNET_GenericReturnValue -postgres_drop_tables (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_Context *conn; - enum GNUNET_GenericReturnValue ret; - - conn = GNUNET_PQ_connect_with_cfg (pg->cfg, - "stasis-postgres", - NULL, - NULL, - NULL); - if (NULL == conn) - return GNUNET_SYSERR; - ret = GNUNET_PQ_exec_sql (conn, - "drop"); - GNUNET_PQ_disconnect (conn); - return ret; -} - - -/** - * Initialize tables. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static enum GNUNET_GenericReturnValue -postgres_create_tables (void *cls) -{ - struct PostgresClosure *pc = cls; - struct GNUNET_PQ_Context *conn; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - conn = GNUNET_PQ_connect_with_cfg (pc->cfg, - "stasis-postgres", - "stasis-", - es, - NULL); - if (NULL == conn) - return GNUNET_SYSERR; - GNUNET_PQ_disconnect (conn); - return GNUNET_OK; -} - - -/** - * Establish connection to the database. - * - * @param cls plugin context - * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure - */ -static enum GNUNET_GenericReturnValue -prepare_statements (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_PreparedStatement ps[] = { - GNUNET_PQ_make_prepare ("user_insert", - "INSERT INTO anastasis_user " - "(user_id" - ",expiration_date" - ") VALUES " - "($1, $2);"), - GNUNET_PQ_make_prepare ("do_commit", - "COMMIT"), - GNUNET_PQ_make_prepare ("user_select", - "SELECT" - " expiration_date " - "FROM anastasis_user" - " WHERE user_id=$1" - " FOR UPDATE;"), - GNUNET_PQ_make_prepare ("user_update", - "UPDATE anastasis_user" - " SET " - " expiration_date=$1" - " WHERE user_id=$2;"), - GNUNET_PQ_make_prepare ("recdoc_payment_insert", - "INSERT INTO anastasis_recdoc_payment " - "(user_id" - ",post_counter" - ",amount" - ",payment_identifier" - ",creation_date" - ") VALUES " - "($1, $2, $3, $4, $5);"), - GNUNET_PQ_make_prepare ("challenge_payment_insert", - "INSERT INTO anastasis_challenge_payment " - "(truth_uuid" - ",amount" - ",payment_identifier" - ",creation_date" - ") VALUES " - "($1, $2, $3, $4);"), - GNUNET_PQ_make_prepare ("truth_payment_insert", - "INSERT INTO anastasis_truth_payment " - "(truth_uuid" - ",amount" - ",expiration" - ") VALUES " - "($1, $2, $3);"), - GNUNET_PQ_make_prepare ("recdoc_payment_done", - "UPDATE anastasis_recdoc_payment " - "SET" - " paid=TRUE " - "WHERE" - " payment_identifier=$1" - " AND" - " user_id=$2" - " AND" - " paid=FALSE;"), - GNUNET_PQ_make_prepare ("challenge_refund_update", - "UPDATE anastasis_challenge_payment " - "SET" - " refunded=TRUE " - "WHERE" - " payment_identifier=$1" - " AND" - " paid=TRUE" - " AND" - " truth_uuid=$2;"), - GNUNET_PQ_make_prepare ("challenge_payment_done", - "UPDATE anastasis_challenge_payment " - "SET" - " paid=TRUE " - "WHERE" - " payment_identifier=$1" - " AND" - " refunded=FALSE" - " AND" - " truth_uuid=$2" - " AND" - " paid=FALSE;"), - GNUNET_PQ_make_prepare ("recdoc_payment_select", - "SELECT" - " creation_date" - ",post_counter" - ",amount" - ",paid" - " FROM anastasis_recdoc_payment" - " WHERE payment_identifier=$1;"), - GNUNET_PQ_make_prepare ("truth_payment_select", - "SELECT" - " expiration" - " FROM anastasis_truth_payment" - " WHERE truth_uuid=$1" - " AND expiration>$2;"), - GNUNET_PQ_make_prepare ("challenge_payment_select", - "SELECT" - " creation_date" - ",amount" - ",paid" - " FROM anastasis_challenge_payment" - " WHERE payment_identifier=$1" - " AND truth_uuid=$2" - " AND refunded=FALSE" - " AND counter>0;"), - GNUNET_PQ_make_prepare ("challenge_pending_payment_select", - "SELECT" - " creation_date" - ",payment_identifier" - ",amount" - " FROM anastasis_challenge_payment" - " WHERE" - " paid=FALSE" - " AND" - " refunded=FALSE" - " AND" - " truth_uuid=$1" - " AND" - " creation_date > $2;"), - GNUNET_PQ_make_prepare ("recdoc_payments_select", - "SELECT" - " user_id" - ",payment_identifier" - ",amount" - " FROM anastasis_recdoc_payment" - " WHERE paid=FALSE;"), - GNUNET_PQ_make_prepare ("gc_accounts", - "DELETE FROM anastasis_user " - "WHERE" - " expiration_date < $1;"), - GNUNET_PQ_make_prepare ("gc_recdoc_pending_payments", - "DELETE FROM anastasis_recdoc_payment " - "WHERE" - " paid=FALSE" - " AND" - " creation_date < $1;"), - GNUNET_PQ_make_prepare ("gc_challenge_pending_payments", - "DELETE FROM anastasis_challenge_payment " - "WHERE" - " (paid=FALSE" - " OR" - " refunded=TRUE)" - " AND" - " creation_date < $1;"), - GNUNET_PQ_make_prepare ("truth_insert", - "INSERT INTO anastasis_truth " - "(truth_uuid" - ",key_share_data" - ",method_name" - ",encrypted_truth" - ",truth_mime" - ",expiration" - ") VALUES " - "($1, $2, $3, $4, $5, $6);"), - GNUNET_PQ_make_prepare ("test_auth_iban_payment", - "SELECT" - " credit" - ",wire_subject" - " FROM anastasis_auth_iban_in" - " WHERE debit_account_details=$1" - " AND execution_date>=$2;"), - GNUNET_PQ_make_prepare ("store_auth_iban_payment_details", - "INSERT INTO anastasis_auth_iban_in " - "(wire_reference" - ",wire_subject" - ",credit" - ",debit_account_details" - ",credit_account_details" - ",execution_date" - ") VALUES " - "($1, $2, $3, $4, $5, $6);"), - GNUNET_PQ_make_prepare ("recovery_document_insert", - "INSERT INTO anastasis_recoverydocument " - "(user_id" - ",version" - ",account_sig" - ",recovery_data_hash" - ",recovery_data" - ",recovery_meta_data" - ",creation_date" - ") VALUES " - "($1, $2, $3, $4, $5, $6, $7);"), - GNUNET_PQ_make_prepare ("truth_select", - "SELECT " - " method_name" - ",encrypted_truth" - ",truth_mime" - " FROM anastasis_truth" - " WHERE truth_uuid=$1;"), - GNUNET_PQ_make_prepare ("recoverydocument_select_meta", - "SELECT " - " version" - ",creation_date" - ",recovery_meta_data" - " FROM anastasis_recoverydocument" - " WHERE user_id=$1" - " AND version < $2" - " ORDER BY version DESC" - " LIMIT 1000;"), - GNUNET_PQ_make_prepare ("latest_recoverydocument_select", - "SELECT " - " version" - ",account_sig" - ",recovery_data_hash" - ",recovery_data" - " FROM anastasis_recoverydocument" - " WHERE user_id=$1" - " ORDER BY version DESC" - " LIMIT 1;"), - GNUNET_PQ_make_prepare ("latest_recovery_version_select", - "SELECT" - " version" - ",recovery_data_hash" - ",expiration_date" - " FROM anastasis_recoverydocument" - " JOIN anastasis_user USING (user_id)" - " WHERE user_id=$1" - " ORDER BY version DESC" - " LIMIT 1;"), - GNUNET_PQ_make_prepare ("recoverydocument_select", - "SELECT " - " account_sig" - ",recovery_data_hash" - ",recovery_data" - " FROM anastasis_recoverydocument" - " WHERE user_id=$1" - " AND version=$2;"), - GNUNET_PQ_make_prepare ("postcounter_select", - "SELECT" - " post_counter" - " FROM anastasis_recdoc_payment" - " WHERE user_id=$1" - " AND payment_identifier=$2;"), - GNUNET_PQ_make_prepare ("postcounter_update", - "UPDATE" - " anastasis_recdoc_payment" - " SET" - " post_counter=$1" - " WHERE user_id =$2" - " AND payment_identifier=$3;"), - GNUNET_PQ_make_prepare ("key_share_select", - "SELECT " - "key_share_data " - "FROM " - "anastasis_truth " - "WHERE truth_uuid =$1;"), - GNUNET_PQ_make_prepare ("challengecode_insert", - "INSERT INTO anastasis_challengecode " - "(truth_uuid" - ",code" - ",creation_date" - ",expiration_date" - ",retry_counter" - ") VALUES " - "($1, $2, $3, $4, $5);"), - GNUNET_PQ_make_prepare ("challengecode_select", - "SELECT " - " code" - ",satisfied" - " FROM anastasis_challengecode" - " WHERE truth_uuid=$1" - " AND expiration_date > $2" - " AND retry_counter != 0;"), - GNUNET_PQ_make_prepare ("challengecode_set_satisfied", - "UPDATE anastasis_challengecode" - " SET satisfied=TRUE" - " WHERE truth_uuid=$1" - " AND code=$2" - " AND creation_date IN" - " (SELECT creation_date" - " FROM anastasis_challengecode" - " WHERE truth_uuid=$1" - " AND code=$2" - " ORDER BY creation_date DESC" - " LIMIT 1);"), - GNUNET_PQ_make_prepare ("challengecode_test_satisfied", - "SELECT 1 FROM anastasis_challengecode" - " WHERE truth_uuid=$1" - " AND satisfied=TRUE" - " AND code=$2" - " AND creation_date >= $3" - " LIMIT 1;"), - GNUNET_PQ_make_prepare ("challengecode_select_meta", - "SELECT " - " code" - ",retry_counter" - ",retransmission_date" - " FROM anastasis_challengecode" - " WHERE truth_uuid=$1" - " AND expiration_date > $2" - " AND creation_date > $3" - " ORDER BY creation_date DESC" - " LIMIT 1;"), - GNUNET_PQ_make_prepare ("challengecode_update_retry", - "UPDATE anastasis_challengecode" - " SET retry_counter=retry_counter - 1" - " WHERE truth_uuid=$1" - " AND code=$2" - " AND retry_counter != 0;"), - GNUNET_PQ_make_prepare ("challengepayment_dec_counter", - "UPDATE anastasis_challenge_payment" - " SET counter=counter - 1" - " WHERE truth_uuid=$1" - " AND payment_identifier=$2" - " AND counter > 0;"), - GNUNET_PQ_make_prepare ("challengecode_mark_sent", - "UPDATE anastasis_challengecode" - " SET retransmission_date=$3" - " WHERE truth_uuid=$1" - " AND code=$2" - " AND creation_date IN" - " (SELECT creation_date" - " FROM anastasis_challengecode" - " WHERE truth_uuid=$1" - " AND code=$2" - " ORDER BY creation_date DESC" - " LIMIT 1);"), - GNUNET_PQ_make_prepare ("get_last_auth_iban_payment", - "SELECT " - " wire_reference" - " FROM anastasis_auth_iban_in" - " WHERE credit_account_details=$1" - " ORDER BY wire_reference DESC" - " LIMIT 1;"), - GNUNET_PQ_make_prepare ("gc_challengecodes", - "DELETE FROM anastasis_challengecode " - "WHERE " - "expiration_date < $1;"), - GNUNET_PQ_PREPARED_STATEMENT_END - }; - - { - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_PQ_prepare_statements (pg->conn, - ps); - if (GNUNET_OK != ret) - return ret; - pg->init = true; - return GNUNET_OK; - } -} - - -/** - * Check that the database connection is still up. - * - * @param cls a `struct PostgresClosure` with connection to check - */ -static void -check_connection (void *cls) -{ - struct PostgresClosure *pg = cls; - - GNUNET_PQ_reconnect_if_down (pg->conn); -} - - -/** - * Connect to the database if the connection does not exist yet. - * - * @param pg the plugin-specific state - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -internal_setup (struct PostgresClosure *pg) -{ - if (NULL == pg->conn) - { -#if AUTO_EXPLAIN - /* Enable verbose logging to see where queries do not - properly use indices */ - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_try_execute ("LOAD 'auto_explain';"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_min_duration=50;"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_timing=TRUE;"), - GNUNET_PQ_make_try_execute ("SET auto_explain.log_analyze=TRUE;"), - /* https://wiki.postgresql.org/wiki/Serializable suggests to really - force the default to 'serializable' if SSI is to be used. */ - GNUNET_PQ_make_try_execute ( - "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE;"), - GNUNET_PQ_make_try_execute ("SET enable_sort=OFF;"), - GNUNET_PQ_make_try_execute ("SET enable_seqscan=OFF;"), - GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; -#else - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("SET search_path TO anastasis;"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; -#endif - struct GNUNET_PQ_Context *db_conn; - - db_conn = GNUNET_PQ_connect_with_cfg2 (pg->cfg, - "stasis-postgres", - "statis-", - es, - NULL, - GNUNET_PQ_FLAG_CHECK_CURRENT); - if (NULL == db_conn) - return GNUNET_SYSERR; - pg->conn = db_conn; - } - if (NULL == pg->transaction_name) - GNUNET_PQ_reconnect_if_down (pg->conn); - if (pg->init) - return GNUNET_OK; - return prepare_statements (pg); -} - - -/** - * Do a pre-flight check that we are not in an uncommitted transaction. - * If we are, try to commit the previous transaction and output a warning. - * Does not return anything, as we will continue regardless of the outcome. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return #GNUNET_OK if everything is fine - * #GNUNET_NO if a transaction was rolled back - * #GNUNET_SYSERR on hard errors - */ -static enum GNUNET_GenericReturnValue -postgres_preflight (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("ROLLBACK"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - if (! pg->init) - { - if (GNUNET_OK != - internal_setup (pg)) - return GNUNET_SYSERR; - } - if (NULL == pg->transaction_name) - return GNUNET_OK; /* all good */ - if (GNUNET_OK == - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BUG: Preflight check rolled back transaction `%s'!\n", - pg->transaction_name); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "BUG: Preflight check failed to rollback transaction `%s'!\n", - pg->transaction_name); - } - pg->transaction_name = NULL; - return GNUNET_NO; -} - - -/** - * Start a transaction. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param name unique name identifying the transaction (for debugging), - * must point to a constant - * @return #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -begin_transaction (void *cls, - const char *name) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - pg->transaction_name = name; - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - TALER_LOG_ERROR ("Failed to start transaction\n"); - GNUNET_break (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** -* Roll back the current transaction of a database connection. -* -* @param cls the `struct PostgresClosure` with the plugin-specific state -* @return #GNUNET_OK on success -*/ -static void -rollback (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_ExecuteStatement es[] = { - GNUNET_PQ_make_execute ("ROLLBACK"), - GNUNET_PQ_EXECUTE_STATEMENT_END - }; - - if (GNUNET_OK != - GNUNET_PQ_exec_statements (pg->conn, - es)) - { - TALER_LOG_ERROR ("Failed to rollback transaction\n"); - GNUNET_break (0); - } - pg->transaction_name = NULL; -} - - -/** - * Commit the current transaction of a database connection. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @return transaction status code - */ -static enum GNUNET_DB_QueryStatus -commit_transaction (void *cls) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_QueryParam no_params[] = { - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "do_commit", - no_params); - pg->transaction_name = NULL; - return qs; -} - - -/** - * Register callback to be invoked on events of type @a es. - * - * @param cls database context to use - * @param es specification of the event to listen for - * @param timeout how long to wait for the event - * @param cb function to call when the event happens, possibly - * multiple times (until cancel is invoked) - * @param cb_cls closure for @a cb - * @return handle useful to cancel the listener - */ -static struct GNUNET_DB_EventHandler * -postgres_event_listen (void *cls, - const struct GNUNET_DB_EventHeaderP *es, - struct GNUNET_TIME_Relative timeout, - GNUNET_DB_EventCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - - return GNUNET_PQ_event_listen (pg->conn, - es, - timeout, - cb, - cb_cls); -} - - -/** - * Stop notifications. - * - * @param eh handle to unregister. - */ -static void -postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh) -{ - GNUNET_PQ_event_listen_cancel (eh); -} - - -/** - * Notify all that listen on @a es of an event. - * - * @param cls database context to use - * @param es specification of the event to generate - * @param extra additional event data provided - * @param extra_size number of bytes in @a extra - */ -static void -postgres_event_notify (void *cls, - const struct GNUNET_DB_EventHeaderP *es, - const void *extra, - size_t extra_size) -{ - struct PostgresClosure *pg = cls; - - return GNUNET_PQ_event_notify (pg->conn, - es, - extra, - extra_size); -} - - -/** - * Function called to perform "garbage collection" on the - * database, expiring records we no longer require. Deletes - * all user records that are not paid up (and by cascade deletes - * the associated recovery documents). Also deletes expired - * truth and financial records older than @a fin_expire. - * - * @param cls closure - * @param expire_backups backups older than the given time stamp should be garbage collected - * @param expire_pending_payments payments still pending from since before - * this value should be garbage collected - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_gc (void *cls, - struct GNUNET_TIME_Absolute expire_backups, - struct GNUNET_TIME_Absolute expire_pending_payments) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_absolute_time (&expire_backups), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_QueryParam params2[] = { - GNUNET_PQ_query_param_absolute_time (&expire_pending_payments), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "gc_accounts", - params); - if (qs < 0) - return qs; - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "gc_recdoc_pending_payments", - params2); -} - - -/** - * Store encrypted recovery document. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param account_sig signature affirming storage request - * @param recovery_data_hash hash of @a data - * @param recovery_data contains encrypted_recovery_document - * @param recovery_data_size size of data blob - * @param payment_secret identifier for the payment, used to later charge on uploads - * @param[out] version set to the version assigned to the document by the database - * @return transaction status, 0 if upload could not be finished because @a payment_secret - * did not have enough upload left; HARD error if @a payment_secret is unknown, ... - */ -static enum ANASTASIS_DB_StoreStatus -postgres_store_recovery_document ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_AccountSignatureP *account_sig, - const struct GNUNET_HashCode *recovery_data_hash, - const void *recovery_data, - size_t recovery_data_size, - const void *recovery_meta_data, - size_t recovery_meta_data_size, - const struct ANASTASIS_PaymentSecretP *payment_secret, - uint32_t *version) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) - { - if (GNUNET_OK != - begin_transaction (pg, - "store_recovery_document")) - { - GNUNET_break (0); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - } - /* get the current version and hash of the latest recovery document - for this account */ - { - struct GNUNET_HashCode dh; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint32 ("version", - version), - GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", - &dh), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "latest_recovery_version_select", - params, - rs); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - *version = 1; - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* had an existing recovery_data, is it identical? */ - if (0 == GNUNET_memcmp (&dh, - recovery_data_hash)) - { - /* Yes. Previous identical recovery data exists */ - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_NO_RESULTS; - } - (*version)++; - break; - default: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - } - } - - /* First, check if account exists */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "user_select", - params, - rs); - } - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* handle interesting case below */ - break; - } - - { - uint32_t postcounter; - - /* lookup if the user has enough uploads left and decrement */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint32 ("post_counter", - &postcounter), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "postcounter_select", - params, - rs); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - } - - if (0 == postcounter) - { - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED; - } - /* Decrement the postcounter by one */ - postcounter--; - - /* Update the postcounter in the Database */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint32 (&postcounter), - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "postcounter_update", - params); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - default: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - } - } - } - - /* finally, actually insert the recovery document */ - { - struct GNUNET_TIME_Timestamp now - = GNUNET_TIME_timestamp_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_uint32 (version), - GNUNET_PQ_query_param_auto_from_type (account_sig), - GNUNET_PQ_query_param_auto_from_type (recovery_data_hash), - GNUNET_PQ_query_param_fixed_size (recovery_data, - recovery_data_size), - GNUNET_PQ_query_param_fixed_size (recovery_meta_data, - recovery_meta_data_size), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "recovery_document_insert", - params); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - rollback (pg); - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - qs = commit_transaction (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - if (qs < 0) - return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; - return ANASTASIS_DB_STORE_STATUS_SUCCESS; - } - } -retry: - rollback (pg); - } - return ANASTASIS_DB_STORE_STATUS_SOFT_ERROR; -} - - -/** - * Increment account lifetime based on payment having been received. - * Does nothing if the payment is not new. - * - * @param cls closure - * @param account_pub which account received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @param lifetime for how long is the account now paid (increment) - * @param[out] paid_until set to the end of the lifetime after the operation - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_increment_lifetime ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_PaymentSecretP *payment_identifier, - struct GNUNET_TIME_Relative lifetime, - struct GNUNET_TIME_Timestamp *paid_until) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incrementing lifetime of account %s based on payment by %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_relative2s (lifetime, - true)); - check_connection (pg); - for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) - { - if (GNUNET_OK != - begin_transaction (pg, - "increment lifetime")) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_identifier), - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "recdoc_payment_done", - params); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - *paid_until = GNUNET_TIME_UNIT_ZERO_TS; - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* Payment not new or payment request unknown. */ - /* continued below */ - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* Payment just now marked as 'paid' */ - /* continued below */ - break; - } - } - - { - enum GNUNET_DB_QueryStatus qs2; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - &expiration), - GNUNET_PQ_result_spec_end - }; - - qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "user_select", - params, - rs); - switch (qs2) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return qs2; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* inconsistent, cannot have recdoc payment but no user!? */ - GNUNET_break (0); - rollback (pg); - return GNUNET_DB_STATUS_HARD_ERROR; - } - else - { - /* user does not exist, create new one */ - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_timestamp (&expiration), - GNUNET_PQ_query_param_end - }; - - expiration = GNUNET_TIME_relative_to_timestamp (lifetime); - GNUNET_break (! GNUNET_TIME_absolute_is_never (expiration.abs_time)); - *paid_until = expiration; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Creating new account %s with initial lifetime of %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_relative2s (lifetime, - true)); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "user_insert", - iparams); - } - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - /* existing rec doc payment (payment replay), return - existing expiration */ - *paid_until = expiration; - rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Payment existed, lifetime of account %s unchanged at %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_timestamp2s (*paid_until)); - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } - else - { - /* user exists, payment is new, update expiration_date */ - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_timestamp (&expiration), - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incrementing lifetime of account %s by %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_relative2s (lifetime, - true)); - expiration - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_add (expiration.abs_time, - lifetime)); - GNUNET_break (! GNUNET_TIME_absolute_is_never ( - expiration.abs_time)); - *paid_until = expiration; - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "user_update", - iparams); - } - break; - } - } - - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - rollback (pg); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - qs = commit_transaction (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - if (qs < 0) - return GNUNET_DB_STATUS_HARD_ERROR; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Incremented lifetime of account %s to %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_timestamp2s (*paid_until)); - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -retry: - rollback (pg); - } - return GNUNET_DB_STATUS_SOFT_ERROR; -} - - -/** - * Update account lifetime to the maximum of the current - * value and @a eol. - * - * @param cls closure - * @param account_pub which account received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @param eol for how long is the account now paid (absolute) - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_update_lifetime ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const struct ANASTASIS_PaymentSecretP *payment_identifier, - struct GNUNET_TIME_Timestamp eol) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) - { - if (GNUNET_OK != - begin_transaction (pg, - "update lifetime")) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_identifier), - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "recdoc_payment_done", - params); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - if (0 >= qs) - { - /* same payment made before, or unknown, or error - => no further action! */ - rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Payment existed, lifetime of account %s unchanged\n", - TALER_B2S (account_pub)); - return qs; - } - } - - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - &expiration), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "user_select", - params, - rs); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - { - /* user does not exist, create new one */ - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_timestamp (&eol), - GNUNET_PQ_query_param_end - }; - - GNUNET_break (! GNUNET_TIME_absolute_is_never (eol.abs_time)); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "user_insert", - iparams); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Created new account %s with expiration %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_timestamp2s (eol)); - } - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - { - /* user exists, update expiration_date */ - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_timestamp (&expiration), - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - - expiration = GNUNET_TIME_timestamp_max (expiration, - eol); - GNUNET_break (! GNUNET_TIME_absolute_is_never (expiration.abs_time)); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "user_update", - iparams); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Updated account %s to new expiration %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_timestamp2s (expiration)); - } - break; - } - } - - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - rollback (pg); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - qs = commit_transaction (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - if (qs < 0) - return GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -retry: - rollback (pg); - } - return GNUNET_DB_STATUS_SOFT_ERROR; -} - - -/** - * Store payment. Used to begin a payment, not indicative - * that the payment actually was made. (That is done - * when we increment the account's lifetime.) - * - * @param cls closure - * @param account_pub anastasis's public key - * @param post_counter how many uploads does @a amount pay for - * @param payment_secret payment secret which the user must provide with every upload - * @param amount how much we asked for - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_record_recdoc_payment ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t post_counter, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct TALER_Amount *amount) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_uint32 (&post_counter), - TALER_PQ_query_param_amount (pg->conn, - amount), - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - - /* because of constraint at user_id, first we have to verify - if user exists, and if not, create one */ - { - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - &expiration), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "user_select", - iparams, - rs); - } - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - { - /* create new user with short lifetime */ - struct GNUNET_TIME_Timestamp exp - = GNUNET_TIME_relative_to_timestamp (TRANSIENT_LIFETIME); - struct GNUNET_PQ_QueryParam iparams[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_timestamp (&exp), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "user_insert", - iparams); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* successful, continue below */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Created new account %s with transient life until %s\n", - TALER_B2S (account_pub), - GNUNET_TIME_timestamp2s (exp)); - break; - } - } - /* continue below */ - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* handle case below */ - break; - } - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "recdoc_payment_insert", - params); -} - - -/** - * Record truth upload payment was made. - * - * @param cls closure - * @param uuid the truth's UUID - * @param amount the amount that was paid - * @param duration how long is the truth paid for - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_record_truth_upload_payment ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, - const struct TALER_Amount *amount, - struct GNUNET_TIME_Relative duration) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp exp = GNUNET_TIME_relative_to_timestamp ( - duration); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (uuid), - TALER_PQ_query_param_amount (pg->conn, - amount), - GNUNET_PQ_query_param_timestamp (&exp), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "truth_payment_insert", - params); -} - - -/** - * Inquire whether truth upload payment was made. - * - * @param cls closure - * @param uuid the truth's UUID - * @param[out] paid_until set for how long this truth is paid for - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_check_truth_upload_paid ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, - struct GNUNET_TIME_Timestamp *paid_until) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (uuid), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration", - paid_until), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "truth_payment_select", - params, - rs); -} - - -/** - * Store payment for challenge. - * - * @param cls closure - * @param truth_uuid identifier of the challenge to pay - * @param payment_secret payment secret which the user must provide with every upload - * @param amount how much we asked for - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_record_challenge_payment ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct TALER_Amount *amount) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - TALER_PQ_query_param_amount (pg->conn, - amount), - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challenge_payment_insert", - params); -} - - -/** - * Store refund granted for challenge. - * - * @param cls closure - * @param truth_uuid identifier of the challenge to refund - * @param payment_secret payment secret which the user must provide with every upload - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_record_challenge_refund ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_secret) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challenge_refund_update", - params); -} - - -/** - * Store inbound IBAN payment made for authentication. - * - * @param cls closure - * @param wire_reference unique identifier inside LibEuFin/Nexus - * @param wire_subject subject of the wire transfer - * @param amount how much was transferred - * @param debit_account account that was debited - * @param credit_account Anastasis operator account credited - * @param execution_date when was the transfer made - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_record_auth_iban_payment ( - void *cls, - uint64_t wire_reference, - const char *wire_subject, - const struct TALER_Amount *amount, - const char *debit_account, - const char *credit_account, - struct GNUNET_TIME_Timestamp execution_date) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint64 (&wire_reference), - GNUNET_PQ_query_param_string (wire_subject), - TALER_PQ_query_param_amount (pg->conn, - amount), - GNUNET_PQ_query_param_string (debit_account), - GNUNET_PQ_query_param_string (credit_account), - GNUNET_PQ_query_param_timestamp (&execution_date), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "store_auth_iban_payment_details", - params); -} - - -/** - * Closure for #test_auth_cb. - */ -struct TestIbanContext -{ - - /** - * Plugin context. - */ - struct PostgresClosure *pg; - - /** - * Function to call on each wire transfer found. - */ - ANASTASIS_DB_AuthIbanTransfercheck cb; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Value to return. - */ - enum GNUNET_DB_QueryStatus qs; -}; - - -/** - * Helper function for #postgres_test_auth_iban_payment(). - * To be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure of type `struct TestIbanContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -test_auth_cb (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct TestIbanContext *tic = cls; - struct PostgresClosure *pg = tic->pg; - - for (unsigned int i = 0; i<num_results; i++) - { - struct TALER_Amount credit; - char *wire_subject; - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("credit", - pg->currency, - &credit), - GNUNET_PQ_result_spec_string ("wire_subject", - &wire_subject), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - tic->qs = GNUNET_DB_STATUS_HARD_ERROR; - return; - } - if (tic->cb (tic->cb_cls, - &credit, - wire_subject)) - { - GNUNET_free (wire_subject); - tic->qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - return; - } - GNUNET_free (wire_subject); - } -} - - -/** - * Function to check if we are aware of a wire transfer - * that satisfies the IBAN plugin's authentication check. - * - * @param cls closure - * @param debit_account which debit account to check - * @param earliest_date earliest date to check - * @param cb function to call on all entries found - * @param cb_cls closure for @a cb - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb - * returned 'true' once - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no - * wire transfers existed for which @a cb returned true - */ -static enum GNUNET_DB_QueryStatus -postgres_test_auth_iban_payment ( - void *cls, - const char *debit_account, - struct GNUNET_TIME_Timestamp earliest_date, - ANASTASIS_DB_AuthIbanTransfercheck cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct TestIbanContext tic = { - .cb = cb, - .cb_cls = cb_cls, - .pg = pg - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (debit_account), - GNUNET_PQ_query_param_timestamp (&earliest_date), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "test_auth_iban_payment", - params, - &test_auth_cb, - &tic); - if (qs < 0) - return qs; - return tic.qs; -} - - -/** - * Function to check the last known IBAN payment. - * - * @param cls closure - * @param credit_account which credit account to check - * @param[out] last_row set to the last known row - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a cb - * returned 'true' once - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if no - * wire transfers existed for which @a cb returned true - */ -static enum GNUNET_DB_QueryStatus -postgres_get_last_auth_iban_payment_row ( - void *cls, - const char *credit_account, - uint64_t *last_row) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (credit_account), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("wire_reference", - last_row), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "get_last_auth_iban_payment", - params, - rs); -} - - -/** - * Check payment identifier. Used to check if a payment identifier given by - * the user is valid (existing and paid). - * - * @param cls closure - * @param payment_secret payment secret which the user must provide with every upload - * @param truth_uuid which truth should we check the payment status of - * @param[out] paid bool value to show if payment is paid - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_check_challenge_payment ( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - bool *paid) -{ - struct PostgresClosure *pg = cls; - uint8_t paid8; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("paid", - &paid8), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "challenge_payment_select", - params, - rs); - *paid = (0 != paid8); - return qs; -} - - -/** - * Check payment identifier. Used to check if a payment identifier given by - * the user is valid (existing and paid). - * - * @param cls closure - * @param payment_secret payment secret which the user must provide with every upload - * @param[out] paid bool value to show if payment is paid - * @param[out] valid_counter bool value to show if post_counter is > 0 - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_check_payment_identifier ( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - bool *paid, - bool *valid_counter) -{ - struct PostgresClosure *pg = cls; - uint32_t counter; - uint8_t paid8; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("paid", - &paid8), - GNUNET_PQ_result_spec_uint32 ("post_counter", - &counter), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "recdoc_payment_select", - params, - rs); - - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - if (counter > 0) - *valid_counter = true; - else - *valid_counter = false; - *paid = (0 != paid8); - } - return qs; -} - - -/** - * Upload Truth, which contains the Truth and the KeyShare. - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param key_share_data contains information of an EncryptedKeyShare - * @param mime_type presumed mime type of data in @a encrypted_truth - * @param encrypted_truth contains the encrypted Truth which includes the ground truth i.e. H(challenge answer), phonenumber, SMS - * @param encrypted_truth_size the size of the Truth - * @param method name of method - * @param truth_expiration time till the according data will be stored - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_store_truth ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share_data, - const char *mime_type, - const void *encrypted_truth, - size_t encrypted_truth_size, - const char *method, - struct GNUNET_TIME_Relative truth_expiration) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp expiration; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_auto_from_type (key_share_data), - GNUNET_PQ_query_param_string (method), - GNUNET_PQ_query_param_fixed_size (encrypted_truth, - encrypted_truth_size), - GNUNET_PQ_query_param_string (mime_type), - GNUNET_PQ_query_param_timestamp (&expiration), - GNUNET_PQ_query_param_end - }; - - - expiration = GNUNET_TIME_relative_to_timestamp (truth_expiration); - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "truth_insert", - params); -} - - -/** - * Get the encrypted truth to validate the challenge response - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param[out] truth contains the encrypted truth - * @param[out] truth_size size of the encrypted truth - * @param[out] truth_mime mime type of truth - * @param[out] method type of the challenge - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_escrow_challenge ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - void **truth, - size_t *truth_size, - char **truth_mime, - char **method) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_variable_size ("encrypted_truth", - truth, - truth_size), - GNUNET_PQ_result_spec_string ("truth_mime", - truth_mime), - GNUNET_PQ_result_spec_string ("method_name", - method), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "truth_select", - params, - rs); -} - - -/** - * Lookup (encrypted) key share by @a truth_uuid. - * - * @param cls closure - * @param truth_uuid the identifier for the Truth - * @param[out] key_share contains the encrypted Keyshare - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_key_share ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("key_share_data", - key_share), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "key_share_select", - params, - rs); -} - - -/** - * Check if an account exists, and if so, return the - * current @a recovery_document_hash. - * - * @param cls closure - * @param account_pub account identifier - * @param[out] paid_until until when is the account paid up? - * @param[out] recovery_data_hash set to hash of @a recovery document - * @param[out] version set to the recovery policy version - * @return transaction status - */ -static enum ANASTASIS_DB_AccountStatus -postgres_lookup_account ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - struct GNUNET_TIME_Timestamp *paid_until, - struct GNUNET_HashCode *recovery_data_hash, - uint32_t *version) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - paid_until), - GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", - recovery_data_hash), - GNUNET_PQ_result_spec_uint32 ("version", - version), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "latest_recovery_version_select", - params, - rs); - } - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - break; /* handle interesting case below */ - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - return ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED; - } - - /* check if account exists */ - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_timestamp ("expiration_date", - paid_until), - GNUNET_PQ_result_spec_end - }; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "user_select", - params, - rs); - } - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - /* indicates: no account */ - return ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - /* indicates: no backup */ - *version = UINT32_MAX; - memset (recovery_data_hash, - 0, - sizeof (*recovery_data_hash)); - return ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS; - default: - GNUNET_break (0); - return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; - } -} - - -/** - * Fetch latest recovery document for user. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param account_sig signature - * @param recovery_data_hash hash of the current recovery data - * @param data_size size of data blob - * @param data blob which contains the recovery document - * @param[out] version set to the version number of the policy being returned - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_latest_recovery_document ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - struct ANASTASIS_AccountSignatureP *account_sig, - struct GNUNET_HashCode *recovery_data_hash, - size_t *data_size, - void **data, - uint32_t *version) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint32 ("version", - version), - GNUNET_PQ_result_spec_auto_from_type ("account_sig", - account_sig), - GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", - recovery_data_hash), - GNUNET_PQ_result_spec_variable_size ("recovery_data", - data, - data_size), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "latest_recoverydocument_select", - params, - rs); -} - - -/** - * Closure for meta_iterator(). - */ -struct MetaIteratorContext -{ - /** - * Function to call on each result. - */ - ANASTASIS_DB_RecoveryMetaCallback cb; - - /** - * Closure for @e cb. - */ - void *cb_cls; - - /** - * Set to true on database failure. - */ - bool db_failure; -}; - - -/** - * Helper function for #postgres_get_recovery_meta_data(). - * To be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure of type `struct MetaIteratorContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -meta_iterator (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct MetaIteratorContext *ctx = cls; - - for (unsigned int i = 0; i<num_results; i++) - { - uint32_t version; - void *meta_data; - size_t meta_data_size; - struct GNUNET_TIME_Timestamp ts; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint32 ("version", - &version), - GNUNET_PQ_result_spec_timestamp ("creation_date", - &ts), - GNUNET_PQ_result_spec_variable_size ("recovery_meta_data", - &meta_data, - &meta_data_size), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_GenericReturnValue ret; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - ctx->db_failure = true; - return; - } - ret = ctx->cb (ctx->cb_cls, - version, - ts, - meta_data, - meta_data_size); - GNUNET_PQ_cleanup_result (rs); - if (GNUNET_OK != ret) - break; - } -} - - -/** - * Fetch recovery document meta data for user. Returns - * meta data in descending order from @a max_version. - * The size of the result set may be limited. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param max_version the maximum version number the user requests - * @param cb function to call on each result - * @param cb_cls closure for @a cb - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_recovery_meta_data ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t max_version, - ANASTASIS_DB_RecoveryMetaCallback cb, - void *cb_cls) -{ - struct PostgresClosure *pg = cls; - struct MetaIteratorContext ctx = { - .cb = cb, - .cb_cls = cb_cls - }; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_uint32 (&max_version), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "recoverydocument_select_meta", - params, - &meta_iterator, - &ctx); - if (qs < 0) - return qs; - if (ctx.db_failure) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Fetch recovery document for user according given version. - * - * @param cls closure - * @param account_pub public key of the user's account - * @param version the version number of the policy the user requests - * @param[out] account_sig signature - * @param[out] recovery_data_hash hash of the current recovery data - * @param[out] data_size size of data blob - * @param[out] data blob which contains the recovery document - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_recovery_document ( - void *cls, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - uint32_t version, - struct ANASTASIS_AccountSignatureP *account_sig, - struct GNUNET_HashCode *recovery_data_hash, - size_t *data_size, - void **data) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (account_pub), - GNUNET_PQ_query_param_uint32 (&version), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("account_sig", - account_sig), - GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", - recovery_data_hash), - GNUNET_PQ_result_spec_variable_size ("recovery_data", - data, - data_size), - GNUNET_PQ_result_spec_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "recoverydocument_select", - params, - rs); -} - - -/** - * Closure for check_valid_code(). - */ -struct CheckValidityContext -{ - /** - * Code to check for. - */ - const struct GNUNET_HashCode *hashed_code; - - /** - * Truth we are processing. - */ - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid; - - /** - * Database context. - */ - struct PostgresClosure *pg; - - /** - * Set to the matching challenge code (if @e valid). - */ - uint64_t code; - - /** - * Set to true if a code matching @e hashed_code was found. - */ - bool valid; - - /** - * Set to true if a code matching @e hashed_code was set to 'satisfied' by the plugin. - */ - bool satisfied; - - /** - * Set to true if we had a database failure. - */ - bool db_failure; - -}; - - -/** - * Helper function for #postgres_verify_challenge_code(). - * To be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure of type `struct CheckValidityContext *` - * @param result the postgres result - * @param num_results the number of results in @a result - */ -static void -check_valid_code (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct CheckValidityContext *cvc = cls; - struct PostgresClosure *pg = cvc->pg; - - for (unsigned int i = 0; i < num_results; i++) - { - uint64_t server_code; - uint8_t sat; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("code", - &server_code), - GNUNET_PQ_result_spec_auto_from_type ("satisfied", - &sat), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) - { - GNUNET_break (0); - cvc->db_failure = true; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Found issued challenge %llu (client: %s)\n", - (unsigned long long) server_code, - GNUNET_h2s (cvc->hashed_code)); - { - struct GNUNET_HashCode shashed_code; - - ANASTASIS_hash_answer (server_code, - &shashed_code); - if (0 == - GNUNET_memcmp (&shashed_code, - cvc->hashed_code)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Challenge is valid challenge (%s)\n", - (0 != sat) ? "satisfied" : "not satisfied"); - cvc->valid = true; - cvc->code = server_code; - cvc->satisfied = (0 != sat); - } - else - { - /* count failures to prevent brute-force attacks */ - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (cvc->truth_uuid), - GNUNET_PQ_query_param_uint64 (&server_code), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challengecode_update_retry", - params); - if (qs <= 0) - { - GNUNET_break (0); - cvc->db_failure = true; - } - } - } - } -} - - -/** - * Verify the provided code with the code on the server. - * If the code matches the function will return with success, if the code - * does not match, the retry counter will be decreased by one. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param hashed_code code which the user provided and wants to verify - * @param[out] code set to the original numeric code - * @param[out] satisfied set to true if the challenge is set to satisfied - * @return code validity status - */ -static enum ANASTASIS_DB_CodeStatus -postgres_verify_challenge_code ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct GNUNET_HashCode *hashed_code, - uint64_t *code, - bool *satisfied) -{ - struct PostgresClosure *pg = cls; - struct CheckValidityContext cvc = { - .truth_uuid = truth_uuid, - .hashed_code = hashed_code, - .pg = pg - }; - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - *satisfied = false; - check_connection (pg); - qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, - "challengecode_select", - params, - &check_valid_code, - &cvc); - if ( (qs < 0) || - (cvc.db_failure) ) - return ANASTASIS_DB_CODE_STATUS_HARD_ERROR; - *code = cvc.code; - if (cvc.valid) - { - *satisfied = cvc.satisfied; - return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED; - } - if (0 == qs) - return ANASTASIS_DB_CODE_STATUS_NO_RESULTS; - return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH; -} - - -/** - * Set the 'satisfied' bit for the given challenge and code to - * 'true'. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param code code which is now satisfied - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_mark_challenge_code_satisfied ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const uint64_t code) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_uint64 (&code), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challengecode_set_satisfied", - params); -} - - -/** - * Check if the 'satisfied' bit for the given challenge and code is - * 'true' and the challenge code is not yet expired. - * - * @param cls closure - * @param truth_uuid identification of the challenge which the code corresponds to - * @param code code which is now satisfied - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the challenge code is not satisfied or expired - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the challenge code has been marked as satisfied - */ -static enum GNUNET_DB_QueryStatus -postgres_test_challenge_code_satisfied ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const uint64_t code, - struct GNUNET_TIME_Timestamp after) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_uint64 (&code), - GNUNET_PQ_query_param_timestamp (&after), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_end - }; - - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "challengecode_test_satisfied", - params, - rs); -} - - -/** - * Lookup pending payment for a certain challenge. - * - * @param cls closure - * @param truth_uuid identification of the challenge - * @param[out] payment_secret set to the challenge payment secret - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_lookup_challenge_payment ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct ANASTASIS_PaymentSecretP *payment_secret) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); - struct GNUNET_TIME_Timestamp recent - = GNUNET_TIME_absolute_to_timestamp ( - GNUNET_TIME_absolute_subtract (now, - ANASTASIS_CHALLENGE_OFFER_LIFETIME)); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_timestamp (&recent), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("payment_identifier", - payment_secret), - GNUNET_PQ_result_spec_end - }; - - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "challenge_pending_payment_select", - params, - rs); -} - - -/** - * Update payment status of challenge - * - * @param cls closure - * @param truth_uuid which challenge received a payment - * @param payment_identifier proof of payment, must be unique and match pending payment - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_update_challenge_payment ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - const struct ANASTASIS_PaymentSecretP *payment_identifier) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (payment_identifier), - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challenge_payment_done", - params); -} - - -/** - * Create a new challenge code for a given challenge identified by the challenge - * public key. The function will first check if there is already a valid code - * for this challenge present and won't insert a new one in this case. - * - * @param cls closure - * @param truth_uuid the identifier for the challenge - * @param rotation_period for how long is the code available - * @param validity_period for how long is the code available - * @param retry_counter amount of retries allowed - * @param[out] retransmission_date when to next retransmit - * @param[out] code set to the code which will be checked for later - * @return transaction status, - * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries, - * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB - */ -static enum GNUNET_DB_QueryStatus -postgres_create_challenge_code ( - void *cls, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - struct GNUNET_TIME_Relative rotation_period, - struct GNUNET_TIME_Relative validity_period, - uint32_t retry_counter, - struct GNUNET_TIME_Timestamp *retransmission_date, - uint64_t *code) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp now = GNUNET_TIME_timestamp_get (); - struct GNUNET_TIME_Timestamp expiration_date; - struct GNUNET_TIME_Absolute ex_rot; - - check_connection (pg); - expiration_date = GNUNET_TIME_relative_to_timestamp (validity_period); - ex_rot = GNUNET_TIME_absolute_subtract (now.abs_time, - rotation_period); - for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) - { - if (GNUNET_OK != - begin_transaction (pg, - "create_challenge_code")) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - - { - uint32_t old_retry_counter; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_absolute_time (&ex_rot), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("code", - code), - GNUNET_PQ_result_spec_uint32 ("retry_counter", - &old_retry_counter), - GNUNET_PQ_result_spec_timestamp ("retransmission_date", - retransmission_date), - GNUNET_PQ_result_spec_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "challengecode_select_meta", - params, - rs); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - rollback (pg); - return qs; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No active challenge found, creating a fresh one\n"); - break; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - if (0 == old_retry_counter) - { - rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Active challenge %llu has zero tries left, refusing to create another one\n", - (unsigned long long) *code); - return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; - } - rollback (pg); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Active challenge has %u tries left, returning old challenge %llu\n", - (unsigned int) old_retry_counter, - (unsigned long long) *code); - return qs; - } - } - - *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, - ANASTASIS_PIN_MAX_VALUE); - *retransmission_date = GNUNET_TIME_UNIT_ZERO_TS; - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_uint64 (code), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_timestamp (&expiration_date), - GNUNET_PQ_query_param_uint32 (&retry_counter), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challengecode_insert", - params); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - rollback (pg); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto retry; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - rollback (pg); - return GNUNET_DB_STATUS_HARD_ERROR; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Created fresh challenge with %u tries left\n", - (unsigned int) retry_counter); - break; - } - } - - { - enum GNUNET_DB_QueryStatus qs; - - qs = commit_transaction (pg); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - if (qs < 0) - return qs; - } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -retry: - rollback (pg); - } - return GNUNET_DB_STATUS_SOFT_ERROR; -} - - -/** - * Remember in the database that we successfully sent a challenge. - * - * @param cls closure - * @param payment_secret payment secret which the user must provide with every upload - * @param truth_uuid the identifier for the challenge - * @param code the challenge that was sent - */ -static enum GNUNET_DB_QueryStatus -postgres_mark_challenge_sent ( - void *cls, - const struct ANASTASIS_PaymentSecretP *payment_secret, - const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, - uint64_t code) -{ - struct PostgresClosure *pg = cls; - enum GNUNET_DB_QueryStatus qs; - - check_connection (pg); - { - struct GNUNET_TIME_Timestamp now; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_uint64 (&code), - GNUNET_PQ_query_param_timestamp (&now), - GNUNET_PQ_query_param_end - }; - - now = GNUNET_TIME_timestamp_get (); - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challengecode_mark_sent", - params); - if (qs <= 0) - return qs; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Marking challenge %llu as issued\n", - (unsigned long long) code); - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (truth_uuid), - GNUNET_PQ_query_param_auto_from_type (payment_secret), - GNUNET_PQ_query_param_end - }; - - qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, - "challengepayment_dec_counter", - params); - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* probably was free */ - return qs; - } -} - - -/** - * Function called to remove all expired codes from the database. - * - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_challenge_gc (void *cls) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Timestamp time_now = GNUNET_TIME_timestamp_get (); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_timestamp (&time_now), - GNUNET_PQ_query_param_end - }; - - check_connection (pg); - GNUNET_break (GNUNET_OK == - postgres_preflight (pg)); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "gc_challengecodes", - params); -} - - -/** - * Initialize Postgres database subsystem. - * - * @param cls a configuration instance - * @return NULL on error, otherwise a `struct TALER_ANASTASISDB_Plugin` - */ -void * -libanastasis_plugin_db_postgres_init (void *cls); - -/* declaration to fix compiler warning */ -void * -libanastasis_plugin_db_postgres_init (void *cls) -{ - struct GNUNET_CONFIGURATION_Handle *cfg = cls; - struct PostgresClosure *pg; - struct ANASTASIS_DatabasePlugin *plugin; - - pg = GNUNET_new (struct PostgresClosure); - pg->cfg = cfg; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - "anastasis", - "CURRENCY", - &pg->currency)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - "anastasis", - "CURRENCY"); - GNUNET_PQ_disconnect (pg->conn); - GNUNET_free (pg); - return NULL; - } - plugin = GNUNET_new (struct ANASTASIS_DatabasePlugin); - plugin->cls = pg; - /* FIXME: Should this be the same? */ - plugin->connect = &postgres_preflight; - plugin->create_tables = &postgres_create_tables; - plugin->drop_tables = &postgres_drop_tables; - plugin->gc = &postgres_gc; - plugin->preflight = &postgres_preflight; - plugin->rollback = &rollback; - plugin->commit = &commit_transaction; - plugin->event_listen = &postgres_event_listen; - plugin->event_listen_cancel = &postgres_event_listen_cancel; - plugin->event_notify = &postgres_event_notify; - plugin->store_recovery_document = &postgres_store_recovery_document; - plugin->record_recdoc_payment = &postgres_record_recdoc_payment; - plugin->store_truth = &postgres_store_truth; - plugin->get_escrow_challenge = &postgres_get_escrow_challenge; - plugin->get_key_share = &postgres_get_key_share; - plugin->get_latest_recovery_document = &postgres_get_latest_recovery_document; - plugin->get_recovery_meta_data = &postgres_get_recovery_meta_data; - plugin->get_recovery_document = &postgres_get_recovery_document; - plugin->lookup_account = &postgres_lookup_account; - plugin->check_payment_identifier = &postgres_check_payment_identifier; - plugin->increment_lifetime = &postgres_increment_lifetime; - plugin->update_lifetime = &postgres_update_lifetime; - plugin->start = &begin_transaction; - plugin->check_connection = &check_connection; - plugin->verify_challenge_code = &postgres_verify_challenge_code; - plugin->mark_challenge_code_satisfied = - &postgres_mark_challenge_code_satisfied; - plugin->test_challenge_code_satisfied = - &postgres_test_challenge_code_satisfied; - plugin->create_challenge_code = &postgres_create_challenge_code; - plugin->mark_challenge_sent = &postgres_mark_challenge_sent; - plugin->challenge_gc = &postgres_challenge_gc; - plugin->record_truth_upload_payment = &postgres_record_truth_upload_payment; - plugin->check_truth_upload_paid = &postgres_check_truth_upload_paid; - plugin->record_challenge_payment = &postgres_record_challenge_payment; - plugin->record_challenge_refund = &postgres_record_challenge_refund; - plugin->check_challenge_payment = &postgres_check_challenge_payment; - plugin->lookup_challenge_payment = &postgres_lookup_challenge_payment; - plugin->update_challenge_payment = &postgres_update_challenge_payment; - plugin->record_auth_iban_payment = &postgres_record_auth_iban_payment; - plugin->test_auth_iban_payment = &postgres_test_auth_iban_payment; - plugin->get_last_auth_iban_payment_row - = &postgres_get_last_auth_iban_payment_row; - return plugin; -} - - -/** - * Shutdown Postgres database subsystem. - * - * @param cls a `struct ANASTASIS_DB_STATUS_Plugin` - * @return NULL (always) - */ -void * -libanastasis_plugin_db_postgres_done (void *cls); - -/* declaration to fix compiler warning */ -void * -libanastasis_plugin_db_postgres_done (void *cls) -{ - struct ANASTASIS_DatabasePlugin *plugin = cls; - struct PostgresClosure *pg = plugin->cls; - - GNUNET_PQ_disconnect (pg->conn); - GNUNET_free (pg->currency); - GNUNET_free (pg); - GNUNET_free (plugin); - return NULL; -} - - -/* end of plugin_anastasisdb_postgres.c */ diff --git a/src/stasis/test_anastasis_db.c b/src/stasis/test_anastasis_db.c @@ -43,12 +43,6 @@ static int result; /** - * Handle to the plugin we are testing. - */ -static struct ANASTASIS_DatabasePlugin *plugin; - - -/** * Main function that will be run by the scheduler. * * @param cls closure with config @@ -88,21 +82,21 @@ run (void *cls) .purpose.size = htonl (sizeof (usp)) }; - if (NULL == (plugin = ANASTASIS_DB_plugin_load (cfg, - true))) + if (GNUNET_OK != + ANASTASIS_DB_init (cfg)) { result = 77; return; } - (void) plugin->drop_tables (plugin->cls); + (void) ANASTASIS_DB_drop_tables (); if (GNUNET_OK != - plugin->create_tables (plugin->cls)) + ANASTASIS_DB_create_tables ()) { result = 77; return; } if (GNUNET_OK != - plugin->connect (plugin->cls)) + ANASTASIS_DB_preflight ()) { result = 77; return; @@ -129,115 +123,115 @@ run (void *cls) memset (&key_share, 1, sizeof (key_share)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->store_truth (plugin->cls, - &truth_uuid, - &key_share, - mime_type, - "encrypted_truth", - strlen ("encrypted_truth"), - method, - rel_time)); + ANASTASIS_DB_store_truth ( + &truth_uuid, + &key_share, + mime_type, + "encrypted_truth", + strlen ("encrypted_truth"), + method, + rel_time)); FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->check_payment_identifier (plugin->cls, - &paymentSecretP, - &paid, - &valid_counter)); + ANASTASIS_DB_check_payment_identifier ( + &paymentSecretP, + &paid, + &valid_counter)); memset (&accountPubP, 2, sizeof (accountPubP)); memset (&accountSig, 3, sizeof (accountSig)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->record_recdoc_payment (plugin->cls, - &accountPubP, - post_counter, - &paymentSecretP, - &amount)); + ANASTASIS_DB_record_recdoc_payment ( + &accountPubP, + post_counter, + &paymentSecretP, + &amount)); { struct GNUNET_TIME_Timestamp res_time; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->increment_lifetime (plugin->cls, - &accountPubP, - &paymentSecretP, - rel_time, - &res_time)); + ANASTASIS_DB_increment_lifetime ( + &accountPubP, + &paymentSecretP, + rel_time, + &res_time)); } FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->check_payment_identifier (plugin->cls, - &paymentSecretP, - &paid, - &valid_counter)); + ANASTASIS_DB_check_payment_identifier ( + &paymentSecretP, + &paid, + &valid_counter)); FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->check_challenge_payment (plugin->cls, - &paymentSecretP, - &truth_uuid, - &paid)); + ANASTASIS_DB_check_challenge_payment ( + &paymentSecretP, + &truth_uuid, + &paid)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->record_challenge_payment (plugin->cls, - &truth_uuid, - &paymentSecretP, - &amount)); + ANASTASIS_DB_record_challenge_payment ( + &truth_uuid, + &paymentSecretP, + &amount)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->update_challenge_payment (plugin->cls, - &truth_uuid, - &paymentSecretP)); + ANASTASIS_DB_update_challenge_payment ( + &truth_uuid, + &paymentSecretP)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->check_challenge_payment (plugin->cls, - &paymentSecretP, - &truth_uuid, - &paid)); + ANASTASIS_DB_check_challenge_payment ( + &paymentSecretP, + &truth_uuid, + &paid)); FAILIF (! paid); FAILIF (ANASTASIS_DB_STORE_STATUS_SUCCESS != - plugin->store_recovery_document (plugin->cls, - &accountPubP, - &accountSig, - &recoveryDataHash, - recovery_data, - strlen (recovery_data), - "meta-data", - strlen ("meta-data"), - &paymentSecretP, - &docVersion)); + ANASTASIS_DB_store_recovery_document ( + &accountPubP, + &accountSig, + &recoveryDataHash, + recovery_data, + strlen (recovery_data), + "meta-data", + strlen ("meta-data"), + &paymentSecretP, + &docVersion)); { uint32_t vrs; struct GNUNET_TIME_Timestamp exp; FAILIF (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED != - plugin->lookup_account (plugin->cls, - &accountPubP, - &exp, - &r, - &vrs)); + ANASTASIS_DB_lookup_account ( + &accountPubP, + &exp, + &r, + &vrs)); } FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_key_share (plugin->cls, - &truth_uuid, - &res_key_share)); + ANASTASIS_DB_get_key_share ( + &truth_uuid, + &res_key_share)); FAILIF (0 != GNUNET_memcmp (&res_key_share, &key_share)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_recovery_document (plugin->cls, - &accountPubP, - recversion, - &res_account_sig, - &res_recovery_data_hash, - &recoverydatasize, - &res_recovery_data)); + ANASTASIS_DB_get_recovery_document ( + &accountPubP, + recversion, + &res_account_sig, + &res_recovery_data_hash, + &recoverydatasize, + &res_recovery_data)); FAILIF (0 != memcmp (res_recovery_data, recovery_data, strlen (recovery_data))); GNUNET_free (res_recovery_data); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_latest_recovery_document (plugin->cls, - &accountPubP, - &res_account_sig, - &res_recovery_data_hash, - &recoverydatasize, - &res_recovery_data, - &res_version)); + ANASTASIS_DB_get_latest_recovery_document ( + &accountPubP, + &res_account_sig, + &res_recovery_data_hash, + &recoverydatasize, + &res_recovery_data, + &res_version)); FAILIF (0 != memcmp (res_recovery_data, recovery_data, strlen (recovery_data))); @@ -247,13 +241,13 @@ run (void *cls) struct GNUNET_TIME_Timestamp rt; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->create_challenge_code (plugin->cls, - &truth_uuid, - GNUNET_TIME_UNIT_HOURS, - GNUNET_TIME_UNIT_DAYS, - 3, /* retry counter */ - &rt, - &challenge_code)); + ANASTASIS_DB_create_challenge_code ( + &truth_uuid, + GNUNET_TIME_UNIT_HOURS, + GNUNET_TIME_UNIT_DAYS, + 3, /* retry counter */ + &rt, + &challenge_code)); FAILIF (! GNUNET_TIME_absolute_is_zero (rt.abs_time)); } { @@ -261,13 +255,13 @@ run (void *cls) uint64_t c2; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->create_challenge_code (plugin->cls, - &truth_uuid, - GNUNET_TIME_UNIT_HOURS, - GNUNET_TIME_UNIT_DAYS, - 3, /* retry counter */ - &rt, - &c2)); + ANASTASIS_DB_create_challenge_code ( + &truth_uuid, + GNUNET_TIME_UNIT_HOURS, + GNUNET_TIME_UNIT_DAYS, + 3, /* retry counter */ + &rt, + &c2)); FAILIF (c2 != challenge_code); } ANASTASIS_hash_answer (123, @@ -277,32 +271,28 @@ run (void *cls) uint64_t r_code; FAILIF (ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH != - plugin->verify_challenge_code (plugin->cls, - &truth_uuid, - &c_hash, - &r_code, - &sat)); + ANASTASIS_DB_verify_challenge_code ( + &truth_uuid, + &c_hash, + &r_code, + &sat)); ANASTASIS_hash_answer (challenge_code, &c_hash); FAILIF (ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED != - plugin->verify_challenge_code (plugin->cls, - &truth_uuid, - &c_hash, - &r_code, - &sat)); + ANASTASIS_DB_verify_challenge_code ( + &truth_uuid, + &c_hash, + &r_code, + &sat)); } if (-1 == result) result = 0; drop: GNUNET_break (GNUNET_OK == - plugin->drop_tables (plugin->cls)); - ANASTASIS_DB_plugin_unload (plugin); - if (NULL != plugin) - { - plugin = NULL; - } + ANASTASIS_DB_drop_tables ()); + ANASTASIS_DB_fini (); } diff --git a/src/util/meson.build b/src/util/meson.build @@ -41,7 +41,12 @@ executable( test_anastasis_crypto = executable( 'test_anastasis_crypto', ['test_anastasis_crypto.c'], - dependencies: [gnunetutil_dep, talerutil_dep, libanastasisutil_dep], + dependencies: [ + gnunetutil_dep, + talerutil_dep, + libanastasisutil_dep, + json_dep, + ], include_directories: [incdir, configuration_inc], build_by_default: false, install: false, @@ -68,5 +73,3 @@ executable( include_directories: [incdir, configuration_inc], install: true, ) - -