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:
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