diff --git a/contrib/gana b/contrib/gana
index 3a616a04f..59de2acb7 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 3a616a04f1cd946bf0641b54cd71f1b858174f74
+Subproject commit 59de2acb7c716c816ed15786b5369e56c325770c
diff --git a/src/exchangedb/pg_batch_ensure_coin_known.c b/src/exchangedb/pg_batch_ensure_coin_known.c
new file mode 100644
index 000000000..d246e6228
--- /dev/null
+++ b/src/exchangedb/pg_batch_ensure_coin_known.c
@@ -0,0 +1,483 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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
+ */
+/**
+ * @file exchangedb/pg_batch_ensure_coin_known.c
+ * @brief Implementation of the batch_ensure_coin_known function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_batch_ensure_coin_known.h"
+#include "pg_helper.h"
+
+
+
+
+
+
+static enum TALER_EXCHANGEDB_CoinKnownStatus
+insert1 (struct PosgresClosure *pg,
+ const struct TALER_CoinPublicInfo *coin[1],
+ const struct TALER_EXCHANGEDB_CoinInfo *result[1])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null = false;
+ bool is_age_hash_null = false;
+ PREPARE (pg,
+ "batch1_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ " FROM exchange_do_batch1_known_coin"
+ " ($1, $2, $3, $4);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ result[0].denom_hash),
+ &is_denom_pub_hash_null),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ result[0].h_age_commitment),
+ &is_age_hash_null),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch1_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ if (! existed)
+ return TALER_EXCHANGEDB_CKS_ADDED;
+ break; /* continued below */
+ }
+
+ if ( (! is_denom_pub_hash_null) &&
+ (0 != GNUNET_memcmp (&denom_hash->hash,
+ &coin->denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+
+ if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (h_age_commitment,
+ &coin->h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+ return TALER_EXCHANGEDB_CKS_PRESENT;
+}
+
+static enum TALER_EXCHANGEDB_CoinKnownStatus
+insert2 (struct PosgresClosure *pg,
+ const struct TALER_CoinPublicInfo *coin[2],
+ const struct TALER_EXCHANGEDB_CoinInfo *result[2])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null = false;
+ bool is_age_hash_null = false;
+ bool is_denom_pub_hash_null2 = false;
+ bool is_age_hash_null2 = false;
+ PREPARE (pg,
+ "batch2_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ ",existed2 AS existed2"
+ ",known_coin_id2 AS known_coin_id2"
+ ",denom_pub_hash2 AS denom_hash2"
+ ",age_commitment_hash2 AS h_age_commitment2"
+ " FROM exchange_do_batch2_known_coin"
+ " ($1, $2, $3, $4, $5, $6, $7, $8);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ result[0].denom_hash),
+ &is_denom_pub_hash_null),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ result[0].h_age_commitment[0]),
+ &is_age_hash_null),
+ GNUNET_PQ_result_spec_bool ("existed2",
+ result[1].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
+ result[1].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
+ &result[1].denom_hash),
+ &is_denom_pub_hash_null2),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
+ &result[1].h_age_commitment),
+ &is_age_hash_null2),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch2_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ if (! existed)
+ return TALER_EXCHANGEDB_CKS_ADDED;
+ break; /* continued below */
+ }
+
+ if ( (! is_denom_pub_hash_null) &&
+ (0 != GNUNET_memcmp (&denom_hash[0].hash,
+ &coin[0].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+
+ if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (h_age_commitment[0],
+ &coin[0].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[0]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+ if ( (! is_denom_pub_hash_null2) &&
+ (0 != GNUNET_memcmp (&denom_hash[1].hash,
+ &coin[1].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+
+ if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (h_age_commitment[1],
+ &coin[1].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[1]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+ return TALER_EXCHANGEDB_CKS_PRESENT;
+}
+
+static enum TALER_EXCHANGEDB_CoinKnownStatus
+insert4 (struct PosgresClosure *pg,
+ const struct TALER_CoinPublicInfo *coin[4],
+ const struct TALER_EXCHANGEDB_CoinInfo *result[4])
+{
+ enum GNUNET_DB_QueryStatus qs;
+ bool is_denom_pub_hash_null = false;
+ bool is_age_hash_null = false;
+ bool is_denom_pub_hash_null2 = false;
+ bool is_age_hash_null2 = false;
+ bool is_denom_pub_hash_null3 = false;
+ bool is_age_hash_null3 = false;
+ bool is_denom_pub_hash_null4 = false;
+ bool is_age_hash_null4 = false;
+ PREPARE (pg,
+ "batch4_known_coin",
+ "SELECT"
+ " existed1 AS existed"
+ ",known_coin_id1 AS known_coin_id"
+ ",denom_pub_hash1 AS denom_hash"
+ ",age_commitment_hash1 AS h_age_commitment"
+ ",existed2 AS existed2"
+ ",known_coin_id2 AS known_coin_id2"
+ ",denom_pub_hash2 AS denom_hash2"
+ ",age_commitment_hash2 AS h_age_commitment2"
+ ",existed3 AS existed3"
+ ",known_coin_id3 AS known_coin_id3"
+ ",denom_pub_hash3 AS denom_hash3"
+ ",age_commitment_hash3 AS h_age_commitment3"
+ ",existed4 AS existed4"
+ ",known_coin_id4 AS known_coin_id4"
+ ",denom_pub_hash4 AS denom_hash4"
+ ",age_commitment_hash4 AS h_age_commitment4"
+ " FROM exchange_do_batch2_known_coin"
+ " ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16);"
+ );
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[0].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[1].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[0].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[2].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[2].denom_sig),
+
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].denom_pub_hash),
+ GNUNET_PQ_query_param_auto_from_type (&coin[3].h_age_commitment),
+ TALER_PQ_query_param_denom_sig (&coin[3].denom_sig),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("existed",
+ result[0].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id",
+ result[0].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
+ result[0].denom_hash),
+ &is_denom_pub_hash_null),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash",
+ result[0].h_age_commitment),
+ &is_age_hash_null),
+ GNUNET_PQ_result_spec_bool ("existed2",
+ result[1].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id2",
+ result[1].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash2",
+ result[1].denom_hash),
+ &is_denom_pub_hash_null2),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash2",
+ result[1].h_age_commitment),
+ &is_age_hash_null2),
+ GNUNET_PQ_result_spec_bool ("existed3",
+ result[2].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id3",
+ result[2].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash3",
+ result[2].denom_hash),
+ &is_denom_pub_hash_null3),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash3",
+ result[2].h_age_commitment),
+ &is_age_hash_null3),
+ GNUNET_PQ_result_spec_bool ("existed4",
+ result[3].existed),
+ GNUNET_PQ_result_spec_uint64 ("known_coin_id4",
+ result[3].known_coin_id),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash4",
+ result[3].denom_hash),
+ &is_denom_pub_hash_null4),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_auto_from_type ("age_commitment_hash4",
+ result[3].h_age_commitment),
+ &is_age_hash_null4),
+ GNUNET_PQ_result_spec_end
+ };
+
+ qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "batch4_known_coin",
+ params,
+ rs);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return TALER_EXCHANGEDB_CKS_SOFT_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ GNUNET_break (0); /* should be impossible */
+ return TALER_EXCHANGEDB_CKS_HARD_FAIL;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ if (! existed)
+ return TALER_EXCHANGEDB_CKS_ADDED;
+ break; /* continued below */
+ }
+
+ if ( (! is_denom_pub_hash_null) &&
+ (0 != GNUNET_memcmp (result[0].denom_hash.hash,
+ &coin[0].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+ if ( (! is_age_hash_null) &&
+ (0 != GNUNET_memcmp (h_age_commitment[0],
+ &coin[0].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[0]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+
+ if ( (! is_denom_pub_hash_null2) &&
+ (0 != GNUNET_memcmp (&denom_hash[1].hash,
+ &coin[1].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+ if ( (! is_age_hash_null2) &&
+ (0 != GNUNET_memcmp (h_age_commitment[1],
+ &coin[1].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[1]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+
+ if ( (! is_denom_pub_hash_null3) &&
+ (0 != GNUNET_memcmp (&denom_hash[2].hash,
+ &coin[2].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+ if ( (! is_age_hash_null3) &&
+ (0 != GNUNET_memcmp (h_age_commitment[2],
+ &coin[2].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[2]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+
+ if ( (! is_denom_pub_hash_null4) &&
+ (0 != GNUNET_memcmp (&denom_hash[3].hash,
+ &coin[3].denom_pub_hash.hash)) )
+ {
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_DENOM_CONFLICT;
+ }
+ if ( (! is_age_hash_null4) &&
+ (0 != GNUNET_memcmp (h_age_commitment[3],
+ &coin[3].h_age_commitment)) )
+ {
+ GNUNET_break (GNUNET_is_zero (h_age_commitment[3]));
+ GNUNET_break_op (0);
+ return TALER_EXCHANGEDB_CKS_AGE_CONFLICT;
+ }
+
+ return TALER_EXCHANGEDB_CKS_PRESENT;
+}
+
+
+
+enum TALER_EXCHANGEDB_CoinKnownStatus
+TEH_PG_batch_ensure_coin_known (void *cls,
+ const struct
+ TALER_CoinPublicInfo *coin,
+ const struct
+ TALER_EXCHANGEDB_CoinInfo *result,
+ unsigned int coin_length,
+ unsigned int batch_size)
+{
+ struct PostgresClosure *pg = cls;
+ enum TALER_EXCHANGEDB_CoinKnownStatus qs1;
+ enum TALER_EXCHANGEDB_CoinKnownStatus qs2;
+ enum TALER_EXCHANGEDB_CoinKnownStatus qs4;
+ unsigned int i = 0;
+
+ while (i < coin_length)
+ {
+ unsigned int bs = GNUNET_MIN (batch_size,
+ coin_length - i);
+ bs = 1;
+ if (bs >= 4)
+ {
+ qs4 = insert4 (pg,
+ &coin[i],
+ &result[i]
+ );
+ i += 4;
+ continue;
+ }
+ switch (bs)
+ {
+ case 3:
+ case 2:
+ qs2 = insert2 (pg,
+ &coin[i],
+ &result[i]
+ );
+ i += 2;
+ break;
+ case 1:
+ qs1 = insert1 (pg,
+ &coin[i],
+ &result[i]
+ );
+ i += 1;
+ break;
+ case 0:
+ GNUNET_assert (0);
+ break;
+ }
+ } /* end while */
+ return TALER_EXCHANGEDB_CKS_PRESENT;
+}
diff --git a/src/exchangedb/pg_batch_ensure_coin_known.h b/src/exchangedb/pg_batch_ensure_coin_known.h
new file mode 100644
index 000000000..ce2b2f4ce
--- /dev/null
+++ b/src/exchangedb/pg_batch_ensure_coin_known.h
@@ -0,0 +1,35 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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
+ */
+/**
+ * @file exchangedb/pg_batch_ensure_coin_known.h
+ * @brief implementation of the batch_ensure_coin_known function for Postgres
+ * @author Christian Grothoff
+ */
+#ifndef PG_BATCH_ENSURE_COIN_KNOWN_H
+#define PG_BATCH_ENSURE_COIN_KNOWN_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+enum TALER_EXCHANGEDB_CoinKnownStatus
+TEH_PG_batch_ensure_coin_known (void *cls,
+ const struct
+ TALER_CoinPublicInfo *coin,
+ const struct
+ TALER_EXCHANGEDB_CoinInfo *result,
+ unsigned int coin_length,
+ unsigned int batch_size);
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 2f3318184..7e3aa2b1e 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -216,7 +216,7 @@
#include "pg_select_aml_process.h"
#include "pg_select_aml_history.h"
#include "pg_insert_aml_decision.h"
-
+#include "pg_batch_ensure_coin_known.h"
/**
* Set to 1 to enable Postgres auto_explain module. This will
@@ -774,6 +774,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->insert_aml_decision
= &TEH_PG_insert_aml_decision;
+ plugin->batch_ensure_coin_known
+ = &TEH_PG_batch_ensure_coin_known;
+
return plugin;
}
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 962bccaa2..cee8fb883 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -29,6 +29,19 @@
#include "taler_extensions_policy.h"
+
+
+struct TALER_EXCHANGEDB_CoinInfo
+{
+ uint64_t *known_coin_id;
+ struct TALER_DenominationHashP *denom_hash;
+ struct TALER_AgeCommitmentHash *h_age_commitment;
+ bool *existed;
+};
+
+
+
+
/**
* Information about a denomination key.
*/
@@ -887,6 +900,21 @@ struct TALER_EXCHANGEDB_DenominationKeyMetaData
};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/**
* Signature of a function called with information about the exchange's
* denomination keys.
@@ -4027,7 +4055,13 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_DenominationHashP *denom_pub_hash,
struct TALER_AgeCommitmentHash *age_hash);
-
+ enum TALER_EXCHANGEDB_CoinKnownStatus
+ (*batch_ensure_coin_known)(void *cls,
+ const struct TALER_CoinPublicInfo *coin,
+ const struct
+ TALER_EXCHANGEDB_CoinInfo *result,
+ unsigned int coin_length,
+ unsigned int batch_size);
/**
* Retrieve information about the given @a coin from the database.
*