commit 0b9dfe45e1d635dcbd4107e7e3094eed525b1c5f
parent 437c041cb5b22b959938f45e8b18fc93269a79b7
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 5 Dec 2025 13:53:16 +0100
work on #10666: get AML file number, output attachments, towards cover page generation (WiP, untested)
Diffstat:
13 files changed, 494 insertions(+), 13 deletions(-)
diff --git a/contrib/typst/Makefile.am b/contrib/typst/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = .
formdatadir = $(datadir)/taler-exchange/typst-forms/
dist_formdata_DATA = \
+ _cover_.typ \
challenger_postal.typ \
challenger_sms.typ \
pointing_finger.svg \
diff --git a/contrib/typst/_cover_.typ b/contrib/typst/_cover_.typ
@@ -0,0 +1,91 @@
+// Cover page for AML files.
+// Renders all account properties, current rules, etc.
+
+#let form(data) = {
+ set page(
+ paper: "a4",
+ margin: (left: 2cm, right: 2cm, top: 2cm, bottom: 2.5cm),
+ footer: context [
+ #grid(
+ columns: (1fr, 1fr),
+ align: (left, right),
+ text(size: 8pt)[
+ ],
+ text(size: 8pt)[
+ Page #here().page() of #counter(page).final().first()
+ ]
+ )
+ ]
+ )
+
+ set text(font: "Liberation Sans", size: 10pt)
+ set par(justify: false, leading: 0.65em)
+
+ // Helper function to get value or empty string
+ let get(key, default: "") = {
+ data.at(key, default: default)
+ }
+
+ // Helper function for checkbox
+ let checkbox(checked) = {
+ box(
+ width: 3mm,
+ height: 3mm,
+ stroke: 0.5pt + black,
+ inset: 0.3mm,
+ if checked == true or checked == "true" {
+ place(center + horizon, text(size: 8pt, sym.checkmark))
+ }
+ )
+ }
+
+ // Header
+ align(center, text(size: 11pt, weight: "bold")[CONFIDENTIAL])
+
+ v(0.5em)
+
+ grid(
+ columns: (50%, 50%),
+ gutter: 1em,
+ image("vss_vqf_verein.png", width: 80%),
+ align(right)[
+ #table(
+ columns: (1fr, 1fr),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ align: (left, left),
+ [VQF member no.], [AMLA File No.],
+ [#get("VQF_MEMBER_NUMBER")], [#get("FILE_NUMBER")]
+ )
+ ]
+ )
+
+ v(1em)
+
+ // Section 1: FIXME -- Validated address
+ text(size: 11pt, weight: "bold")[Validated address:]
+
+ v(0.5em)
+
+ block(breakable: false)[
+ #v(0.5em)
+ #table(
+ columns: (35%, 65%),
+ stroke: 0.5pt + black,
+ inset: 5pt,
+ [Contact name:], [#get("CONTACT_NAME")],
+ [Address:], [#get("ADDRESS_LINES").split("\n").join(linebreak())],
+ [Country:], [#get("ADDRESS_COUNTRY")],
+ )
+ #v(0.5em)
+ ]
+}
+
+// Example usage:
+#form((
+ "FILE_NUMBER": "42",
+ "properties": { "PROP" : "VALUE" }, // FIXME: better example...
+ "rules": { }, // FIXME: object?
+ "is_active" : false,
+ "to_investigate" : false,
+))
+\ No newline at end of file
diff --git a/src/exchange/taler-exchange-httpd_aml-attributes-get.c b/src/exchange/taler-exchange-httpd_aml-attributes-get.c
@@ -185,6 +185,82 @@ free_rc (struct TEH_RequestContext *rc)
/**
+ * Dump attachment @a attach into @a rc.
+ *
+ * @param[in,out] rc response context to update
+ * @param attach attachment to dump
+ * @return true on success
+ */
+static bool
+dump_attachment (struct ResponseContext *rc,
+ const json_t *attach)
+{
+ if (! json_is_string (attach))
+ return false;
+ if (rc->details.pdf.off >= MAX_RECORDS)
+ {
+ GNUNET_break (0);
+ return false;
+ }
+ rc->details.pdf.jdata[rc->details.pdf.off]
+ = json_incref ((json_t *) attach);
+ rc->details.pdf.docs[rc->details.pdf.off].form_name
+ = NULL; /* inline PDF */
+ rc->details.pdf.docs[rc->details.pdf.off].data
+ = rc->details.pdf.jdata[rc->details.pdf.off];
+ rc->details.pdf.off++;
+ return true;
+}
+
+
+/**
+ * Recursively scan @a attrs for ".FILE" members and dump those
+ * attachments into @a rc.
+ *
+ * @param[in,out] rc response context to update
+ * @param attrs attributes to scan recursively
+ * @return true on success
+ */
+static bool
+dump_attachments (struct ResponseContext *rc,
+ const json_t *attrs)
+{
+ bool ret = true;
+
+ if (json_is_array (attrs))
+ {
+ const json_t *e;
+ size_t i;
+
+ json_array_foreach ((json_t *) attrs, i, e)
+ if (! dump_attachments (rc,
+ e))
+ ret = false;
+ }
+ if (json_is_object (attrs))
+ {
+ const json_t *e;
+ const char *k;
+
+ json_object_foreach ((json_t *) attrs, k, e)
+ {
+ if (0 == strcmp (k,
+ "FILE"))
+ {
+ if (! dump_attachment (rc,
+ e))
+ ret = false;
+ }
+ else if (! dump_attachments (rc,
+ e))
+ ret = false;
+ }
+ }
+ return ret;
+}
+
+
+/**
* Return AML account attributes.
*
* @param cls closure
@@ -249,7 +325,11 @@ detail_cb (
)));
break;
case RCF_PDF:
- GNUNET_assert (rc->details.pdf.off < MAX_RECORDS);
+ if (rc->details.pdf.off >= MAX_RECORDS)
+ {
+ GNUNET_break (0);
+ return;
+ }
GNUNET_assert (0 ==
json_object_set_new (attrs,
"DATADIR",
@@ -270,8 +350,6 @@ detail_cb (
json_true ()));
GNUNET_free (have);
}
- // FIXME: probably should move this into a helper function,
- // we'll need it in various places:
rc->details.pdf.jdata[rc->details.pdf.off]
= attrs;
rc->details.pdf.docs[rc->details.pdf.off].form_name
@@ -279,8 +357,12 @@ detail_cb (
rc->details.pdf.docs[rc->details.pdf.off].data
= rc->details.pdf.jdata[rc->details.pdf.off];
rc->details.pdf.off++;
- // All attachments are under "SOMETHING.FILE", so look for ".FILE"!
- // FIXME: deal with attachments, use extra slots for those!
+ if (! dump_attachments (rc,
+ attrs))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Failed to dump some attachment!\n");
+ }
break;
}
}
@@ -320,6 +402,66 @@ pdf_cb (void *cls,
}
+/**
+ * Function called with the latest AML decision on an
+ * account. Used to build the cover page.
+ *
+ * @param cls a `struct ResponseContext *`
+ * @param outcome_serial_id row ID of the decision
+ * @param decision_time when was the decision taken
+ * @param justification what was the given justification
+ * @param decider_pub which key signed the decision
+ * @param jproperties what are the new account properties
+ * @param jnew_rules what are the new account rules
+ * @param to_investigate should AML staff investigate
+ * after the decision
+ * @param is_active is this the active decision
+ */
+static void
+build_cover_page (
+ void *cls,
+ uint64_t outcome_serial_id,
+ struct GNUNET_TIME_Timestamp decision_time,
+ const char *justification,
+ const struct TALER_AmlOfficerPublicKeyP *decider_pub,
+ const json_t *jproperties,
+ const json_t *jnew_rules,
+ bool to_investigate,
+ bool is_active)
+{
+ struct ResponseContext *rc = cls;
+ json_t *cover;
+
+ cover = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("properties",
+ (json_t *) jproperties)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_object_incref ("rules",
+ (json_t *) jnew_rules)),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("last_justification",
+ justification)),
+ GNUNET_JSON_pack_bool ("is_active",
+ is_active),
+ GNUNET_JSON_pack_bool ("to_investigate",
+ to_investigate));
+ /* first page, so we really cannot have hit the maximum yet */
+ GNUNET_assert (rc->details.pdf.off < MAX_RECORDS);
+ {
+ GNUNET_break (0);
+ return;
+ }
+ rc->details.pdf.jdata[rc->details.pdf.off]
+ = cover;
+ rc->details.pdf.docs[rc->details.pdf.off].form_name
+ = "_cover_.typst";
+ rc->details.pdf.docs[rc->details.pdf.off].data
+ = rc->details.pdf.jdata[rc->details.pdf.off];
+ rc->details.pdf.off++;
+}
+
+
MHD_RESULT
TEH_handler_aml_attributes_get (
struct TEH_RequestContext *rc,
@@ -330,6 +472,7 @@ TEH_handler_aml_attributes_get (
int64_t limit = -20;
uint64_t offset;
struct TALER_NormalizedPaytoHashP h_payto;
+ uint64_t file_number;
if (NULL == rctx)
{
@@ -385,6 +528,34 @@ TEH_handler_aml_attributes_get (
&offset);
{
+ enum GNUNET_DB_QueryStatus qs;
+
+ qs = TEH_plugin->lookup_aml_file_number (TEH_plugin->cls,
+ &h_payto,
+ &file_number);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "lookup_aml_file_number");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* Account unknown, return 404 */
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_TARGET_ACCOUNT_UNKNOWN,
+ args[0]);
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ }
+
+ {
const char *mime;
mime = MHD_lookup_connection_value (rc->connection,
@@ -402,6 +573,8 @@ TEH_handler_aml_attributes_get (
else if (0 == strcmp (mime,
"application/pdf"))
{
+ enum GNUNET_DB_QueryStatus qs;
+
rctx->format = RCF_PDF;
rctx->details.pdf.global_attrs = json_object ();
GNUNET_assert (NULL != rctx->details.pdf.global_attrs);
@@ -409,10 +582,43 @@ TEH_handler_aml_attributes_get (
GNUNET_assert (0 ==
json_object_update (rctx->details.pdf.global_attrs,
TEH_global_pdf_form_data));
- // FIXME: need to obtain globals about the
- // account here first, like the FILE_NUMBER.
- // Might just as well select current rules/etc
- // at the same time and generate a cover page!
+ GNUNET_assert (0 ==
+ json_object_set_new (rctx->details.pdf.global_attrs,
+ "FILE_NUMBER",
+ json_integer (file_number)));
+ /* Kookup latest AML decision & account rules & properties
+ to build the cover page */
+ qs = TEH_plugin->lookup_aml_history (TEH_plugin->cls,
+ &h_payto,
+ UINT64_MAX, /* offset */
+ -1, /* latest decision only */
+ &build_cover_page,
+ rctx);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (
+ rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_aml_attributes");
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* no decision was ever taken, build empty cover page */
+ build_cover_page (rctx,
+ 0,
+ GNUNET_TIME_UNIT_ZERO_TS,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ false);
+ break;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
}
else
{
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
@@ -207,6 +207,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
pg_kyc_provider_account_lookup.h pg_kyc_provider_account_lookup.c \
pg_kycauth_in_insert.h pg_kycauth_in_insert.c \
pg_lookup_active_legitimization.h pg_lookup_active_legitimization.c \
+ pg_lookup_aml_file_number.h pg_lookup_aml_file_number.c \
pg_lookup_aml_history.h pg_lookup_aml_history.c \
pg_lookup_aml_officer.h pg_lookup_aml_officer.c \
pg_lookup_auditor_status.h pg_lookup_auditor_status.c \
diff --git a/src/exchangedb/exchangedb_history.c b/src/exchangedb/exchangedb_history.c
@@ -28,6 +28,7 @@
* Function called to expand AML history for the account.
*
* @param cls a `json_t *` array to build
+ * @param outcome_serial_id row ID of the decision
* @param decision_time when was the decision taken
* @param justification what was the given justification
* @param decider_pub which key signed the decision
@@ -40,6 +41,7 @@
static void
add_aml_history_entry (
void *cls,
+ uint64_t outcome_serial_id,
struct GNUNET_TIME_Timestamp decision_time,
const char *justification,
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
@@ -86,6 +88,8 @@ TALER_EXCHANGEDB_aml_history_builder (void *cls)
qs = hbc->db_plugin->lookup_aml_history (
hbc->db_plugin->cls,
acc,
+ UINT64_MAX, /* offset */
+ -16 * 1024, /* limit: none for all practical purposes (for now) */
&add_aml_history_entry,
aml_history);
switch (qs)
diff --git a/src/exchangedb/pg_lookup_aml_file_number.c b/src/exchangedb/pg_lookup_aml_file_number.c
@@ -0,0 +1,58 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_aml_file_number.c
+ * @brief Implementation of the lookup_aml_file_number function for Postgres
+ * @author Christian Grothoff
+ */
+#include "taler/platform.h"
+#include "taler/taler_error_codes.h"
+#include "taler/taler_dbevents.h"
+#include "taler/taler_pq_lib.h"
+#include "pg_lookup_aml_file_number.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_file_number (
+ void *cls,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t *kyc_target_row)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 (
+ "kyc_target_serial_id",
+ kyc_target_row),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "lookup_aml_file_number",
+ "SELECT "
+ " kyc_target_serial_id"
+ " FROM kyc_targets"
+ " WHERE h_normalized_payto=$1");
+ return GNUNET_PQ_eval_prepared_singleton_select (
+ pg->conn,
+ "lookup_aml_file_number",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_lookup_aml_file_number.h b/src/exchangedb/pg_lookup_aml_file_number.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2025 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file exchangedb/pg_lookup_aml_file_number.h
+ * @brief implementation of the lookup_aml_file_number function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_LOOKUP_AML_FILE_NUMBER_H
+#define PG_LOOKUP_AML_FILE_NUMBER_H
+
+#include "taler/taler_util.h"
+#include "taler/taler_json_lib.h"
+#include "taler/taler_exchangedb_plugin.h"
+
+
+/**
+ * Lookup AML file number by the payto address.
+ *
+ * @param cls closure
+ * @param h_payto account for which to find the row ID
+ * @param[out] kyc_target_row set to row in the kyc_targets table for @a h_payto
+ * @return database transaction status
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_lookup_aml_file_number (
+ void *cls,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t *kyc_target_row);
+
+
+#endif
diff --git a/src/exchangedb/pg_lookup_aml_history.c b/src/exchangedb/pg_lookup_aml_history.c
@@ -72,6 +72,7 @@ handle_aml_entry (void *cls,
for (unsigned int i = 0; i < num_results; i++)
{
+ uint64_t outcome_serial_id;
struct GNUNET_TIME_Timestamp decision_time;
char *justification;
struct TALER_AmlOfficerPublicKeyP decider_pub;
@@ -80,6 +81,8 @@ handle_aml_entry (void *cls,
bool to_investigate;
bool is_active;
struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_uint64 ("outcome_serial_id",
+ &outcome_serial_id),
GNUNET_PQ_result_spec_timestamp ("decision_time",
&decision_time),
GNUNET_PQ_result_spec_string ("justification",
@@ -109,6 +112,7 @@ handle_aml_entry (void *cls,
return;
}
ahc->cb (ahc->cb_cls,
+ outcome_serial_id,
decision_time,
justification,
&decider_pub,
@@ -125,6 +129,8 @@ enum GNUNET_DB_QueryStatus
TEH_PG_lookup_aml_history (
void *cls,
const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t offset,
+ int64_t limit,
TALER_EXCHANGEDB_AmlHistoryCallback cb,
void *cb_cls)
{
@@ -134,16 +140,20 @@ TEH_PG_lookup_aml_history (
.cb = cb,
.cb_cls = cb_cls
};
+ uint64_t ulimit = (limit < 0) ? (-limit) : limit;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_uint64 (&offset),
+ GNUNET_PQ_query_param_uint64 (&ulimit),
GNUNET_PQ_query_param_end
};
enum GNUNET_DB_QueryStatus qs;
PREPARE (pg,
- "lookup_aml_history",
+ "lookup_aml_history_desc",
"SELECT"
" lo.decision_time"
+ ",lo.outcome_serial_id"
",ah.justification"
",ah.decider_pub"
",lo.jproperties::TEXT"
@@ -154,10 +164,32 @@ TEH_PG_lookup_aml_history (
" JOIN legitimization_outcomes lo"
" USING (outcome_serial_id)"
" WHERE ah.h_payto=$1"
- " ORDER BY decision_time DESC, outcome_serial_id DESC;");
+ " AND lo.outcome_serial_id < $2"
+ " ORDER BY outcome_serial_id DESC"
+ " LIMIT $3;");
+ PREPARE (pg,
+ "lookup_aml_history_desc",
+ "SELECT"
+ " lo.decision_time"
+ ",lo.outcome_serial_id"
+ ",ah.justification"
+ ",ah.decider_pub"
+ ",lo.jproperties::TEXT"
+ ",lo.jnew_rules::TEXT"
+ ",lo.to_investigate"
+ ",lo.is_active"
+ " FROM aml_history ah"
+ " JOIN legitimization_outcomes lo"
+ " USING (outcome_serial_id)"
+ " WHERE ah.h_payto=$1"
+ " AND lo.outcome_serial_id > $2"
+ " ORDER BY outcome_serial_id ASC"
+ " LIMIT $3;");
qs = GNUNET_PQ_eval_prepared_multi_select (
pg->conn,
- "lookup_aml_history",
+ (limit < 0)
+ ? "lookup_aml_history_desc"
+ : "lookup_aml_history_asc",
params,
&handle_aml_entry,
&ahc);
diff --git a/src/exchangedb/pg_lookup_aml_history.h b/src/exchangedb/pg_lookup_aml_history.h
@@ -32,6 +32,8 @@
*
* @param cls closure
* @param h_payto hash of account to lookup history for
+ * @param offset row ID to start returning results from
+ * @param limit how many results to return, negative for descending order
* @param cb function to call on results
* @param cb_cls closure for @a cb
* @return database transaction status
@@ -40,6 +42,8 @@ enum GNUNET_DB_QueryStatus
TEH_PG_lookup_aml_history (
void *cls,
const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t offset,
+ int64_t limit,
TALER_EXCHANGEDB_AmlHistoryCallback cb,
void *cb_cls);
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -136,6 +136,7 @@
#include "pg_kyc_provider_account_lookup.h"
#include "pg_kycauth_in_insert.h"
#include "pg_lookup_active_legitimization.h"
+#include "pg_lookup_aml_file_number.h"
#include "pg_lookup_aml_history.h"
#include "pg_lookup_aml_officer.h"
#include "pg_lookup_auditor_status.h"
@@ -629,6 +630,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &TEH_PG_gc;
plugin->select_coin_deposits_above_serial_id
= &TEH_PG_select_coin_deposits_above_serial_id;
+ plugin->lookup_aml_file_number
+ = &TEH_PG_lookup_aml_file_number;
plugin->lookup_aml_history
= &TEH_PG_lookup_aml_history;
plugin->lookup_kyc_history
diff --git a/src/include/taler/taler_error_codes.h b/src/include/taler/taler_error_codes.h
@@ -808,6 +808,14 @@ enum TALER_ErrorCode
/**
+ * The exchange is not aware of the given target account. The specified account is not a customer of this service.
+ * Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
+ * (A value of 0 indicates that the error is generated client-side).
+ */
+ TALER_EC_EXCHANGE_GENERIC_TARGET_ACCOUNT_UNKNOWN = 1049,
+
+
+ /**
* The exchange did not find information about the specified transaction in the database.
* Returned with an HTTP status code of #MHD_HTTP_NOT_FOUND (404).
* (A value of 0 indicates that the error is generated client-side).
diff --git a/src/include/taler/taler_exchangedb_plugin.h b/src/include/taler/taler_exchangedb_plugin.h
@@ -3321,6 +3321,7 @@ typedef void
* account.
*
* @param cls closure
+ * @param outcome_serial_id row ID of the decision
* @param decision_time when was the decision taken
* @param justification what was the given justification
* @param decider_pub which key signed the decision
@@ -3333,6 +3334,7 @@ typedef void
typedef void
(*TALER_EXCHANGEDB_AmlHistoryCallback) (
void *cls,
+ uint64_t outcome_serial_id,
struct GNUNET_TIME_Timestamp decision_time,
const char *justification,
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
@@ -7852,6 +7854,8 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls closure
* @param h_payto hash of account to lookup history for
+ * @param offset row ID to start returning results from
+ * @param limit how many results to return, negative for descending order
* @param cb function to call on results
* @param cb_cls closure for @a cb
* @return database transaction status
@@ -7860,6 +7864,8 @@ struct TALER_EXCHANGEDB_Plugin
(*lookup_aml_history)(
void *cls,
const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t offset,
+ int64_t limit,
TALER_EXCHANGEDB_AmlHistoryCallback cb,
void *cb_cls);
@@ -8185,6 +8191,20 @@ struct TALER_EXCHANGEDB_Plugin
void *cls,
const char *schema);
+ /**
+ * Lookup AML file number by the payto address.
+ *
+ * @param cls closure
+ * @param h_payto account for which to find the row ID
+ * @param[out] kyw_target_row set to row in the kyc_targets table for @a h_payto
+ * @return database transaction status
+ */
+ enum GNUNET_DB_QueryStatus
+ (*lookup_aml_file_number) (
+ void *cls,
+ const struct TALER_NormalizedPaytoHashP *h_payto,
+ uint64_t *kyc_target_row);
+
};
#endif /* _TALER_EXCHANGE_DB_H */
diff --git a/src/util/taler_error_codes.c b/src/util/taler_error_codes.c
@@ -811,6 +811,14 @@ static const struct ErrorCodeAndHint code_hint_pairs[] = {
},
{
+ /* 1049 */
+ .ec = TALER_EC_EXCHANGE_GENERIC_TARGET_ACCOUNT_UNKNOWN,
+ .hint = gettext_noop (
+ "The exchange is not aware of the given target account. The specified account is not a customer of this service."),
+ .http_code = MHD_HTTP_NOT_FOUND
+ },
+
+ {
/* 1100 */
.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_NOT_FOUND,
.hint = gettext_noop (
@@ -5491,7 +5499,7 @@ static const struct ErrorCodeAndHint code_hint_pairs[] = {
/**
* The length of @e code_hint_pairs.
*/
-static const unsigned int code_hint_pairs_length = 687;
+static const unsigned int code_hint_pairs_length = 688;
const char *