Merge branch 'master' into auction_brandt

This commit is contained in:
Özgür Kesim 2022-10-04 11:35:21 +02:00
commit 77266e6c93
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
42 changed files with 3019 additions and 313 deletions

@ -1 +1 @@
Subproject commit 9dee7d6e8f967fdc58ae224e19ec03989ac35c52
Subproject commit 57d96e8e123df90c804a821874fc6cb88671ab75

View File

@ -9,6 +9,6 @@ if ! uncrustify --version >/dev/null; then
exit 1
fi
find "$DIR/../src" \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) | grep -v mustach \
find "$DIR/../src" \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) \
-exec uncrustify -c "$DIR/uncrustify.cfg" --replace --no-backup {} + \
|| true

View File

@ -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 \

View File

@ -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++)

View File

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

View File

@ -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 */

View File

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

View File

@ -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 */

View File

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

View File

@ -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]);
}
{

View File

@ -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 */

View File

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

View File

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

View File

@ -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 */

View File

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

View File

@ -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 = \

View File

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

View File

@ -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);
}

View File

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

149
src/exchangedb/pg_helper.h Normal file
View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -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);
}

View File

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

View File

@ -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.
@ -81,69 +67,6 @@
} while (0)
/**
* 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.
*
@ -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)
@ -16236,44 +16152,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.
*
@ -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;
}

View File

@ -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;
IF NOT FOUND
THEN
out_final_balance_val=0;
out_final_balance_frac=0;
out_balance_ok = FALSE;
out_conflict = FALSE;
END IF;
INSERT INTO exchange.close_requests
(reserve_pub
,close_timestamp
,reserve_sig
,close_val
,close_frac)
INSERT INTO exchange.reserves_open_deposits
(reserve_sig
,reserve_pub
,request_timestamp
,coin_pub
,coin_sig
,contribution_val
,contribution_frac
)
VALUES
(in_reserve_pub
,in_close_timestamp
,in_reserve_sig
,out_final_balance_val
,out_final_balance_frac)
(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;
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;
IF NOT FOUND
THEN
-- Idempotent request known, return success.
out_insufficient_funds=FALSE;
RETURN;
END IF;
-- 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) ) );
IF NOT FOUND
THEN
-- Insufficient balance.
out_insufficient_funds=TRUE;
RETURN;
END IF;
-- Everything fine, return success!
out_insufficient_funds=FALSE;
END $$;
COMMIT;

View File

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

View File

@ -877,6 +877,25 @@ typedef void
const char *kyc_provider_section_name);
/**
* 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.
*
@ -4043,6 +4062,98 @@ struct TALER_EXCHANGEDB_Plugin
void *rec_cls);
/**
* 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.
*
@ -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);
/**
@ -5725,6 +5840,24 @@ struct TALER_EXCHANGEDB_Plugin
void *spc_cls);
/**
* 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.

View File

@ -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
};
@ -301,6 +306,27 @@ TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
const char **configuration_section);
/**
* 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.
*

View File

@ -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 */

View File

@ -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;

View File

@ -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",

View File

@ -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 \

View File

@ -26,7 +26,6 @@
#include <gnunet/gnunet_curl_lib.h>
#include "taler_testing_lib.h"
/**
* State for a "attest" CMD.
*/
@ -43,11 +42,6 @@ struct AttestState
*/
struct TALER_EXCHANGE_ReservesAttestHandle *rsh;
/**
* Expected reserve balance.
*/
const char *expected_balance;
/**
* Private key of the reserve being analyzed.
*/
@ -58,6 +52,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.
*/
@ -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,

View File

@ -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,19 +137,29 @@ 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_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
&ss->reserve_pub.eddsa_pub);
}
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_LOG_ERROR (
"Failed to find reserve_priv for get_attestable query\n");
TALER_TESTING_interpreter_fail (is);
return;
}
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,
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,

View File

@ -1342,14 +1342,9 @@ struct TALER_ReserveOpenDepositPS
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* When was the request created.
* Which reserve's opening signature should be paid for?
*/
struct GNUNET_TIME_TimestampNBO request_timestamp;
/**
* Which reserve's opening 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,