aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/exchange/Makefile.am4
-rw-r--r--src/exchange/taler-exchange-httpd.c2
-rw-r--r--src/exchange/taler-exchange-httpd_common_deposit.c48
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_attest.c378
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_attest.h41
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_close.c419
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_close.h41
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_get.c2
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_get_attest.c243
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_get_attest.h44
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_history.h5
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_open.c432
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_open.h41
-rw-r--r--src/exchangedb/Makefile.am8
-rw-r--r--src/exchangedb/common-0001.sql8
-rw-r--r--src/exchangedb/pg_do_reserve_open.c73
-rw-r--r--src/exchangedb/pg_do_reserve_open.h55
-rw-r--r--src/exchangedb/pg_helper.h149
-rw-r--r--src/exchangedb/pg_insert_close_request.c67
-rw-r--r--src/exchangedb/pg_insert_close_request.h52
-rw-r--r--src/exchangedb/pg_insert_reserve_open_deposit.c66
-rw-r--r--src/exchangedb/pg_insert_reserve_open_deposit.h54
-rw-r--r--src/exchangedb/pg_iterate_kyc_reference.c129
-rw-r--r--src/exchangedb/pg_iterate_kyc_reference.h46
-rw-r--r--src/exchangedb/pg_iterate_reserve_close_info.c129
-rw-r--r--src/exchangedb/pg_iterate_reserve_close_info.h50
-rw-r--r--src/exchangedb/pg_select_reserve_close_info.c61
-rw-r--r--src/exchangedb/pg_select_reserve_close_info.h49
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c150
-rw-r--r--src/exchangedb/procedures.sql117
-rw-r--r--src/include/taler_crypto_lib.h15
-rw-r--r--src/include/taler_exchangedb_plugin.h137
-rw-r--r--src/include/taler_kyclogic_lib.h28
-rw-r--r--src/kyclogic/kyclogic_api.c17
-rw-r--r--src/lib/exchange_api_reserves_attest.c2
-rw-r--r--src/lib/exchange_api_reserves_open.c15
-rw-r--r--src/testing/Makefile.am2
-rw-r--r--src/testing/testing_api_cmd_reserve_attest.c45
-rw-r--r--src/testing/testing_api_cmd_reserve_get_attestable.c81
-rw-r--r--src/util/wallet_signatures.c21
40 files changed, 3016 insertions, 310 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 4d7d6d94..b2365eec 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -161,8 +161,12 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \
taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \
taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
+ taler-exchange-httpd_reserves_attest.c taler-exchange-httpd_reserves_attest.h \
+ taler-exchange-httpd_reserves_close.c taler-exchange-httpd_reserves_close.h \
taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
+ taler-exchange-httpd_reserves_get_attest.c taler-exchange-httpd_reserves_get_attest.h \
taler-exchange-httpd_reserves_history.c taler-exchange-httpd_reserves_history.h \
+ taler-exchange-httpd_reserves_open.c taler-exchange-httpd_reserves_open.h \
taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \
taler-exchange-httpd_reserves_status.c taler-exchange-httpd_reserves_status.h \
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c
index ffe5c1f5..39f383af 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -398,7 +398,7 @@ handle_post_reserves (struct TEH_RequestContext *rc,
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_RESERVE_PUB_MALFORMED,
+ TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
for (unsigned int i = 0; NULL != h[i].op; i++)
diff --git a/src/exchange/taler-exchange-httpd_common_deposit.c b/src/exchange/taler-exchange-httpd_common_deposit.c
index ecb13630..0c91f0bc 100644
--- a/src/exchange/taler-exchange-httpd_common_deposit.c
+++ b/src/exchange/taler-exchange-httpd_common_deposit.c
@@ -265,51 +265,3 @@ TEH_common_purse_deposit_free_coin (struct TEH_PurseDepositedCoin *coin)
if (! coin->cpi.no_age_commitment)
GNUNET_free (coin->age_commitment.keys); /* Only the keys have been allocated */
}
-
-
-#if LEGACY
-
-if (0 >
- TALER_amount_add (&pcc->deposit_total,
- &pcc->deposit_total,
- &coin->amount_minus_fee))
-{
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
- "total deposit contribution");
-}
-
-
-{
- MHD_RESULT mhd_ret = MHD_NO;
- enum GNUNET_DB_QueryStatus qs;
-
- /* make sure coin is 'known' in database */
- for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++)
- {
- qs = TEH_make_coin_known (&coin->cpi,
- connection,
- &coin->known_coin_id,
- &mhd_ret);
- /* no transaction => no serialization failures should be possible */
- if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
- break;
- }
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- GNUNET_break (0);
- return (MHD_YES ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- "make_coin_known"))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- if (qs < 0)
- return (MHD_YES == mhd_ret) ? GNUNET_NO : GNUNET_SYSERR;
-}
-return GNUNET_OK;
-}
-#endif
diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.c b/src/exchange/taler-exchange-httpd_reserves_attest.c
new file mode 100644
index 00000000..a740bb25
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_attest.c
@@ -0,0 +1,378 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_attest.c
+ * @brief Handle /reserves/$RESERVE_PUB/attest requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_dbevents.h"
+#include "taler_kyclogic_lib.h"
+#include "taler_json_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_reserves_attest.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * How far do we allow a client's time to be off when
+ * checking the request timestamp?
+ */
+#define TIMESTAMP_TOLERANCE \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+
+
+/**
+ * Closure for #reserve_attest_transaction.
+ */
+struct ReserveAttestContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+
+ /**
+ * Hash of the payto URI of this reserve.
+ */
+ struct TALER_PaytoHashP h_payto;
+
+ /**
+ * Timestamp of the request.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Expiration time for the attestation.
+ */
+ struct GNUNET_TIME_Timestamp etime;
+
+ /**
+ * List of requested details.
+ */
+ json_t *details;
+
+ /**
+ * Client signature approving the request.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+
+ /**
+ * Attributes we are affirming.
+ */
+ json_t *json_attest;
+
+ /**
+ * Error code encountered in interaction with KYC provider.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * Set to true if we did not find the reserve.
+ */
+ bool not_found;
+
+};
+
+
+/**
+ * Send reserve attest to client.
+ *
+ * @param connection connection to the client
+ * @param rhc reserve attest to return
+ * @return MHD result code
+ */
+static MHD_RESULT
+reply_reserve_attest_success (struct MHD_Connection *connection,
+ const struct ReserveAttestContext *rhc)
+{
+ struct TALER_ExchangeSignatureP exchange_sig;
+ struct TALER_ExchangePublicKeyP exchange_pub;
+ enum TALER_ErrorCode ec;
+ struct GNUNET_TIME_Timestamp now;
+
+ if (NULL == rhc->json_attest)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
+ NULL);
+ }
+ now = GNUNET_TIME_timestamp_get ();
+ ec = TALER_exchange_online_reserve_attest_details_sign (
+ &TEH_keys_exchange_sign_,
+ now,
+ rhc->etime,
+ rhc->reserve_pub,
+ rhc->json_attest,
+ &exchange_pub,
+ &exchange_sig);
+ if (TALER_EC_NONE != ec)
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_ec (connection,
+ ec,
+ NULL);
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_data_auto ("exchange_sig",
+ &exchange_sig),
+ GNUNET_JSON_pack_data_auto ("exchange_pub",
+ &exchange_pub),
+ GNUNET_JSON_pack_array_steal ("attest",
+ rhc->json_attest));
+}
+
+
+/**
+ * Function called with information about all applicable
+ * legitimization processes for the given user. Finds the
+ * available attributes and merges them into our result
+ * set based on the details requested by the client.
+ *
+ * @param cls our `struct ReserveAttestContext *`
+ * @param provider_section KYC provider configuration section
+ * @param provider_user_id UID at a provider (can be NULL)
+ * @param legi_id legitimization process ID (can be NULL)
+ */
+static void
+kyc_process_cb (void *cls,
+ const char *provider_section,
+ const char *provider_user_id,
+ const char *legi_id)
+{
+ struct ReserveAttestContext *rsc = cls;
+ struct GNUNET_TIME_Timestamp etime;
+ json_t *attrs;
+ bool match = false;
+
+ rsc->ec = TALER_KYCLOGIC_user_to_attributes (provider_section,
+ provider_user_id,
+ legi_id,
+ &etime,
+ &attrs);
+ if (TALER_EC_NONE != rsc->ec)
+ return;
+ if (GNUNET_TIME_absolute_is_past (etime.abs_time))
+ {
+ json_decref (attrs);
+ return;
+ }
+ {
+ json_t *val;
+ const char *name;
+
+ json_object_foreach (attrs, name, val)
+ {
+ bool requested = false;
+ size_t idx;
+ json_t *str;
+
+ if (NULL != json_object_get (rsc->json_attest,
+ name))
+ continue; /* duplicate */
+ json_array_foreach (rsc->details, idx, str)
+ {
+ if (0 == strcmp (json_string_value (str),
+ name))
+ {
+ requested = true;
+ break;
+ }
+ }
+ if (! requested)
+ continue;
+ match = true;
+ GNUNET_assert (0 ==
+ json_object_set (rsc->json_attest, /* NOT set_new! */
+ name,
+ val));
+ }
+ }
+ json_decref (attrs);
+ if (! match)
+ return;
+ rsc->etime = GNUNET_TIME_timestamp_min (etime,
+ rsc->etime);
+}
+
+
+/**
+ * Function implementing /reserves/$RID/attest transaction. Given the public
+ * key of a reserve, return the associated transaction attest. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveAttestContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!); unused
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_attest_transaction (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct ReserveAttestContext *rsc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ rsc->json_attest = json_array ();
+ GNUNET_assert (NULL != rsc->json_attest);
+ qs = TEH_plugin->iterate_kyc_reference (TEH_plugin->cls,
+ &rsc->h_payto,
+ &kyc_process_cb,
+ rsc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "iterate_kyc_reference");
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ rsc->not_found = true;
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ rsc->not_found = false;
+ break;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_reserves_attest (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root)
+{
+ struct ReserveAttestContext rsc = {
+ .etime = GNUNET_TIME_UNIT_FOREVER_TS
+ };
+ MHD_RESULT mhd_ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("request_timestamp",
+ &rsc.timestamp),
+ GNUNET_JSON_spec_json ("details",
+ &rsc.details),
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &rsc.reserve_sig),
+ GNUNET_JSON_spec_end ()
+ };
+ struct GNUNET_TIME_Timestamp now;
+
+ rsc.reserve_pub = reserve_pub;
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO; /* hard failure */
+ }
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ return MHD_YES; /* failure */
+ }
+ }
+ now = GNUNET_TIME_timestamp_get ();
+ if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
+ rsc.timestamp.abs_time,
+ TIMESTAMP_TOLERANCE))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
+ NULL);
+ }
+
+ if (GNUNET_OK !=
+ TALER_wallet_reserve_attest_request_verify (rsc.timestamp,
+ rsc.details,
+ reserve_pub,
+ &rsc.reserve_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVES_ATTEST_BAD_SIGNATURE,
+ NULL);
+ }
+
+ {
+ char *payto_uri;
+
+ payto_uri = TALER_reserve_make_payto (TEH_base_url,
+ rsc.reserve_pub);
+ TALER_payto_hash (payto_uri,
+ &rsc.h_payto);
+ GNUNET_free (payto_uri);
+ }
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "post reserve attest",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &reserve_attest_transaction,
+ &rsc))
+ {
+ return mhd_ret;
+ }
+ if (rsc.not_found)
+ {
+ json_decref (rsc.json_attest);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ }
+ if (TALER_EC_NONE != rsc.ec)
+ {
+ json_decref (rsc.json_attest);
+ return TALER_MHD_reply_with_ec (rc->connection,
+ rsc.ec,
+ NULL);
+ }
+ mhd_ret = reply_reserve_attest_success (rc->connection,
+ &rsc);
+ return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_reserves_attest.c */
diff --git a/src/exchange/taler-exchange-httpd_reserves_attest.h b/src/exchange/taler-exchange-httpd_reserves_attest.h
new file mode 100644
index 00000000..d5ec8fd7
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_attest.h
@@ -0,0 +1,41 @@
+/*
+ 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_attest.h
+ * @brief Handle /reserves/$RESERVE_PUB/attest requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_RESERVES_ATTEST_H
+#define TALER_EXCHANGE_HTTPD_RESERVES_ATTEST_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a POST "/reserves/$RID/attest" request.
+ *
+ * @param rc request context
+ * @param reserve_pub public key of the reserve
+ * @param root uploaded body from the client
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_reserves_attest (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_reserves_close.c b/src/exchange/taler-exchange-httpd_reserves_close.c
new file mode 100644
index 00000000..be36f1e7
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_close.c
@@ -0,0 +1,419 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2022 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_close.c
+ * @brief Handle /reserves/$RESERVE_PUB/close requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_kyclogic_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_json_lib.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_wire.h"
+#include "taler-exchange-httpd_reserves_close.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * How far do we allow a client's time to be off when
+ * checking the request timestamp?
+ */
+#define TIMESTAMP_TOLERANCE \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+
+
+/**
+ * Closure for #reserve_close_transaction.
+ */
+struct ReserveCloseContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+
+ /**
+ * Timestamp of the request.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Client signature approving the request.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+
+ /**
+ * Amount that will be wired (after closing fees).
+ */
+ struct TALER_Amount wire_amount;
+
+ /**
+ * Current balance of the reserve.
+ */
+ struct TALER_Amount balance;
+
+ /**
+ * Where to wire the funds, may be NULL.
+ */
+ const char *payto_uri;
+
+ /**
+ * Hash of the @e payto_uri, if given (otherwise zero).
+ */
+ struct TALER_PaytoHashP h_payto;
+
+ /**
+ * KYC status for the request.
+ */
+ struct TALER_EXCHANGEDB_KycStatus kyc;
+
+ /**
+ * Hash of the payto-URI that was used for the KYC decision.
+ */
+ struct TALER_PaytoHashP kyc_payto;
+
+ /**
+ * Query status from the amount_it() helper function.
+ */
+ enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Send reserve close to client.
+ *
+ * @param connection connection to the client
+ * @param rhc reserve close to return
+ * @return MHD result code
+ */
+static MHD_RESULT
+reply_reserve_close_success (struct MHD_Connection *connection,
+ const struct ReserveCloseContext *rhc)
+{
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_OK,
+ TALER_JSON_pack_amount ("wire_amount",
+ &rhc->wire_amount));
+}
+
+
+/**
+ * Function called to iterate over KYC-relevant
+ * transaction amounts for a particular time range.
+ * Called within a database transaction, so must
+ * not start a new one.
+ *
+ * @param cls closure, identifies the event type and
+ * account to iterate over events for
+ * @param limit maximum time-range for which events
+ * should be fetched (timestamp in the past)
+ * @param cb function to call on each event found,
+ * events must be returned in reverse chronological
+ * order
+ * @param cb_cls closure for @a cb
+ */
+static void
+amount_it (void *cls,
+ struct GNUNET_TIME_Absolute limit,
+ TALER_EXCHANGEDB_KycAmountCallback cb,
+ void *cb_cls)
+{
+ struct ReserveCloseContext *rcc = cls;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = cb (cb_cls,
+ &rcc->balance,
+ GNUNET_TIME_absolute_get ());
+ GNUNET_break (GNUNET_SYSERR != ret);
+ if (GNUNET_OK != ret)
+ return;
+ rcc->qs
+ = TEH_plugin->iterate_reserve_close_info (
+ TEH_plugin->cls,
+ &rcc->kyc_payto,
+ limit,
+ cb,
+ cb_cls);
+}
+
+
+/**
+ * Function implementing /reserves/$RID/close transaction. Given the public
+ * key of a reserve, return the associated transaction close. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveCloseContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!); unused
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_close_transaction (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct ReserveCloseContext *rcc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_Amount balance;
+ char *payto_uri = NULL;
+ const struct TALER_WireFeeSet *wf;
+
+ qs = TEH_plugin->select_reserve_close_info (
+ TEH_plugin->cls,
+ rcc->reserve_pub,
+ &balance,
+ &payto_uri);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select_reserve_close_info");
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+
+ if ( (NULL == rcc->payto_uri) &&
+ (NULL == payto_uri) )
+ {
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_CONFLICT,
+ TALER_EC_EXCHANGE_RESERVES_CLOSE_NO_TARGET_ACCOUNT,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ if ( (NULL != rcc->payto_uri) &&
+ ( (NULL == payto_uri) ||
+ (0 != strcmp (payto_uri,
+ rcc->payto_uri)) ) )
+ {
+ const char *kyc_needed;
+
+ TALER_payto_hash (rcc->payto_uri,
+ &rcc->kyc_payto);
+ rcc->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+ kyc_needed
+ = TALER_KYCLOGIC_kyc_test_required (
+ TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE,
+ &rcc->kyc_payto,
+ TEH_plugin->select_satisfied_kyc_processes,
+ TEH_plugin->cls,
+ &amount_it,
+ rcc);
+ if (rcc->qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == rcc->qs)
+ return rcc->qs;
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "iterate_reserve_close_info");
+ return qs;
+ }
+ rcc->kyc.ok = false;
+ return TEH_plugin->insert_kyc_requirement_for_account (
+ TEH_plugin->cls,
+ kyc_needed,
+ &rcc->kyc_payto,
+ &rcc->kyc.requirement_row);
+ }
+
+ rcc->kyc.ok = true;
+ if (NULL == rcc->payto_uri)
+ rcc->payto_uri = payto_uri;
+
+ {
+ char *method;
+
+ method = TALER_payto_get_method (rcc->payto_uri);
+ wf = TEH_wire_fees_by_time (rcc->timestamp,
+ method);
+ if (NULL == wf)
+ {
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_WIRE_FEES_NOT_CONFIGURED,
+ method);
+ GNUNET_free (method);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ GNUNET_free (method);
+ }
+
+ if (0 >
+ TALER_amount_subtract (&rcc->wire_amount,
+ &balance,
+ &wf->closing))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Client attempted to close reserve with insufficient balance.\n");
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (TEH_currency,
+ &rcc->wire_amount));
+ *mhd_ret = reply_reserve_close_success (connection,
+ rcc);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ qs = TEH_plugin->insert_close_request (TEH_plugin->cls,
+ rcc->reserve_pub,
+ payto_uri,
+ &rcc->reserve_sig,
+ rcc->timestamp,
+ &balance,
+ &wf->closing);
+ GNUNET_free (payto_uri);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "insert_close_request");
+ return qs;
+ }
+ if (qs <= 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_reserves_close (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root)
+{
+ struct ReserveCloseContext rcc = {
+ .payto_uri = NULL,
+ .reserve_pub = reserve_pub
+ };
+ MHD_RESULT mhd_ret;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("request_timestamp",
+ &rcc.timestamp),
+ GNUNET_JSON_spec_mark_optional (
+ GNUNET_JSON_spec_string ("payto_uri",
+ &rcc.payto_uri),
+ NULL),
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &rcc.reserve_sig),
+ GNUNET_JSON_spec_end ()
+ };
+
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO; /* hard failure */
+ }
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ return MHD_YES; /* failure */
+ }
+ }
+
+ {
+ struct GNUNET_TIME_Timestamp now;
+
+ now = GNUNET_TIME_timestamp_get ();
+ if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
+ rcc.timestamp.abs_time,
+ TIMESTAMP_TOLERANCE))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
+ NULL);
+ }
+ }
+
+ if (NULL != rcc.payto_uri)
+ TALER_payto_hash (rcc.payto_uri,
+ &rcc.h_payto);
+ if (GNUNET_OK !=
+ TALER_wallet_reserve_close_verify (rcc.timestamp,
+ &rcc.h_payto,
+ reserve_pub,
+ &rcc.reserve_sig))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVES_CLOSE_BAD_SIGNATURE,
+ NULL);
+ }
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "reserve close",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &reserve_close_transaction,
+ &rcc))
+ {
+ return mhd_ret;
+ }
+ if (! rcc.kyc.ok)
+ return TEH_RESPONSE_reply_kyc_required (rc->connection,
+ &rcc.kyc_payto,
+ &rcc.kyc);
+
+ return reply_reserve_close_success (rc->connection,
+ &rcc);
+}
+
+
+/* end of taler-exchange-httpd_reserves_close.c */
diff --git a/src/exchange/taler-exchange-httpd_reserves_close.h b/src/exchange/taler-exchange-httpd_reserves_close.h
new file mode 100644
index 00000000..4c70b17c
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_close.h
@@ -0,0 +1,41 @@
+/*
+ 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_close.h
+ * @brief Handle /reserves/$RESERVE_PUB/close requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_RESERVES_CLOSE_H
+#define TALER_EXCHANGE_HTTPD_RESERVES_CLOSE_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a POST "/reserves/$RID/close" request.
+ *
+ * @param rc request context
+ * @param reserve_pub public key of the reserve
+ * @param root uploaded body from the client
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_reserves_close (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c b/src/exchange/taler-exchange-httpd_reserves_get.c
index 19fb7df8..88f7aca9 100644
--- a/src/exchange/taler-exchange-httpd_reserves_get.c
+++ b/src/exchange/taler-exchange-httpd_reserves_get.c
@@ -247,7 +247,7 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_BAD_REQUEST,
- TALER_EC_MERCHANT_GENERIC_RESERVE_PUB_MALFORMED,
+ TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
args[0]);
}
{
diff --git a/src/exchange/taler-exchange-httpd_reserves_get_attest.c b/src/exchange/taler-exchange-httpd_reserves_get_attest.c
new file mode 100644
index 00000000..aa0f26a4
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_get_attest.c
@@ -0,0 +1,243 @@
+/*
+ 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_get_attest.c
+ * @brief Handle GET /reserves/$RESERVE_PUB/attest requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_kyclogic_lib.h"
+#include "taler_mhd_lib.h"
+#include "taler_json_lib.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_reserves_get_attest.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Closure for #reserve_attest_transaction.
+ */
+struct ReserveAttestContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
+ * Hash of the payto URI of this reserve.
+ */
+ struct TALER_PaytoHashP h_payto;
+
+ /**
+ * Available attributes.
+ */
+ json_t *attributes;
+
+ /**
+ * Error code encountered in interaction with KYC provider.
+ */
+ enum TALER_ErrorCode ec;
+
+ /**
+ * Set to true if we did not find the reserve.
+ */
+ bool not_found;
+};
+
+
+/**
+ * Function called with information about all applicable
+ * legitimization processes for the given user.
+ *
+ * @param cls our `struct ReserveAttestContext *`
+ * @param provider_section KYC provider configuration section
+ * @param provider_user_id UID at a provider (can be NULL)
+ * @param legi_id legitimization process ID (can be NULL)
+ */
+static void
+kyc_process_cb (void *cls,
+ const char *provider_section,
+ const char *provider_user_id,
+ const char *legi_id)
+{
+ struct ReserveAttestContext *rsc = cls;
+ struct GNUNET_TIME_Timestamp etime;
+ json_t *attrs;
+
+ rsc->ec = TALER_KYCLOGIC_user_to_attributes (provider_section,
+ provider_user_id,
+ legi_id,
+ &etime,
+ &attrs);
+ if (TALER_EC_NONE != rsc->ec)
+ return;
+
+ {
+ json_t *val;
+ const char *name;
+
+ json_object_foreach (attrs, name, val)
+ {
+ bool duplicate = false;
+ size_t idx;
+ json_t *str;
+
+ json_array_foreach (rsc->attributes, idx, str)
+ {
+ if (0 == strcmp (json_string_value (str),
+ name))
+ {
+ duplicate = true;
+ break;
+ }
+ }
+ if (duplicate)
+ continue;
+ GNUNET_assert (0 ==
+ json_array_append (rsc->attributes,
+ json_string (name)));
+ }
+ }
+}
+
+
+/**
+ * Function implementing GET /reserves/$RID/attest transaction.
+ * Execute a /reserves/ get attest. Given the public key of a reserve,
+ * return the associated transaction attest. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction
+ * logic MUST NOT queue a MHD response. IF it returns an hard error,
+ * the transaction logic MUST queue a MHD response and set @a mhd_ret.
+ * IF it returns the soft error code, the function MAY be called again
+ * to retry and MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveAttestContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_attest_transaction (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct ReserveAttestContext *rsc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ rsc->attributes = json_array ();
+ GNUNET_assert (NULL != rsc->attributes);
+ qs = TEH_plugin->iterate_kyc_reference (TEH_plugin->cls,
+ &rsc->h_payto,
+ &kyc_process_cb,
+ rsc);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "iterate_kyc_reference");
+ return qs;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ GNUNET_break (0);
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ rsc->not_found = true;
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ rsc->not_found = false;
+ break;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_reserves_get_attest (struct TEH_RequestContext *rc,
+ const char *const args[1])
+{
+ struct ReserveAttestContext rsc = {
+ .attributes = NULL
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (args[0],
+ strlen (args[0]),
+ &rsc.reserve_pub,
+ sizeof (rsc.reserve_pub)))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
+ args[0]);
+ }
+ {
+ char *payto_uri;
+
+ payto_uri = TALER_reserve_make_payto (TEH_base_url,
+ &rsc.reserve_pub);
+ TALER_payto_hash (payto_uri,
+ &rsc.h_payto);
+ GNUNET_free (payto_uri);
+ }
+ {
+ MHD_RESULT mhd_ret;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "get-attestable",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &reserve_attest_transaction,
+ &rsc))
+ {
+ json_decref (rsc.attributes);
+ return mhd_ret;
+ }
+ }
+ /* generate proper response */
+ if (rsc.not_found)
+ {
+ json_decref (rsc.attributes);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ args[0]);
+ }
+ if (TALER_EC_NONE != rsc.ec)
+ {
+ json_decref (rsc.attributes);
+ return TALER_MHD_reply_with_ec (rc->connection,
+ rsc.ec,
+ NULL);
+ }
+ return TALER_MHD_REPLY_JSON_PACK (
+ rc->connection,
+ MHD_HTTP_OK,
+ GNUNET_JSON_pack_object_steal ("attributes",
+ rsc.attributes));
+}
+
+
+/* end of taler-exchange-httpd_reserves_get_attest.c */
diff --git a/src/exchange/taler-exchange-httpd_reserves_get_attest.h b/src/exchange/taler-exchange-httpd_reserves_get_attest.h
new file mode 100644
index 00000000..8b5e3aba
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_get_attest.h
@@ -0,0 +1,44 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2020 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_get_attest.h
+ * @brief Handle /reserves/$RESERVE_PUB GET_ATTEST requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_RESERVES_GET_ATTEST_H
+#define TALER_EXCHANGE_HTTPD_RESERVES_GET_ATTEST_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a GET "/reserves/$RID/attest" request. Parses the
+ * given "reserve_pub" in @a args (which should contain the
+ * EdDSA public key of a reserve) and then responds with the
+ * available attestations for the reserve.
+ *
+ * @param rc request context
+ * @param args array of additional options (length: 1, just the reserve_pub)
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_reserves_get_attest (struct TEH_RequestContext *rc,
+ const char *const args[1]);
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_reserves_history.h b/src/exchange/taler-exchange-httpd_reserves_history.h
index 9a2a9378..e02cb4d9 100644
--- a/src/exchange/taler-exchange-httpd_reserves_history.h
+++ b/src/exchange/taler-exchange-httpd_reserves_history.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
+ Copyright (C) 2014-2022 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -15,9 +15,8 @@
*/
/**
* @file taler-exchange-httpd_reserves_history.h
- * @brief Handle /reserves/$RESERVE_PUB HISTORY requests
+ * @brief Handle /reserves/$RESERVE_PUB/history requests
* @author Florian Dold
- * @author Benedikt Mueller
* @author Christian Grothoff
*/
#ifndef TALER_EXCHANGE_HTTPD_RESERVES_HISTORY_H
diff --git a/src/exchange/taler-exchange-httpd_reserves_open.c b/src/exchange/taler-exchange-httpd_reserves_open.c
new file mode 100644
index 00000000..d446d9b4
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_open.c
@@ -0,0 +1,432 @@
+/*
+ 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_open.c
+ * @brief Handle /reserves/$RESERVE_PUB/open requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include "taler_mhd_lib.h"
+#include "taler_json_lib.h"
+#include "taler_dbevents.h"
+#include "taler-exchange-httpd_common_deposit.h"
+#include "taler-exchange-httpd_keys.h"
+#include "taler-exchange-httpd_reserves_open.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * How far do we allow a client's time to be off when
+ * checking the request timestamp?
+ */
+#define TIMESTAMP_TOLERANCE \
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+
+
+/**
+ * Closure for #reserve_open_transaction.
+ */
+struct ReserveOpenContext
+{
+ /**
+ * Public key of the reserve the inquiry is about.
+ */
+ const struct TALER_ReservePublicKeyP *reserve_pub;
+
+ /**
+ * Desired (minimum) expiration time for the reserve.
+ */
+ struct GNUNET_TIME_Timestamp desired_expiration;
+
+ /**
+ * Actual expiration time for the reserve.
+ */
+ struct GNUNET_TIME_Timestamp reserve_expiration;
+
+ /**
+ * Timestamp of the request.
+ */
+ struct GNUNET_TIME_Timestamp timestamp;
+
+ /**
+ * Client signature approving the request.
+ */
+ struct TALER_ReserveSignatureP reserve_sig;
+
+ /**
+ * Global fees applying to the request.
+ */
+ const struct TEH_GlobalFee *gf;
+
+ /**
+ * Amount to be paid from the reserve.
+ */
+ struct TALER_Amount reserve_payment;
+
+ /**
+ * Actual cost to open the reserve.
+ */
+ struct TALER_Amount open_cost;
+
+ /**
+ * Total amount that was deposited.
+ */
+ struct TALER_Amount total;
+
+ /**
+ * Information about payments by coin.
+ */
+ struct TEH_PurseDepositedCoin *payments;
+
+ /**
+ * Length of the @e payments array.
+ */
+ unsigned int payments_len;
+
+ /**
+ * Desired minimum purse limit.
+ */
+ uint32_t purse_limit;
+};
+
+
+/**
+ * Send reserve open to client.
+ *
+ * @param connection connection to the client
+ * @param rhc reserve open to return
+ * @return MHD result code
+ */
+static MHD_RESULT
+reply_reserve_open_success (struct MHD_Connection *connection,
+ const struct ReserveOpenContext *rsc)
+{
+ unsigned int status;
+
+ status = MHD_HTTP_OK;
+ if (GNUNET_TIME_timestamp_cmp (rsc->reserve_expiration,
+ <,
+ rsc->desired_expiration))
+ status = MHD_HTTP_PAYMENT_REQUIRED;
+ return TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ status,
+ GNUNET_JSON_pack_timestamp ("reserve_expiration",
+ rsc->reserve_expiration),
+ TALER_JSON_pack_amount ("open_cost",
+ &rsc->open_cost));
+}
+
+
+/**
+ * Cleans up information in @a rsc, but does not
+ * free @a rsc itself (allocated on the stack!).
+ *
+ * @param[in] rsc struct with information to clean up
+ */
+static void
+cleanup_rsc (struct ReserveOpenContext *rsc)
+{
+ for (unsigned int i = 0; i<rsc->payments_len; i++)
+ {
+ TEH_common_purse_deposit_free_coin (&rsc->payments[i]);
+ }
+ GNUNET_free (rsc->payments);
+}
+
+
+/**
+ * Function implementing /reserves/$RID/open transaction. Given the public
+ * key of a reserve, return the associated transaction open. Runs the
+ * transaction logic; IF it returns a non-error code, the transaction logic
+ * MUST NOT queue a MHD response. IF it returns an hard error, the
+ * transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
+ * returns the soft error code, the function MAY be called again to retry and
+ * MUST not queue a MHD response.
+ *
+ * @param cls a `struct ReserveOpenContext *`
+ * @param connection MHD request which triggered the transaction
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ * if transaction failed (!)
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+reserve_open_transaction (void *cls,
+ struct MHD_Connection *connection,
+ MHD_RESULT *mhd_ret)
+{
+ struct ReserveOpenContext *rsc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ for (unsigned int i = 0; i<rsc->payments_len; i++)
+ {
+ struct TEH_PurseDepositedCoin *coin = &rsc->payments[i];
+ bool insufficient_funds = true;
+
+ qs = TEH_make_coin_known (&coin->cpi,
+ connection,
+ &coin->known_coin_id,
+ mhd_ret);
+ if (qs < 0)
+ return qs;
+ qs = TEH_plugin->insert_reserve_open_deposit (
+ TEH_plugin->cls,
+ &coin->cpi,
+ &coin->coin_sig,
+ coin->known_coin_id,
+ &coin->amount,
+ &rsc->reserve_sig,
+ rsc->reserve_pub,
+ &insufficient_funds);
+ /* 0 == qs is fine, then the coin was already
+ spent for this very operation as identified
+ by reserve_sig! */
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ GNUNET_break (0);
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_reserve_open_deposit");
+ return qs;
+ }
+ if (insufficient_funds)
+ {
+ *mhd_ret
+ = TEH_RESPONSE_reply_coin_insufficient_funds (
+ connection,
+ TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+ &coin->cpi.denom_pub_hash,
+ &coin->cpi.coin_pub);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+
+ qs = TEH_plugin->do_reserve_open (TEH_plugin->cls,
+ /* inputs */
+ rsc->reserve_pub,
+ &rsc->total,
+ rsc->purse_limit,
+ &rsc->reserve_sig,
+ rsc->desired_expiration,
+ rsc->timestamp,
+ &rsc->gf->fees.account,
+ /* outputs */
+ &rsc->open_cost,
+ &rsc->reserve_expiration);
+ switch (qs)
+ {
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "do_reserve_open");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
+ NULL);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ break;
+ }
+ return qs;
+}
+
+
+MHD_RESULT
+TEH_handler_reserves_open (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root)
+{
+ struct ReserveOpenContext rsc;
+ json_t *payments;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_timestamp ("request_timestamp",
+ &rsc.timestamp),
+ GNUNET_JSON_spec_timestamp ("reserve_expiration",
+ &rsc.desired_expiration),
+ GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+ &rsc.reserve_sig),
+ GNUNET_JSON_spec_uint32 ("purse_limit",
+ &rsc.purse_limit),
+ GNUNET_JSON_spec_json ("payments",
+ &payments),
+ TALER_JSON_spec_amount ("reserve_payment",
+ TEH_currency,
+ &rsc.reserve_payment),
+ GNUNET_JSON_spec_end ()
+ };
+
+ rsc.reserve_pub = reserve_pub;
+ {
+ enum GNUNET_GenericReturnValue res;
+
+ res = TALER_MHD_parse_json_data (rc->connection,
+ root,
+ spec);
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ return MHD_NO; /* hard failure */
+ }
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ return MHD_YES; /* failure */
+ }
+ }
+
+ {
+ struct GNUNET_TIME_Timestamp now;
+
+ now = GNUNET_TIME_timestamp_get ();
+ if (! GNUNET_TIME_absolute_approx_eq (now.abs_time,
+ rsc.timestamp.abs_time,
+ TIMESTAMP_TOLERANCE))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_CLOCK_SKEW,
+ NULL);
+ }
+ }
+
+ rsc.payments_len = json_array_size (payments);
+ rsc.payments = GNUNET_new_array (rsc.payments_len,
+ struct TEH_PurseDepositedCoin);
+ rsc.total = rsc.reserve_payment;
+ for (unsigned int i = 0; i<rsc.payments_len; i++)
+ {
+ struct TEH_PurseDepositedCoin *coin = &rsc.payments[i];
+ enum GNUNET_GenericReturnValue res;
+
+ res = TEH_common_purse_deposit_parse_coin (
+ rc->connection,
+ coin,
+ json_array_get (payments,
+ i));
+ if (GNUNET_SYSERR == res)
+ {
+ GNUNET_break (0);
+ cleanup_rsc (&rsc);
+ return MHD_NO; /* hard failure */
+ }
+ if (GNUNET_NO == res)
+ {
+ GNUNET_break_op (0);
+ cleanup_rsc (&rsc);
+ return MHD_YES; /* failure */
+ }
+ /* FIXME-DOLD: Alternatively, we could here add coin->amount_minus_fee and
+ thereby charge the deposit fee even when paying the reserve-open fee.
+ To be decided... */
+ if (0 >
+ TALER_amount_add (&rsc.total,
+ &rsc.total,
+ &coin->amount))
+ {
+ GNUNET_break (0);
+ cleanup_rsc (&rsc);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
+ NULL);
+ }
+ }
+
+ {
+ struct TEH_KeyStateHandle *keys;
+
+ keys = TEH_keys_get_state ();
+ if (NULL == keys)
+ {
+ GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
+ cleanup_rsc (&rsc);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+ NULL);
+ }
+ rsc.gf = TEH_keys_global_fee_by_time (keys,
+ rsc.timestamp);
+ }
+ if (NULL == rsc.gf)
+ {
+ GNUNET_break (0);
+ cleanup_rsc (&rsc);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
+ NULL);
+ }
+
+ if (GNUNET_OK !=
+ TALER_wallet_reserve_open_verify (&rsc.reserve_payment,
+ rsc.timestamp,
+ rsc.desired_expiration,
+ rsc.purse_limit,
+ reserve_pub,
+ &rsc.reserve_sig))
+ {
+ GNUNET_break_op (0);
+ cleanup_rsc (&rsc);
+ return TALER_MHD_reply_with_error (rc->connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVES_OPEN_BAD_SIGNATURE,
+ NULL);
+ }
+
+ {
+ MHD_RESULT mhd_ret;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (rc->connection,
+ "reserve open",
+ TEH_MT_REQUEST_OTHER,
+ &mhd_ret,
+ &reserve_open_transaction,
+ &rsc))
+ {
+ cleanup_rsc (&rsc);
+ return mhd_ret;
+ }
+ }
+
+ {
+ MHD_RESULT mhd_ret;
+
+ mhd_ret = reply_reserve_open_success (rc->connection,
+ &rsc);
+ cleanup_rsc (&rsc);
+ return mhd_ret;
+ }
+}
+
+
+/* end of taler-exchange-httpd_reserves_open.c */
diff --git a/src/exchange/taler-exchange-httpd_reserves_open.h b/src/exchange/taler-exchange-httpd_reserves_open.h
new file mode 100644
index 00000000..e28c22c0
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserves_open.h
@@ -0,0 +1,41 @@
+/*
+ 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 Affero 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_reserves_open.h
+ * @brief Handle /reserves/$RESERVE_PUB/open requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_RESERVES_OPEN_H
+#define TALER_EXCHANGE_HTTPD_RESERVES_OPEN_H
+
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a POST "/reserves/$RID/open" request.
+ *
+ * @param rc request context
+ * @param reserve_pub public key of the reserve
+ * @param root uploaded body from the client
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_reserves_open (struct TEH_RequestContext *rc,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const json_t *root);
+
+#endif
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 01ce13de..1fc89d1b 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -68,7 +68,13 @@ plugin_LTLIBRARIES = \
endif
libtaler_plugin_exchangedb_postgres_la_SOURCES = \
- plugin_exchangedb_postgres.c
+ plugin_exchangedb_postgres.c pg_helper.h \
+ pg_do_reserve_open.c pg_do_reserve_open.h \
+ pg_insert_close_request.c pg_insert_close_request.h \
+ pg_insert_reserve_open_deposit.c pg_insert_reserve_open_deposit.h \
+ pg_iterate_kyc_reference.c pg_iterate_kyc_reference.h \
+ pg_iterate_reserve_close_info.c pg_iterate_reserve_close_info.h \
+ pg_select_reserve_close_info.c pg_select_reserve_close_info.h
libtaler_plugin_exchangedb_postgres_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_exchangedb_postgres_la_LDFLAGS = \
diff --git a/src/exchangedb/common-0001.sql b/src/exchangedb/common-0001.sql
index 4a0aac38..564bf3b3 100644
--- a/src/exchangedb/common-0001.sql
+++ b/src/exchangedb/common-0001.sql
@@ -459,7 +459,8 @@ BEGIN
PERFORM create_partitioned_table(
'CREATE TABLE IF NOT EXISTS %I'
'(reserve_open_deposit_uuid BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE / PRIMARY KEY'
- ',reserve_pub BYTEA NOT NULL' -- REFERENCES reserves (reserve_pub) ON DELETE CASCADE'
+ ',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
+ ',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=32)'
',request_timestamp INT8 NOT NULL'
',coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)'
',coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)'
@@ -496,7 +497,7 @@ BEGIN
EXECUTE FORMAT (
'ALTER TABLE reserves_open_deposits_' || partition_suffix || ' '
'ADD CONSTRAINT reserves_open_deposits_' || partition_suffix || '_coin_unique '
- 'PRIMARY KEY (coin_pub,reserve_pub)'
+ 'PRIMARY KEY (coin_pub,coin_sig)'
);
END
$$;
@@ -1749,6 +1750,9 @@ BEGIN
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
',close_val INT8 NOT NULL'
',close_frac INT4 NOT NULL'
+ ',close_fee_val INT8 NOT NULL'
+ ',close_fee_frac INT4 NOT NULL'
+ ',payto_uri VARCHAR NOT NULL'
',PRIMARY KEY (reserve_pub,close_timestamp)'
') %s ;'
,table_name
diff --git a/src/exchangedb/pg_do_reserve_open.c b/src/exchangedb/pg_do_reserve_open.c
new file mode 100644
index 00000000..bd4f7240
--- /dev/null
+++ b/src/exchangedb/pg_do_reserve_open.c
@@ -0,0 +1,73 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_do_reserve_open.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_do_reserve_open.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_reserve_open (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *total_paid,
+ uint32_t min_purse_limit,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ struct GNUNET_TIME_Timestamp desired_expiration,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *open_fee,
+ struct TALER_Amount *open_cost,
+ struct GNUNET_TIME_Timestamp *final_expiration)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ TALER_PQ_query_param_amount (total_paid),
+ GNUNET_PQ_query_param_uint32 (&min_purse_limit),
+ GNUNET_PQ_query_param_auto_from_type (reserve_sig),
+ GNUNET_PQ_query_param_timestamp (&desired_expiration),
+ GNUNET_PQ_query_param_timestamp (&now),
+ TALER_PQ_query_param_amount (open_fee),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_AMOUNT ("open_cost",
+ open_cost),
+ GNUNET_PQ_result_spec_timestamp ("final_expiration",
+ final_expiration),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "do_reserve_open",
+ "SELECT "
+ " open_cost_val"
+ ",open_cost_frac"
+ ",final_expiration"
+ " FROM exchange_do_reserve_open"
+ " ($1,$2,$3,$4,$5,$6,$7,$8,$9);");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "do_reserve_open",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_do_reserve_open.h b/src/exchangedb/pg_do_reserve_open.h
new file mode 100644
index 00000000..aeef59eb
--- /dev/null
+++ b/src/exchangedb/pg_do_reserve_open.h
@@ -0,0 +1,55 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_do_reserve_open.h
+ * @brief implementation of the do_reserve_open function
+ * @author Christian Grothoff
+ */
+#ifndef PG_DO_RESERVE_OPEN_H
+#define PG_DO_RESERVE_OPEN_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Insert reserve close operation into database.
+ *
+ * @param cls closure
+ * @param reserve_pub which reserve is this about?
+ * @param execution_date when did we perform the transfer?
+ * @param receiver_account to which account do we transfer, in payto://-format
+ * @param wtid identifier for the wire transfer
+ * @param amount_with_fee amount we charged to the reserve
+ * @param closing_fee how high is the closing fee
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_do_reserve_open (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *total_paid,
+ uint32_t min_purse_limit,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ struct GNUNET_TIME_Timestamp desired_expiration,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *open_fee,
+ struct TALER_Amount *open_cost,
+ struct GNUNET_TIME_Timestamp *final_expiration);
+
+
+#endif
diff --git a/src/exchangedb/pg_helper.h b/src/exchangedb/pg_helper.h
new file mode 100644
index 00000000..e0a4be49
--- /dev/null
+++ b/src/exchangedb/pg_helper.h
@@ -0,0 +1,149 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_helper.h
+ * @brief shared internal definitions for postgres DB plugin
+ * @author Christian Grothoff
+ */
+#ifndef PG_HELPER_H
+#define PG_HELPER_H
+
+
+/**
+ * Type of the "cls" argument given to each of the functions in
+ * our API.
+ */
+struct PostgresClosure
+{
+
+ /**
+ * Our configuration.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Directory with SQL statements to run to create tables.
+ */
+ char *sql_dir;
+
+ /**
+ * After how long should idle reserves be closed?
+ */
+ struct GNUNET_TIME_Relative idle_reserve_expiration_time;
+
+ /**
+ * After how long should reserves that have seen withdraw operations
+ * be garbage collected?
+ */
+ struct GNUNET_TIME_Relative legal_reserve_expiration_time;
+
+ /**
+ * What delay should we introduce before ready transactions
+ * are actually aggregated?
+ */
+ struct GNUNET_TIME_Relative aggregator_shift;
+
+ /**
+ * Which currency should we assume all amounts to be in?
+ */
+ char *currency;
+
+ /**
+ * Our base URL.
+ */
+ char *exchange_url;
+
+ /**
+ * Postgres connection handle.
+ */
+ struct GNUNET_PQ_Context *conn;
+
+ /**
+ * Name of the current transaction, for debugging.
+ */
+ const char *transaction_name;
+
+ /**
+ * Counts how often we have established a fresh @e conn
+ * to the database. Used to re-prepare statements.
+ */
+ unsigned long long prep_gen;
+
+ /**
+ * Did we initialize the prepared statements
+ * for this session? (To be replaced with @e prep_gen.)
+ */
+ bool init;
+
+};
+
+
+/**
+ * Prepares SQL statement @a sql under @a name for
+ * connection @a pg once.
+ * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure.
+ *
+ * @param pg a `struct PostgresClosure`
+ * @param name name to prepare the statement under
+ * @param sql actual SQL text
+ */
+#define PREPARE(pg,name,sql) \
+ do { \
+ static unsigned long long prep_cnt; \
+ \
+ if (prep_cnt < pg->prep_gen) \
+ { \
+ struct GNUNET_PQ_PreparedStatement ps[] = { \
+ GNUNET_PQ_make_prepare (name, sql, 0), \
+ GNUNET_PQ_PREPARED_STATEMENT_END \
+ }; \
+ \
+ if (GNUNET_OK != \
+ GNUNET_PQ_prepare_statements (pg->conn, \
+ ps)) \
+ { \
+ GNUNET_break (0); \
+ return GNUNET_DB_STATUS_HARD_ERROR; \
+ } \
+ prep_cnt = pg->prep_gen; \
+ } \
+ } while (0)
+
+
+/**
+ * Wrapper macro to add the currency from the plugin's state
+ * when fetching amounts from the database.
+ *
+ * @param field name of the database field to fetch amount from
+ * @param[out] amountp pointer to amount to set
+ */
+#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
+ field,pg->currency,amountp)
+
+
+/**
+ * Wrapper macro to add the currency from the plugin's state
+ * when fetching amounts from the database. NBO variant.
+ *
+ * @param field name of the database field to fetch amount from
+ * @param[out] amountp pointer to amount to set
+ */
+#define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, \
+ amountp) TALER_PQ_result_spec_amount_nbo ( \
+ field,pg->currency,amountp)
+
+
+#endif
diff --git a/src/exchangedb/pg_insert_close_request.c b/src/exchangedb/pg_insert_close_request.c
new file mode 100644
index 00000000..43ca944f
--- /dev/null
+++ b/src/exchangedb/pg_insert_close_request.c
@@ -0,0 +1,67 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_insert_close_request.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_close_request.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_close_request (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *payto_uri,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_Amount *balance,
+ const struct TALER_Amount *closing_fee)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_timestamp (&request_timestamp),
+ GNUNET_PQ_query_param_auto_from_type (reserve_sig),
+ TALER_PQ_query_param_amount (balance),
+ TALER_PQ_query_param_amount (closing_fee),
+ GNUNET_PQ_query_param_string (payto_uri),
+ GNUNET_PQ_query_param_end
+ };
+
+ PREPARE (pg,
+ "insert_account_close",
+ "INSERT INTO close_requests"
+ "(reserve_pub"
+ ",close_timestamp"
+ ",reserve_sig"
+ ",close_val"
+ ",close_frac,"
+ ",close_fee_val"
+ ",close_fee_frac"
+ ",payto_uri"
+ ")"
+ "VALUES ($1, $2, $3, $4, $5, $6, $7)"
+ " ON CONFLICT DO NOTHING;");
+ return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+ "insert_account_close",
+ params);
+}
diff --git a/src/exchangedb/pg_insert_close_request.h b/src/exchangedb/pg_insert_close_request.h
new file mode 100644
index 00000000..c014a10b
--- /dev/null
+++ b/src/exchangedb/pg_insert_close_request.h
@@ -0,0 +1,52 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_insert_close_request.h
+ * @brief implementation of the insert_close_request function
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_CLOSE_REQUEST_H
+#define PG_INSERT_CLOSE_REQUEST_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Function called to initiate closure of an account.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param reserve_pub public key of the account to close
+ * @param payto_uri where to wire the funds
+ * @param reserve_sig signature affiming that the account is to be closed
+ * @param request_timestamp time of the close request (client-side?)
+ * @param balance final balance in the reserve
+ * @param closing_fee closing fee to charge
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_close_request (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *payto_uri,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_Amount *balance,
+ const struct TALER_Amount *closing_fee);
+
+
+#endif
diff --git a/src/exchangedb/pg_insert_reserve_open_deposit.c b/src/exchangedb/pg_insert_reserve_open_deposit.c
new file mode 100644
index 00000000..8bf70e7b
--- /dev/null
+++ b/src/exchangedb/pg_insert_reserve_open_deposit.c
@@ -0,0 +1,66 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_insert_reserve_open_deposit.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_reserve_open_deposit.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_reserve_open_deposit (
+ void *cls,
+ const struct TALER_CoinPublicInfo *cpi,
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ uint64_t known_coin_id,
+ const struct TALER_Amount *coin_total,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ bool *insufficient_funds)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (&cpi->coin_pub),
+ GNUNET_PQ_query_param_uint64 (&known_coin_id),
+ GNUNET_PQ_query_param_auto_from_type (coin_sig),
+ GNUNET_PQ_query_param_auto_from_type (reserve_sig),
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ TALER_PQ_query_param_amount (coin_total),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("out_insufficient_funds",
+ insufficient_funds),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "insert_reserve_open_deposit",
+ "SELECT "
+ " out_insufficient_funds"
+ " FROM exchange_do_reserve_open_deposit"
+ " ($1,$2,$3,$4,$5,$6,$7);");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "insert_reserve_open_deposit",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_insert_reserve_open_deposit.h b/src/exchangedb/pg_insert_reserve_open_deposit.h
new file mode 100644
index 00000000..7eb2fe09
--- /dev/null
+++ b/src/exchangedb/pg_insert_reserve_open_deposit.h
@@ -0,0 +1,54 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_insert_reserve_open_deposit.h
+ * @brief implementation of the insert_reserve_open_deposit function
+ * @author Christian Grothoff
+ */
+#ifndef PG_INSERT_RESERVE_OPEN_DEPOSIT_H
+#define PG_INSERT_RESERVE_OPEN_DEPOSIT_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Insert reserve open coin deposit data into database.
+ * Subtracts the @a coin_total from the coin's balance.
+ *
+ * @param cls closure
+ * @param cpi public information about the coin
+ * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT
+ * @param known_coin_id ID of the coin in the known_coins table
+ * @param coin_total amount to be spent of the coin (including deposit fee)
+ * @param reserve_sig signature by the reserve affirming the open operation
+ * @param reserve_pub public key of the reserve being opened
+ * @param[out] insufficient_funds set to true if the coin's balance is insufficient, otherwise to false
+ * @return transaction status code, 0 if operation is already in the DB
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_insert_reserve_open_deposit (
+ void *cls,
+ const struct TALER_CoinPublicInfo *cpi,
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ uint64_t known_coin_id,
+ const struct TALER_Amount *coin_total,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ bool *insufficient_funds);
+
+#endif
diff --git a/src/exchangedb/pg_iterate_kyc_reference.c b/src/exchangedb/pg_iterate_kyc_reference.c
new file mode 100644
index 00000000..772c51e2
--- /dev/null
+++ b/src/exchangedb/pg_iterate_kyc_reference.c
@@ -0,0 +1,129 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_iterate_kyc_reference.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_iterate_kyc_reference.h"
+#include "pg_helper.h"
+
+
+/**
+ * Closure for #iterate_kyc_reference_cb()
+ */
+struct IteratorContext
+{
+ /**
+ * Function to call with the results.
+ */
+ TALER_EXCHANGEDB_LegitimizationProcessCallback cb;
+
+ /**
+ * Closure to pass to @e cb
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+};
+
+
+/**
+ * Helper function for #TEH_PG_iterate_kyc_reference().
+ * Calls the callback with each denomination key.
+ *
+ * @param cls a `struct IteratorContext`
+ * @param result db results
+ * @param num_results number of results in @a result
+ */
+static void
+iterate_kyc_reference_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct IteratorContext *ic = cls;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ char *kyc_provider_section_name;
+ char *provider_user_id;
+ char *legitimization_id;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_string ("provider_section",
+ &kyc_provider_section_name),
+ GNUNET_PQ_result_spec_string ("provider_user_id",
+ &provider_user_id),
+ GNUNET_PQ_result_spec_string ("provider_legitimization_id",
+ &legitimization_id),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ic->cb (ic->cb_cls,
+ kyc_provider_section_name,
+ provider_user_id,
+ legitimization_id);
+ GNUNET_PQ_cleanup_result (rs);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_iterate_kyc_reference (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_LegitimizationProcessCallback lpc,
+ void *lpc_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_end
+ };
+ struct IteratorContext ic = {
+ .cb = lpc,
+ .cb_cls = lpc_cls,
+ .pg = pg
+ };
+
+ PREPARE (pg,
+ "iterate_kyc_reference",
+ "SELECT "
+ " provider_section"
+ ",provider_user_id"
+ ",provider_legitimization_id"
+ " FROM legitimization_processes"
+ " WHERE h_payto=$1;");
+ return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+ "iterate_kyc_reference",
+ params,
+ &iterate_kyc_reference_cb,
+ &ic);
+}
diff --git a/src/exchangedb/pg_iterate_kyc_reference.h b/src/exchangedb/pg_iterate_kyc_reference.h
new file mode 100644
index 00000000..0242fdcf
--- /dev/null
+++ b/src/exchangedb/pg_iterate_kyc_reference.h
@@ -0,0 +1,46 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_iterate_kyc_reference.h
+ * @brief implementation of the iterate_kyc_reference function
+ * @author Christian Grothoff
+ */
+#ifndef PG_ITERATE_KYC_REFERENCE_H
+#define PG_ITERATE_KYC_REFERENCE_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Call us on KYC legitimization processes satisfied and not expired for the
+ * given account.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param h_payto account identifier
+ * @param lpc function to call for each satisfied KYC legitimization process
+ * @param lpc_cls closure for @a lpc
+ * @return transaction status code
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_iterate_kyc_reference (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_LegitimizationProcessCallback lpc,
+ void *lpc_cls);
+
+#endif
diff --git a/src/exchangedb/pg_iterate_reserve_close_info.c b/src/exchangedb/pg_iterate_reserve_close_info.c
new file mode 100644
index 00000000..f1b2d452
--- /dev/null
+++ b/src/exchangedb/pg_iterate_reserve_close_info.c
@@ -0,0 +1,129 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_iterate_reserve_close_info.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_insert_reserve_open_deposit.h"
+#include "pg_helper.h"
+
+/**
+ * Closure for #iterate_reserve_close_info_cb()
+ */
+struct IteratorContext
+{
+ /**
+ * Function to call with the results.
+ */
+ TALER_EXCHANGEDB_KycAmountCallback cb;
+
+ /**
+ * Closure to pass to @e cb
+ */
+ void *cb_cls;
+
+ /**
+ * Plugin context.
+ */
+ struct PostgresClosure *pg;
+};
+
+
+/**
+ * Helper function for #TEH_PG_iterate_reserve_close_info().
+ * Calls the callback with each denomination key.
+ *
+ * @param cls a `struct IteratorContext`
+ * @param result db results
+ * @param num_results number of results in @a result
+ */
+static void
+iterate_reserve_close_info_cb (void *cls,
+ PGresult *result,
+ unsigned int num_results)
+{
+ struct IteratorContext *ic = cls;
+ struct PostgresClosure *pg = ic->pg;
+
+ for (unsigned int i = 0; i<num_results; i++)
+ {
+ struct TALER_Amount amount;
+ struct GNUNET_TIME_Absolute ts;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_absolute_time ("execution_date",
+ &ts),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
+ &amount),
+ GNUNET_PQ_result_spec_end
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_PQ_extract_result (result,
+ rs,
+ i))
+ {
+ GNUNET_break (0);
+ return;
+ }
+ ic->cb (ic->cb_cls,
+ &amount,
+ ts);
+ }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_iterate_reserve_close_info (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ struct GNUNET_TIME_Absolute time_limit,
+ TALER_EXCHANGEDB_KycAmountCallback kac,
+ void *kac_cls)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (h_payto),
+ GNUNET_PQ_query_param_absolute_time (&time_limit),
+ GNUNET_PQ_query_param_end
+ };
+ struct IteratorContext ic = {
+ .cb = kac,
+ .cb_cls = kac_cls,
+ .pg = pg
+ };
+
+ PREPARE (pg,
+ "iterate_reserve_close_info",
+ "SELECT"
+ " amount_val"
+ ",amount_frac"
+ ",execution_date"
+ " FROM reserves_close"
+ " WHERE wire_target_h_payto=$1"
+ " AND execution_date >= $2"
+ " ORDER BY execution_date DESC");
+ return GNUNET_PQ_eval_prepared_multi_select (
+ pg->conn,
+ "iterate_reserve_close_info",
+ params,
+ &iterate_reserve_close_info_cb,
+ &ic);
+}
diff --git a/src/exchangedb/pg_iterate_reserve_close_info.h b/src/exchangedb/pg_iterate_reserve_close_info.h
new file mode 100644
index 00000000..a7e7c8d4
--- /dev/null
+++ b/src/exchangedb/pg_iterate_reserve_close_info.h
@@ -0,0 +1,50 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_iterate_reserve_close_info.h
+ * @brief implementation of the iterate_reserve_close_info function
+ * @author Christian Grothoff
+ */
+#ifndef PG_ITERATE_RESERVE_CLOSE_INFO_H
+#define PG_ITERATE_RESERVE_CLOSE_INFO_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Select information needed for KYC checks on reserve close: historic
+ * reserve closures going to the same account.
+ *
+ * @param cls closure
+ * @param h_payto which target account is this about?
+ * @param h_payto account identifier
+ * @param time_limit oldest transaction that could be relevant
+ * @param kac function to call for each applicable amount, in reverse chronological order (or until @a kac aborts by returning anything except #GNUNET_OK).
+ * @param kac_cls closure for @a kac
+ * @return transaction status code, @a kac aborting with #GNUNET_NO is not an error
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_iterate_reserve_close_info (
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ struct GNUNET_TIME_Absolute time_limit,
+ TALER_EXCHANGEDB_KycAmountCallback kac,
+ void *kac_cls);
+
+
+#endif
diff --git a/src/exchangedb/pg_select_reserve_close_info.c b/src/exchangedb/pg_select_reserve_close_info.c
new file mode 100644
index 00000000..0b373b7b
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_close_info.c
@@ -0,0 +1,61 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_select_reserve_close_info.c
+ * @brief Low-level (statement-level) Postgres database access for the exchange
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_error_codes.h"
+#include "taler_dbevents.h"
+#include "taler_pq_lib.h"
+#include "pg_select_reserve_close_info.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_close_info (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ struct TALER_Amount *balance,
+ char **payto_uri)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ TALER_PQ_RESULT_SPEC_AMOUNT ("close",
+ balance),
+ GNUNET_PQ_result_spec_string ("payto_uri",
+ payto_uri),
+ GNUNET_PQ_result_spec_end
+ };
+
+ PREPARE (pg,
+ "select_reserve_close_info",
+ "SELECT "
+ " close_frac"
+ ",close_val"
+ ",payto_uri"
+ " FROM close_requests"
+ " WHERE reserve_pub=$1;");
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "select_reserve_close_info",
+ params,
+ rs);
+}
diff --git a/src/exchangedb/pg_select_reserve_close_info.h b/src/exchangedb/pg_select_reserve_close_info.h
new file mode 100644
index 00000000..2b90ffd0
--- /dev/null
+++ b/src/exchangedb/pg_select_reserve_close_info.h
@@ -0,0 +1,49 @@
+/*
+ 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 <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file pg_select_reserve_close_info.h
+ * @brief implementation of the select_reserve_close_info function
+ * @author Christian Grothoff
+ */
+#ifndef PG_SELECT_RESERVE_CLOSE_INFO_H
+#define PG_SELECT_RESERVE_CLOSE_INFO_H
+
+#include "taler_util.h"
+#include "taler_json_lib.h"
+#include "taler_exchangedb_plugin.h"
+
+
+/**
+ * Select information needed to see if we can close
+ * a reserve.
+ *
+ * @param cls closure
+ * @param reserve_pub which reserve is this about?
+ * @param[out] balance current reserve balance
+ * @param[out] payto_uri set to URL of account that
+ * originally funded the reserve;
+ * could be set to NULL if not known
+ * @return transaction status code, 0 if reserve unknown
+ */
+enum GNUNET_DB_QueryStatus
+TEH_PG_select_reserve_close_info (
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ struct TALER_Amount *balance,
+ char **payto_uri);
+
+
+#endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index e3da5216..d2e2eb5d 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -29,6 +29,12 @@
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_exchangedb_plugin.h"
+#include "pg_helper.h"
+#include "pg_insert_close_request.h"
+#include "pg_insert_reserve_open_deposit.h"
+#include "pg_iterate_kyc_reference.h"
+#include "pg_iterate_reserve_close_info.h"
+#include "pg_select_reserve_close_info.h"
#include <poll.h>
#include <pthread.h>
#include <libpq-fe.h>
@@ -42,26 +48,6 @@
*/
#define AUTO_EXPLAIN 1
-/**
- * Wrapper macro to add the currency from the plugin's state
- * when fetching amounts from the database.
- *
- * @param field name of the database field to fetch amount from
- * @param[out] amountp pointer to amount to set
- */
-#define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) TALER_PQ_result_spec_amount ( \
- field,pg->currency,amountp)
-
-/**
- * Wrapper macro to add the currency from the plugin's state
- * when fetching amounts from the database. NBO variant.
- *
- * @param field name of the database field to fetch amount from
- * @param[out] amountp pointer to amount to set
- */
-#define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, \
- amountp) TALER_PQ_result_spec_amount_nbo ( \
- field,pg->currency,amountp)
/**
* Log a really unexpected PQ error with all the details we can get hold of.
@@ -82,69 +68,6 @@
/**
- * Type of the "cls" argument given to each of the functions in
- * our API.
- */
-struct PostgresClosure
-{
-
- /**
- * Our configuration.
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
-
- /**
- * Directory with SQL statements to run to create tables.
- */
- char *sql_dir;
-
- /**
- * After how long should idle reserves be closed?
- */
- struct GNUNET_TIME_Relative idle_reserve_expiration_time;
-
- /**
- * After how long should reserves that have seen withdraw operations
- * be garbage collected?
- */
- struct GNUNET_TIME_Relative legal_reserve_expiration_time;
-
- /**
- * What delay should we introduce before ready transactions
- * are actually aggregated?
- */
- struct GNUNET_TIME_Relative aggregator_shift;
-
- /**
- * Which currency should we assume all amounts to be in?
- */
- char *currency;
-
- /**
- * Our base URL.
- */
- char *exchange_url;
-
- /**
- * Postgres connection handle.
- */
- struct GNUNET_PQ_Context *conn;
-
- /**
- * Name of the current transaction, for debugging.
- */
- const char *transaction_name;
-
- /**
- * Did we initialize the prepared statements
- * for this session?
- */
- bool init;
-
-};
-
-
-/**
* Drop all Taler tables. This should only be used by testcases.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
@@ -4467,15 +4390,7 @@ prepare_statements (struct PostgresClosure *pg)
" FROM exchange_do_history_request"
" ($1, $2, $3, $4, $5)",
5),
- /* Used in #postgres_insert_close_request() */
- GNUNET_PQ_make_prepare (
- "call_account_close",
- "SELECT "
- " out_final_balance_val"
- ",out_final_balance_frac"
- " FROM exchange_do_close_request"
- " ($1, $2, $3)",
- 3),
+
/* Used in #postgres_insert_kyc_requirement_for_account() */
GNUNET_PQ_make_prepare (
"insert_legitimization_requirement",
@@ -4674,6 +4589,7 @@ internal_setup (struct PostgresClosure *pg,
NULL);
if (NULL == db_conn)
return GNUNET_SYSERR;
+ pg->prep_gen++;
pg->conn = db_conn;
}
if (NULL == pg->transaction_name)
@@ -16237,44 +16153,6 @@ postgres_insert_history_request (
/**
- * Function called to initiate closure of an account.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param reserve_pub public key of the account to close
- * @param reserve_sig signature affiming that the account is to be closed
- * @param request_timestamp time of the close request (client-side?)
- * @param[out] final_balance set to the final balance in the account that will be wired back to the origin account
- * @return transaction status code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_close_request (
- void *cls,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- const struct TALER_ReserveSignatureP *reserve_sig,
- struct GNUNET_TIME_Timestamp request_timestamp,
- struct TALER_Amount *final_balance)
-{
- struct PostgresClosure *pg = cls;
- struct GNUNET_PQ_QueryParam params[] = {
- GNUNET_PQ_query_param_auto_from_type (reserve_pub),
- GNUNET_PQ_query_param_timestamp (&request_timestamp),
- GNUNET_PQ_query_param_auto_from_type (reserve_sig),
- GNUNET_PQ_query_param_end
- };
- struct GNUNET_PQ_ResultSpec rs[] = {
- TALER_PQ_RESULT_SPEC_AMOUNT ("out_final_balance",
- final_balance),
- GNUNET_PQ_result_spec_end
- };
-
- return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
- "call_account_close",
- params,
- rs);
-}
-
-
-/**
* Function called to persist a request to drain profits.
*
* @param cls the @e cls of this struct with the plugin-specific state
@@ -17389,8 +17267,6 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_purse_merge;
plugin->insert_history_request
= &postgres_insert_history_request;
- plugin->insert_close_request
- = &postgres_insert_close_request;
plugin->insert_drain_profit
= &postgres_insert_drain_profit;
plugin->profit_drains_get_pending
@@ -17419,6 +17295,16 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_aggregation_amounts_for_kyc_check;
plugin->select_merge_amounts_for_kyc_check
= &postgres_select_merge_amounts_for_kyc_check;
+ /* NEW style, sort alphabetically! */
+ plugin->insert_close_request
+ = &TEH_PG_insert_close_request;
+ plugin->iterate_reserve_close_info
+ = &TEH_PG_iterate_reserve_close_info;
+ plugin->iterate_kyc_reference
+ = &TEH_PG_iterate_kyc_reference;
+ plugin->select_reserve_close_info
+ = &TEH_PG_select_reserve_close_info;
+
return plugin;
}
diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql
index 1940fa7b..8407f20c 100644
--- a/src/exchangedb/procedures.sql
+++ b/src/exchangedb/procedures.sql
@@ -1778,7 +1778,7 @@ ELSE
my_amount_val = my_amount_val + my_amount_frac / 100000000;
my_amount_frac = my_amount_frac % 100000000;
- UPDATE reserves
+ UPDATE exchange.reserves
SET
current_balance_frac=current_balance_frac+my_amount_frac
- CASE
@@ -1795,7 +1795,7 @@ ELSE
WHERE reserve_pub=in_reserve_pub;
-- ... and mark purse as finished.
- UPDATE purse_requests
+ UPDATE exchange.purse_requests
SET finished=true
WHERE purse_pub=in_purse_pub;
END IF;
@@ -1881,7 +1881,7 @@ THEN
out_no_funds=TRUE;
RETURN;
END IF;
- UPDATE reserves
+ UPDATE exchange.reserves
SET purses_active=purses_active+1
WHERE reserve_pub=in_reserve_pub
AND purses_active < purses_allowed;
@@ -1901,7 +1901,7 @@ ELSE
RETURN;
END IF;
ELSE
- UPDATE reserves
+ UPDATE exchange.reserves
SET
current_balance_frac=current_balance_frac-in_purse_fee_frac
+ CASE
@@ -1993,7 +1993,7 @@ THEN
RETURN;
END IF;
-UPDATE purse_requests
+UPDATE exchange.purse_requests
SET refunded=TRUE,
finished=TRUE
WHERE purse_pub=my_purse_pub;
@@ -2011,7 +2011,7 @@ FOR my_deposit IN
FROM exchange.purse_deposits
WHERE purse_pub = my_purse_pub
LOOP
- UPDATE known_coins SET
+ UPDATE exchange.known_coins SET
remaining_frac=remaining_frac+my_deposit.amount_with_fee_frac
- CASE
WHEN remaining_frac+my_deposit.amount_with_fee_frac >= 100000000
@@ -2071,7 +2071,7 @@ BEGIN
out_idempotent=FALSE;
-- Update reserve balance.
- UPDATE reserves
+ UPDATE exchange.reserves
SET
current_balance_frac=current_balance_frac-in_history_fee_frac
+ CASE
@@ -2103,57 +2103,76 @@ BEGIN
END $$;
-CREATE OR REPLACE FUNCTION exchange_do_close_request(
- IN in_reserve_pub BYTEA,
- IN in_close_timestamp INT8,
+CREATE OR REPLACE FUNCTION exchange_do_reserve_open_deposit(
+ IN in_coin_pub BYTEA,
+ IN in_known_coin_id INT8,
+ IN in_coin_sig BYTEA,
IN in_reserve_sig BYTEA,
- OUT out_final_balance_val INT8,
- OUT out_final_balance_frac INT4,
- OUT out_balance_ok BOOLEAN,
- OUT out_conflict BOOLEAN)
+ IN in_reserve_pub BYTEA,
+ IN in_coin_total_val INT8,
+ IN in_coin_total_frac INT4,
+ OUT out_insufficient_funds BOOLEAN)
LANGUAGE plpgsql
AS $$
BEGIN
- SELECT
- current_balance_val
- ,current_balance_frac
- INTO
- out_final_balance_val
- ,out_final_balance_frac
- FROM exchange.reserves
- WHERE reserve_pub=in_reserve_pub;
+INSERT INTO exchange.reserves_open_deposits
+ (reserve_sig
+ ,reserve_pub
+ ,request_timestamp
+ ,coin_pub
+ ,coin_sig
+ ,contribution_val
+ ,contribution_frac
+ )
+ VALUES
+ (in_reserve_sig
+ ,in_reserve_pub
+ ,in_request_timestamp
+ ,in_coin_pub
+ ,in_coin_sig
+ ,in_coin_total_val
+ ,in_coin_total_frac)
+ ON CONFLICT DO NOTHING;
- IF NOT FOUND
- THEN
- out_final_balance_val=0;
- out_final_balance_frac=0;
- out_balance_ok = FALSE;
- out_conflict = FALSE;
- END IF;
+IF NOT FOUND
+THEN
+ -- Idempotent request known, return success.
+ out_insufficient_funds=FALSE;
+ RETURN;
+END IF;
- INSERT INTO exchange.close_requests
- (reserve_pub
- ,close_timestamp
- ,reserve_sig
- ,close_val
- ,close_frac)
- VALUES
- (in_reserve_pub
- ,in_close_timestamp
- ,in_reserve_sig
- ,out_final_balance_val
- ,out_final_balance_frac)
- ON CONFLICT DO NOTHING;
- out_conflict = NOT FOUND;
- UPDATE reserves SET
- current_balance_val=0
- ,current_balance_frac=0
- WHERE reserve_pub=in_reserve_pub;
- out_balance_ok = TRUE;
+-- Check and update balance of the coin.
+UPDATE exchange.known_coins
+ SET
+ remaining_frac=remaining_frac-in_coin_total_frac
+ + CASE
+ WHEN remaining_frac < in_coin_total_frac
+ THEN 100000000
+ ELSE 0
+ END,
+ remaining_val=remaining_val-in_coin_total_val
+ - CASE
+ WHEN remaining_frac < in_coin_total_frac
+ THEN 1
+ ELSE 0
+ END
+ WHERE coin_pub=in_coin_pub
+ AND ( (remaining_val > in_coin_total_val) OR
+ ( (remaining_frac >= in_coin_total_frac) AND
+ (remaining_val >= in_coin_total_val) ) );
-END $$;
+IF NOT FOUND
+THEN
+ -- Insufficient balance.
+ out_insufficient_funds=TRUE;
+ RETURN;
+END IF;
+-- Everything fine, return success!
+out_insufficient_funds=FALSE;
+
+END $$;
COMMIT;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index b5ae2083..a861563b 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -3107,16 +3107,14 @@ TALER_wallet_reserve_open_verify (
* Sign to deposit coin to pay for keeping a reserve open.
*
* @param coin_contribution how much the coin should contribute
- * @param reserve_pub public key of the reserve
- * @param request_timestamp time of the open request
+ * @param reserve_sig signature over the reserve open operation
* @param coin_priv private key of the coin
* @param[out] coin_sig signature by the coin
*/
void
TALER_wallet_reserve_open_deposit_sign (
const struct TALER_Amount *coin_contribution,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_CoinSpendSignatureP *coin_sig);
@@ -3125,8 +3123,7 @@ TALER_wallet_reserve_open_deposit_sign (
* Verify signature that deposits coin to pay for keeping a reserve open.
*
* @param coin_contribution how much the coin should contribute
- * @param reserve_pub public key of the reserve
- * @param request_timestamp time of the open request
+ * @param reserve_sig signature over the reserve open operation
* @param coin_pub public key of the coin
* @param coin_sig signature by the coin
* @return #GNUNET_OK if the signature is valid
@@ -3134,8 +3131,7 @@ TALER_wallet_reserve_open_deposit_sign (
enum GNUNET_GenericReturnValue
TALER_wallet_reserve_open_deposit_verify (
const struct TALER_Amount *coin_contribution,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig);
@@ -3223,7 +3219,7 @@ TALER_wallet_reserve_attest_request_sign (
*
* @param request_timestamp when was the request created
* @param details which attributes are requested
- * @param reserve_priv private key of the reserve
+ * @param reserve_pub public key of the reserve
* @param reserve_sig where to store the signature
* @return #GNUNET_OK if the signature is valid
*/
@@ -4291,7 +4287,6 @@ TALER_exchange_online_reserve_attest_details_sign (
* Verify signature by exchange affirming that a reserve
* has had certain attributes verified via KYC.
*
- * @param scb function to call to create the signature
* @param attest_timestamp our time
* @param expiration_time when does the KYC data expire
* @param reserve_pub for which reserve are attributes attested
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 261ffb18..0ce8ff47 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -878,6 +878,25 @@ typedef void
/**
+ * Function called on all legitimization operations
+ * we have performed for the given account so far
+ * (and that have not yet expired).
+ *
+ * @param cls closure
+ * @param kyc_provider_section_name configuration section
+ * of the respective KYC process
+ * @param provider_user_id UID at a provider (can be NULL)
+ * @param legi_id legitimization process ID (can be NULL)
+ */
+typedef void
+(*TALER_EXCHANGEDB_LegitimizationProcessCallback)(
+ void *cls,
+ const char *kyc_provider_section_name,
+ const char *provider_user_id,
+ const char *legi_id);
+
+
+/**
* Function called with information about the exchange's auditors.
*
* @param cls closure with a `struct TEH_KeyStateHandle *`
@@ -4044,6 +4063,98 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Insert reserve open coin deposit data into database.
+ * Subtracts the @a coin_total from the coin's balance.
+ *
+ * @param cls closure
+ * @param cpi public information about the coin
+ * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT
+ * @param known_coin_id ID of the coin in the known_coins table
+ * @param coin_total amount to be spent of the coin (including deposit fee)
+ * @param reserve_sig signature by the reserve affirming the open operation
+ * @param reserve_pub public key of the reserve being opened
+ * @param[out] insufficient_funds set to true if the coin's balance is insufficient, otherwise to false
+ * @return transaction status code, 0 if operation is already in the DB
+ */
+ enum GNUNET_DB_QueryStatus
+ (*insert_reserve_open_deposit)(
+ void *cls,
+ const struct TALER_CoinPublicInfo *cpi,
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ uint64_t known_coin_id,
+ const struct TALER_Amount *coin_total,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ bool *insufficient_funds);
+
+
+ /**
+ * Insert reserve close operation into database.
+ *
+ * @param cls closure
+ * @param reserve_pub which reserve is this about?
+ * @param execution_date when did we perform the transfer?
+ * @param receiver_account to which account do we transfer, in payto://-format
+ * @param wtid identifier for the wire transfer
+ * @param amount_with_fee amount we charged to the reserve
+ * @param closing_fee how high is the closing fee
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*do_reserve_open)(void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ const struct TALER_Amount *total_paid,
+ uint32_t min_purse_limit,
+ const struct TALER_ReserveSignatureP *reserve_sig,
+ struct GNUNET_TIME_Timestamp desired_expiration,
+ struct GNUNET_TIME_Timestamp now,
+ const struct TALER_Amount *open_fee,
+ struct TALER_Amount *open_cost,
+ struct GNUNET_TIME_Timestamp *final_expiration);
+
+
+ /**
+ * Select information needed to see if we can close
+ * a reserve.
+ *
+ * @param cls closure
+ * @param reserve_pub which reserve is this about?
+ * @param[out] balance current reserve balance
+ * @param[out] payto_uri set to URL of account that
+ * originally funded the reserve;
+ * could be set to NULL if not known
+ * @return transaction status code, 0 if reserve unknown
+ */
+ enum GNUNET_DB_QueryStatus
+ (*select_reserve_close_info)(
+ void *cls,
+ const struct TALER_ReservePublicKeyP *reserve_pub,
+ struct TALER_Amount *balance,
+ char **payto_uri);
+
+
+ /**
+ * Select information needed for KYC checks on reserve close: historic
+ * reserve closures going to the same account.
+ *
+ * @param cls closure
+ * @param h_payto which target account is this about?
+ * @param h_payto account identifier
+ * @param time_limit oldest transaction that could be relevant
+ * @param kac function to call for each applicable amount, in reverse chronological order (or until @a kac aborts by returning anything except #GNUNET_OK).
+ * @param kac_cls closure for @a kac
+ * @return transaction status code, @a kac aborting with #GNUNET_NO is not an error
+ */
+ enum GNUNET_DB_QueryStatus
+ (*iterate_reserve_close_info)(
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ struct GNUNET_TIME_Absolute time_limit,
+ TALER_EXCHANGEDB_KycAmountCallback kac,
+ void *kac_cls);
+
+
+ /**
* Insert reserve close operation into database.
*
* @param cls closure
@@ -5488,17 +5599,21 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the account to close
+ * @param payto_uri where to wire the funds
* @param reserve_sig signature affiming that the account is to be closed
* @param request_timestamp timestamp of the close request
- * @param[out] final_balance set to the final balance in the account that will be wired back to the origin account
+ * @param balance balance at the time of closing
+ * @param closing_fee closing fee to charge
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
(*insert_close_request)(void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
+ const char *payto_uri,
const struct TALER_ReserveSignatureP *reserve_sig,
struct GNUNET_TIME_Timestamp request_timestamp,
- struct TALER_Amount *final_balance);
+ const struct TALER_Amount *balance,
+ const struct TALER_Amount *closing_fee);
/**
@@ -5726,6 +5841,24 @@ struct TALER_EXCHANGEDB_Plugin
/**
+ * Call us on KYC legitimization processes satisfied and not expired for the
+ * given account.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param h_payto account identifier
+ * @param lpc function to call for each satisfied KYC legitimization process
+ * @param lpc_cls closure for @a lpc
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*iterate_kyc_reference)(
+ void *cls,
+ const struct TALER_PaytoHashP *h_payto,
+ TALER_EXCHANGEDB_LegitimizationProcessCallback lpc,
+ void *lpc_cls);
+
+
+ /**
* Call @a kac on withdrawn amounts after @a time_limit which are relevant
* for a KYC trigger for a the (debited) account identified by @a h_payto.
*
diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h
index 494f7787..7f4bf5b5 100644
--- a/src/include/taler_kyclogic_lib.h
+++ b/src/include/taler_kyclogic_lib.h
@@ -68,7 +68,12 @@ enum TALER_KYCLOGIC_KycTriggerEvent
/**
* Wallet balance exceeds threshold.
*/
- TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE = 3
+ TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE = 3,
+
+ /**
+ * Reserve is being closed by force.
+ */
+ TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE = 4
};
@@ -302,6 +307,27 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
/**
+ * Obtain attributes we collected about a user from a
+ * provider.
+ *
+ * @param provider_section configuration section of a
+ * provider that triggered KYC process for a user
+ * @param provider_user user ID of the user at the provider
+ * @param legitimization_id legitimizatin ID of a process
+ * of that user at the provider
+ * @param[out] attr_expiration set to when the @a attrs expire
+ * @param[out] attrs attributes we have about the user
+ * @return error code, #TALER_EC_NONE on success
+ */
+enum TALER_ErrorCode
+TALER_KYCLOGIC_user_to_attributes (const char *provider_section,
+ const char *provider_user_id,
+ const char *legitimization_id,
+ struct GNUNET_TIME_Timestamp *attr_expiration,
+ json_t **attrs);
+
+
+/**
* Obtain the provider logic for a given @a name.
*
* @param name name of the logic or provider section
diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c
index 96bf16aa..0c4a5112 100644
--- a/src/kyclogic/kyclogic_api.c
+++ b/src/kyclogic/kyclogic_api.c
@@ -183,6 +183,7 @@ TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s,
{ "deposit", TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT },
{ "merge", TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE },
{ "balance", TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE },
+ { "close", TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE },
{ NULL, 0 }
};
@@ -213,6 +214,8 @@ TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger)
return "merge";
case TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE:
return "balance";
+ case TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE:
+ return "close";
}
GNUNET_break (0);
return NULL;
@@ -1308,4 +1311,18 @@ TALER_KYCLOGIC_kyc_iterate_thresholds (
}
+enum TALER_ErrorCode
+TALER_KYCLOGIC_user_to_attributes (const char *provider_section,
+ const char *provider_user_id,
+ const char *legitimization_id,
+ struct GNUNET_TIME_Timestamp *attr_expiration,
+ json_t **attrs)
+{
+ GNUNET_break (0); // FIXME: not yet implemented!!!
+ *attrs = json_object ();
+ *attr_expiration = GNUNET_TIME_UNIT_ZERO_TS;
+ return TALER_EC_NONE;
+}
+
+
/* end of taler-exchange-httpd_kyc.c */
diff --git a/src/lib/exchange_api_reserves_attest.c b/src/lib/exchange_api_reserves_attest.c
index 5ccc18e5..a7a89a2e 100644
--- a/src/lib/exchange_api_reserves_attest.c
+++ b/src/lib/exchange_api_reserves_attest.c
@@ -131,8 +131,6 @@ handle_reserves_attest_ok (struct TALER_EXCHANGE_ReservesAttestHandle *rsh,
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
- // FIXME: verify exchange signature!
- // => needs crypto API first!
rsh->cb (rsh->cb_cls,
&rs);
rsh->cb = NULL;
diff --git a/src/lib/exchange_api_reserves_open.c b/src/lib/exchange_api_reserves_open.c
index eb63f8fe..08d267fb 100644
--- a/src/lib/exchange_api_reserves_open.c
+++ b/src/lib/exchange_api_reserves_open.c
@@ -393,6 +393,12 @@ TALER_EXCHANGE_reserves_open (
GNUNET_free (roh);
return NULL;
}
+ TALER_wallet_reserve_open_sign (reserve_contribution,
+ roh->ts,
+ expiration_time,
+ min_purses,
+ reserve_priv,
+ &roh->reserve_sig);
cpa = json_array ();
GNUNET_assert (NULL != cpa);
for (unsigned int i = 0; i<coin_payments_length; i++)
@@ -412,8 +418,7 @@ TALER_EXCHANGE_reserves_open (
achp = &ahac;
}
TALER_wallet_reserve_open_deposit_sign (&pd->amount,
- &roh->reserve_pub,
- roh->ts,
+ &roh->reserve_sig,
&pd->coin_priv,
&coin_sig);
GNUNET_CRYPTO_eddsa_key_get_public (&pd->coin_priv.eddsa_priv,
@@ -437,12 +442,6 @@ TALER_EXCHANGE_reserves_open (
json_array_append_new (cpa,
cp));
}
- TALER_wallet_reserve_open_sign (reserve_contribution,
- roh->ts,
- expiration_time,
- min_purses,
- reserve_priv,
- &roh->reserve_sig);
{
json_t *open_obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_timestamp ("request_timestamp",
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index 78d4cdce..256276ce 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -85,7 +85,9 @@ libtalertesting_la_SOURCES = \
testing_api_cmd_recoup_refresh.c \
testing_api_cmd_refund.c \
testing_api_cmd_refresh.c \
+ testing_api_cmd_reserve_attest.c \
testing_api_cmd_reserve_get.c \
+ testing_api_cmd_reserve_get_attestable.c \
testing_api_cmd_reserve_history.c \
testing_api_cmd_reserve_open.c \
testing_api_cmd_reserve_purse.c \
diff --git a/src/testing/testing_api_cmd_reserve_attest.c b/src/testing/testing_api_cmd_reserve_attest.c
index 8ed95924..5ed25c13 100644
--- a/src/testing/testing_api_cmd_reserve_attest.c
+++ b/src/testing/testing_api_cmd_reserve_attest.c
@@ -26,7 +26,6 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_testing_lib.h"
-
/**
* State for a "attest" CMD.
*/
@@ -44,11 +43,6 @@ struct AttestState
struct TALER_EXCHANGE_ReservesAttestHandle *rsh;
/**
- * Expected reserve balance.
- */
- const char *expected_balance;
-
- /**
* Private key of the reserve being analyzed.
*/
const struct TALER_ReservePrivateKeyP *reserve_priv;
@@ -59,6 +53,16 @@ struct AttestState
struct TALER_ReservePublicKeyP reserve_pub;
/**
+ * Array of attributes to request, of length @e attrs_len.
+ */
+ const char **attrs;
+
+ /**
+ * Length of the @e attrs array.
+ */
+ unsigned int attrs_len;
+
+ /**
* Expected HTTP response code.
*/
unsigned int expected_response_code;
@@ -78,12 +82,12 @@ struct AttestState
* @param rs HTTP response details
*/
static void
-reserve_attest_cb (void *cls,
- const struct TALER_EXCHANGE_ReserveAttest *rs)
+reserve_attest_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ReservePostAttestResult *rs)
{
struct AttestState *ss = cls;
struct TALER_TESTING_Interpreter *is = ss->is;
- struct TALER_Amount eb;
ss->rsh = NULL;
if (ss->expected_response_code != rs->hr.http_status)
@@ -104,6 +108,7 @@ reserve_attest_cb (void *cls,
TALER_TESTING_interpreter_next (is);
return;
}
+ /* FIXME: persist attestation... */
TALER_TESTING_interpreter_next (is);
}
@@ -147,6 +152,8 @@ attest_run (void *cls,
&ss->reserve_pub.eddsa_pub);
ss->rsh = TALER_EXCHANGE_reserves_attest (is->exchange,
ss->reserve_priv,
+ ss->attrs_len,
+ ss->attrs,
&reserve_attest_cb,
ss);
}
@@ -174,6 +181,7 @@ attest_cleanup (void *cls,
TALER_EXCHANGE_reserves_attest_cancel (ss->rsh);
ss->rsh = NULL;
}
+ GNUNET_free (ss->attrs);
GNUNET_free (ss);
}
@@ -185,12 +193,29 @@ TALER_TESTING_cmd_reserve_attest (const char *label,
...)
{
struct AttestState *ss;
+ unsigned int num_args;
+ const char *ea;
+ va_list ap;
+
+ num_args = 0;
+ va_start (ap, expected_response_code);
+ while (NULL != va_arg (ap, const char *))
+ num_args++;
+ va_end (ap);
GNUNET_assert (NULL != reserve_reference);
ss = GNUNET_new (struct AttestState);
ss->reserve_reference = reserve_reference;
- ss->expected_balance = expected_balance;
ss->expected_response_code = expected_response_code;
+ ss->attrs_len = num_args;
+ ss->attrs = GNUNET_new_array (num_args,
+ const char *);
+ num_args = 0;
+ va_start (ap, expected_response_code);
+ while (NULL != (ea = va_arg (ap, const char *)))
+ ss->attrs[num_args++] = ea;
+ va_end (ap);
+
{
struct TALER_TESTING_Command cmd = {
.cls = ss,
diff --git a/src/testing/testing_api_cmd_reserve_get_attestable.c b/src/testing/testing_api_cmd_reserve_get_attestable.c
index c0cae832..29c8f6a3 100644
--- a/src/testing/testing_api_cmd_reserve_get_attestable.c
+++ b/src/testing/testing_api_cmd_reserve_get_attestable.c
@@ -41,17 +41,17 @@ struct GetAttestableState
/**
* Handle to the "reserve get_attestable" operation.
*/
- struct TALER_EXCHANGE_ReservesGetAttestableHandle *rsh;
+ struct TALER_EXCHANGE_ReservesGetAttestHandle *rgah;
/**
- * Expected reserve balance.
+ * Expected attestable attributes.
*/
- const char *expected_balance;
+ const char **expected_attestables;
/**
- * Private key of the reserve being analyzed.
+ * Length of the @e expected_attestables array.
*/
- const struct TALER_ReservePrivateKeyP *reserve_priv;
+ unsigned int expected_attestables_length;
/**
* Public key of the reserve being analyzed.
@@ -78,14 +78,14 @@ struct GetAttestableState
* @param rs HTTP response details
*/
static void
-reserve_get_attestable_cb (void *cls,
- const struct TALER_EXCHANGE_ReserveGetAttestable *rs)
+reserve_get_attestable_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_ReserveGetAttestResult *rs)
{
struct GetAttestableState *ss = cls;
struct TALER_TESTING_Interpreter *is = ss->is;
- struct TALER_Amount eb;
- ss->rsh = NULL;
+ ss->rgah = NULL;
if (ss->expected_response_code != rs->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -104,6 +104,7 @@ reserve_get_attestable_cb (void *cls,
TALER_TESTING_interpreter_next (is);
return;
}
+ // FIXME: check returned list matches expectations!
TALER_TESTING_interpreter_next (is);
}
@@ -122,6 +123,8 @@ get_attestable_run (void *cls,
{
struct GetAttestableState *ss = cls;
const struct TALER_TESTING_Command *create_reserve;
+ const struct TALER_ReservePrivateKeyP *reserve_priv;
+ const struct TALER_ReservePublicKeyP *reserve_pub;
ss->is = is;
create_reserve
@@ -134,21 +137,31 @@ get_attestable_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
- if (GNUNET_OK !=
+ if (GNUNET_OK ==
TALER_TESTING_get_trait_reserve_priv (create_reserve,
- &ss->reserve_priv))
+ &reserve_priv))
{
- GNUNET_break (0);
- TALER_LOG_ERROR ("Failed to find reserve_priv for get_attestable query\n");
- TALER_TESTING_interpreter_fail (is);
- return;
+ GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+ &ss->reserve_pub.eddsa_pub);
}
- GNUNET_CRYPTO_eddsa_key_get_public (&ss->reserve_priv->eddsa_priv,
- &ss->reserve_pub.eddsa_pub);
- ss->rsh = TALER_EXCHANGE_reserves_get_attestable (is->exchange,
- ss->reserve_priv,
- &reserve_get_attestable_cb,
- ss);
+ else
+ {
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_reserve_pub (create_reserve,
+ &reserve_pub))
+ {
+ GNUNET_break (0);
+ TALER_LOG_ERROR (
+ "Failed to find reserve_priv for get_attestable query\n");
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ ss->reserve_pub = *reserve_pub;
+ }
+ ss->rgah = TALER_EXCHANGE_reserves_get_attestable (is->exchange,
+ &ss->reserve_pub,
+ &reserve_get_attestable_cb,
+ ss);
}
@@ -165,15 +178,16 @@ get_attestable_cleanup (void *cls,
{
struct GetAttestableState *ss = cls;
- if (NULL != ss->rsh)
+ if (NULL != ss->rgah)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
ss->is->ip,
cmd->label);
- TALER_EXCHANGE_reserves_get_attestable_cancel (ss->rsh);
- ss->rsh = NULL;
+ TALER_EXCHANGE_reserves_get_attestable_cancel (ss->rgah);
+ ss->rgah = NULL;
}
+ GNUNET_free (ss->expected_attestables);
GNUNET_free (ss);
}
@@ -185,12 +199,29 @@ TALER_TESTING_cmd_reserve_get_attestable (const char *label,
...)
{
struct GetAttestableState *ss;
+ va_list ap;
+ unsigned int num_expected;
+ const char *ea;
+
+ num_expected = 0;
+ va_start (ap, expected_response_code);
+ while (NULL != va_arg (ap, const char *))
+ num_expected++;
+ va_end (ap);
GNUNET_assert (NULL != reserve_reference);
ss = GNUNET_new (struct GetAttestableState);
ss->reserve_reference = reserve_reference;
- ss->expected_balance = expected_balance;
ss->expected_response_code = expected_response_code;
+ ss->expected_attestables_length = num_expected;
+ ss->expected_attestables = GNUNET_new_array (num_expected,
+ const char *);
+ num_expected = 0;
+ va_start (ap, expected_response_code);
+ while (NULL != (ea = va_arg (ap, const char *)))
+ ss->expected_attestables[num_expected++] = ea;
+ va_end (ap);
+
{
struct TALER_TESTING_Command cmd = {
.cls = ss,
diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c
index 4e4932a2..c57506bd 100644
--- a/src/util/wallet_signatures.c
+++ b/src/util/wallet_signatures.c
@@ -1342,14 +1342,9 @@ struct TALER_ReserveOpenDepositPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
- * When was the request created.
- */
- struct GNUNET_TIME_TimestampNBO request_timestamp;
-
- /**
- * Which reserve's opening should be paid for?
+ * Which reserve's opening signature should be paid for?
*/
- struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_ReserveSignatureP reserve_sig;
/**
* Specifies how much of the coin's value should be spent on opening this
@@ -1364,16 +1359,14 @@ GNUNET_NETWORK_STRUCT_END
void
TALER_wallet_reserve_open_deposit_sign (
const struct TALER_Amount *coin_contribution,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_ReserveOpenDepositPS rod = {
.purpose.size = htonl (sizeof (rod)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT),
- .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp),
- .reserve_pub = *reserve_pub
+ .reserve_sig = *reserve_sig
};
TALER_amount_hton (&rod.coin_contribution,
@@ -1388,16 +1381,14 @@ TALER_wallet_reserve_open_deposit_sign (
enum GNUNET_GenericReturnValue
TALER_wallet_reserve_open_deposit_verify (
const struct TALER_Amount *coin_contribution,
- const struct TALER_ReservePublicKeyP *reserve_pub,
- struct GNUNET_TIME_Timestamp request_timestamp,
+ const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_ReserveOpenDepositPS rod = {
.purpose.size = htonl (sizeof (rod)),
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT),
- .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp),
- .reserve_pub = *reserve_pub
+ .reserve_sig = *reserve_sig
};
TALER_amount_hton (&rod.coin_contribution,