exchange

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

commit 4a2f87e8e6a25adf70dc74bd230587d4f9647a4a
parent 98b9c113c1a447054767dca64371911ca4d070cb
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat,  1 Aug 2026 18:18:40 +0200

fix taler-auditor-sync to cover all tables, fix some mismatches in rows

Diffstat:
Msrc/auditor/.gitignore | 1+
Msrc/auditor/taler-auditor-sync.c | 232++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/auditor/test-sync.sh | 26++++++++++++++++++++++----
Msrc/exchangedb/insert_records_by_table.c | 49++++++++++++++++++++++++++++++++++++-------------
Msrc/exchangedb/iterate_records_by_table.c | 73+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/include/exchange-database/iterate_records_by_table.h | 18+++++++++++++++---
6 files changed, 337 insertions(+), 62 deletions(-)

diff --git a/src/auditor/.gitignore b/src/auditor/.gitignore @@ -28,5 +28,6 @@ taler-helper-auditor-purses generate-kyc-basedb.conf.edited generate-auditor-basedb.conf.edited wallet.wdb +wallet-*.wdb libeufin-bank.pid taler-helper-auditor-transfer diff --git a/src/auditor/taler-auditor-sync.c b/src/auditor/taler-auditor-sync.c @@ -258,6 +258,129 @@ static int exit_if_synced; /** + * Highest value in `enum TALER_EXCHANGEDB_ReplicatedTable`. Update + * together with #rt_name() and #tables when the exchange gains another + * replicated table. + */ +#define RT_MAX TALER_EXCHANGEDB_RT_KYCAUTHS_IN + + +/** + * Return the name of the database table @a rt lives in. + * + * The switch is exhaustive and deliberately has no @e default case: when + * a table is added to `enum TALER_EXCHANGEDB_ReplicatedTable`, the + * compiler points at this function (-Wswitch), which is the reminder + * that #RT_MAX and #tables below must be extended as well. #check_tables() + * catches it at runtime if they were not. + * + * @param rt table to name + * @return name of the table in the database + */ +static const char * +rt_name (enum TALER_EXCHANGEDB_ReplicatedTable rt) +{ + switch (rt) + { + case TALER_EXCHANGEDB_RT_DENOMINATIONS: + return "denominations"; + case TALER_EXCHANGEDB_RT_DENOMINATION_REVOCATIONS: + return "denomination_revocations"; + case TALER_EXCHANGEDB_RT_KYC_TARGETS: + return "kyc_targets"; + case TALER_EXCHANGEDB_RT_WIRE_TARGETS: + return "wire_targets"; + case TALER_EXCHANGEDB_RT_RESERVES: + return "reserves"; + case TALER_EXCHANGEDB_RT_RESERVES_IN: + return "reserves_in"; + case TALER_EXCHANGEDB_RT_RESERVES_CLOSE: + return "reserves_close"; + case TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS: + return "reserves_open_requests"; + case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS: + return "reserves_open_deposits"; + case TALER_EXCHANGEDB_RT_AUDITORS: + return "auditors"; + case TALER_EXCHANGEDB_RT_AUDITOR_DENOM_SIGS: + return "auditor_denom_sigs"; + case TALER_EXCHANGEDB_RT_EXCHANGE_SIGN_KEYS: + return "exchange_sign_keys"; + case TALER_EXCHANGEDB_RT_SIGNKEY_REVOCATIONS: + return "signkey_revocations"; + case TALER_EXCHANGEDB_RT_KNOWN_COINS: + return "known_coins"; + case TALER_EXCHANGEDB_RT_REFRESH: + return "refresh"; + case TALER_EXCHANGEDB_RT_BATCH_DEPOSITS: + return "batch_deposits"; + case TALER_EXCHANGEDB_RT_COIN_DEPOSITS: + return "coin_deposits"; + case TALER_EXCHANGEDB_RT_REFUNDS: + return "refunds"; + case TALER_EXCHANGEDB_RT_WIRE_OUT: + return "wire_out"; + case TALER_EXCHANGEDB_RT_AGGREGATION_TRACKING: + return "aggregation_tracking"; + case TALER_EXCHANGEDB_RT_WIRE_FEE: + return "wire_fee"; + case TALER_EXCHANGEDB_RT_GLOBAL_FEE: + return "global_fee"; + case TALER_EXCHANGEDB_RT_RECOUP: + return "recoup"; + case TALER_EXCHANGEDB_RT_RECOUP_REFRESH: + return "recoup_refresh"; + case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: + return "purse_requests"; + case TALER_EXCHANGEDB_RT_PURSE_DECISION: + return "purse_decision"; + case TALER_EXCHANGEDB_RT_PURSE_MERGES: + return "purse_merges"; + case TALER_EXCHANGEDB_RT_PURSE_DEPOSITS: + return "purse_deposits"; + case TALER_EXCHANGEDB_RT_ACCOUNT_MERGES: + return "account_merges"; + case TALER_EXCHANGEDB_RT_HISTORY_REQUESTS: + return "history_requests"; + case TALER_EXCHANGEDB_RT_CLOSE_REQUESTS: + return "close_requests"; + case TALER_EXCHANGEDB_RT_WADS_OUT: + return "wads_out"; + case TALER_EXCHANGEDB_RT_WADS_OUT_ENTRIES: + return "wad_out_entries"; + case TALER_EXCHANGEDB_RT_WADS_IN: + return "wads_in"; + case TALER_EXCHANGEDB_RT_WADS_IN_ENTRIES: + return "wad_in_entries"; + case TALER_EXCHANGEDB_RT_PROFIT_DRAINS: + return "profit_drains"; + case TALER_EXCHANGEDB_RT_AML_STAFF: + return "aml_staff"; + case TALER_EXCHANGEDB_RT_PURSE_DELETION: + return "purse_deletion"; + case TALER_EXCHANGEDB_RT_WITHDRAW: + return "withdraw"; + case TALER_EXCHANGEDB_RT_LEGITIMIZATION_MEASURES: + return "legitimization_measures"; + case TALER_EXCHANGEDB_RT_LEGITIMIZATION_OUTCOMES: + return "legitimization_outcomes"; + case TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES: + return "legitimization_processes"; + case TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES: + return "kyc_attributes"; + case TALER_EXCHANGEDB_RT_AML_HISTORY: + return "aml_history"; + case TALER_EXCHANGEDB_RT_KYC_EVENTS: + return "kyc_events"; + case TALER_EXCHANGEDB_RT_KYCAUTHS_IN: + return "kycauths_in"; + } + GNUNET_break (0); + return "<unknown>"; +} + + +/** * Information we track per replicated table. */ struct Table @@ -286,6 +409,12 @@ struct Table /** * Information about replicated tables. + * + * The order matters: a table must be listed after every table its + * foreign keys point to, as the destination database enforces those + * constraints just like the source does. Must cover every value of + * `enum TALER_EXCHANGEDB_ReplicatedTable`, which #check_tables() verifies + * at startup. */ static struct Table tables[] = { { .rt = TALER_EXCHANGEDB_RT_DENOMINATIONS}, @@ -295,8 +424,17 @@ static struct Table tables[] = { { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_MEASURES}, { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_OUTCOMES}, { .rt = TALER_EXCHANGEDB_RT_LEGITIMIZATION_PROCESSES}, + /* kyc_attributes.legitimization_serial references + legitimization_processes */ + { .rt = TALER_EXCHANGEDB_RT_KYC_ATTRIBUTES}, + { .rt = TALER_EXCHANGEDB_RT_AML_STAFF}, + /* aml_history.outcome_serial_id references legitimization_outcomes, + aml_history.kyc_attributes_serial_id references kyc_attributes */ + { .rt = TALER_EXCHANGEDB_RT_AML_HISTORY}, + { .rt = TALER_EXCHANGEDB_RT_KYC_EVENTS}, { .rt = TALER_EXCHANGEDB_RT_RESERVES}, { .rt = TALER_EXCHANGEDB_RT_RESERVES_IN}, + { .rt = TALER_EXCHANGEDB_RT_KYCAUTHS_IN}, { .rt = TALER_EXCHANGEDB_RT_RESERVES_CLOSE}, { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_REQUESTS}, { .rt = TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS}, @@ -320,6 +458,7 @@ static struct Table tables[] = { { .rt = TALER_EXCHANGEDB_RT_PURSE_DECISION}, { .rt = TALER_EXCHANGEDB_RT_PURSE_MERGES}, { .rt = TALER_EXCHANGEDB_RT_PURSE_DEPOSITS}, + { .rt = TALER_EXCHANGEDB_RT_PURSE_DELETION}, { .rt = TALER_EXCHANGEDB_RT_ACCOUNT_MERGES}, { .rt = TALER_EXCHANGEDB_RT_HISTORY_REQUESTS}, { .rt = TALER_EXCHANGEDB_RT_CLOSE_REQUESTS}, @@ -333,6 +472,45 @@ static struct Table tables[] = { /** + * Check that #tables lists every replicated table exactly once. A table + * missing here is not an error the operator could ever notice: the sync + * would simply run to completion with that table left empty. + * + * @return #GNUNET_OK if #tables is complete + */ +static enum GNUNET_GenericReturnValue +check_tables (void) +{ + bool seen[RT_MAX + 1] = { false }; + enum GNUNET_GenericReturnValue ret = GNUNET_OK; + + for (unsigned int i = 0; ! tables[i].end; i++) + { + enum TALER_EXCHANGEDB_ReplicatedTable rt = tables[i].rt; + + if (seen[rt]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Table `%s' is listed for replication more than once\n", + rt_name (rt)); + ret = GNUNET_SYSERR; + } + seen[rt] = true; + } + for (unsigned int i = 0; i <= RT_MAX; i++) + { + if (seen[i]) + continue; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Table `%s' would not be replicated\n", + rt_name ((enum TALER_EXCHANGEDB_ReplicatedTable) i)); + ret = GNUNET_SYSERR; + } + return ret; +} + + +/** * Closure for #do_insert. */ struct InsertContext @@ -376,18 +554,18 @@ do_insert (struct InsertContext *ctx, break; case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to insert record into table %d: no change\n", - td->table); + "Failed to insert record into table `%s': no change\n", + rt_name (td->table)); break; case GNUNET_DB_STATUS_SOFT_ERROR: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Serialization error inserting record into table %d (will retry)\n", - td->table); + "Serialization error inserting record into table `%s' (will retry)\n", + rt_name (td->table)); break; case GNUNET_DB_STATUS_HARD_ERROR: GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to insert record into table %d: hard error\n", - td->table); + "Failed to insert record into table `%s': hard error\n", + rt_name (td->table)); break; } ctx->qs = qs; @@ -436,8 +614,8 @@ transact (void) if (table->start_serial == table->end_serial) continue; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Replicating table %d from %llu to %llu\n", - i, + "Replicating table `%s' from %llu to %llu\n", + rt_name (table->rt), (unsigned long long) table->start_serial, (unsigned long long) table->end_serial); ctx.table = table; @@ -463,23 +641,23 @@ transact (void) if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to lookup records from table %d: hard error\n", - i); + "Failed to lookup records from table `%s': hard error\n", + rt_name (table->rt)); global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } if (GNUNET_DB_STATUS_SOFT_ERROR == qs) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Serialization error looking up records from table %d (will retry)\n", - i); + "Serialization error looking up records from table `%s' (will retry)\n", + rt_name (table->rt)); return GNUNET_SYSERR; /* will retry */ } if (0 == qs) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to lookup records from table %d: no results\n", - i); + "Failed to lookup records from table `%s': no results\n", + rt_name (table->rt)); GNUNET_break (0); /* should be impossible */ global_ret = EXIT_FAILURE; return GNUNET_SYSERR; @@ -491,15 +669,15 @@ transact (void) if (GNUNET_DB_STATUS_SOFT_ERROR == qs) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Serialization error committing transaction on table %d (will retry)\n", - i); + "Serialization error committing transaction on table `%s' (will retry)\n", + rt_name (table->rt)); continue; } if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Hard error committing transaction on table %d\n", - i); + "Hard error committing transaction on table `%s'\n", + rt_name (table->rt)); global_ret = EXIT_FAILURE; return GNUNET_SYSERR; } @@ -522,6 +700,7 @@ static void do_sync (void *cls) { static struct GNUNET_TIME_Relative delay; + enum GNUNET_GenericReturnValue ret; (void) cls; sync_task = NULL; @@ -540,7 +719,8 @@ do_sync (void *cls) "Failed to begin transaction with data destination. Exiting\n"); return; } - if (GNUNET_OK != transact ()) + ret = transact (); + if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Transaction failed, rolling back\n"); @@ -553,7 +733,11 @@ do_sync (void *cls) "Transaction failed permanently, exiting\n"); return; } - if ( (0 == actual_size) && + /* Only a pass that ran to completion proves the databases agree: + a rolled-back pass copies nothing, and reporting that as "in sync" + would let -t exit successfully having replicated nothing. */ + if ( (GNUNET_OK == ret) && + (0 == actual_size) && (exit_if_synced) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -804,6 +988,14 @@ main (int argc, /* suppress compiler warnings... */ GNUNET_assert (NULL != src_cfgfile); GNUNET_assert (NULL != dst_cfgfile); + if (GNUNET_OK != + check_tables ()) + { + GNUNET_break (0); + GNUNET_free (src_cfgfile); + GNUNET_free (dst_cfgfile); + return EXIT_FAILURE; + } if (0 == strcmp (src_cfgfile, dst_cfgfile)) { diff --git a/src/auditor/test-sync.sh b/src/auditor/test-sync.sh @@ -55,7 +55,7 @@ function perform_initdb() { if [[ -n "$INITDB_BIN" ]]; then echo " FOUND (in path) at $INITDB_BIN" else - HAVE_INITDB=$(find / -name "initdb" | head -1 2> /dev/null | grep postgres) || exit_skip " MISSING" + HAVE_INITDB=$(find /usr -name "initdb" | head -1 2> /dev/null | grep postgres) || exit_skip " MISSING" echo " FOUND at " "$(dirname "$HAVE_INITDB")" INITDB_BIN=$(echo "$HAVE_INITDB" | grep bin/initdb | grep postgres | sort -n | tail -n1) fi @@ -166,12 +166,30 @@ then if [ x$fail != x0 ] then exit "$fail" - else - echo "Cleaning up $MYDIR..." - rm -rf "$MYDIR" || echo "Removing $MYDIR failed" fi else echo "Generation failed" exit 77 fi + +# The plain reference database has no KYC activity at all, so every +# KYC-related table in it is empty and replicating them is vacuously +# correct. Run the whole thing again against the KYC reference database, +# which does populate kycauths_in, kyc_attributes and the +# legitimization_* tables. +echo "Generating fresh KYC database at $MYDIR" +if faketime -f '-1 d' ./generate-kyc-basedb.sh -d "$MYDIR/kyc-basedb" +then + check_with_database "$MYDIR/kyc-basedb" + if [ x$fail != x0 ] + then + exit "$fail" + fi +else + echo "Generation failed" + exit 77 +fi + +echo "Cleaning up $MYDIR..." +rm -rf "$MYDIR" || echo "Removing $MYDIR failed" exit 0 diff --git a/src/exchangedb/insert_records_by_table.c b/src/exchangedb/insert_records_by_table.c @@ -256,7 +256,7 @@ irbt_cb_table_legitimization_measures ( GNUNET_PQ_query_param_uint64 (&td->serial), GNUNET_PQ_query_param_auto_from_type ( &td->details.legitimization_measures.target_token), - GNUNET_PQ_query_param_timestamp ( + GNUNET_PQ_query_param_absolute_time ( &td->details.legitimization_measures.start_time), TALER_PQ_query_param_json ( td->details.legitimization_measures.measures), @@ -300,7 +300,9 @@ irbt_cb_table_legitimization_outcomes ( &td->details.legitimization_outcomes.decision_time), GNUNET_PQ_query_param_timestamp ( &td->details.legitimization_outcomes.expiration_time), - TALER_PQ_query_param_json ( + NULL == td->details.legitimization_outcomes.properties + ? GNUNET_PQ_query_param_null () + : TALER_PQ_query_param_json ( td->details.legitimization_outcomes.properties), GNUNET_PQ_query_param_bool ( td->details.legitimization_outcomes.to_investigate), @@ -342,7 +344,7 @@ irbt_cb_table_legitimization_processes ( GNUNET_PQ_query_param_uint64 (&td->serial), GNUNET_PQ_query_param_auto_from_type ( &td->details.legitimization_processes.h_payto), - GNUNET_PQ_query_param_timestamp ( + GNUNET_PQ_query_param_absolute_time ( &td->details.legitimization_processes.start_time), GNUNET_PQ_query_param_timestamp ( &td->details.legitimization_processes.expiration_time), @@ -352,11 +354,17 @@ irbt_cb_table_legitimization_processes ( &td->details.legitimization_processes.measure_index), GNUNET_PQ_query_param_string ( td->details.legitimization_processes.provider_name), - GNUNET_PQ_query_param_string ( + NULL == td->details.legitimization_processes.provider_user_id + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string ( td->details.legitimization_processes.provider_user_id), - GNUNET_PQ_query_param_string ( + NULL == td->details.legitimization_processes.provider_legitimization_id + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string ( td->details.legitimization_processes.provider_legitimization_id), - GNUNET_PQ_query_param_string ( + NULL == td->details.legitimization_processes.redirect_url + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string ( td->details.legitimization_processes.redirect_url), GNUNET_PQ_query_param_end }; @@ -1263,7 +1271,7 @@ irbt_cb_table_recoup (struct TALER_EXCHANGEDB_PostgresContext *pg, ",amount" ",recoup_timestamp" ",coin_pub" - ",withdraw_serial_id" + ",withdraw_id" ") VALUES " "($1, $2, $3, $4, $5, $6, $7);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, @@ -1403,7 +1411,7 @@ irbt_cb_table_purse_decision (struct TALER_EXCHANGEDB_PostgresContext *pg, PREPARE (pg, "insert_records_by_table_into_table_purse_decision", "INSERT INTO purse_decision" - "(purse_refunds_serial_id" + "(purse_decision_serial_id" ",purse_pub" ",action_timestamp" ",refunded" @@ -1929,8 +1937,12 @@ irbt_cb_table_kyc_attributes (struct TALER_EXCHANGEDB_PostgresContext *pg, &td->details.kyc_attributes.collection_time), GNUNET_PQ_query_param_timestamp ( &td->details.kyc_attributes.expiration_time), - GNUNET_PQ_query_param_uint64 ( - &td->details.kyc_attributes.trigger_outcome_serial), + NULL == td->details.kyc_attributes.form_name + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_string ( + td->details.kyc_attributes.form_name), + GNUNET_PQ_query_param_bool ( + td->details.kyc_attributes.by_aml_officer), GNUNET_PQ_query_param_fixed_size ( td->details.kyc_attributes.encrypted_attributes, td->details.kyc_attributes.encrypted_attributes_size), @@ -1945,10 +1957,11 @@ irbt_cb_table_kyc_attributes (struct TALER_EXCHANGEDB_PostgresContext *pg, ",legitimization_serial" ",collection_time" ",expiration_time" - ",trigger_outcome_serial" + ",form_name" + ",by_aml_officer" ",encrypted_attributes" ") VALUES " - "($1, $2, $3, $4, $5, $6, $7);"); + "($1, $2, $3, $4, $5, $6, $7, $8);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_records_by_table_into_table_kyc_attributes", params); @@ -1977,6 +1990,14 @@ irbt_cb_table_aml_history (struct TALER_EXCHANGEDB_PostgresContext *pg, &td->details.aml_history.decider_pub), GNUNET_PQ_query_param_auto_from_type ( &td->details.aml_history.decider_sig), + td->details.aml_history.no_kyc_attributes_hash + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_auto_from_type ( + &td->details.aml_history.kyc_attributes_hash), + td->details.aml_history.no_kyc_attributes_serial_id + ? GNUNET_PQ_query_param_null () + : GNUNET_PQ_query_param_uint64 ( + &td->details.aml_history.kyc_attributes_serial_id), GNUNET_PQ_query_param_end }; @@ -1989,8 +2010,10 @@ irbt_cb_table_aml_history (struct TALER_EXCHANGEDB_PostgresContext *pg, ",justification" ",decider_pub" ",decider_sig" + ",kyc_attributes_hash" + ",kyc_attributes_serial_id" ") VALUES " - "($1, $2, $3, $4, $5, $6);"); + "($1, $2, $3, $4, $5, $6, $7, $8);"); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "insert_records_by_table_into_table_aml_history", params); diff --git a/src/exchangedb/iterate_records_by_table.c b/src/exchangedb/iterate_records_by_table.c @@ -1456,7 +1456,7 @@ lrbt_cb_table_recoup (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "coin_pub", &td.details.recoup.coin_pub), - GNUNET_PQ_result_spec_uint64 ("withdraw_serial_id", + GNUNET_PQ_result_spec_uint64 ("withdraw_id", &td.details.recoup.withdraw_serial_id), GNUNET_PQ_result_spec_end }; @@ -1861,6 +1861,9 @@ lrbt_cb_table_history_requests (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "reserve_pub", &td.details.history_requests.reserve_pub), + GNUNET_PQ_result_spec_timestamp ( + "request_timestamp", + &td.details.history_requests.request_timestamp), GNUNET_PQ_result_spec_auto_from_type ( "reserve_sig", &td.details.history_requests.reserve_sig), @@ -2540,7 +2543,7 @@ lrbt_cb_table_legitimization_measures (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "access_token", &td.details.legitimization_measures.target_token), - GNUNET_PQ_result_spec_timestamp ( + GNUNET_PQ_result_spec_absolute_time ( "start_time", &td.details.legitimization_measures.start_time), TALER_PQ_result_spec_json ( @@ -2605,7 +2608,7 @@ lrbt_cb_table_legitimization_outcomes (void *cls, &td.details.legitimization_outcomes.properties), NULL), GNUNET_PQ_result_spec_bool ( - "to_investigate_id", + "to_investigate", &td.details.legitimization_outcomes.to_investigate), TALER_PQ_result_spec_json ( "jnew_rules", @@ -2654,7 +2657,7 @@ lrbt_cb_table_legitimization_processes (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "h_payto", &td.details.legitimization_processes.h_payto), - GNUNET_PQ_result_spec_timestamp ( + GNUNET_PQ_result_spec_absolute_time ( "start_time", &td.details.legitimization_processes.start_time), GNUNET_PQ_result_spec_timestamp ( @@ -2669,15 +2672,21 @@ lrbt_cb_table_legitimization_processes (void *cls, GNUNET_PQ_result_spec_string ( "provider_name", &td.details.legitimization_processes.provider_name), - GNUNET_PQ_result_spec_string ( - "provider_user_id", - &td.details.legitimization_processes.provider_user_id), - GNUNET_PQ_result_spec_string ( - "provider_legitimization_id", - &td.details.legitimization_processes.provider_legitimization_id), - GNUNET_PQ_result_spec_string ( - "redirect_url", - &td.details.legitimization_processes.redirect_url), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ( + "provider_user_id", + &td.details.legitimization_processes.provider_user_id), + NULL), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ( + "provider_legitimization_id", + &td.details.legitimization_processes.provider_legitimization_id), + NULL), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ( + "redirect_url", + &td.details.legitimization_processes.redirect_url), + NULL), GNUNET_PQ_result_spec_end }; @@ -2732,9 +2741,14 @@ lrbt_cb_table_kyc_attributes (void *cls, GNUNET_PQ_result_spec_timestamp ( "expiration_time", &td.details.kyc_attributes.expiration_time), - GNUNET_PQ_result_spec_uint64 ( - "trigger_outcome_serial", - &td.details.kyc_attributes.trigger_outcome_serial), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ( + "form_name", + &td.details.kyc_attributes.form_name), + NULL), + GNUNET_PQ_result_spec_bool ( + "by_aml_officer", + &td.details.kyc_attributes.by_aml_officer), GNUNET_PQ_result_spec_variable_size ( "encrypted_attributes", &td.details.kyc_attributes.encrypted_attributes, @@ -2796,6 +2810,16 @@ lrbt_cb_table_aml_history (void *cls, GNUNET_PQ_result_spec_auto_from_type ( "decider_sig", &td.details.aml_history.decider_sig), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_auto_from_type ( + "kyc_attributes_hash", + &td.details.aml_history.kyc_attributes_hash), + &td.details.aml_history.no_kyc_attributes_hash), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_uint64 ( + "kyc_attributes_serial_id", + &td.details.aml_history.kyc_attributes_serial_id), + &td.details.aml_history.no_kyc_attributes_serial_id), GNUNET_PQ_result_spec_end }; @@ -2836,7 +2860,7 @@ lrbt_cb_table_kyc_events (void *cls, { struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ( - "kyc_event_serial_id", + "serial", &td.serial), GNUNET_PQ_result_spec_timestamp ( "event_timestamp", @@ -3026,15 +3050,15 @@ TALER_EXCHANGEDB_iterate_records_by_table ( case TALER_EXCHANGEDB_RT_RESERVES_OPEN_DEPOSITS: XPREPARE ("select_above_serial_by_table_reserves_open_deposits", "SELECT" - " reserves_open_deposit_uuid AS serial" + " reserve_open_deposit_uuid AS serial" ",reserve_sig" ",reserve_pub" ",coin_pub" ",coin_sig" ",contribution" " FROM reserves_open_deposits" - " WHERE reserves_open_deposit_uuid > $1" - " ORDER BY reserves_open_deposit_uuid ASC;"); + " WHERE reserve_open_deposit_uuid > $1" + " ORDER BY reserve_open_deposit_uuid ASC;"); rh = &lrbt_cb_table_reserves_open_deposits; break; case TALER_EXCHANGEDB_RT_AUDITORS: @@ -3240,7 +3264,7 @@ TALER_EXCHANGEDB_iterate_records_by_table ( ",amount" ",recoup_timestamp" ",coin_pub" - ",reserve_out_serial_id" + ",withdraw_id" " FROM recoup" " WHERE recoup_uuid > $1" " ORDER BY recoup_uuid ASC;"); @@ -3355,6 +3379,8 @@ TALER_EXCHANGEDB_iterate_records_by_table ( ",close_timestamp" ",reserve_sig" ",close" + ",close_fee" + ",payto_uri" " FROM close_requests" " WHERE close_request_serial_id > $1" " ORDER BY close_request_serial_id ASC;"); @@ -3542,7 +3568,8 @@ TALER_EXCHANGEDB_iterate_records_by_table ( ",legitimization_serial" ",collection_time" ",expiration_time" - ",trigger_outcome_serial" + ",form_name" + ",by_aml_officer" ",encrypted_attributes" " FROM kyc_attributes" " WHERE kyc_attributes_serial_id > $1" @@ -3558,6 +3585,8 @@ TALER_EXCHANGEDB_iterate_records_by_table ( ",justification" ",decider_pub" ",decider_sig" + ",kyc_attributes_hash" + ",kyc_attributes_serial_id" " FROM aml_history" " WHERE aml_history_serial_id > $1" " ORDER BY aml_history_serial_id ASC;"); diff --git a/src/include/exchange-database/iterate_records_by_table.h b/src/include/exchange-database/iterate_records_by_table.h @@ -143,7 +143,10 @@ struct TALER_EXCHANGEDB_TableData struct { struct TALER_AccountAccessTokenP target_token; - struct GNUNET_TIME_Timestamp start_time; + /* absolute, not a timestamp: do_trigger_kyc_rule_for_account.c + writes this column with microsecond precision (whereas + insert_active_legitimization_measure.c rounds to seconds) */ + struct GNUNET_TIME_Absolute start_time; json_t *measures; uint32_t display_priority; } legitimization_measures; @@ -161,7 +164,9 @@ struct TALER_EXCHANGEDB_TableData struct { struct TALER_NormalizedPaytoHashP h_payto; - struct GNUNET_TIME_Timestamp start_time; + /* absolute, not a timestamp: the exchange stores this column with + microsecond precision (see insert_legitimization_process.c) */ + struct GNUNET_TIME_Absolute start_time; struct GNUNET_TIME_Timestamp expiration_time; uint64_t legitimization_measure_serial_id; uint32_t measure_index; @@ -177,7 +182,8 @@ struct TALER_EXCHANGEDB_TableData uint64_t legitimization_serial; struct GNUNET_TIME_Timestamp collection_time; struct GNUNET_TIME_Timestamp expiration_time; - uint64_t trigger_outcome_serial; + char *form_name; /**< NULL if the attributes came from a provider */ + bool by_aml_officer; void *encrypted_attributes; size_t encrypted_attributes_size; } kyc_attributes; @@ -189,6 +195,12 @@ struct TALER_EXCHANGEDB_TableData char *justification; struct TALER_AmlOfficerPublicKeyP decider_pub; struct TALER_AmlOfficerSignatureP decider_sig; + /* both NULL/0 unless the AML officer attached attributes to the + decision (see insert_aml_decision.sql) */ + struct GNUNET_HashCode kyc_attributes_hash; + bool no_kyc_attributes_hash; + uint64_t kyc_attributes_serial_id; + bool no_kyc_attributes_serial_id; } aml_history; struct