major rework of the KYC logic, making it more configurable, not complete, but tests pass again
This commit is contained in:
parent
b061ea85c8
commit
1009084e94
@ -1 +1 @@
|
||||
Subproject commit 2075d42719b00f2763fe71d327ef4b9fd23be476
|
||||
Subproject commit 88f1513c159014a1cbc6d0745568770538d2b0a9
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
1659874124
|
||||
1660251795
|
||||
|
@ -3,7 +3,7 @@ KEYFILE = ${TALER_DATA_HOME}/merchant/default.priv
|
||||
NAME = Merchant Inc.
|
||||
|
||||
[exchange-account-1]
|
||||
PAYTO_URI = payto://iban/SANDBOXX/DE614691?receiver-name=Exchange+Company
|
||||
PAYTO_URI = payto://iban/SANDBOXX/DE546854?receiver-name=Exchange+Company
|
||||
enable_debit = yes
|
||||
enable_credit = yes
|
||||
|
||||
@ -19,7 +19,7 @@ HONOR_default = YES
|
||||
ACTIVE_default = YES
|
||||
|
||||
[merchant-exchange-default]
|
||||
MASTER_KEY = BV7EQTVVH06981REGB1EZKNBTYK2WMAZWD0SDXK9RSBVNXPBCW9G
|
||||
MASTER_KEY = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0
|
||||
EXCHANGE_BASE_URL = http://localhost:8081/
|
||||
CURRENCY = TESTKUDOS
|
||||
|
||||
@ -155,7 +155,7 @@ UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[exchange]
|
||||
MASTER_PUBLIC_KEY = BV7EQTVVH06981REGB1EZKNBTYK2WMAZWD0SDXK9RSBVNXPBCW9G
|
||||
MASTER_PUBLIC_KEY = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0
|
||||
SIGNKEY_DURATION = 4 weeks
|
||||
LOOKAHEAD_SIGN = 32 weeks 1 day
|
||||
SIGNKEY_LEGAL_DURATION = 4 weeks
|
||||
@ -177,7 +177,7 @@ CONFIG = postgres:///auditor-basedb
|
||||
[auditor]
|
||||
BASE_URL = http://localhost:8083/
|
||||
TINY_AMOUNT = TESTKUDOS:0.01
|
||||
PUBLIC_KEY = EB8DHSV6EPXPVDEB3YN9X90MHJ9BEXD0KH5H4CQVC5HTK1AQJ1Y0
|
||||
PUBLIC_KEY = JZYPE53YY23MQ0HTTV3DYHRABW4RM6SJS1Y0HF2HMSEPEPRJ77WG
|
||||
|
||||
[PATHS]
|
||||
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
|
||||
|
@ -1 +1 @@
|
||||
BV7EQTVVH06981REGB1EZKNBTYK2WMAZWD0SDXK9RSBVNXPBCW9G
|
||||
SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
1659871737
|
||||
1660252041
|
||||
|
@ -1 +1 @@
|
||||
S4VPGN022Y8533H5Y8NGVJHJHRJ6X9WKS2R22GXRVC67NYVWK2S0
|
||||
QKEQ98KRJ7RGKBZ2FSW0S0Y07G1AVJMBDHPZ23098JC5731MNYY0
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@ taler_exchange_aggregator_SOURCES = \
|
||||
taler-exchange-aggregator.c
|
||||
taler_exchange_aggregator_LDADD = \
|
||||
$(LIBGCRYPT_LIBS) \
|
||||
$(top_builddir)/src/kyclogic/libtalerkyclogic.la \
|
||||
$(top_builddir)/src/json/libtalerjson.la \
|
||||
$(top_builddir)/src/util/libtalerutil.la \
|
||||
$(top_builddir)/src/bank-lib/libtalerbank.la \
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2016-2021 Taler Systems SA
|
||||
Copyright (C) 2016-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
|
||||
@ -26,6 +26,7 @@
|
||||
#include "taler_exchangedb_lib.h"
|
||||
#include "taler_exchangedb_plugin.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_bank_service.h"
|
||||
|
||||
|
||||
@ -42,6 +43,12 @@ struct AggregationUnit
|
||||
*/
|
||||
struct TALER_MerchantPublicKeyP merchant_pub;
|
||||
|
||||
/**
|
||||
* Transient amount already found aggregated,
|
||||
* set only if @e have_transient is true.
|
||||
*/
|
||||
struct TALER_Amount trans;
|
||||
|
||||
/**
|
||||
* Total amount to be transferred, before subtraction of @e fees.wire and rounding down.
|
||||
*/
|
||||
@ -84,6 +91,19 @@ struct AggregationUnit
|
||||
*/
|
||||
const struct TALER_EXCHANGEDB_AccountInfo *wa;
|
||||
|
||||
/**
|
||||
* Set to #GNUNET_OK during transient checking
|
||||
* while everything is OK. Otherwise see return
|
||||
* value of #do_aggregate().
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
/**
|
||||
* Do we have an entry in the transient table for
|
||||
* this aggregation?
|
||||
*/
|
||||
bool have_transient;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -151,7 +171,6 @@ static struct TALER_EXCHANGEDB_Plugin *db_plugin;
|
||||
*/
|
||||
static struct GNUNET_SCHEDULER_Task *task;
|
||||
|
||||
|
||||
/**
|
||||
* How long should we sleep when idle before trying to find more work?
|
||||
*/
|
||||
@ -186,12 +205,12 @@ run_aggregation (void *cls);
|
||||
|
||||
|
||||
/**
|
||||
* Select a shard to work on.
|
||||
* Work on transactions unlocked by KYC.
|
||||
*
|
||||
* @param cls NULL
|
||||
*/
|
||||
static void
|
||||
run_shard (void *cls);
|
||||
drain_kyc_alerts (void *cls);
|
||||
|
||||
|
||||
/**
|
||||
@ -226,6 +245,7 @@ shutdown_task (void *cls)
|
||||
GNUNET_SCHEDULER_cancel (task);
|
||||
task = NULL;
|
||||
}
|
||||
TALER_KYCLOGIC_kyc_done ();
|
||||
TALER_EXCHANGEDB_plugin_unload (db_plugin);
|
||||
db_plugin = NULL;
|
||||
TALER_EXCHANGEDB_unload_accounts ();
|
||||
@ -353,14 +373,330 @@ release_shard (struct Shard *s)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Trigger the wire transfer for the @a au_active
|
||||
* and delete the record of the aggregation.
|
||||
*
|
||||
* @param au_active information about the aggregation
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
trigger_wire_transfer (const struct AggregationUnit *au_active)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Preparing wire transfer of %s to %s\n",
|
||||
TALER_amount2s (&au_active->final_amount),
|
||||
TALER_B2S (&au_active->merchant_pub));
|
||||
{
|
||||
void *buf;
|
||||
size_t buf_size;
|
||||
|
||||
TALER_BANK_prepare_transfer (au_active->payto_uri,
|
||||
&au_active->final_amount,
|
||||
exchange_base_url,
|
||||
&au_active->wtid,
|
||||
&buf,
|
||||
&buf_size);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Storing %u bytes of wire prepare data\n",
|
||||
(unsigned int) buf_size);
|
||||
/* Commit our intention to execute the wire transfer! */
|
||||
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
|
||||
au_active->wa->method,
|
||||
buf,
|
||||
buf_size);
|
||||
GNUNET_free (buf);
|
||||
}
|
||||
/* Commit the WTID data to 'wire_out' */
|
||||
if (qs >= 0)
|
||||
qs = db_plugin->store_wire_transfer_out (db_plugin->cls,
|
||||
au_active->execution_time,
|
||||
&au_active->wtid,
|
||||
&au_active->h_payto,
|
||||
au_active->wa->section_name,
|
||||
&au_active->final_amount);
|
||||
|
||||
if ( (qs >= 0) &&
|
||||
au_active->have_transient)
|
||||
qs = db_plugin->delete_aggregation_transient (db_plugin->cls,
|
||||
&au_active->h_payto,
|
||||
&au_active->wtid);
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback to return all applicable amounts for the KYC
|
||||
* decision to @ a cb.
|
||||
*
|
||||
* @param cls a `struct AggregationUnit *`
|
||||
* @param limit time limit for the iteration
|
||||
* @param cb function to call with the amounts
|
||||
* @param cb_cls closure for @a cb
|
||||
*/
|
||||
static void
|
||||
return_relevant_amounts (void *cls,
|
||||
struct GNUNET_TIME_Absolute limit,
|
||||
TALER_EXCHANGEDB_KycAmountCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
const struct AggregationUnit *au_active = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Returning amount %s in KYC check\n",
|
||||
TALER_amount2s (&au_active->total_amount));
|
||||
if (GNUNET_OK !=
|
||||
cb (cb_cls,
|
||||
&au_active->total_amount,
|
||||
GNUNET_TIME_absolute_get ()))
|
||||
return;
|
||||
qs = db_plugin->select_aggregation_amounts_for_kyc_check (
|
||||
db_plugin->cls,
|
||||
&au_active->h_payto,
|
||||
limit,
|
||||
cb,
|
||||
cb_cls);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to select aggregation amounts for KYC limit check!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test if KYC is required for a transfer to @a h_payto.
|
||||
*
|
||||
* @param au_active aggregation unit to check for
|
||||
* @return true if KYC checks are satisfied
|
||||
*/
|
||||
static bool
|
||||
kyc_satisfied (const struct AggregationUnit *au_active)
|
||||
{
|
||||
const char *requirement;
|
||||
uint64_t legi_row;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
requirement = TALER_KYCLOGIC_kyc_test_required (
|
||||
TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
|
||||
&au_active->h_payto,
|
||||
db_plugin->select_satisfied_kyc_processes,
|
||||
db_plugin->cls,
|
||||
&return_relevant_amounts,
|
||||
(void *) au_active);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC requirement for %s is %s\n",
|
||||
TALER_amount2s (&au_active->total_amount),
|
||||
requirement);
|
||||
if (NULL == requirement)
|
||||
return true;
|
||||
qs = db_plugin->insert_kyc_requirement_for_account (
|
||||
db_plugin->cls,
|
||||
requirement,
|
||||
&au_active->h_payto,
|
||||
&legi_row);
|
||||
if (qs < 0)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to persist KYC requirement `%s' in DB!\n",
|
||||
requirement);
|
||||
}
|
||||
else
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"New legitimization process %llu started\n",
|
||||
(unsigned long long) legi_row);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform the main aggregation work for @a au. Expects to be in
|
||||
* a working transaction, which the caller must also ultimately commit
|
||||
* (or rollback) depending on our return value.
|
||||
*
|
||||
* @param[in,out] au aggregation unit to work on
|
||||
* @return #GNUNET_OK if aggregation succeeded,
|
||||
* #GNUNET_NO to rollback and try again (serialization issue)
|
||||
* #GNUNET_SYSERR hard error, terminate aggregator process
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
do_aggregate (struct AggregationUnit *au)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
au->wa = TALER_EXCHANGEDB_find_account_by_payto_uri (
|
||||
au->payto_uri);
|
||||
if (NULL == au->wa)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"No exchange account configured for `%s', please fix your setup to continue!\n",
|
||||
au->payto_uri);
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
{
|
||||
struct GNUNET_TIME_Timestamp start_date;
|
||||
struct GNUNET_TIME_Timestamp end_date;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
|
||||
qs = db_plugin->get_wire_fee (db_plugin->cls,
|
||||
au->wa->method,
|
||||
au->execution_time,
|
||||
&start_date,
|
||||
&end_date,
|
||||
&au->fees,
|
||||
&master_sig);
|
||||
if (0 >= qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Could not get wire fees for %s at %s. Aborting run.\n",
|
||||
au->wa->method,
|
||||
GNUNET_TIME_timestamp2s (au->execution_time));
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now try to find other deposits to aggregate */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Found ready deposit for %s, aggregating by target %s\n",
|
||||
TALER_B2S (&au->merchant_pub),
|
||||
au->payto_uri);
|
||||
qs = db_plugin->select_aggregation_transient (db_plugin->cls,
|
||||
&au->h_payto,
|
||||
&au->merchant_pub,
|
||||
au->wa->section_name,
|
||||
&au->wtid,
|
||||
&au->trans);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to lookup transient aggregates!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
/* serializiability issue, try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue, trying again later!\n");
|
||||
return GNUNET_NO;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
&au->wtid,
|
||||
sizeof (au->wtid));
|
||||
au->have_transient = false;
|
||||
break;
|
||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||
au->have_transient = true;
|
||||
break;
|
||||
}
|
||||
qs = db_plugin->aggregate (db_plugin->cls,
|
||||
&au->h_payto,
|
||||
&au->merchant_pub,
|
||||
&au->wtid,
|
||||
&au->total_amount);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to execute aggregation!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
{
|
||||
/* serializiability issue, try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue, trying again later!\n");
|
||||
return GNUNET_NO;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Aggregation total is %s.\n",
|
||||
TALER_amount2s (&au->total_amount));
|
||||
|
||||
/* Subtract wire transfer fee and round to the unit supported by the
|
||||
wire transfer method; Check if after rounding down, we still have
|
||||
an amount to transfer, and if not mark as 'tiny'. */
|
||||
if (au->have_transient)
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&au->total_amount,
|
||||
&au->total_amount,
|
||||
&au->trans));
|
||||
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Rounding aggregate of %s\n",
|
||||
TALER_amount2s (&au->total_amount));
|
||||
if ( (0 >=
|
||||
TALER_amount_subtract (&au->final_amount,
|
||||
&au->total_amount,
|
||||
&au->fees.wire)) ||
|
||||
(GNUNET_SYSERR ==
|
||||
TALER_amount_round_down (&au->final_amount,
|
||||
¤cy_round_unit)) ||
|
||||
(TALER_amount_is_zero (&au->final_amount)) ||
|
||||
(! kyc_satisfied (au)) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Not ready for wire transfer (%d/%s)\n",
|
||||
qs,
|
||||
TALER_amount2s (&au->final_amount));
|
||||
if (au->have_transient)
|
||||
qs = db_plugin->update_aggregation_transient (db_plugin->cls,
|
||||
&au->h_payto,
|
||||
&au->wtid,
|
||||
&au->total_amount);
|
||||
else
|
||||
qs = db_plugin->create_aggregation_transient (db_plugin->cls,
|
||||
&au->h_payto,
|
||||
au->wa->section_name,
|
||||
&au->merchant_pub,
|
||||
&au->wtid,
|
||||
&au->total_amount);
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Serialization issue, trying again later!\n");
|
||||
return GNUNET_NO;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* commit */
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
qs = trigger_wire_transfer (au);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Serialization issue during aggregation; trying again later!\n");
|
||||
return GNUNET_NO;
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_break (0);
|
||||
global_ret = EXIT_FAILURE;
|
||||
return GNUNET_SYSERR;
|
||||
default:
|
||||
return GNUNET_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
run_aggregation (void *cls)
|
||||
{
|
||||
struct Shard *s = cls;
|
||||
struct AggregationUnit au_active;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_Amount trans;
|
||||
bool have_transient = true; /* squash compiler warning */
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
task = NULL;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
@ -384,7 +720,6 @@ run_aggregation (void *cls)
|
||||
db_plugin->cls,
|
||||
s->shard_start,
|
||||
s->shard_end,
|
||||
kyc_off ? true : false,
|
||||
&au_active.merchant_pub,
|
||||
&au_active.payto_uri);
|
||||
switch (qs)
|
||||
@ -432,10 +767,10 @@ run_aggregation (void *cls)
|
||||
/* If we ended up doing zero work, sleep a bit */
|
||||
if (0 == counter)
|
||||
task = GNUNET_SCHEDULER_add_delayed (aggregator_idle_sleep_interval,
|
||||
&run_shard,
|
||||
&drain_kyc_alerts,
|
||||
NULL);
|
||||
else
|
||||
task = GNUNET_SCHEDULER_add_now (&run_shard,
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
@ -444,254 +779,31 @@ run_aggregation (void *cls)
|
||||
/* continued below */
|
||||
break;
|
||||
}
|
||||
au_active.wa = TALER_EXCHANGEDB_find_account_by_payto_uri (
|
||||
au_active.payto_uri);
|
||||
if (NULL == au_active.wa)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"No exchange account configured for `%s', please fix your setup to continue!\n",
|
||||
au_active.payto_uri);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
release_shard (s);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
struct GNUNET_TIME_Timestamp start_date;
|
||||
struct GNUNET_TIME_Timestamp end_date;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
|
||||
qs = db_plugin->get_wire_fee (db_plugin->cls,
|
||||
au_active.wa->method,
|
||||
au_active.execution_time,
|
||||
&start_date,
|
||||
&end_date,
|
||||
&au_active.fees,
|
||||
&master_sig);
|
||||
if (0 >= qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Could not get wire fees for %s at %s. Aborting run.\n",
|
||||
au_active.wa->method,
|
||||
GNUNET_TIME_timestamp2s (au_active.execution_time));
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
release_shard (s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Now try to find other deposits to aggregate */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Found ready deposit for %s, aggregating by target %s\n",
|
||||
TALER_B2S (&au_active.merchant_pub),
|
||||
au_active.payto_uri);
|
||||
TALER_payto_hash (au_active.payto_uri,
|
||||
&au_active.h_payto);
|
||||
|
||||
qs = db_plugin->select_aggregation_transient (db_plugin->cls,
|
||||
&au_active.h_payto,
|
||||
au_active.wa->section_name,
|
||||
&au_active.wtid,
|
||||
&trans);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to lookup transient aggregates!\n");
|
||||
cleanup_au (&au_active);
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
release_shard (s);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
/* serializiability issue, try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue, trying again later!\n");
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
cleanup_au (&au_active);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
&au_active.wtid,
|
||||
sizeof (au_active.wtid));
|
||||
have_transient = false;
|
||||
break;
|
||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||
have_transient = true;
|
||||
break;
|
||||
}
|
||||
qs = db_plugin->aggregate (db_plugin->cls,
|
||||
&au_active.h_payto,
|
||||
&au_active.merchant_pub,
|
||||
&au_active.wtid,
|
||||
&au_active.total_amount);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to execute aggregation!\n");
|
||||
cleanup_au (&au_active);
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
release_shard (s);
|
||||
return;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
{
|
||||
/* serializiability issue, try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue, trying again later!\n");
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
cleanup_au (&au_active);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
return;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Aggregation total is %s.\n",
|
||||
TALER_amount2s (&au_active.total_amount));
|
||||
|
||||
/* Subtract wire transfer fee and round to the unit supported by the
|
||||
wire transfer method; Check if after rounding down, we still have
|
||||
an amount to transfer, and if not mark as 'tiny'. */
|
||||
if (have_transient)
|
||||
GNUNET_assert (0 <=
|
||||
TALER_amount_add (&au_active.total_amount,
|
||||
&au_active.total_amount,
|
||||
&trans));
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Rounding aggregate of %s\n",
|
||||
TALER_amount2s (&au_active.total_amount));
|
||||
if ( (0 >=
|
||||
TALER_amount_subtract (&au_active.final_amount,
|
||||
&au_active.total_amount,
|
||||
&au_active.fees.wire)) ||
|
||||
(GNUNET_SYSERR ==
|
||||
TALER_amount_round_down (&au_active.final_amount,
|
||||
¤cy_round_unit)) ||
|
||||
(TALER_amount_is_zero (&au_active.final_amount)) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Aggregate value too low for transfer (%d/%s)\n",
|
||||
qs,
|
||||
TALER_amount2s (&au_active.final_amount));
|
||||
if (have_transient)
|
||||
qs = db_plugin->update_aggregation_transient (db_plugin->cls,
|
||||
&au_active.h_payto,
|
||||
&au_active.wtid,
|
||||
&au_active.total_amount);
|
||||
else
|
||||
qs = db_plugin->create_aggregation_transient (db_plugin->cls,
|
||||
&au_active.h_payto,
|
||||
au_active.wa->section_name,
|
||||
&au_active.wtid,
|
||||
&au_active.total_amount);
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Serialization issue, trying again later!\n");
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
cleanup_au (&au_active);
|
||||
/* start again */
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
return;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
cleanup_au (&au_active);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
release_shard (s);
|
||||
return;
|
||||
}
|
||||
/* commit */
|
||||
(void) commit_or_warn ();
|
||||
cleanup_au (&au_active);
|
||||
|
||||
/* start again */
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
return;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Preparing wire transfer of %s to %s\n",
|
||||
TALER_amount2s (&au_active.final_amount),
|
||||
TALER_B2S (&au_active.merchant_pub));
|
||||
{
|
||||
void *buf;
|
||||
size_t buf_size;
|
||||
|
||||
TALER_BANK_prepare_transfer (au_active.payto_uri,
|
||||
&au_active.final_amount,
|
||||
exchange_base_url,
|
||||
&au_active.wtid,
|
||||
&buf,
|
||||
&buf_size);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Storing %u bytes of wire prepare data\n",
|
||||
(unsigned int) buf_size);
|
||||
/* Commit our intention to execute the wire transfer! */
|
||||
qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
|
||||
au_active.wa->method,
|
||||
buf,
|
||||
buf_size);
|
||||
GNUNET_free (buf);
|
||||
}
|
||||
/* Commit the WTID data to 'wire_out' */
|
||||
if (qs >= 0)
|
||||
qs = db_plugin->store_wire_transfer_out (db_plugin->cls,
|
||||
au_active.execution_time,
|
||||
&au_active.wtid,
|
||||
&au_active.h_payto,
|
||||
au_active.wa->section_name,
|
||||
&au_active.final_amount);
|
||||
|
||||
if ( (qs >= 0) &&
|
||||
have_transient)
|
||||
qs = db_plugin->delete_aggregation_transient (db_plugin->cls,
|
||||
&au_active.h_payto,
|
||||
&au_active.wtid);
|
||||
ret = do_aggregate (&au_active);
|
||||
cleanup_au (&au_active);
|
||||
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
switch (ret)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue for prepared wire data; trying again later!\n");
|
||||
case GNUNET_SYSERR:
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
release_shard (s);
|
||||
return;
|
||||
case GNUNET_NO:
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
/* start again */
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
return;
|
||||
}
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
/* die hard */
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
release_shard (s);
|
||||
return;
|
||||
case GNUNET_OK:
|
||||
/* continued below */
|
||||
break;
|
||||
}
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Stored wire transfer out instructions\n");
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Committing aggregation result\n");
|
||||
|
||||
/* Now we can finally commit the overall transaction, as we are
|
||||
again consistent if all of this passes. */
|
||||
@ -699,8 +811,8 @@ run_aggregation (void *cls)
|
||||
{
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
/* try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Commit issue for prepared wire data; trying again later!\n");
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Serialization issue on commit; trying again later!\n");
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
@ -714,7 +826,7 @@ run_aggregation (void *cls)
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Preparation complete, going again\n");
|
||||
"Commit complete, going again\n");
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||
s);
|
||||
@ -791,6 +903,197 @@ run_shard (void *cls)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called on transient aggregations matching
|
||||
* a particular hash of a payto URI.
|
||||
*
|
||||
* @param cls
|
||||
* @param payto_uri corresponding payto URI
|
||||
* @param wtid wire transfer identifier of transient aggregation
|
||||
* @param merchant_pub public key of the merchant
|
||||
* @param total amount aggregated so far
|
||||
* @return true to continue to iterate
|
||||
*/
|
||||
static bool
|
||||
handle_transient_cb (
|
||||
void *cls,
|
||||
const char *payto_uri,
|
||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const struct TALER_Amount *total)
|
||||
{
|
||||
struct AggregationUnit *au = cls;
|
||||
|
||||
if (GNUNET_OK != au->ret)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return false;
|
||||
}
|
||||
au->payto_uri = GNUNET_strdup (payto_uri);
|
||||
au->wtid = *wtid;
|
||||
au->merchant_pub = *merchant_pub;
|
||||
au->trans = *total;
|
||||
au->have_transient = true;
|
||||
au->ret = do_aggregate (au);
|
||||
GNUNET_free (au->payto_uri);
|
||||
return (GNUNET_OK == au->ret);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
drain_kyc_alerts (void *cls)
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct AggregationUnit au;
|
||||
|
||||
(void) cls;
|
||||
task = NULL;
|
||||
memset (&au,
|
||||
0,
|
||||
sizeof (au));
|
||||
au.execution_time = GNUNET_TIME_timestamp_get ();
|
||||
if (GNUNET_SYSERR ==
|
||||
db_plugin->preflight (db_plugin->cls))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to obtain database connection!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
return;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
db_plugin->start (db_plugin->cls,
|
||||
"handle kyc alerts"))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to start database transaction!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
return;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
qs = db_plugin->drain_kyc_alert (db_plugin->cls,
|
||||
1,
|
||||
&au.h_payto);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_break (0);
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
qs = db_plugin->commit (db_plugin->cls);
|
||||
if (qs < 0)
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"Failed to commit KYC drain\n");
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_shard,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||
/* handled below */
|
||||
break;
|
||||
}
|
||||
|
||||
au.ret = GNUNET_OK;
|
||||
qs = db_plugin->find_aggregation_transient (db_plugin->cls,
|
||||
&au.h_payto,
|
||||
&handle_transient_cb,
|
||||
&au);
|
||||
switch (qs)
|
||||
{
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to lookup transient aggregates!\n");
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
/* serializiability issue, try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Serialization issue, trying again later!\n");
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
continue; /* while (1) */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} /* while(1) */
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
ret = au.ret;
|
||||
cleanup_au (&au);
|
||||
switch (ret)
|
||||
{
|
||||
case GNUNET_SYSERR:
|
||||
GNUNET_break (0);
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls); /* just in case */
|
||||
return;
|
||||
case GNUNET_NO:
|
||||
db_plugin->rollback (db_plugin->cls);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_OK:
|
||||
/* continued below */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (commit_or_warn ())
|
||||
{
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
/* try again */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Serialization issue on commit; trying again later!\n");
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
GNUNET_break (0);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls); /* just in case */
|
||||
return;
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Commit complete, going again\n");
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
return;
|
||||
default:
|
||||
GNUNET_break (0);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
db_plugin->rollback (db_plugin->cls); /* just in case */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* First task.
|
||||
*
|
||||
@ -811,7 +1114,8 @@ run (void *cls,
|
||||
(void) cfgfile;
|
||||
|
||||
cfg = c;
|
||||
if (GNUNET_OK != parse_aggregator_config ())
|
||||
if (GNUNET_OK !=
|
||||
parse_aggregator_config ())
|
||||
{
|
||||
cfg = NULL;
|
||||
global_ret = EXIT_NOTCONFIGURED;
|
||||
@ -832,11 +1136,18 @@ run (void *cls,
|
||||
shard_size = 1U + INT32_MAX;
|
||||
else
|
||||
shard_size = (uint32_t) ass;
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&run_shard,
|
||||
NULL);
|
||||
if (GNUNET_OK !=
|
||||
TALER_KYCLOGIC_kyc_init (cfg))
|
||||
{
|
||||
cfg = NULL;
|
||||
global_ret = EXIT_NOTCONFIGURED;
|
||||
return;
|
||||
}
|
||||
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
|
||||
cls);
|
||||
NULL);
|
||||
GNUNET_assert (NULL == task);
|
||||
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||
NULL);
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <sched.h>
|
||||
#include <sys/resource.h>
|
||||
#include <limits.h>
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler-exchange-httpd_auditors.h"
|
||||
#include "taler-exchange-httpd_batch-deposit.h"
|
||||
@ -1215,13 +1216,14 @@ handle_mhd_request (void *cls,
|
||||
.url = "kyc-check",
|
||||
.method = MHD_HTTP_METHOD_GET,
|
||||
.handler.get = &TEH_handler_kyc_check,
|
||||
.nargs = 1
|
||||
.nargs = 2
|
||||
},
|
||||
{
|
||||
.url = "kyc-proof",
|
||||
.method = MHD_HTTP_METHOD_GET,
|
||||
.handler.get = &TEH_handler_kyc_proof,
|
||||
.nargs = 1
|
||||
.nargs = 128,
|
||||
.nargs_is_upper_bound = true
|
||||
},
|
||||
{
|
||||
.url = "kyc-wallet",
|
||||
@ -1680,6 +1682,11 @@ parse_kyc_oauth_cfg (void)
|
||||
static enum GNUNET_GenericReturnValue
|
||||
exchange_serve_process_config (void)
|
||||
{
|
||||
if (GNUNET_OK !=
|
||||
TALER_KYCLOGIC_kyc_init (TEH_cfg))
|
||||
{
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
{
|
||||
char *kyc_mode;
|
||||
|
||||
@ -2094,8 +2101,12 @@ do_shutdown (void *cls)
|
||||
TEH_purses_get_cleanup ();
|
||||
TEH_kyc_check_cleanup ();
|
||||
TEH_kyc_proof_cleanup ();
|
||||
TALER_KYCLOGIC_kyc_done ();
|
||||
if (NULL != mhd)
|
||||
{
|
||||
MHD_stop_daemon (mhd);
|
||||
mhd = NULL;
|
||||
}
|
||||
TEH_wire_done ();
|
||||
TEH_extensions_done ();
|
||||
TEH_keys_finished ();
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include <jansson.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler-exchange-httpd_batch-withdraw.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
@ -86,6 +87,17 @@ struct BatchWithdrawContext
|
||||
*/
|
||||
struct PlanchetContext *planchets;
|
||||
|
||||
/**
|
||||
* Hash of the payto-URI representing the reserve
|
||||
* from which we are withdrawing.
|
||||
*/
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
|
||||
/**
|
||||
* Current time for the DB transaction.
|
||||
*/
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
|
||||
/**
|
||||
* Total amount from all coins with fees.
|
||||
*/
|
||||
@ -99,6 +111,45 @@ struct BatchWithdrawContext
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
batch_withdraw_amount_cb (void *cls,
|
||||
struct GNUNET_TIME_Absolute limit,
|
||||
TALER_EXCHANGEDB_KycAmountCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct BatchWithdrawContext *wc = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
cb (cb_cls,
|
||||
&wc->batch_total,
|
||||
wc->now.abs_time))
|
||||
return;
|
||||
qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
|
||||
TEH_plugin->cls,
|
||||
&wc->h_payto,
|
||||
limit,
|
||||
cb,
|
||||
cb_cls);
|
||||
GNUNET_break (qs >= 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing withdraw transaction. Runs the
|
||||
* transaction logic; IF it returns a non-error code, the transaction
|
||||
@ -127,15 +178,46 @@ batch_withdraw_transaction (void *cls,
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
bool balance_ok = false;
|
||||
bool found = false;
|
||||
const char *kyc_required;
|
||||
|
||||
now = GNUNET_TIME_timestamp_get ();
|
||||
wc->now = GNUNET_TIME_timestamp_get ();
|
||||
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
||||
wc->reserve_pub,
|
||||
&wc->h_payto);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
{
|
||||
*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;
|
||||
}
|
||||
kyc_required = TALER_KYCLOGIC_kyc_test_required (
|
||||
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
||||
&wc->h_payto,
|
||||
TEH_plugin->select_satisfied_kyc_processes,
|
||||
TEH_plugin->cls,
|
||||
&batch_withdraw_amount_cb,
|
||||
wc);
|
||||
if (NULL != kyc_required)
|
||||
{
|
||||
/* insert KYC requirement into DB! */
|
||||
wc->kyc.ok = false;
|
||||
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||
TEH_plugin->cls,
|
||||
kyc_required,
|
||||
&wc->h_payto,
|
||||
&wc->kyc.payment_target_uuid);
|
||||
}
|
||||
wc->kyc.ok = true;
|
||||
qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
|
||||
now,
|
||||
wc->reserve_pub,
|
||||
&wc->batch_total,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&wc->kyc,
|
||||
&ruuid);
|
||||
if (0 > qs)
|
||||
{
|
||||
@ -164,55 +246,6 @@ batch_withdraw_transaction (void *cls,
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
|
||||
{
|
||||
/* Wallet-to-wallet payments _always_ require KYC */
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
|
||||
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
|
||||
{
|
||||
/* Withdraws require KYC if above threshold */
|
||||
enum GNUNET_DB_QueryStatus qs2;
|
||||
bool below_limit;
|
||||
|
||||
qs2 = TEH_plugin->do_withdraw_limit_check (
|
||||
TEH_plugin->cls,
|
||||
ruuid,
|
||||
GNUNET_TIME_absolute_subtract (now.abs_time,
|
||||
TEH_kyc_config.withdraw_period),
|
||||
&TEH_kyc_config.withdraw_limit,
|
||||
&below_limit);
|
||||
if (0 > qs2)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs2)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"do_withdraw_limit_check");
|
||||
return qs2;
|
||||
}
|
||||
if (! below_limit)
|
||||
{
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add information about each planchet in the batch */
|
||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||
{
|
||||
@ -291,6 +324,16 @@ generate_reply_success (const struct TEH_RequestContext *rc,
|
||||
{
|
||||
json_t *sigs;
|
||||
|
||||
if (! wc->kyc.ok)
|
||||
{
|
||||
/* KYC required */
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
rc->connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
}
|
||||
|
||||
sigs = json_array ();
|
||||
GNUNET_assert (NULL != sigs);
|
||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||
@ -633,7 +676,6 @@ TEH_handler_batch_withdraw (struct TEH_RequestContext *rc,
|
||||
TALER_amount_set_zero (TEH_currency,
|
||||
&wc.batch_total));
|
||||
wc.reserve_pub = reserve_pub;
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
|
||||
|
@ -184,7 +184,7 @@ deposit_transaction (void *cls,
|
||||
}
|
||||
if (in_conflict)
|
||||
{
|
||||
/* FIXME #7267: conficting contract != insufficient funds */
|
||||
/* FIXME #7267: conflicting contract != insufficient funds */
|
||||
*mhd_ret
|
||||
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
||||
connection,
|
||||
@ -426,7 +426,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
||||
&h_wire,
|
||||
&deposit.h_contract_terms,
|
||||
&deposit.coin.h_age_commitment,
|
||||
NULL /* h_extensions! */,
|
||||
NULL /* FIXME: h_extensions! */,
|
||||
&deposit.coin.denom_pub_hash,
|
||||
deposit.timestamp,
|
||||
&deposit.merchant_pub,
|
||||
@ -481,7 +481,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
||||
res = reply_deposit_success (connection,
|
||||
&deposit.coin.coin_pub,
|
||||
&h_wire,
|
||||
NULL /* h_extensions! */,
|
||||
NULL /* FIXME: h_extensions! */,
|
||||
&deposit.h_contract_terms,
|
||||
dc.exchange_timestamp,
|
||||
deposit.refund_deadline,
|
||||
|
@ -40,12 +40,12 @@ struct DepositWtidContext
|
||||
/**
|
||||
* Hash over the proposal data of the contract for which this deposit is made.
|
||||
*/
|
||||
struct TALER_PrivateContractHashP h_contract_terms GNUNET_PACKED;
|
||||
struct TALER_PrivateContractHashP h_contract_terms;
|
||||
|
||||
/**
|
||||
* Hash over the wiring information of the merchant.
|
||||
*/
|
||||
struct TALER_MerchantWireHashP h_wire GNUNET_PACKED;
|
||||
struct TALER_MerchantWireHashP h_wire;
|
||||
|
||||
/**
|
||||
* The Merchant's public key. The deposit inquiry request is to be
|
||||
@ -251,8 +251,12 @@ handle_track_transaction_request (
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
ctx->kyc.payment_target_uuid),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
(0 == ctx->kyc.payment_target_uuid)
|
||||
? GNUNET_JSON_pack_string ("legitimization_uuid",
|
||||
NULL)
|
||||
: GNUNET_JSON_pack_uint64 ("legitimization_uuid",
|
||||
ctx->kyc.payment_target_uuid)),
|
||||
GNUNET_JSON_pack_bool ("kyc_ok",
|
||||
ctx->kyc.ok),
|
||||
GNUNET_JSON_pack_timestamp ("execution_time",
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "platform.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_dbevents.h"
|
||||
#include "taler-exchange-httpd.h"
|
||||
#include "taler-exchange-httpd_keys.h"
|
||||
@ -1721,6 +1722,27 @@ setup_general_response_headers (struct TEH_KeyStateHandle *ksh,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called with wallet balance threshholds.
|
||||
*
|
||||
* @param[in,out] cls a `json **` where to put the array of json amounts discovered
|
||||
* @param threshold another threshold amount to add
|
||||
*/
|
||||
static void
|
||||
wallet_threshold_cb (void *cls,
|
||||
const struct TALER_Amount *threshold)
|
||||
{
|
||||
json_t **ret = cls;
|
||||
|
||||
if (NULL == *ret)
|
||||
*ret = json_array ();
|
||||
GNUNET_assert (0 ==
|
||||
json_array_append_new (*ret,
|
||||
TALER_JSON_from_amount (
|
||||
threshold)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize @a krd using the given values for @a signkeys,
|
||||
* @a recoup and @a denoms.
|
||||
@ -1854,17 +1876,20 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
||||
GNUNET_assert (NULL != keys);
|
||||
|
||||
/* Set wallet limit if KYC is configured */
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(GNUNET_OK ==
|
||||
TALER_amount_is_valid (&TEH_kyc_config.wallet_balance_limit)) )
|
||||
{
|
||||
GNUNET_assert (
|
||||
0 ==
|
||||
json_object_set_new (
|
||||
keys,
|
||||
"wallet_balance_limit_without_kyc",
|
||||
TALER_JSON_from_amount (
|
||||
&TEH_kyc_config.wallet_balance_limit)));
|
||||
json_t *wblwk = NULL;
|
||||
|
||||
TALER_KYCLOGIC_kyc_iterate_thresholds (
|
||||
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
||||
&wallet_threshold_cb,
|
||||
&wblwk);
|
||||
if (NULL != wblwk)
|
||||
GNUNET_assert (
|
||||
0 ==
|
||||
json_object_set_new (
|
||||
keys,
|
||||
"wallet_balance_limit_without_kyc",
|
||||
wblwk));
|
||||
}
|
||||
|
||||
/* Signal support for the configured, enabled extensions. */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2021 Taler Systems SA
|
||||
Copyright (C) 2021-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
|
||||
@ -25,6 +25,7 @@
|
||||
#include <microhttpd.h>
|
||||
#include <pthread.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_signatures.h"
|
||||
#include "taler_dbevents.h"
|
||||
@ -53,6 +54,17 @@ struct KycPoller
|
||||
*/
|
||||
struct MHD_Connection *connection;
|
||||
|
||||
/**
|
||||
* Logic for @e ih
|
||||
*/
|
||||
struct TALER_KYCLOGIC_Plugin *ih_logic;
|
||||
|
||||
/**
|
||||
* Handle to asynchronously running KYC initiation
|
||||
* request.
|
||||
*/
|
||||
struct TALER_KYCLOGIC_InitiateHandle *ih;
|
||||
|
||||
/**
|
||||
* Subscription for the database event we are
|
||||
* waiting for.
|
||||
@ -62,12 +74,7 @@ struct KycPoller
|
||||
/**
|
||||
* UUID being checked.
|
||||
*/
|
||||
uint64_t auth_payment_target_uuid;
|
||||
|
||||
/**
|
||||
* Current KYC status.
|
||||
*/
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
uint64_t legitimization_uuid;
|
||||
|
||||
/**
|
||||
* Hash of the payto:// URI we are confirming to
|
||||
@ -75,21 +82,46 @@ struct KycPoller
|
||||
*/
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
|
||||
/**
|
||||
* Payto URL as a string, as given to us by t
|
||||
*/
|
||||
const char *hps;
|
||||
|
||||
/**
|
||||
* When will this request time out?
|
||||
*/
|
||||
struct GNUNET_TIME_Absolute timeout;
|
||||
|
||||
/**
|
||||
* Type of KYC check required for this client.
|
||||
*/
|
||||
char *required;
|
||||
|
||||
/**
|
||||
* Set to starting URL of KYC process if KYC is required.
|
||||
*/
|
||||
char *kyc_url;
|
||||
|
||||
/**
|
||||
* Set to error details, on error (@ec not TALER_EC_NONE).
|
||||
*/
|
||||
char *hint;
|
||||
|
||||
/**
|
||||
* Set to error encountered with KYC logic, if any.
|
||||
*/
|
||||
enum TALER_ErrorCode ec;
|
||||
|
||||
/**
|
||||
* True if we are still suspended.
|
||||
*/
|
||||
bool suspended;
|
||||
|
||||
/**
|
||||
* True if KYC was required but is fully satisfied.
|
||||
*/
|
||||
bool found;
|
||||
|
||||
/**
|
||||
* True if we once tried the KYC initiation.
|
||||
*/
|
||||
bool ih_done;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -114,6 +146,11 @@ TEH_kyc_check_cleanup ()
|
||||
GNUNET_CONTAINER_DLL_remove (kyp_head,
|
||||
kyp_tail,
|
||||
kyp);
|
||||
if (NULL != kyp->ih)
|
||||
{
|
||||
kyp->ih_logic->initiate_cancel (kyp->ih);
|
||||
kyp->ih = NULL;
|
||||
}
|
||||
if (kyp->suspended)
|
||||
{
|
||||
kyp->suspended = false;
|
||||
@ -143,10 +180,83 @@ kyp_cleanup (struct TEH_RequestContext *rc)
|
||||
kyp->eh);
|
||||
kyp->eh = NULL;
|
||||
}
|
||||
if (NULL != kyp->ih)
|
||||
{
|
||||
kyp->ih_logic->initiate_cancel (kyp->ih);
|
||||
kyp->ih = NULL;
|
||||
}
|
||||
GNUNET_free (kyp->kyc_url);
|
||||
GNUNET_free (kyp->hint);
|
||||
GNUNET_free (kyp->required);
|
||||
GNUNET_free (kyp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called with the result of a KYC initiation
|
||||
* operation.
|
||||
*
|
||||
* @param cls closure with our `struct KycPoller *`
|
||||
* @param ec #TALER_EC_NONE on success
|
||||
* @param redirect_url set to where to redirect the user on success, NULL on failure
|
||||
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
|
||||
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
||||
* @param error_msg_hint set to additional details to return to user, NULL on success
|
||||
*/
|
||||
static void
|
||||
initiate_cb (
|
||||
void *cls,
|
||||
enum TALER_ErrorCode ec,
|
||||
const char *redirect_url,
|
||||
const char *provider_user_id,
|
||||
const char *provider_legitimization_id,
|
||||
const char *error_msg_hint)
|
||||
{
|
||||
struct KycPoller *kyp = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
kyp->ih = NULL;
|
||||
kyp->ih_done = true;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC initiation completed with status %d (%s)\n",
|
||||
ec,
|
||||
(TALER_EC_NONE == ec)
|
||||
? redirect_url
|
||||
: error_msg_hint);
|
||||
kyp->ec = ec;
|
||||
if (TALER_EC_NONE == ec)
|
||||
{
|
||||
kyp->kyc_url = GNUNET_strdup (redirect_url);
|
||||
}
|
||||
else
|
||||
{
|
||||
kyp->hint = GNUNET_strdup (error_msg_hint);
|
||||
}
|
||||
qs = TEH_plugin->update_kyc_requirement_by_row (
|
||||
TEH_plugin->cls,
|
||||
kyp->legitimization_uuid,
|
||||
kyp->required,
|
||||
&kyp->h_payto,
|
||||
provider_user_id,
|
||||
provider_legitimization_id,
|
||||
GNUNET_TIME_UNIT_ZERO_ABS);
|
||||
if (qs < 0)
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"KYC requirement update failed for %s with status %d at %s:%u\n",
|
||||
TALER_B2S (&kyp->h_payto),
|
||||
qs,
|
||||
__FILE__,
|
||||
__LINE__);
|
||||
GNUNET_assert (kyp->suspended);
|
||||
kyp->suspended = false;
|
||||
GNUNET_CONTAINER_DLL_remove (kyp_head,
|
||||
kyp_tail,
|
||||
kyp);
|
||||
MHD_resume_connection (kyp->connection);
|
||||
TALER_MHD_daemon_trigger ();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing database transaction to check wallet's KYC status.
|
||||
* Runs the transaction logic; IF it returns a non-error code, the transaction
|
||||
@ -168,21 +278,82 @@ kyc_check (void *cls,
|
||||
{
|
||||
struct KycPoller *kyp = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_KYCLOGIC_ProviderDetails *pd;
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
char *provider_account_id;
|
||||
char *provider_legitimization_id;
|
||||
|
||||
qs = TEH_plugin->select_kyc_status (TEH_plugin->cls,
|
||||
&kyp->h_payto,
|
||||
&kyp->kyc);
|
||||
if (qs < 0)
|
||||
qs = TEH_plugin->lookup_kyc_requirement_by_row (
|
||||
TEH_plugin->cls,
|
||||
kyp->legitimization_uuid,
|
||||
&kyp->required,
|
||||
&h_payto,
|
||||
&expiration,
|
||||
&provider_account_id,
|
||||
&provider_legitimization_id);
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
{
|
||||
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_FETCH_FAILED,
|
||||
"inselect_wallet_status");
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"No KYC requirements open for %llu\n",
|
||||
(unsigned long long) kyp->legitimization_uuid);
|
||||
return qs;
|
||||
}
|
||||
if (qs < 0)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
|
||||
return qs;
|
||||
}
|
||||
GNUNET_free (provider_account_id);
|
||||
GNUNET_free (provider_legitimization_id);
|
||||
if (0 !=
|
||||
GNUNET_memcmp (&kyp->h_payto,
|
||||
&h_payto))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"Account %llu provided, but h_payto does not match\n",
|
||||
(unsigned long long) kyp->legitimization_uuid);
|
||||
GNUNET_break_op (0);
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
|
||||
"h_payto");
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
kyp->found = true;
|
||||
if (GNUNET_TIME_absolute_is_future (expiration))
|
||||
{
|
||||
/* kyc not required, we are done */
|
||||
return qs;
|
||||
}
|
||||
|
||||
ret = TALER_KYCLOGIC_kyc_get_logic (kyp->required,
|
||||
&kyp->ih_logic,
|
||||
&pd);
|
||||
if (GNUNET_OK != ret)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC logic for `%s' not configured but used in database!\n",
|
||||
kyp->required);
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_GONE,
|
||||
kyp->required);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if (kyp->ih_done)
|
||||
return qs;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Initiating KYC check with logic %s\n",
|
||||
kyp->required);
|
||||
kyp->ih = kyp->ih_logic->initiate (kyp->ih_logic->cls,
|
||||
pd,
|
||||
&h_payto,
|
||||
kyp->legitimization_uuid,
|
||||
&initiate_cb,
|
||||
kyp);
|
||||
GNUNET_break (NULL != kyp->ih);
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -230,7 +401,7 @@ db_event_cb (void *cls,
|
||||
MHD_RESULT
|
||||
TEH_handler_kyc_check (
|
||||
struct TEH_RequestContext *rc,
|
||||
const char *const args[])
|
||||
const char *const args[2])
|
||||
{
|
||||
struct KycPoller *kyp = rc->rh_ctx;
|
||||
MHD_RESULT res;
|
||||
@ -245,24 +416,37 @@ TEH_handler_kyc_check (
|
||||
rc->rh_cleaner = &kyp_cleanup;
|
||||
|
||||
{
|
||||
// FIXME: now 'legitimization_uuid'!
|
||||
unsigned long long payment_target_uuid;
|
||||
unsigned long long legitimization_uuid;
|
||||
char dummy;
|
||||
|
||||
if (1 !=
|
||||
sscanf (args[0],
|
||||
"%llu%c",
|
||||
&payment_target_uuid,
|
||||
&legitimization_uuid,
|
||||
&dummy))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"payment_target_uuid");
|
||||
"legitimization_uuid");
|
||||
}
|
||||
kyp->auth_payment_target_uuid = (uint64_t) payment_target_uuid;
|
||||
kyp->legitimization_uuid = (uint64_t) legitimization_uuid;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[1],
|
||||
strlen (args[1]),
|
||||
&kyp->h_payto,
|
||||
sizeof (kyp->h_payto)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"h_payto");
|
||||
}
|
||||
|
||||
{
|
||||
const char *ts;
|
||||
|
||||
@ -291,41 +475,8 @@ TEH_handler_kyc_check (
|
||||
tms));
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: replace with args[1]!
|
||||
kyp->hps = MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"h_payto");
|
||||
if (NULL == kyp->hps)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MISSING,
|
||||
"h_payto");
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (kyp->hps,
|
||||
strlen (kyp->hps),
|
||||
&kyp->h_payto,
|
||||
sizeof (kyp->h_payto)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"h_payto");
|
||||
}
|
||||
}
|
||||
|
||||
if (TEH_KYC_NONE == TEH_kyc_config.mode)
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
if ( (NULL == kyp->eh) &&
|
||||
GNUNET_TIME_absolute_is_future (kyp->timeout) )
|
||||
{
|
||||
@ -353,27 +504,48 @@ TEH_handler_kyc_check (
|
||||
&kyc_check,
|
||||
kyp);
|
||||
if (GNUNET_SYSERR == ret)
|
||||
return res;
|
||||
|
||||
if (kyp->auth_payment_target_uuid !=
|
||||
kyp->kyc.payment_target_uuid)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"Account %llu provided, but payto %s is for %llu\n",
|
||||
(unsigned long long) kyp->auth_payment_target_uuid,
|
||||
kyp->hps,
|
||||
(unsigned long long) kyp->kyc.payment_target_uuid);
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
|
||||
"h_payto");
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Transaction failed.\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
if ( (NULL == kyp->ih) &&
|
||||
(! kyp->found) )
|
||||
{
|
||||
/* KYC not required */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC not required %llu\n",
|
||||
(unsigned long long) kyp->legitimization_uuid);
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
|
||||
if (NULL != kyp->ih)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Suspending HTTP request on KYC logic...\n");
|
||||
kyp->suspended = true;
|
||||
GNUNET_CONTAINER_DLL_insert (kyp_head,
|
||||
kyp_tail,
|
||||
kyp);
|
||||
MHD_suspend_connection (kyp->connection);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/* long polling? */
|
||||
if ( (! kyp->kyc.ok) &&
|
||||
if ( (NULL != kyp->required) &&
|
||||
GNUNET_TIME_absolute_is_future (kyp->timeout))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Suspending HTTP request on timeout (%s) now...\n",
|
||||
GNUNET_TIME_relative2s (GNUNET_TIME_absolute_get_duration (
|
||||
kyp->timeout),
|
||||
true));
|
||||
GNUNET_assert (NULL != kyp->eh);
|
||||
kyp->suspended = true;
|
||||
GNUNET_CONTAINER_DLL_insert (kyp_head,
|
||||
@ -383,37 +555,24 @@ TEH_handler_kyc_check (
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
/* KYC failed? */
|
||||
if (! kyp->kyc.ok)
|
||||
/* KYC plugin generated reply? */
|
||||
if (NULL != kyp->kyc_url)
|
||||
{
|
||||
char *url;
|
||||
char *redirect_uri;
|
||||
char *redirect_uri_encoded;
|
||||
|
||||
GNUNET_assert (TEH_KYC_OAUTH2 == TEH_kyc_config.mode);
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%s/kyc-proof/%s",
|
||||
TEH_base_url,
|
||||
kyp->hps);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
GNUNET_asprintf (&url,
|
||||
"%s?client_id=%s&redirect_uri=%s",
|
||||
TEH_kyc_config.details.oauth2.login_url,
|
||||
TEH_kyc_config.details.oauth2.client_id,
|
||||
redirect_uri_encoded);
|
||||
GNUNET_free (redirect_uri_encoded);
|
||||
|
||||
res = TALER_MHD_REPLY_JSON_PACK (
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
rc->connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
GNUNET_JSON_pack_string ("kyc_url",
|
||||
url));
|
||||
GNUNET_free (url);
|
||||
return res;
|
||||
kyp->kyc_url));
|
||||
}
|
||||
|
||||
/* KYC succeeded! */
|
||||
if (TALER_EC_NONE != kyp->ec)
|
||||
{
|
||||
return TALER_MHD_reply_with_ec (rc->connection,
|
||||
kyp->ec,
|
||||
kyp->hint);
|
||||
}
|
||||
|
||||
/* KYC must have succeeded! */
|
||||
{
|
||||
struct TALER_ExchangePublicKeyP pub;
|
||||
struct TALER_ExchangeSignatureP sig;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2021 Taler Systems SA
|
||||
Copyright (C) 2021-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
|
||||
@ -25,6 +25,7 @@
|
||||
#include <microhttpd.h>
|
||||
#include <pthread.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler-exchange-httpd_kyc-proof.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
@ -52,31 +53,37 @@ struct KycProofContext
|
||||
struct TEH_RequestContext *rc;
|
||||
|
||||
/**
|
||||
* Handle for the OAuth 2.0 CURL request.
|
||||
* Proof logic to run.
|
||||
*/
|
||||
struct GNUNET_CURL_Job *job;
|
||||
struct TALER_KYCLOGIC_Plugin *logic;
|
||||
|
||||
/**
|
||||
* Configuration for @a logic.
|
||||
*/
|
||||
struct TALER_KYCLOGIC_ProviderDetails *pd;
|
||||
|
||||
/**
|
||||
* Asynchronous operation with the proof system.
|
||||
*/
|
||||
struct TALER_KYCLOGIC_ProofHandle *ph;
|
||||
|
||||
/**
|
||||
* Process information about the user for the plugin from the database, can
|
||||
* be NULL.
|
||||
*/
|
||||
char *provider_user_id;
|
||||
|
||||
/**
|
||||
* Process information about the legitimization process for the plugin from the
|
||||
* database, can be NULL.
|
||||
*/
|
||||
char *provider_legitimization_id;
|
||||
|
||||
/**
|
||||
* OAuth 2.0 authorization code.
|
||||
*/
|
||||
const char *authorization_code;
|
||||
|
||||
/**
|
||||
* OAuth 2.0 token URL we are using for the
|
||||
* request.
|
||||
*/
|
||||
char *token_url;
|
||||
|
||||
/**
|
||||
* Body of the POST request.
|
||||
*/
|
||||
char *post_body;
|
||||
|
||||
/**
|
||||
* User ID extracted from the OAuth 2.0 service, or NULL.
|
||||
*/
|
||||
char *id;
|
||||
|
||||
/**
|
||||
* Hash of payment target URI this is about.
|
||||
*/
|
||||
@ -87,17 +94,25 @@ struct KycProofContext
|
||||
*/
|
||||
struct MHD_Response *response;
|
||||
|
||||
/**
|
||||
* Configuration section for the logic we are running.
|
||||
*/
|
||||
char *provider_section;
|
||||
|
||||
/**
|
||||
* Row in the database for this legitimization operation.
|
||||
*/
|
||||
uint64_t legi_row;
|
||||
|
||||
/**
|
||||
* HTTP response code to return.
|
||||
*/
|
||||
unsigned int response_code;
|
||||
|
||||
/**
|
||||
* #GNUNET_YES if we are suspended,
|
||||
* #GNUNET_NO if not.
|
||||
* #GNUNET_SYSERR if we had some error.
|
||||
* True if we are suspended,
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue suspended;
|
||||
bool suspended;
|
||||
|
||||
};
|
||||
|
||||
@ -122,7 +137,7 @@ static void
|
||||
kpc_resume (struct KycProofContext *kpc)
|
||||
{
|
||||
GNUNET_assert (GNUNET_YES == kpc->suspended);
|
||||
kpc->suspended = GNUNET_NO;
|
||||
kpc->suspended = false;
|
||||
GNUNET_CONTAINER_DLL_remove (kpc_head,
|
||||
kpc_tail,
|
||||
kpc);
|
||||
@ -138,10 +153,10 @@ TEH_kyc_proof_cleanup (void)
|
||||
|
||||
while (NULL != (kpc = kpc_head))
|
||||
{
|
||||
if (NULL != kpc->job)
|
||||
if (NULL != kpc->ph)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (kpc->job);
|
||||
kpc->job = NULL;
|
||||
kpc->logic->proof_cancel (kpc->ph);
|
||||
kpc->ph = NULL;
|
||||
}
|
||||
kpc_resume (kpc);
|
||||
}
|
||||
@ -149,348 +164,69 @@ TEH_kyc_proof_cleanup (void)
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing database transaction to check proof's KYC status.
|
||||
* 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.
|
||||
* Function called with the result of a proof check operation.
|
||||
*
|
||||
* @param cls closure with a `struct KycProofContext *`
|
||||
* @param connection MHD proof 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
|
||||
persist_kyc_ok (void *cls,
|
||||
struct MHD_Connection *connection,
|
||||
MHD_RESULT *mhd_ret)
|
||||
{
|
||||
struct KycProofContext *kpc = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = TEH_plugin->set_kyc_ok (TEH_plugin->cls,
|
||||
&kpc->h_payto,
|
||||
kpc->id);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
*mhd_ret = TALER_MHD_reply_with_ec (connection,
|
||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||
"set_kyc_ok");
|
||||
}
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The request for @a kpc failed. We may have gotten a useful error
|
||||
* message in @a j. Generate a failure response.
|
||||
* Note that the "decref" for the @a response
|
||||
* will be done by the callee and MUST NOT be done by the plugin.
|
||||
*
|
||||
* @param[in,out] kpc request that failed
|
||||
* @param j reply from the server (or NULL)
|
||||
* @param cls closure
|
||||
* @param status KYC status
|
||||
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
|
||||
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
||||
* @param expiration until when is the KYC check valid
|
||||
* @param http_status HTTP status code of @a response
|
||||
* @param[in] response to return to the HTTP client
|
||||
*/
|
||||
static void
|
||||
handle_error (struct KycProofContext *kpc,
|
||||
const json_t *j)
|
||||
proof_cb (
|
||||
void *cls,
|
||||
enum TALER_KYCLOGIC_KycStatus status,
|
||||
const char *provider_user_id,
|
||||
const char *provider_legitimization_id,
|
||||
struct GNUNET_TIME_Absolute expiration,
|
||||
unsigned int http_status,
|
||||
struct MHD_Response *response)
|
||||
{
|
||||
const char *msg;
|
||||
const char *desc;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("error",
|
||||
&msg),
|
||||
GNUNET_JSON_spec_string ("error_description",
|
||||
&desc),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct KycProofContext *kpc = cls;
|
||||
struct TEH_RequestContext *rc = kpc->rc;
|
||||
struct GNUNET_AsyncScopeSave old_scope;
|
||||
|
||||
kpc->ph = NULL;
|
||||
GNUNET_async_scope_enter (&rc->async_scope_id,
|
||||
&old_scope);
|
||||
|
||||
if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
const char *emsg;
|
||||
unsigned int line;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
res = GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
qs = TEH_plugin->update_kyc_requirement_by_row (TEH_plugin->cls,
|
||||
kpc->legi_row,
|
||||
kpc->provider_section,
|
||||
&kpc->h_payto,
|
||||
provider_user_id,
|
||||
provider_legitimization_id,
|
||||
expiration);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected response from KYC gateway");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
GNUNET_break (0);
|
||||
kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
|
||||
kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||
"set_kyc_ok");
|
||||
GNUNET_async_scope_restore (&old_scope);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED,
|
||||
we MAY want to in the future look at the requested content type
|
||||
and possibly respond in JSON if indicated. */
|
||||
else
|
||||
{
|
||||
char *reply;
|
||||
|
||||
GNUNET_asprintf (&reply,
|
||||
"<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>",
|
||||
msg,
|
||||
msg,
|
||||
desc);
|
||||
kpc->response
|
||||
= MHD_create_response_from_buffer (strlen (reply),
|
||||
reply,
|
||||
MHD_RESPMEM_MUST_COPY);
|
||||
GNUNET_assert (NULL != kpc->response);
|
||||
GNUNET_free (reply);
|
||||
}
|
||||
kpc->response_code = MHD_HTTP_FORBIDDEN;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The request for @a kpc succeeded (presumably).
|
||||
* Parse the user ID and store it in @a kpc (if possible).
|
||||
*
|
||||
* @param[in,out] kpc request that succeeded
|
||||
* @param j reply from the server
|
||||
*/
|
||||
static void
|
||||
parse_success_reply (struct KycProofContext *kpc,
|
||||
const json_t *j)
|
||||
{
|
||||
const char *state;
|
||||
json_t *data;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("status",
|
||||
&state),
|
||||
GNUNET_JSON_spec_json ("data",
|
||||
&data),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
const char *emsg;
|
||||
unsigned int line;
|
||||
|
||||
res = GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected response from KYC gateway");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
return;
|
||||
}
|
||||
if (0 != strcasecmp (state,
|
||||
"success"))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
handle_error (kpc,
|
||||
j);
|
||||
return;
|
||||
}
|
||||
{
|
||||
const char *id;
|
||||
struct GNUNET_JSON_Specification ispec[] = {
|
||||
GNUNET_JSON_spec_string ("id",
|
||||
&id),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
res = GNUNET_JSON_parse (data,
|
||||
ispec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected response from KYC gateway");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
return;
|
||||
}
|
||||
kpc->id = GNUNET_strdup (id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After we are done with the CURL interaction we
|
||||
* need to update our database state with the information
|
||||
* retrieved.
|
||||
*
|
||||
* @param cls our `struct KycProofContext`
|
||||
* @param response_code HTTP response code from server, 0 on hard error
|
||||
* @param response in JSON, NULL if response was not in JSON format
|
||||
*/
|
||||
static void
|
||||
handle_curl_fetch_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct KycProofContext *kpc = cls;
|
||||
const json_t *j = response;
|
||||
|
||||
kpc->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
parse_success_reply (kpc,
|
||||
j);
|
||||
break;
|
||||
default:
|
||||
handle_error (kpc,
|
||||
j);
|
||||
break;
|
||||
}
|
||||
kpc_resume (kpc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After we are done with the CURL interaction we
|
||||
* need to fetch the user's account details.
|
||||
*
|
||||
* @param cls our `struct KycProofContext`
|
||||
* @param response_code HTTP response code from server, 0 on hard error
|
||||
* @param response in JSON, NULL if response was not in JSON format
|
||||
*/
|
||||
static void
|
||||
handle_curl_login_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct KycProofContext *kpc = cls;
|
||||
const json_t *j = response;
|
||||
|
||||
kpc->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
const char *access_token;
|
||||
const char *token_type;
|
||||
uint64_t expires_in_s;
|
||||
const char *refresh_token;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("access_token",
|
||||
&access_token),
|
||||
GNUNET_JSON_spec_string ("token_type",
|
||||
&token_type),
|
||||
GNUNET_JSON_spec_uint64 ("expires_in",
|
||||
&expires_in_s),
|
||||
GNUNET_JSON_spec_string ("refresh_token",
|
||||
&refresh_token),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
CURL *eh;
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
const char *emsg;
|
||||
unsigned int line;
|
||||
|
||||
res = GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected response from KYC gateway");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 != strcasecmp (token_type,
|
||||
"bearer"))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected token type in response from KYC gateway");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We guard against a few characters that could
|
||||
conceivably be abused to mess with the HTTP header */
|
||||
if ( (NULL != strchr (access_token,
|
||||
'\n')) ||
|
||||
(NULL != strchr (access_token,
|
||||
'\r')) ||
|
||||
(NULL != strchr (access_token,
|
||||
' ')) ||
|
||||
(NULL != strchr (access_token,
|
||||
';')) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Illegal character in access token");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
|
||||
eh = curl_easy_init ();
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
kpc->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
||||
"curl_easy_init");
|
||||
kpc->response_code
|
||||
= MHD_HTTP_INTERNAL_SERVER_ERROR;
|
||||
break;
|
||||
}
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_URL,
|
||||
TEH_kyc_config.details.oauth2.info_url));
|
||||
{
|
||||
char *hdr;
|
||||
struct curl_slist *slist;
|
||||
|
||||
GNUNET_asprintf (&hdr,
|
||||
"%s: Bearer %s",
|
||||
MHD_HTTP_HEADER_AUTHORIZATION,
|
||||
access_token);
|
||||
slist = curl_slist_append (NULL,
|
||||
hdr);
|
||||
kpc->job = GNUNET_CURL_job_add2 (TEH_curl_ctx,
|
||||
eh,
|
||||
slist,
|
||||
&handle_curl_fetch_finished,
|
||||
kpc);
|
||||
curl_slist_free_all (slist);
|
||||
GNUNET_free (hdr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
handle_error (kpc,
|
||||
j);
|
||||
break;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC logic #%llu failed with status %d\n",
|
||||
(unsigned long long) kpc->legi_row,
|
||||
status);
|
||||
}
|
||||
kpc->response_code = http_status;
|
||||
kpc->response = response;
|
||||
kpc_resume (kpc);
|
||||
GNUNET_async_scope_restore (&old_scope);
|
||||
}
|
||||
|
||||
|
||||
@ -504,19 +240,19 @@ clean_kpc (struct TEH_RequestContext *rc)
|
||||
{
|
||||
struct KycProofContext *kpc = rc->rh_ctx;
|
||||
|
||||
if (NULL != kpc->job)
|
||||
if (NULL != kpc->ph)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (kpc->job);
|
||||
kpc->job = NULL;
|
||||
kpc->logic->proof_cancel (kpc->ph);
|
||||
kpc->ph = NULL;
|
||||
}
|
||||
if (NULL != kpc->response)
|
||||
{
|
||||
MHD_destroy_response (kpc->response);
|
||||
kpc->response = NULL;
|
||||
}
|
||||
GNUNET_free (kpc->post_body);
|
||||
GNUNET_free (kpc->token_url);
|
||||
GNUNET_free (kpc->id);
|
||||
GNUNET_free (kpc->provider_user_id);
|
||||
GNUNET_free (kpc->provider_legitimization_id);
|
||||
GNUNET_free (kpc->provider_section);
|
||||
GNUNET_free (kpc);
|
||||
}
|
||||
|
||||
@ -529,7 +265,18 @@ TEH_handler_kyc_proof (
|
||||
struct KycProofContext *kpc = rc->rh_ctx;
|
||||
|
||||
if (NULL == kpc)
|
||||
{ /* first time */
|
||||
{
|
||||
/* first time */
|
||||
if ( (NULL == args[0]) ||
|
||||
(NULL == args[1]) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
||||
"'/kyc-proof/$H_PATYO/$LOGIC' required");
|
||||
}
|
||||
|
||||
kpc = GNUNET_new (struct KycProofContext);
|
||||
kpc->rc = rc;
|
||||
rc->rh_ctx = kpc;
|
||||
@ -546,166 +293,98 @@ TEH_handler_kyc_proof (
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"h_payto");
|
||||
}
|
||||
kpc->authorization_code
|
||||
= MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"code");
|
||||
if (NULL == kpc->authorization_code)
|
||||
kpc->provider_section = GNUNET_strdup (args[1]);
|
||||
if (GNUNET_OK !=
|
||||
TALER_KYCLOGIC_kyc_get_logic (kpc->provider_section,
|
||||
&kpc->logic,
|
||||
&kpc->pd))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"code");
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
|
||||
kpc->provider_section);
|
||||
}
|
||||
if (TEH_KYC_NONE == TEH_kyc_config.mode)
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
{
|
||||
CURL *eh;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
|
||||
eh = curl_easy_init ();
|
||||
if (NULL == eh)
|
||||
qs = TEH_plugin->lookup_kyc_requirement_by_account (
|
||||
TEH_plugin->cls,
|
||||
kpc->provider_section,
|
||||
&kpc->h_payto,
|
||||
&kpc->legi_row,
|
||||
&expiration,
|
||||
&kpc->provider_user_id,
|
||||
&kpc->provider_legitimization_id);
|
||||
switch (qs)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||
return TALER_MHD_reply_with_ec (rc->connection,
|
||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||
"lookup_kyc_requirement_by_account");
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
||||
"curl_easy_init");
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
|
||||
kpc->provider_section);
|
||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||
break;
|
||||
}
|
||||
GNUNET_asprintf (&kpc->token_url,
|
||||
"%s",
|
||||
TEH_kyc_config.details.oauth2.auth_url);
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_URL,
|
||||
kpc->token_url));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_POST,
|
||||
1));
|
||||
if (GNUNET_TIME_absolute_is_future (expiration))
|
||||
{
|
||||
char *client_id;
|
||||
char *redirect_uri;
|
||||
char *client_secret;
|
||||
char *authorization_code;
|
||||
|
||||
client_id = curl_easy_escape (eh,
|
||||
TEH_kyc_config.details.oauth2.client_id,
|
||||
0);
|
||||
GNUNET_assert (NULL != client_id);
|
||||
{
|
||||
char *request_uri;
|
||||
|
||||
GNUNET_asprintf (&request_uri,
|
||||
"%s?client_id=%s",
|
||||
TEH_kyc_config.details.oauth2.login_url,
|
||||
TEH_kyc_config.details.oauth2.client_id);
|
||||
redirect_uri = curl_easy_escape (eh,
|
||||
request_uri,
|
||||
0);
|
||||
GNUNET_free (request_uri);
|
||||
}
|
||||
GNUNET_assert (NULL != redirect_uri);
|
||||
client_secret = curl_easy_escape (eh,
|
||||
TEH_kyc_config.details.oauth2.
|
||||
client_secret,
|
||||
0);
|
||||
GNUNET_assert (NULL != client_secret);
|
||||
authorization_code = curl_easy_escape (eh,
|
||||
kpc->authorization_code,
|
||||
0);
|
||||
GNUNET_assert (NULL != authorization_code);
|
||||
GNUNET_asprintf (&kpc->post_body,
|
||||
"client_id=%s&redirect_uri=%s&client_secret=%s&code=%s&grant_type=authorization_code",
|
||||
client_id,
|
||||
redirect_uri,
|
||||
client_secret,
|
||||
authorization_code);
|
||||
curl_free (authorization_code);
|
||||
curl_free (client_secret);
|
||||
curl_free (redirect_uri);
|
||||
curl_free (client_id);
|
||||
/* KYC not required */
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_POSTFIELDS,
|
||||
kpc->post_body));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_FOLLOWLOCATION,
|
||||
1L));
|
||||
/* limit MAXREDIRS to 5 as a simple security measure against
|
||||
a potential infinite loop caused by a malicious target */
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_MAXREDIRS,
|
||||
5L));
|
||||
|
||||
kpc->job = GNUNET_CURL_job_add (TEH_curl_ctx,
|
||||
eh,
|
||||
&handle_curl_login_finished,
|
||||
kpc);
|
||||
kpc->suspended = GNUNET_YES;
|
||||
GNUNET_CONTAINER_DLL_insert (kpc_head,
|
||||
kpc_tail,
|
||||
kpc);
|
||||
MHD_suspend_connection (rc->connection);
|
||||
return MHD_YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (NULL != kpc->response)
|
||||
{
|
||||
/* handle _failed_ resumed cases */
|
||||
return MHD_queue_response (rc->connection,
|
||||
kpc->response_code,
|
||||
kpc->response);
|
||||
}
|
||||
|
||||
/* _successfully_ resumed case */
|
||||
{
|
||||
MHD_RESULT res;
|
||||
enum GNUNET_GenericReturnValue ret;
|
||||
|
||||
ret = TEH_DB_run_transaction (kpc->rc->connection,
|
||||
"check proof kyc",
|
||||
TEH_MT_REQUEST_OTHER,
|
||||
&res,
|
||||
&persist_kyc_ok,
|
||||
kpc);
|
||||
if (GNUNET_SYSERR == ret)
|
||||
return res;
|
||||
}
|
||||
|
||||
{
|
||||
struct MHD_Response *response;
|
||||
MHD_RESULT res;
|
||||
|
||||
response = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
if (NULL == response)
|
||||
kpc->ph = kpc->logic->proof (kpc->logic->cls,
|
||||
kpc->pd,
|
||||
&args[2],
|
||||
rc->connection,
|
||||
&kpc->h_payto,
|
||||
kpc->legi_row,
|
||||
kpc->provider_user_id,
|
||||
kpc->provider_legitimization_id,
|
||||
&proof_cb,
|
||||
kpc);
|
||||
if (NULL == kpc->ph)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return MHD_NO;
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
|
||||
"could not start proof with KYC logic");
|
||||
}
|
||||
GNUNET_break (MHD_YES ==
|
||||
MHD_add_response_header (
|
||||
response,
|
||||
MHD_HTTP_HEADER_LOCATION,
|
||||
TEH_kyc_config.details.oauth2.post_kyc_redirect_url));
|
||||
res = MHD_queue_response (rc->connection,
|
||||
MHD_HTTP_SEE_OTHER,
|
||||
response);
|
||||
MHD_destroy_response (response);
|
||||
return res;
|
||||
|
||||
|
||||
kpc->suspended = true;
|
||||
GNUNET_CONTAINER_DLL_insert (kpc_head,
|
||||
kpc_tail,
|
||||
kpc);
|
||||
MHD_suspend_connection (rc->connection);
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
if (NULL == kpc->response)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
|
||||
"handler resumed without response");
|
||||
}
|
||||
|
||||
/* return response from KYC logic */
|
||||
return MHD_queue_response (rc->connection,
|
||||
kpc->response_code,
|
||||
kpc->response);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2021 Taler Systems SA
|
||||
Copyright (C) 2021, 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
|
||||
@ -26,6 +26,7 @@
|
||||
#include <pthread.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler-exchange-httpd_kyc-wallet.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
|
||||
@ -38,15 +39,54 @@ struct KycRequestContext
|
||||
/**
|
||||
* Public key of the reserve/wallet this is about.
|
||||
*/
|
||||
struct TALER_ReservePublicKeyP reserve_pub;
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
|
||||
/**
|
||||
* Current KYC status.
|
||||
* Row with the legitimization requirement.
|
||||
*/
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
uint64_t legi_row;
|
||||
|
||||
/**
|
||||
* Balance threshold crossed by the wallet.
|
||||
*/
|
||||
struct TALER_Amount balance;
|
||||
|
||||
/**
|
||||
* Name of the required check.
|
||||
*/
|
||||
const char *required;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function called to iterate over KYC-relevant
|
||||
* transaction amounts for a particular time range.
|
||||
* Returns the wallet balance.
|
||||
*
|
||||
* @param cls closure, a `struct KycRequestContext`
|
||||
* @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
|
||||
balance_iterator (void *cls,
|
||||
struct GNUNET_TIME_Absolute limit,
|
||||
TALER_EXCHANGEDB_KycAmountCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct KycRequestContext *krc = cls;
|
||||
|
||||
(void) limit;
|
||||
cb (cb_cls,
|
||||
&krc->balance,
|
||||
GNUNET_TIME_absolute_get ());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing database transaction to check wallet's KYC status.
|
||||
* Runs the transaction logic; IF it returns a non-error code, the transaction
|
||||
@ -69,9 +109,23 @@ wallet_kyc_check (void *cls,
|
||||
struct KycRequestContext *krc = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
qs = TEH_plugin->inselect_wallet_kyc_status (TEH_plugin->cls,
|
||||
&krc->reserve_pub,
|
||||
&krc->kyc);
|
||||
krc->required = TALER_KYCLOGIC_kyc_test_required (
|
||||
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
||||
&krc->h_payto,
|
||||
TEH_plugin->select_satisfied_kyc_processes,
|
||||
TEH_plugin->cls,
|
||||
&balance_iterator,
|
||||
krc);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"KYC check required at %s is `%s'\n",
|
||||
TALER_amount2s (&krc->balance),
|
||||
krc->required);
|
||||
if (NULL == krc->required)
|
||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||
qs = TEH_plugin->insert_kyc_requirement_for_account (TEH_plugin->cls,
|
||||
krc->required,
|
||||
&krc->h_payto,
|
||||
&krc->legi_row);
|
||||
if (qs < 0)
|
||||
{
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||
@ -80,9 +134,14 @@ wallet_kyc_check (void *cls,
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"inselect_wallet_status");
|
||||
"insert_kyc_requirement_for_account");
|
||||
return qs;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC requirement inserted for wallet %s (%llu, %d)\n",
|
||||
TALER_B2S (&krc->h_payto),
|
||||
(unsigned long long) krc->legi_row,
|
||||
qs);
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -95,11 +154,17 @@ TEH_handler_kyc_wallet (
|
||||
{
|
||||
struct TALER_ReserveSignatureP reserve_sig;
|
||||
struct KycRequestContext krc;
|
||||
struct TALER_ReservePublicKeyP reserve_pub;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||
&reserve_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
|
||||
&krc.reserve_pub),
|
||||
&reserve_pub),
|
||||
// FIXME: add balance threshold crossed to the request
|
||||
// to spec and client API!
|
||||
TALER_JSON_spec_amount ("balance",
|
||||
TEH_currency,
|
||||
&krc.balance),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
MHD_RESULT res;
|
||||
@ -115,8 +180,10 @@ TEH_handler_kyc_wallet (
|
||||
return MHD_YES; /* failure */
|
||||
|
||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||
// FIXME: add balance threshold crossed to
|
||||
// what the wallet signs over!
|
||||
if (GNUNET_OK !=
|
||||
TALER_wallet_account_setup_verify (&krc.reserve_pub,
|
||||
TALER_wallet_account_setup_verify (&reserve_pub,
|
||||
&reserve_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
@ -126,13 +193,19 @@ TEH_handler_kyc_wallet (
|
||||
TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
|
||||
NULL);
|
||||
}
|
||||
if (TEH_KYC_NONE == TEH_kyc_config.mode)
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
{
|
||||
char *payto_uri;
|
||||
|
||||
payto_uri = TALER_reserve_make_payto (TEH_base_url,
|
||||
&reserve_pub);
|
||||
TALER_payto_hash (payto_uri,
|
||||
&krc.h_payto);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"h_payto of wallet %s is %s\n",
|
||||
payto_uri,
|
||||
TALER_B2S (&krc.h_payto));
|
||||
GNUNET_free (payto_uri);
|
||||
}
|
||||
ret = TEH_DB_run_transaction (rc->connection,
|
||||
"check wallet kyc",
|
||||
TEH_MT_REQUEST_OTHER,
|
||||
@ -141,11 +214,21 @@ TEH_handler_kyc_wallet (
|
||||
&krc);
|
||||
if (GNUNET_SYSERR == ret)
|
||||
return res;
|
||||
if (NULL == krc.required)
|
||||
{
|
||||
/* KYC not required or already satisfied */
|
||||
return TALER_MHD_reply_static (
|
||||
rc->connection,
|
||||
MHD_HTTP_NO_CONTENT,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
rc->connection,
|
||||
MHD_HTTP_OK,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
krc.kyc.payment_target_uuid));
|
||||
krc.legi_row));
|
||||
}
|
||||
|
||||
|
||||
|
@ -421,11 +421,11 @@ TEH_handler_purses_merge (
|
||||
"Received payto: `%s'\n",
|
||||
pcc.payto_uri);
|
||||
if ( (0 != strncmp (pcc.payto_uri,
|
||||
"payto://taler/",
|
||||
strlen ("payto://taler/"))) &&
|
||||
"payto://taler-reserve/",
|
||||
strlen ("payto://taler-reserve/"))) &&
|
||||
(0 != strncmp (pcc.payto_uri,
|
||||
"payto://taler+http/",
|
||||
strlen ("payto://taler+http/"))) )
|
||||
"payto://taler-reserve+http/",
|
||||
strlen ("payto://taler-reserve+http/"))) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (
|
||||
@ -436,13 +436,13 @@ TEH_handler_purses_merge (
|
||||
}
|
||||
|
||||
http = (0 == strncmp (pcc.payto_uri,
|
||||
"payto://taler+http/",
|
||||
strlen ("payto://taler+http/")));
|
||||
"payto://taler-reserve+http/",
|
||||
strlen ("payto://taler-reserve+http/")));
|
||||
|
||||
{
|
||||
const char *host = &pcc.payto_uri[http
|
||||
? strlen ("payto://taler+http/")
|
||||
: strlen ("payto://taler/")];
|
||||
? strlen ("payto://taler-reserve+http/")
|
||||
: strlen ("payto://taler-reserve/")];
|
||||
const char *slash = strchr (host,
|
||||
'/');
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2021 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
|
||||
@ -27,6 +27,7 @@
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include <jansson.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler-exchange-httpd_withdraw.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
@ -44,15 +45,6 @@ struct WithdrawContext
|
||||
*/
|
||||
struct TALER_BlindedCoinHashP h_coin_envelope;
|
||||
|
||||
/**
|
||||
* Value of the coin being exchanged (matching the denomination key)
|
||||
* plus the transaction fee. We include this in what is being
|
||||
* signed so that we can verify a reserve's remaining total balance
|
||||
* without needing to access the respective denomination key
|
||||
* information each time.
|
||||
*/
|
||||
struct TALER_Amount amount_with_fee;
|
||||
|
||||
/**
|
||||
* Blinded planchet.
|
||||
*/
|
||||
@ -68,9 +60,66 @@ struct WithdrawContext
|
||||
*/
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
|
||||
/**
|
||||
* Hash of the payto-URI representing the reserve
|
||||
* from which we are withdrawing.
|
||||
*/
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
|
||||
/**
|
||||
* Current time for the DB transaction.
|
||||
*/
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
withdraw_amount_cb (void *cls,
|
||||
struct GNUNET_TIME_Absolute limit,
|
||||
TALER_EXCHANGEDB_KycAmountCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct WithdrawContext *wc = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Signaling amount %s for KYC check\n",
|
||||
TALER_amount2s (&wc->collectable.amount_with_fee));
|
||||
if (GNUNET_OK !=
|
||||
cb (cb_cls,
|
||||
&wc->collectable.amount_with_fee,
|
||||
wc->now.abs_time))
|
||||
return;
|
||||
qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
|
||||
TEH_plugin->cls,
|
||||
&wc->h_payto,
|
||||
limit,
|
||||
cb,
|
||||
cb_cls);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Got %d additional transactions for this withdrawal and limit %llu\n",
|
||||
qs,
|
||||
(unsigned long long) limit.abs_value_us);
|
||||
GNUNET_break (qs >= 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function implementing withdraw transaction. Runs the
|
||||
* transaction logic; IF it returns a non-error code, the transaction
|
||||
@ -98,25 +147,55 @@ withdraw_transaction (void *cls,
|
||||
bool found = false;
|
||||
bool balance_ok = false;
|
||||
bool nonce_ok = false;
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
uint64_t ruuid;
|
||||
const struct TALER_CsNonce *nonce;
|
||||
const struct TALER_BlindedPlanchet *bp;
|
||||
const char *kyc_required;
|
||||
|
||||
now = GNUNET_TIME_timestamp_get ();
|
||||
wc->now = GNUNET_TIME_timestamp_get ();
|
||||
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
||||
&wc->collectable.reserve_pub,
|
||||
&wc->h_payto);
|
||||
if (qs < 0)
|
||||
return qs;
|
||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||
{
|
||||
*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;
|
||||
}
|
||||
|
||||
kyc_required = TALER_KYCLOGIC_kyc_test_required (
|
||||
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
||||
&wc->h_payto,
|
||||
TEH_plugin->select_satisfied_kyc_processes,
|
||||
TEH_plugin->cls,
|
||||
&withdraw_amount_cb,
|
||||
wc);
|
||||
if (NULL != kyc_required)
|
||||
{
|
||||
/* insert KYC requirement into DB! */
|
||||
wc->kyc.ok = false;
|
||||
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||
TEH_plugin->cls,
|
||||
kyc_required,
|
||||
&wc->h_payto,
|
||||
&wc->kyc.payment_target_uuid);
|
||||
}
|
||||
wc->kyc.ok = true;
|
||||
bp = &wc->blinded_planchet;
|
||||
nonce =
|
||||
(TALER_DENOMINATION_CS == bp->cipher)
|
||||
nonce = (TALER_DENOMINATION_CS == bp->cipher)
|
||||
? &bp->details.cs_blinded_planchet.nonce
|
||||
: NULL;
|
||||
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
||||
nonce,
|
||||
&wc->collectable,
|
||||
now,
|
||||
wc->now,
|
||||
&found,
|
||||
&balance_ok,
|
||||
&nonce_ok,
|
||||
&wc->kyc,
|
||||
&ruuid);
|
||||
if (0 > qs)
|
||||
{
|
||||
@ -153,56 +232,8 @@ withdraw_transaction (void *cls,
|
||||
NULL);
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_W2W == wc->kyc.type) )
|
||||
{
|
||||
/* Wallet-to-wallet payments _always_ require KYC */
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
if ( (TEH_KYC_NONE != TEH_kyc_config.mode) &&
|
||||
(! wc->kyc.ok) &&
|
||||
(TALER_EXCHANGEDB_KYC_WITHDRAW == wc->kyc.type) &&
|
||||
(! GNUNET_TIME_relative_is_zero (TEH_kyc_config.withdraw_period)) )
|
||||
{
|
||||
/* Withdraws require KYC if above threshold */
|
||||
enum GNUNET_DB_QueryStatus qs2;
|
||||
bool below_limit;
|
||||
|
||||
qs2 = TEH_plugin->do_withdraw_limit_check (
|
||||
TEH_plugin->cls,
|
||||
ruuid,
|
||||
GNUNET_TIME_absolute_subtract (now.abs_time,
|
||||
TEH_kyc_config.withdraw_period),
|
||||
&TEH_kyc_config.withdraw_limit,
|
||||
&below_limit);
|
||||
if (0 > qs2)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs2);
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs2)
|
||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
"do_withdraw_limit_check");
|
||||
return qs2;
|
||||
}
|
||||
if (! below_limit)
|
||||
{
|
||||
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc->kyc.payment_target_uuid));
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
}
|
||||
}
|
||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||
TEH_METRICS_num_success[TEH_MT_SUCCESS_BATCH_WITHDRAW]++;
|
||||
TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++;
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -274,7 +305,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
0,
|
||||
sizeof (wc));
|
||||
wc.collectable.reserve_pub = *reserve_pub;
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
|
||||
@ -459,6 +489,14 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
||||
/* Clean up and send back final response */
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
|
||||
if (! wc.kyc.ok)
|
||||
{
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
rc->connection,
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
||||
wc.kyc.payment_target_uuid));
|
||||
}
|
||||
{
|
||||
MHD_RESULT ret;
|
||||
|
||||
|
@ -99,11 +99,12 @@ BEGIN
|
||||
PERFORM create_partitioned_table(
|
||||
'CREATE TABLE IF NOT EXISTS %I'
|
||||
'(legitimization_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
|
||||
',h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=64)'
|
||||
',h_payto BYTEA NOT NULL CHECK (LENGTH(h_payto)=32)'
|
||||
',expiration_time INT8 NOT NULL DEFAULT (0)'
|
||||
',provider_section VARCHAR NOT NULL'
|
||||
',provider_user_id VARCHAR DEFAULT NULL'
|
||||
',provider_legitimization_id VARCHAR DEFAULT NULL'
|
||||
',UNIQUE (h_payto, provider_section)'
|
||||
') %s ;'
|
||||
,'legitimizations'
|
||||
,'PARTITION BY HASH (h_payto)'
|
||||
@ -898,6 +899,7 @@ BEGIN
|
||||
'(amount_val INT8 NOT NULL'
|
||||
',amount_frac INT4 NOT NULL'
|
||||
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
|
||||
',merchant_pub BYTEA CHECK (LENGTH(merchant_pub)=32)'
|
||||
',exchange_account_section TEXT NOT NULL'
|
||||
',wtid_raw BYTEA NOT NULL CHECK (LENGTH(wtid_raw)=32)'
|
||||
') %s ;'
|
||||
|
@ -63,6 +63,22 @@ COMMENT ON TABLE denomination_revocations
|
||||
IS 'remembering which denomination keys have been revoked';
|
||||
|
||||
|
||||
|
||||
-- -------------------------- kyc_alerts ----------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS kyc_alerts
|
||||
(h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32)
|
||||
,trigger_type INT4 NOT NULL
|
||||
,UNIQUE(trigger_type,h_payto)
|
||||
);
|
||||
COMMENT ON TABLE kyc_alerts
|
||||
IS 'alerts about completed KYC events reliably notifying other components (even if they are not running)';
|
||||
COMMENT ON COLUMN kyc_alerts.h_payto
|
||||
IS 'hash of the payto://-URI for which the KYC status changed';
|
||||
COMMENT ON COLUMN kyc_alerts.trigger_type
|
||||
IS 'identifies the receiver of the alert, as the same h_payto may require multiple components to be notified';
|
||||
|
||||
|
||||
-- ------------------------------ profit drains ----------------------------------------
|
||||
|
||||
CREATE TABLE IF NOT EXISTS profit_drains
|
||||
|
@ -589,6 +589,14 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" WHERE reserve_pub=$1"
|
||||
" LIMIT 1;",
|
||||
1),
|
||||
/* Used in #postgres_reserves_get_origin() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"get_h_wire_source_of_reserve",
|
||||
"SELECT"
|
||||
" wire_source_h_payto"
|
||||
" FROM reserves_in"
|
||||
" WHERE reserve_pub=$1",
|
||||
1),
|
||||
/* Used in #postgres_set_kyc_ok() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"set_kyc_ok",
|
||||
@ -638,6 +646,18 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" FROM wire_targets"
|
||||
" WHERE wire_target_h_payto=$1;",
|
||||
1),
|
||||
/* Used in #postgres_drain_kyc_alert() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"drain_kyc_alert",
|
||||
"DELETE FROM kyc_alerts"
|
||||
" WHERE trigger_type=$1"
|
||||
" AND h_payto = "
|
||||
" (SELECT h_payto "
|
||||
" FROM kyc_alerts"
|
||||
" WHERE trigger_type=$1"
|
||||
" LIMIT 1)"
|
||||
" RETURNING h_payto;",
|
||||
1),
|
||||
/* Used in #reserves_get() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"reserves_get",
|
||||
@ -876,8 +896,6 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" reserve_found"
|
||||
",balance_ok"
|
||||
",nonce_ok"
|
||||
",kycok AS kyc_ok"
|
||||
",account_uuid AS payment_target_uuid"
|
||||
",ruuid"
|
||||
" FROM exchange_do_withdraw"
|
||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
|
||||
@ -889,8 +907,6 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"SELECT "
|
||||
" reserve_found"
|
||||
",balance_ok"
|
||||
",kycok AS kyc_ok"
|
||||
",account_uuid AS payment_target_uuid"
|
||||
",ruuid"
|
||||
" FROM exchange_do_batch_withdraw"
|
||||
" ($1,$2,$3,$4,$5);",
|
||||
@ -1719,8 +1735,8 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
GNUNET_PQ_make_prepare (
|
||||
"get_deposit_without_wtid",
|
||||
"SELECT"
|
||||
" wt.kyc_ok"
|
||||
",wt.wire_target_serial_id AS payment_target_uuid"
|
||||
" legi.expiration_time"
|
||||
",legi.legitimization_serial_id"
|
||||
",dep.wire_salt"
|
||||
",wt.payto_uri"
|
||||
",dep.amount_with_fee_val"
|
||||
@ -1732,9 +1748,12 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" JOIN wire_targets wt USING (wire_target_h_payto)"
|
||||
" JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)"
|
||||
" JOIN denominations denom USING (denominations_serial)"
|
||||
" LEFT JOIN legitimizations legi ON (wt.wire_target_h_payto = legi.h_payto)"
|
||||
" WHERE dep.coin_pub=$1"
|
||||
" AND dep.merchant_pub=$3"
|
||||
" AND dep.h_contract_terms=$2;",
|
||||
" AND dep.h_contract_terms=$2"
|
||||
" ORDER BY legi.expiration_time ASC"
|
||||
" LIMIT 1;",
|
||||
3),
|
||||
/* Used in #postgres_get_ready_deposit() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
@ -1744,18 +1763,18 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
",merchant_pub"
|
||||
" FROM deposits_by_ready dbr"
|
||||
" JOIN deposits dep"
|
||||
" ON (dbr.coin_pub = dep.coin_pub AND dbr.deposit_serial_id = dep.deposit_serial_id)"
|
||||
" ON (dbr.coin_pub = dep.coin_pub AND"
|
||||
" dbr.deposit_serial_id = dep.deposit_serial_id)"
|
||||
" JOIN wire_targets wt"
|
||||
" USING (wire_target_h_payto)"
|
||||
" WHERE dbr.wire_deadline<=$1"
|
||||
" AND dbr.shard >= $2"
|
||||
" AND dbr.shard <= $3"
|
||||
" AND (wt.kyc_ok OR $4)"
|
||||
" ORDER BY "
|
||||
" dbr.wire_deadline ASC"
|
||||
" ,dbr.shard ASC"
|
||||
" LIMIT 1;",
|
||||
4),
|
||||
3),
|
||||
/* Used in #postgres_aggregate() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"aggregate",
|
||||
@ -1850,11 +1869,12 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"INSERT INTO aggregation_transient"
|
||||
" (amount_val"
|
||||
" ,amount_frac"
|
||||
" ,merchant_pub"
|
||||
" ,wire_target_h_payto"
|
||||
" ,exchange_account_section"
|
||||
" ,wtid_raw)"
|
||||
" VALUES ($1, $2, $3, $4, $5);",
|
||||
5),
|
||||
" VALUES ($1, $2, $3, $4, $5, $6);",
|
||||
6),
|
||||
/* Used in #postgres_select_aggregation_transient() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"select_aggregation_transient",
|
||||
@ -1864,8 +1884,22 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" ,wtid_raw"
|
||||
" FROM aggregation_transient"
|
||||
" WHERE wire_target_h_payto=$1"
|
||||
" AND exchange_account_section=$2;",
|
||||
2),
|
||||
" AND merchant_pub=$2"
|
||||
" AND exchange_account_section=$3;",
|
||||
3),
|
||||
/* Used in #postgres_find_aggregation_transient() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"find_transient_aggregations",
|
||||
"SELECT"
|
||||
" amount_val"
|
||||
" ,amount_frac"
|
||||
" ,wtid_raw"
|
||||
" ,merchant_pub"
|
||||
" ,payto_uri"
|
||||
" FROM aggregation_transient atr"
|
||||
" JOIN wire_targets wt USING (wire_target_h_payto)"
|
||||
" WHERE atr.wire_target_h_payto=$1;",
|
||||
1),
|
||||
/* Used in #postgres_update_aggregation_transient() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"update_aggregation_transient",
|
||||
@ -4525,6 +4559,8 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" ,provider_section"
|
||||
" ) VALUES "
|
||||
" ($1, $2)"
|
||||
" ON CONFLICT (h_payto,provider_section) "
|
||||
" DO UPDATE SET h_payto=$1" /* syntax requirement: dummy op */
|
||||
" RETURNING legitimization_serial_id",
|
||||
2),
|
||||
/* Used in #postgres_update_kyc_requirement_by_row() */
|
||||
@ -4533,12 +4569,20 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
"UPDATE legitimizations"
|
||||
" SET provider_user_id=$4"
|
||||
" ,provider_legitimization_id=$5"
|
||||
" ,expiration_time=$6"
|
||||
" ,expiration_time=GREATEST(expiration_time,$6)"
|
||||
" WHERE"
|
||||
" h_payto=$3"
|
||||
" AND legitimization_serial_id=$1"
|
||||
" AND provider_section=$2;",
|
||||
6),
|
||||
GNUNET_PQ_make_prepare (
|
||||
"alert_kyc_status_change",
|
||||
"INSERT INTO kyc_alerts"
|
||||
" (h_payto"
|
||||
" ,trigger_type)"
|
||||
" VALUES"
|
||||
" ($1,$2);",
|
||||
2),
|
||||
/* Used in #postgres_lookup_kyc_requirement_by_row() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
"lookup_legitimization_by_row",
|
||||
@ -4598,7 +4642,6 @@ prepare_statements (struct PostgresClosure *pg)
|
||||
" AND ro.execution_date >= $2"
|
||||
" ORDER BY ro.execution_date DESC",
|
||||
2),
|
||||
|
||||
/* Used in #postgres_select_aggregation_amounts_for_kyc_check (
|
||||
() */
|
||||
GNUNET_PQ_make_prepare (
|
||||
@ -5705,7 +5748,6 @@ postgres_reserves_get (void *cls,
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"reserves_get_with_kyc",
|
||||
params,
|
||||
@ -5713,6 +5755,38 @@ postgres_reserves_get (void *cls,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the origin of funds of a reserve.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] h_payto set to hash of the wire source payto://-URI
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_reserves_get_origin (
|
||||
void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_PaytoHashP *h_payto)
|
||||
{
|
||||
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[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("wire_source_h_payto",
|
||||
h_payto),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"get_h_wire_source_of_reserve",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the KYC status to "OK" for a bank account.
|
||||
*
|
||||
@ -5766,6 +5840,37 @@ postgres_set_kyc_ok (void *cls,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract next KYC alert. Deletes the alert.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param trigger_type which type of alert to drain
|
||||
* @param[out] h_payto set to hash of payto-URI where KYC status changed
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_drain_kyc_alert (void *cls,
|
||||
uint32_t trigger_type,
|
||||
struct TALER_PaytoHashP *h_payto)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint32 (&trigger_type),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("h_payto",
|
||||
h_payto),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"drain_kyc_alert",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the @a kyc status and @a h_payto by UUID.
|
||||
*
|
||||
@ -5792,7 +5897,6 @@ postgres_select_kyc_status (void *cls,
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"select_kyc_status_by_payto",
|
||||
params,
|
||||
@ -5863,7 +5967,6 @@ inselect_account_kyc_status (
|
||||
kyc->ok = false;
|
||||
}
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
||||
return qs;
|
||||
}
|
||||
|
||||
@ -5888,7 +5991,7 @@ postgres_inselect_wallet_kyc_status (
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
|
||||
payto_uri = TALER_payto_from_reserve (pg->exchange_url,
|
||||
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
|
||||
reserve_pub);
|
||||
qs = inselect_account_kyc_status (pg,
|
||||
payto_uri,
|
||||
@ -6284,7 +6387,6 @@ postgres_get_withdraw_info (
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] nonce_ok set to false if the nonce was reused
|
||||
* @param[out] kyc set to true if the kyc status of the reserve is satisfied
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -6297,7 +6399,6 @@ postgres_do_withdraw (
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
bool *nonce_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
||||
uint64_t *ruuid)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
@ -6321,12 +6422,8 @@ postgres_do_withdraw (
|
||||
found),
|
||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||
balance_ok),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
||||
nonce_ok),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||
ruuid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -6335,7 +6432,6 @@ postgres_do_withdraw (
|
||||
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||
GNUNET_TIME_absolute_add (now.abs_time,
|
||||
pg->legal_reserve_expiration_time));
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_withdraw",
|
||||
params,
|
||||
@ -6354,7 +6450,6 @@ postgres_do_withdraw (
|
||||
* @param amount total amount to withdraw
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc set to the KYC status of the reserve
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -6366,7 +6461,6 @@ postgres_do_batch_withdraw (
|
||||
const struct TALER_Amount *amount,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
||||
uint64_t *ruuid)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
@ -6383,10 +6477,6 @@ postgres_do_batch_withdraw (
|
||||
found),
|
||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||
balance_ok),
|
||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||
ruuid),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -6395,7 +6485,6 @@ postgres_do_batch_withdraw (
|
||||
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||
GNUNET_TIME_absolute_add (now.abs_time,
|
||||
pg->legal_reserve_expiration_time));
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"call_batch_withdraw",
|
||||
params,
|
||||
@ -7743,12 +7832,14 @@ postgres_create_aggregation_transient (
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const char *exchange_account_section,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
const struct TALER_Amount *total)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
TALER_PQ_query_param_amount (total),
|
||||
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_string (exchange_account_section),
|
||||
GNUNET_PQ_query_param_auto_from_type (wtid),
|
||||
@ -7775,6 +7866,7 @@ static enum GNUNET_DB_QueryStatus
|
||||
postgres_select_aggregation_transient (
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const char *exchange_account_section,
|
||||
struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
struct TALER_Amount *total)
|
||||
@ -7782,6 +7874,7 @@ postgres_select_aggregation_transient (
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
|
||||
GNUNET_PQ_query_param_string (exchange_account_section),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
@ -7800,6 +7893,129 @@ postgres_select_aggregation_transient (
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closure for #get_refunds_cb().
|
||||
*/
|
||||
struct FindAggregationTransientContext
|
||||
{
|
||||
/**
|
||||
* Function to call on each result.
|
||||
*/
|
||||
TALER_EXCHANGEDB_TransientAggregationCallback cb;
|
||||
|
||||
/**
|
||||
* Closure for @a cb.
|
||||
*/
|
||||
void *cb_cls;
|
||||
|
||||
/**
|
||||
* Plugin context.
|
||||
*/
|
||||
struct PostgresClosure *pg;
|
||||
|
||||
/**
|
||||
* Set to #GNUNET_SYSERR on error.
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue status;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function to be called with the results of a SELECT statement
|
||||
* that has returned @a num_results results.
|
||||
*
|
||||
* @param cls closure of type `struct SelectRefundContext *`
|
||||
* @param result the postgres result
|
||||
* @param num_results the number of results in @a result
|
||||
*/
|
||||
static void
|
||||
get_transients_cb (void *cls,
|
||||
PGresult *result,
|
||||
unsigned int num_results)
|
||||
{
|
||||
struct FindAggregationTransientContext *srctx = cls;
|
||||
struct PostgresClosure *pg = srctx->pg;
|
||||
|
||||
for (unsigned int i = 0; i<num_results; i++)
|
||||
{
|
||||
struct TALER_Amount amount;
|
||||
char *payto_uri;
|
||||
struct TALER_WireTransferIdentifierRawP wtid;
|
||||
struct TALER_MerchantPublicKeyP merchant_pub;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
|
||||
&merchant_pub),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
|
||||
&wtid),
|
||||
GNUNET_PQ_result_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
|
||||
&amount),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
bool cont;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_PQ_extract_result (result,
|
||||
rs,
|
||||
i))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
srctx->status = GNUNET_SYSERR;
|
||||
return;
|
||||
}
|
||||
cont = srctx->cb (srctx->cb_cls,
|
||||
payto_uri,
|
||||
&wtid,
|
||||
&merchant_pub,
|
||||
&amount);
|
||||
GNUNET_free (payto_uri);
|
||||
if (! cont)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find existing entry in the transient aggregation table.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param h_payto destination of the wire transfer
|
||||
* @param cb function to call on each matching entry
|
||||
* @param cb_cls closure for @a cb
|
||||
* @return transaction status
|
||||
*/
|
||||
static enum GNUNET_DB_QueryStatus
|
||||
postgres_find_aggregation_transient (
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
TALER_EXCHANGEDB_TransientAggregationCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct FindAggregationTransientContext srctx = {
|
||||
.cb = cb,
|
||||
.cb_cls = cb_cls,
|
||||
.pg = pg,
|
||||
.status = GNUNET_OK
|
||||
};
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
|
||||
"find_transient_aggregations",
|
||||
params,
|
||||
&get_transients_cb,
|
||||
&srctx);
|
||||
if (GNUNET_SYSERR == srctx.status)
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update existing entry in the transient aggregation table.
|
||||
* @a h_payto is only needed for query performance.
|
||||
@ -7867,8 +8083,6 @@ postgres_delete_aggregation_transient (
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param start_shard_row minimum shard row to select
|
||||
* @param end_shard_row maximum shard row to select (inclusive)
|
||||
* @param kyc_off true if we should not check the KYC status because
|
||||
* this exchange does not need/support KYC checks.
|
||||
* @param[out] merchant_pub set to the public key of a merchant with a ready deposit
|
||||
* @param[out] payto_uri set to the account of the merchant, to be freed by caller
|
||||
* @return transaction status code
|
||||
@ -7877,7 +8091,6 @@ static enum GNUNET_DB_QueryStatus
|
||||
postgres_get_ready_deposit (void *cls,
|
||||
uint64_t start_shard_row,
|
||||
uint64_t end_shard_row,
|
||||
bool kyc_off,
|
||||
struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
char **payto_uri)
|
||||
{
|
||||
@ -7887,7 +8100,6 @@ postgres_get_ready_deposit (void *cls,
|
||||
GNUNET_PQ_query_param_absolute_time (&now),
|
||||
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
||||
GNUNET_PQ_query_param_uint64 (&end_shard_row),
|
||||
GNUNET_PQ_query_param_bool (kyc_off),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
@ -9644,7 +9856,11 @@ postgres_lookup_transfer_by_deposit (
|
||||
deposit_fee),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
|
||||
memset (kyc,
|
||||
0,
|
||||
sizeof (*kyc));
|
||||
/* check if the aggregation record exists and get it */
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"lookup_deposit_wtid",
|
||||
@ -9663,10 +9879,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
h_wire))
|
||||
{
|
||||
*pending = false;
|
||||
memset (kyc,
|
||||
0,
|
||||
sizeof (*kyc));
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
kyc->ok = true;
|
||||
return qs;
|
||||
}
|
||||
@ -9685,15 +9897,21 @@ postgres_lookup_transfer_by_deposit (
|
||||
/* Check if transaction exists in deposits, so that we just
|
||||
do not have a WTID yet. In that case, return without wtid
|
||||
(by setting 'pending' true). */
|
||||
bool no_kyc = false;
|
||||
struct GNUNET_PQ_ResultSpec rs2[] = {
|
||||
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
||||
&wire_salt),
|
||||
GNUNET_PQ_result_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
||||
&kyc->payment_target_uuid),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
||||
&kyc->ok),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
GNUNET_PQ_result_spec_uint64 ("legitimization_serial_id",
|
||||
|
||||
&kyc->payment_target_uuid),
|
||||
&no_kyc),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
GNUNET_PQ_result_spec_absolute_time ("expiration_time",
|
||||
&expiration),
|
||||
&no_kyc),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||
amount_with_fee),
|
||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
||||
@ -9711,6 +9929,10 @@ postgres_lookup_transfer_by_deposit (
|
||||
{
|
||||
struct TALER_MerchantWireHashP wh;
|
||||
|
||||
if (no_kyc)
|
||||
kyc->payment_target_uuid = 0;
|
||||
else
|
||||
kyc->ok = GNUNET_TIME_absolute_is_future (expiration);
|
||||
TALER_merchant_wire_signature_hash (payto_uri,
|
||||
&wire_salt,
|
||||
&wh);
|
||||
@ -9720,7 +9942,6 @@ postgres_lookup_transfer_by_deposit (
|
||||
h_wire))
|
||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||
}
|
||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
||||
return qs;
|
||||
}
|
||||
}
|
||||
@ -16149,7 +16370,7 @@ postgres_do_purse_merge (
|
||||
{
|
||||
char *payto_uri;
|
||||
|
||||
payto_uri = TALER_payto_from_reserve (pg->exchange_url,
|
||||
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
|
||||
reserve_pub);
|
||||
TALER_payto_hash (payto_uri,
|
||||
&h_payto);
|
||||
@ -16228,7 +16449,7 @@ postgres_do_reserve_purse (
|
||||
{
|
||||
char *payto_uri;
|
||||
|
||||
payto_uri = TALER_payto_from_reserve (pg->exchange_url,
|
||||
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
|
||||
reserve_pub);
|
||||
TALER_payto_hash (payto_uri,
|
||||
&h_payto);
|
||||
@ -16612,16 +16833,57 @@ postgres_update_kyc_requirement_by_row (
|
||||
GNUNET_PQ_query_param_uint64 (&legi_row),
|
||||
GNUNET_PQ_query_param_string (provider_section),
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_string (provider_account_id),
|
||||
GNUNET_PQ_query_param_string (provider_legitimization_id),
|
||||
(NULL != provider_account_id)
|
||||
? GNUNET_PQ_query_param_string (provider_account_id)
|
||||
: GNUNET_PQ_query_param_null (),
|
||||
(NULL != provider_legitimization_id)
|
||||
? GNUNET_PQ_query_param_string (provider_legitimization_id)
|
||||
: GNUNET_PQ_query_param_null (),
|
||||
GNUNET_PQ_query_param_absolute_time (&expiration),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
return GNUNET_PQ_eval_prepared_non_select (
|
||||
qs = GNUNET_PQ_eval_prepared_non_select (
|
||||
pg->conn,
|
||||
"update_legitimization_requirement",
|
||||
params);
|
||||
if (qs <= 0)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"Failed to update legitimization: %d\n",
|
||||
qs);
|
||||
return qs;
|
||||
}
|
||||
if (GNUNET_TIME_absolute_is_future (expiration))
|
||||
{
|
||||
enum GNUNET_DB_QueryStatus qs2;
|
||||
struct TALER_KycCompletedEventP rep = {
|
||||
.header.size = htons (sizeof (rep)),
|
||||
.header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
|
||||
.h_payto = *h_payto
|
||||
};
|
||||
uint32_t trigger_type = 1;
|
||||
struct GNUNET_PQ_QueryParam params2[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_uint32 (&trigger_type),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
|
||||
postgres_event_notify (pg,
|
||||
&rep.header,
|
||||
NULL,
|
||||
0);
|
||||
qs2 = GNUNET_PQ_eval_prepared_non_select (
|
||||
pg->conn,
|
||||
"alert_kyc_status_change",
|
||||
params2);
|
||||
if (qs2 < 0)
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to store KYC alert: %d\n",
|
||||
qs2);
|
||||
}
|
||||
return qs;
|
||||
}
|
||||
|
||||
|
||||
@ -16831,6 +17093,9 @@ get_legitimizations_cb (void *cls,
|
||||
ctx->status = GNUNET_SYSERR;
|
||||
return;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Found satisfied LEGI: %s\n",
|
||||
provider_section);
|
||||
ctx->cb (ctx->cb_cls,
|
||||
provider_section);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
@ -16877,6 +17142,9 @@ postgres_select_satisfied_kyc_processes (
|
||||
params,
|
||||
&get_legitimizations_cb,
|
||||
&ctx);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Satisfied LEGI check returned %d\n",
|
||||
qs);
|
||||
if (GNUNET_OK != ctx.status)
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
return qs;
|
||||
@ -17220,7 +17488,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
||||
&postgres_iterate_auditor_denominations;
|
||||
plugin->select_kyc_status = &postgres_select_kyc_status;
|
||||
plugin->reserves_get = &postgres_reserves_get;
|
||||
plugin->reserves_get_origin = &postgres_reserves_get_origin;
|
||||
plugin->set_kyc_ok = &postgres_set_kyc_ok;
|
||||
plugin->drain_kyc_alert = &postgres_drain_kyc_alert;
|
||||
plugin->inselect_wallet_kyc_status = &postgres_inselect_wallet_kyc_status;
|
||||
plugin->reserves_in_insert = &postgres_reserves_in_insert;
|
||||
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
||||
@ -17247,6 +17517,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
||||
= &postgres_create_aggregation_transient;
|
||||
plugin->select_aggregation_transient
|
||||
= &postgres_select_aggregation_transient;
|
||||
plugin->find_aggregation_transient
|
||||
= &postgres_find_aggregation_transient;
|
||||
plugin->update_aggregation_transient
|
||||
= &postgres_update_aggregation_transient;
|
||||
plugin->delete_aggregation_transient
|
||||
|
@ -37,8 +37,6 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
|
||||
OUT reserve_found BOOLEAN,
|
||||
OUT balance_ok BOOLEAN,
|
||||
OUT nonce_ok BOOLEAN,
|
||||
OUT kycok BOOLEAN,
|
||||
OUT account_uuid INT8,
|
||||
OUT ruuid INT8)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@ -67,8 +65,6 @@ THEN
|
||||
-- denomination unknown, should be impossible!
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE;
|
||||
account_uuid=0;
|
||||
ruuid=0;
|
||||
ASSERT false, 'denomination unknown';
|
||||
RETURN;
|
||||
@ -94,8 +90,6 @@ THEN
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
nonce_ok=TRUE;
|
||||
kycok=FALSE;
|
||||
account_uuid=0;
|
||||
ruuid=2;
|
||||
RETURN;
|
||||
END IF;
|
||||
@ -128,8 +122,6 @@ THEN
|
||||
reserve_found=TRUE;
|
||||
balance_ok=TRUE;
|
||||
nonce_ok=TRUE;
|
||||
kycok=TRUE;
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
|
||||
@ -153,8 +145,6 @@ ELSE
|
||||
reserve_found=TRUE;
|
||||
nonce_ok=TRUE; -- we do not really know
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE; -- we do not really know or care
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
END IF;
|
||||
@ -201,8 +191,6 @@ THEN
|
||||
THEN
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE;
|
||||
account_uuid=0;
|
||||
nonce_ok=FALSE;
|
||||
RETURN;
|
||||
END IF;
|
||||
@ -211,40 +199,9 @@ ELSE
|
||||
nonce_ok=TRUE; -- no nonce, hence OK!
|
||||
END IF;
|
||||
|
||||
|
||||
|
||||
-- Obtain KYC status based on the last wire transfer into
|
||||
-- this reserve. FIXME: likely not adequate for reserves that got P2P transfers!
|
||||
-- SELECT
|
||||
-- kyc_ok
|
||||
-- ,wire_target_serial_id
|
||||
-- INTO
|
||||
-- kycok
|
||||
-- ,account_uuid
|
||||
-- FROM exchange.reserves_in
|
||||
-- JOIN wire_targets ON (wire_source_h_payto = wire_target_h_payto)
|
||||
-- WHERE reserve_pub=rpub
|
||||
-- LIMIT 1; -- limit 1 should not be required (without p2p transfers)
|
||||
|
||||
WITH my_reserves_in AS materialized (
|
||||
SELECT wire_source_h_payto
|
||||
FROM exchange.reserves_in
|
||||
WHERE reserve_pub=rpub
|
||||
)
|
||||
SELECT
|
||||
kyc_ok
|
||||
,wire_target_serial_id
|
||||
INTO
|
||||
kycok
|
||||
,account_uuid
|
||||
FROM exchange.wire_targets
|
||||
WHERE wire_target_h_payto = (
|
||||
SELECT wire_source_h_payto
|
||||
FROM my_reserves_in
|
||||
);
|
||||
|
||||
END $$;
|
||||
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
|
||||
IS 'Checks whether the reserve has sufficient balance for a withdraw operation (or the request is repeated and was previously approved) and if so updates the database with the result';
|
||||
|
||||
@ -259,8 +216,6 @@ CREATE OR REPLACE FUNCTION exchange_do_batch_withdraw(
|
||||
IN min_reserve_gc INT8,
|
||||
OUT reserve_found BOOLEAN,
|
||||
OUT balance_ok BOOLEAN,
|
||||
OUT kycok BOOLEAN,
|
||||
OUT account_uuid INT8,
|
||||
OUT ruuid INT8)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@ -295,8 +250,6 @@ THEN
|
||||
-- reserve unknown
|
||||
reserve_found=FALSE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE;
|
||||
account_uuid=0;
|
||||
ruuid=2;
|
||||
RETURN;
|
||||
END IF;
|
||||
@ -320,8 +273,6 @@ ELSE
|
||||
ELSE
|
||||
reserve_found=TRUE;
|
||||
balance_ok=FALSE;
|
||||
kycok=FALSE; -- we do not really know or care
|
||||
account_uuid=0;
|
||||
RETURN;
|
||||
END IF;
|
||||
END IF;
|
||||
@ -340,37 +291,6 @@ WHERE
|
||||
reserve_found=TRUE;
|
||||
balance_ok=TRUE;
|
||||
|
||||
|
||||
-- Obtain KYC status based on the last wire transfer into
|
||||
-- this reserve. FIXME: likely not adequate for reserves that got P2P transfers!
|
||||
-- SELECT
|
||||
-- kyc_ok
|
||||
-- ,wire_target_serial_id
|
||||
-- INTO
|
||||
-- kycok
|
||||
-- ,account_uuid
|
||||
-- FROM exchange.reserves_in
|
||||
-- JOIN wire_targets ON (wire_source_h_payto = wire_target_h_payto)
|
||||
-- WHERE reserve_pub=rpub
|
||||
-- LIMIT 1; -- limit 1 should not be required (without p2p transfers)
|
||||
|
||||
WITH my_reserves_in AS materialized (
|
||||
SELECT wire_source_h_payto
|
||||
FROM exchange.reserves_in
|
||||
WHERE reserve_pub=rpub
|
||||
)
|
||||
SELECT
|
||||
kyc_ok
|
||||
,wire_target_serial_id
|
||||
INTO
|
||||
kycok
|
||||
,account_uuid
|
||||
FROM exchange.wire_targets
|
||||
WHERE wire_target_h_payto = (
|
||||
SELECT wire_source_h_payto
|
||||
FROM my_reserves_in
|
||||
);
|
||||
|
||||
END $$;
|
||||
|
||||
COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8)
|
||||
|
@ -1399,7 +1399,6 @@ run (void *cls)
|
||||
bool found;
|
||||
bool nonce_ok;
|
||||
bool balance_ok;
|
||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||
uint64_t ruuid;
|
||||
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
@ -1410,12 +1409,10 @@ run (void *cls)
|
||||
&found,
|
||||
&balance_ok,
|
||||
&nonce_ok,
|
||||
&kyc,
|
||||
&ruuid));
|
||||
GNUNET_assert (found);
|
||||
GNUNET_assert (nonce_ok);
|
||||
GNUNET_assert (balance_ok);
|
||||
GNUNET_assert (! kyc.ok);
|
||||
}
|
||||
FAILIF (GNUNET_OK !=
|
||||
check_reserve (&reserve_pub,
|
||||
@ -2159,7 +2156,6 @@ run (void *cls)
|
||||
plugin->get_ready_deposit (plugin->cls,
|
||||
0,
|
||||
INT32_MAX,
|
||||
true,
|
||||
&merchant_pub2,
|
||||
&payto_uri2));
|
||||
FAILIF (0 != GNUNET_memcmp (&merchant_pub2,
|
||||
@ -2205,6 +2201,7 @@ run (void *cls)
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
||||
plugin->select_aggregation_transient (plugin->cls,
|
||||
&wire_target_h_payto,
|
||||
&deposit.merchant_pub,
|
||||
"x-bank",
|
||||
&wtid2,
|
||||
&total2));
|
||||
@ -2212,11 +2209,13 @@ run (void *cls)
|
||||
plugin->create_aggregation_transient (plugin->cls,
|
||||
&wire_target_h_payto,
|
||||
"x-bank",
|
||||
&deposit.merchant_pub,
|
||||
&wtid,
|
||||
&total));
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->select_aggregation_transient (plugin->cls,
|
||||
&wire_target_h_payto,
|
||||
&deposit.merchant_pub,
|
||||
"x-bank",
|
||||
&wtid2,
|
||||
&total2));
|
||||
@ -2237,6 +2236,7 @@ run (void *cls)
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||
plugin->select_aggregation_transient (plugin->cls,
|
||||
&wire_target_h_payto,
|
||||
&deposit.merchant_pub,
|
||||
"x-bank",
|
||||
&wtid2,
|
||||
&total2));
|
||||
@ -2253,6 +2253,7 @@ run (void *cls)
|
||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
||||
plugin->select_aggregation_transient (plugin->cls,
|
||||
&wire_target_h_payto,
|
||||
&deposit.merchant_pub,
|
||||
"x-bank",
|
||||
&wtid2,
|
||||
&total2));
|
||||
|
@ -304,10 +304,16 @@ struct TALER_EXCHANGE_Keys
|
||||
struct GNUNET_TIME_Relative reserve_closing_delay;
|
||||
|
||||
/**
|
||||
* Maximum amount a wallet is allowed to hold from
|
||||
* this exchange before it must undergo a KYC check.
|
||||
* Array of amounts a wallet is allowed to hold from
|
||||
* this exchange before it must undergo further KYC checks.
|
||||
*/
|
||||
struct TALER_Amount wallet_balance_limit_without_kyc;
|
||||
struct TALER_Amount *wallet_balance_limit_without_kyc;
|
||||
|
||||
/**
|
||||
* Length of the @e wallet_balance_limit_without_kyc
|
||||
* array.
|
||||
*/
|
||||
unsigned int wblwk_length;
|
||||
|
||||
/**
|
||||
* Timestamp indicating the /keys generation.
|
||||
@ -3422,7 +3428,7 @@ typedef void
|
||||
* of a merchant.
|
||||
*
|
||||
* @param eh exchange handle to use
|
||||
* @param payment_target number identifying the target
|
||||
* @param legitimization_uuid number identifying the legitimization process
|
||||
* @param h_payto hash of the payto:// URI at @a payment_target
|
||||
* @param timeout how long to wait for a positive KYC status
|
||||
* @param cb function to call with the result
|
||||
@ -3431,7 +3437,7 @@ typedef void
|
||||
*/
|
||||
struct TALER_EXCHANGE_KycCheckHandle *
|
||||
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *eh,
|
||||
uint64_t payment_target,
|
||||
uint64_t legitimization_uuid,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
struct GNUNET_TIME_Relative timeout,
|
||||
TALER_EXCHANGE_KycStatusCallback cb,
|
||||
@ -3500,8 +3506,10 @@ struct TALER_EXCHANGE_KycProofHandle;
|
||||
*
|
||||
* @param eh exchange handle to use
|
||||
* @param h_payto hash of payto URI identifying the target account
|
||||
* @param code OAuth 2.0 code argument
|
||||
* @param state OAuth 2.0 state argument
|
||||
* @param logic name of the KYC logic to run
|
||||
* @param args additional args to pass, can be NULL
|
||||
* or a string to append to the URL. Must
|
||||
* then begin with '/' or '?'.
|
||||
* @param cb function to call with the result
|
||||
* @param cb_cls closure for @a cb
|
||||
* @return NULL on error
|
||||
@ -3509,8 +3517,8 @@ struct TALER_EXCHANGE_KycProofHandle;
|
||||
struct TALER_EXCHANGE_KycProofHandle *
|
||||
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *eh,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const char *code,
|
||||
const char *state,
|
||||
const char *logic,
|
||||
const char *args,
|
||||
TALER_EXCHANGE_KycProofCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
@ -3573,6 +3581,7 @@ typedef void
|
||||
*
|
||||
* @param eh exchange handle to use
|
||||
* @param reserve_priv wallet private key to check
|
||||
* @param balance balance (or balance threshold) crossed by the wallet
|
||||
* @param cb function to call with the result
|
||||
* @param cb_cls closure for @a cb
|
||||
* @return NULL on error
|
||||
@ -3580,6 +3589,7 @@ typedef void
|
||||
struct TALER_EXCHANGE_KycWalletHandle *
|
||||
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *eh,
|
||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||
const struct TALER_Amount *balance,
|
||||
TALER_EXCHANGE_KycWalletCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
|
@ -2327,11 +2327,13 @@ struct TALER_EXCHANGEDB_KycStatus
|
||||
* Number that identifies the KYC target the operation
|
||||
* was about.
|
||||
*/
|
||||
// FIXME: rename to 'legitimization_uuid'
|
||||
uint64_t payment_target_uuid;
|
||||
|
||||
/**
|
||||
* What kind of KYC operation is this?
|
||||
*/
|
||||
// FIXME: kill!
|
||||
enum TALER_EXCHANGEDB_KycType type;
|
||||
|
||||
/**
|
||||
@ -2582,6 +2584,26 @@ typedef enum GNUNET_GenericReturnValue
|
||||
const struct TALER_Amount *amount);
|
||||
|
||||
|
||||
/**
|
||||
* Function called on transient aggregations matching
|
||||
* a particular hash of a payto URI.
|
||||
*
|
||||
* @param cls
|
||||
* @param payto_uri corresponding payto URI
|
||||
* @param wtid wire transfer identifier of transient aggregation
|
||||
* @param merchant_pub public key of the merchant
|
||||
* @param total amount aggregated so far
|
||||
* @return true to continue iterating
|
||||
*/
|
||||
typedef bool
|
||||
(*TALER_EXCHANGEDB_TransientAggregationCallback)(
|
||||
void *cls,
|
||||
const char *payto_uri,
|
||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const struct TALER_Amount *total);
|
||||
|
||||
|
||||
/**
|
||||
* Callback with data about a prepared wire transfer.
|
||||
*
|
||||
@ -3097,6 +3119,21 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc);
|
||||
|
||||
|
||||
/**
|
||||
* Get the origin of funds of a reserve.
|
||||
*
|
||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @param[out] h_payto set to hash of the wire source payto://-URI
|
||||
* @return transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*reserves_get_origin)(
|
||||
void *cls,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||
struct TALER_PaytoHashP *h_payto);
|
||||
|
||||
|
||||
/**
|
||||
* Set the KYC status to "OK" for a bank account.
|
||||
*
|
||||
@ -3111,6 +3148,20 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
const char *id);
|
||||
|
||||
|
||||
/**
|
||||
* Extract next KYC alert. Deletes the alert.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param trigger_type which type of alert to drain
|
||||
* @param[out] h_payto set to hash of payto-URI where KYC status changed
|
||||
* @return transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*drain_kyc_alert)(void *cls,
|
||||
uint32_t trigger_type,
|
||||
struct TALER_PaytoHashP *h_payto);
|
||||
|
||||
|
||||
/**
|
||||
* Get the @a kyc status and @a h_payto by UUID.
|
||||
*
|
||||
@ -3207,7 +3258,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] nonce_ok set to false if the nonce was reused
|
||||
* @param[out] kyc set to the KYC status of the reserve
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -3220,7 +3270,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
bool *nonce_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
||||
uint64_t *ruuid);
|
||||
|
||||
|
||||
@ -3235,7 +3284,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param amount total amount to withdraw
|
||||
* @param[out] found set to true if the reserve was found
|
||||
* @param[out] balance_ok set to true if the balance was sufficient
|
||||
* @param[out] kyc set to the KYC status of the reserve
|
||||
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||
* @return query execution status
|
||||
*/
|
||||
@ -3247,7 +3295,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
const struct TALER_Amount *amount,
|
||||
bool *found,
|
||||
bool *balance_ok,
|
||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
||||
uint64_t *ruuid);
|
||||
|
||||
|
||||
@ -3692,8 +3739,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param start_shard_row minimum shard row to select
|
||||
* @param end_shard_row maximum shard row to select (inclusive)
|
||||
* @param kyc_off true if we should not check the KYC status because
|
||||
* this exchange does not need/support KYC checks.
|
||||
* @param[out] merchant_pub set to the public key of a merchant with a ready deposit
|
||||
* @param[out] payto_uri set to the account of the merchant, to be freed by caller
|
||||
* @return transaction status code
|
||||
@ -3702,7 +3747,6 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
(*get_ready_deposit)(void *cls,
|
||||
uint64_t start_shard_row,
|
||||
uint64_t end_shard_row,
|
||||
bool kyc_off,
|
||||
struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
char **payto_uri);
|
||||
|
||||
@ -3740,6 +3784,7 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param h_payto destination of the wire transfer
|
||||
* @param exchange_account_section exchange account to use
|
||||
* @param merchant_pub public key of the merchant
|
||||
* @param wtid the raw wire transfer identifier to be used
|
||||
* @param total amount to be wired in the future
|
||||
* @return transaction status
|
||||
@ -3749,15 +3794,17 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const char *exchange_account_section,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
const struct TALER_Amount *total);
|
||||
|
||||
|
||||
/**
|
||||
* Find existing entry in the transient aggregation table.
|
||||
* Select existing entry in the transient aggregation table.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param h_payto destination of the wire transfer
|
||||
* @param merchant_pub public key of the merchant
|
||||
* @param exchange_account_section exchange account to use
|
||||
* @param[out] wtid set to the raw wire transfer identifier to be used
|
||||
* @param[out] total existing amount to be wired in the future
|
||||
@ -3767,11 +3814,29 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
(*select_aggregation_transient)(
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
const char *exchange_account_section,
|
||||
struct TALER_WireTransferIdentifierRawP *wtid,
|
||||
struct TALER_Amount *total);
|
||||
|
||||
|
||||
/**
|
||||
* Find existing entry in the transient aggregation table.
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param h_payto destination of the wire transfer
|
||||
* @param cb function to call on each matching entry
|
||||
* @param cb_cls closure for @a cb
|
||||
* @return transaction status
|
||||
*/
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*find_aggregation_transient)(
|
||||
void *cls,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
TALER_EXCHANGEDB_TransientAggregationCallback cb,
|
||||
void *cb_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Update existing entry in the transient aggregation table.
|
||||
* @a h_payto is only needed for query performance.
|
||||
|
@ -162,6 +162,19 @@ typedef void
|
||||
void *cb_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Function called to iterate over KYC-relevant
|
||||
* transaction thresholds amounts.
|
||||
*
|
||||
* @param cls closure, identifies the event type and
|
||||
* account to iterate over events for
|
||||
* @param threshold a relevant threshold amount
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_KYCLOGIC_KycThresholdIterator)(void *cls,
|
||||
const struct TALER_Amount *threshold);
|
||||
|
||||
|
||||
/**
|
||||
* Call us on KYC processes satisfied for the given
|
||||
* account. Must match the ``select_satisfied_kyc_processes`` of the exchange database plugin.
|
||||
@ -209,6 +222,21 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
|
||||
void *ai_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Iterate over all thresholds that are applicable
|
||||
* to a particular type of @a event
|
||||
*
|
||||
* @param event tresholds to look up
|
||||
* @param it function to call on each
|
||||
* @param it_cls closure for @a it
|
||||
*/
|
||||
void
|
||||
TALER_KYCLOGIC_kyc_iterate_thresholds (
|
||||
enum TALER_KYCLOGIC_KycTriggerEvent event,
|
||||
TALER_KYCLOGIC_KycThresholdIterator it,
|
||||
void *it_cls);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the provider logic for a given @a provider_section_name.
|
||||
*
|
||||
|
@ -295,6 +295,7 @@ struct TALER_KYCLOGIC_Plugin
|
||||
* @param url_path rest of the URL after `/kyc-webhook/$H_PAYTO/$LOGIC`
|
||||
* @param connection MHD connection object (for HTTP headers)
|
||||
* @param account_id which account to trigger process for
|
||||
* @param legi_row row in the table the legitimization is for
|
||||
* @param provider_user_id user ID (or NULL) the proof is for
|
||||
* @param provider_legitimization_id legitimization ID the proof is for
|
||||
* @param cb function to call with the result
|
||||
@ -307,6 +308,7 @@ struct TALER_KYCLOGIC_Plugin
|
||||
const char *const url_path[],
|
||||
struct MHD_Connection *connection,
|
||||
const struct TALER_PaytoHashP *account_id,
|
||||
uint64_t legi_row,
|
||||
const char *provider_user_id,
|
||||
const char *provider_legitimization_id,
|
||||
TALER_KYCLOGIC_ProofCallback cb,
|
||||
|
@ -2421,12 +2421,14 @@ TALER_TESTING_cmd_revoke_sign_key (
|
||||
*
|
||||
* @param label command label.
|
||||
* @param reserve_reference command with reserve private key to use (or NULL to create a fresh reserve key).
|
||||
* @param threshold_balance balance amount to pass to the exchange
|
||||
* @param expected_response_code expected HTTP status
|
||||
* @return the command
|
||||
*/
|
||||
struct TALER_TESTING_Command
|
||||
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
||||
const char *reserve_reference,
|
||||
const char *threshold_balance,
|
||||
unsigned int expected_response_code);
|
||||
|
||||
|
||||
@ -2445,21 +2447,26 @@ TALER_TESTING_cmd_check_kyc_get (const char *label,
|
||||
|
||||
|
||||
/**
|
||||
* Create a KYC proof request.
|
||||
* Create a KYC proof request. Only useful in conjunction with the OAuth2.0
|
||||
* logic, as it generates an OAuth2.0-specific request.
|
||||
*
|
||||
* @param label command label.
|
||||
* @param payment_target_reference command with a payment target to query
|
||||
* @param logic_section name of the KYC provider section
|
||||
* in the exchange configuration for this proof
|
||||
* @param code OAuth 2.0 code to use
|
||||
* @param state OAuth 2.0 state to use
|
||||
* @param expected_response_code expected HTTP status
|
||||
* @return the command
|
||||
*/
|
||||
struct TALER_TESTING_Command
|
||||
TALER_TESTING_cmd_proof_kyc (const char *label,
|
||||
const char *payment_target_reference,
|
||||
const char *code,
|
||||
const char *state,
|
||||
unsigned int expected_response_code);
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 (
|
||||
const char *label,
|
||||
const char *payment_target_reference,
|
||||
const char *logic_section,
|
||||
const char *code,
|
||||
const char *state,
|
||||
unsigned int expected_response_code);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -361,19 +361,6 @@ char *
|
||||
TALER_payto_get_method (const char *payto_uri);
|
||||
|
||||
|
||||
/**
|
||||
* Construct a payto://-URI from a Taler @a reserve_pub at
|
||||
* @a exchange_base_url
|
||||
*
|
||||
* @param exchange_base_url the URL of the exchange
|
||||
* @param reserve_pub public key of the reserve
|
||||
* @return payto:// URI encoding the reserve's address
|
||||
*/
|
||||
char *
|
||||
TALER_payto_from_reserve (const char *exchange_base_url,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the account name from a payto URL.
|
||||
*
|
||||
|
@ -279,12 +279,22 @@ load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
||||
GNUNET_asprintf (&lib_name,
|
||||
"libtaler_plugin_kyclogic_%s",
|
||||
name);
|
||||
for (unsigned int i = 0; i<num_kyc_logics; i++)
|
||||
if (0 == strcmp (lib_name,
|
||||
kyc_logics[i]->library_name))
|
||||
{
|
||||
GNUNET_free (lib_name);
|
||||
return kyc_logics[i];
|
||||
}
|
||||
plugin = GNUNET_PLUGIN_load (lib_name,
|
||||
(void *) cfg);
|
||||
if (NULL != plugin)
|
||||
plugin->library_name = lib_name;
|
||||
else
|
||||
GNUNET_free (lib_name);
|
||||
GNUNET_array_append (kyc_logics,
|
||||
num_kyc_logics,
|
||||
plugin);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@ -471,6 +481,14 @@ add_provider (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse configuration @a cfg in section @a section for
|
||||
* the specification of a KYC trigger.
|
||||
*
|
||||
* @param cfg configuration to parse
|
||||
* @param section configuration section to parse
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
||||
const char *section)
|
||||
@ -797,6 +815,9 @@ eval_trigger (void *cls,
|
||||
struct GNUNET_TIME_Relative duration;
|
||||
bool bump = true;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check with new amount %s\n",
|
||||
TALER_amount2s (amount));
|
||||
duration = GNUNET_TIME_absolute_get_duration (date);
|
||||
if (ttc->have_total)
|
||||
{
|
||||
@ -812,19 +833,31 @@ eval_trigger (void *cls,
|
||||
else
|
||||
{
|
||||
ttc->total = *amount;
|
||||
ttc->have_total = true;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check: new total is %s\n",
|
||||
TALER_amount2s (&ttc->total));
|
||||
for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)
|
||||
{
|
||||
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
||||
|
||||
if (ttc->event != kt->trigger)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check #%u: trigger type does not match\n",
|
||||
i);
|
||||
continue;
|
||||
}
|
||||
duration = GNUNET_TIME_relative_max (duration,
|
||||
kt->timeframe);
|
||||
if (GNUNET_TIME_relative_cmp (kt->timeframe,
|
||||
>,
|
||||
duration))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check #%u: amount is beyond time limit\n",
|
||||
i);
|
||||
if (bump)
|
||||
ttc->start = i;
|
||||
return GNUNET_OK;
|
||||
@ -833,6 +866,9 @@ eval_trigger (void *cls,
|
||||
TALER_amount_cmp (&ttc->total,
|
||||
&kt->threshold))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check #%u: amount is below treshold\n",
|
||||
i);
|
||||
if (bump)
|
||||
ttc->start = i;
|
||||
bump = false;
|
||||
@ -848,6 +884,9 @@ eval_trigger (void *cls,
|
||||
for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
|
||||
if (ttc->needed[k] == rc)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC rule #%u already listed\n",
|
||||
j);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -857,6 +896,11 @@ eval_trigger (void *cls,
|
||||
(*ttc->needed_cnt)++;
|
||||
}
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"KYC check #%u (%s) is applicable, %u checks needed so far\n",
|
||||
i,
|
||||
ttc->needed[(*ttc->needed_cnt) - 1]->name,
|
||||
*ttc->needed_cnt);
|
||||
}
|
||||
if (bump)
|
||||
return GNUNET_NO; /* we hit all possible triggers! */
|
||||
@ -903,13 +947,22 @@ remove_satisfied (void *cls,
|
||||
if (0 != strcasecmp (provider_name,
|
||||
kp->provider_section_name))
|
||||
continue;
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Provider `%s' satisfied\n",
|
||||
provider_name);
|
||||
for (unsigned int j = 0; j<kp->num_checks; j++)
|
||||
{
|
||||
const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Provider satisfies check `%s'\n",
|
||||
kc->name);
|
||||
for (unsigned int k = 0; k<*rc->needed_cnt; k++)
|
||||
if (kc == rc->needed[k])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Removing check `%s' from list\n",
|
||||
kc->name);
|
||||
rc->needed[k] = rc->needed[*rc->needed_cnt - 1];
|
||||
(*rc->needed_cnt)--;
|
||||
if (0 == *rc->needed_cnt)
|
||||
@ -973,15 +1026,14 @@ TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
|
||||
|
||||
/* Check what provider checks are already satisfied for h_payto (with
|
||||
database), remove those from the 'needed' array. */
|
||||
GNUNET_break (0);
|
||||
// FIXME: do via callback!
|
||||
qs = ki (ki_cls,
|
||||
h_payto,
|
||||
&
|
||||
remove_satisfied,
|
||||
&remove_satisfied,
|
||||
&rc);
|
||||
GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
|
||||
}
|
||||
if (0 == needed_cnt)
|
||||
return NULL;
|
||||
|
||||
/* Count maximum number of remaining checks covered by any
|
||||
provider */
|
||||
@ -1059,4 +1111,22 @@ TALER_KYCLOGIC_kyc_get_logic (const char *provider_section_name,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TALER_KYCLOGIC_kyc_iterate_thresholds (
|
||||
enum TALER_KYCLOGIC_KycTriggerEvent event,
|
||||
TALER_KYCLOGIC_KycThresholdIterator it,
|
||||
void *it_cls)
|
||||
{
|
||||
for (unsigned int i = 0; i<num_kyc_triggers; i++)
|
||||
{
|
||||
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
||||
|
||||
if (event != kt->trigger)
|
||||
continue;
|
||||
it (it_cls,
|
||||
&kt->threshold);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* end of taler-exchange-httpd_kyc.c */
|
||||
|
@ -68,6 +68,11 @@ struct TALER_KYCLOGIC_ProviderDetails
|
||||
*/
|
||||
struct PluginState *ps;
|
||||
|
||||
/**
|
||||
* Configuration section that configured us.
|
||||
*/
|
||||
char *section;
|
||||
|
||||
/**
|
||||
* URL of the OAuth2.0 endpoint for KYC checks.
|
||||
* (token/auth)
|
||||
@ -265,6 +270,7 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
static void
|
||||
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
||||
{
|
||||
GNUNET_free (pd->section);
|
||||
GNUNET_free (pd->auth_url);
|
||||
GNUNET_free (pd->login_url);
|
||||
GNUNET_free (pd->info_url);
|
||||
@ -292,6 +298,7 @@ oauth2_load_configuration (void *cls,
|
||||
|
||||
pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
|
||||
pd->ps = ps;
|
||||
pd->section = GNUNET_strdup (provider_section_name);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
|
||||
provider_section_name,
|
||||
@ -467,9 +474,10 @@ initiate_task (void *cls)
|
||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
||||
sizeof (ih->h_payto));
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%s/kyc-proof/%s/oauth2/%s",
|
||||
"%s/kyc-proof/%s/%s/%s",
|
||||
ps->exchange_base_url,
|
||||
hps,
|
||||
pd->section,
|
||||
legi_s);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
@ -532,7 +540,11 @@ oauth2_initiate (void *cls,
|
||||
static void
|
||||
oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
|
||||
{
|
||||
GNUNET_SCHEDULER_cancel (ih->task);
|
||||
if (NULL != ih->task)
|
||||
{
|
||||
GNUNET_SCHEDULER_cancel (ih->task);
|
||||
ih->task = NULL;
|
||||
}
|
||||
GNUNET_free (ih);
|
||||
}
|
||||
|
||||
@ -659,6 +671,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (j,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
@ -691,6 +706,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
json_dumpf (data,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
@ -741,6 +759,9 @@ handle_curl_proof_finished (void *cls,
|
||||
j);
|
||||
break;
|
||||
default:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"OAuth2.0 info URL returned HTTP status %u\n",
|
||||
(unsigned int) response_code);
|
||||
handle_proof_error (ph,
|
||||
j);
|
||||
break;
|
||||
@ -750,6 +771,147 @@ handle_curl_proof_finished (void *cls,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* After we are done with the CURL interaction we
|
||||
* need to fetch the user's account details.
|
||||
*
|
||||
* @param cls our `struct KycProofContext`
|
||||
* @param response_code HTTP response code from server, 0 on hard error
|
||||
* @param response in JSON, NULL if response was not in JSON format
|
||||
*/
|
||||
static void
|
||||
handle_curl_login_finished (void *cls,
|
||||
long response_code,
|
||||
const void *response)
|
||||
{
|
||||
struct TALER_KYCLOGIC_ProofHandle *ph = cls;
|
||||
const json_t *j = response;
|
||||
|
||||
ph->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
const char *access_token;
|
||||
const char *token_type;
|
||||
uint64_t expires_in_s;
|
||||
const char *refresh_token;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("access_token",
|
||||
&access_token),
|
||||
GNUNET_JSON_spec_string ("token_type",
|
||||
&token_type),
|
||||
GNUNET_JSON_spec_uint64 ("expires_in",
|
||||
&expires_in_s),
|
||||
GNUNET_JSON_spec_string ("refresh_token",
|
||||
&refresh_token),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
CURL *eh;
|
||||
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
const char *emsg;
|
||||
unsigned int line;
|
||||
|
||||
res = GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
&emsg,
|
||||
&line);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected response from KYC gateway");
|
||||
ph->http_status
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 != strcasecmp (token_type,
|
||||
"bearer"))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Unexpected token type in response from KYC gateway");
|
||||
ph->http_status
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
|
||||
/* We guard against a few characters that could
|
||||
conceivably be abused to mess with the HTTP header */
|
||||
if ( (NULL != strchr (access_token,
|
||||
'\n')) ||
|
||||
(NULL != strchr (access_token,
|
||||
'\r')) ||
|
||||
(NULL != strchr (access_token,
|
||||
' ')) ||
|
||||
(NULL != strchr (access_token,
|
||||
';')) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
||||
"Illegal character in access token");
|
||||
ph->http_status
|
||||
= MHD_HTTP_BAD_GATEWAY;
|
||||
break;
|
||||
}
|
||||
|
||||
eh = curl_easy_init ();
|
||||
if (NULL == eh)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ph->response
|
||||
= TALER_MHD_make_error (
|
||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
||||
"curl_easy_init");
|
||||
ph->http_status
|
||||
= MHD_HTTP_INTERNAL_SERVER_ERROR;
|
||||
break;
|
||||
}
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_URL,
|
||||
ph->pd->info_url));
|
||||
{
|
||||
char *hdr;
|
||||
struct curl_slist *slist;
|
||||
|
||||
GNUNET_asprintf (&hdr,
|
||||
"%s: Bearer %s",
|
||||
MHD_HTTP_HEADER_AUTHORIZATION,
|
||||
access_token);
|
||||
slist = curl_slist_append (NULL,
|
||||
hdr);
|
||||
ph->job = GNUNET_CURL_job_add2 (ph->pd->ps->curl_ctx,
|
||||
eh,
|
||||
slist,
|
||||
&handle_curl_proof_finished,
|
||||
ph);
|
||||
curl_slist_free_all (slist);
|
||||
GNUNET_free (hdr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"OAuth2.0 login URL returned HTTP status %u\n",
|
||||
(unsigned int) response_code);
|
||||
handle_proof_error (ph,
|
||||
j);
|
||||
break;
|
||||
}
|
||||
return_proof_response (ph);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check KYC status and return status to human.
|
||||
*
|
||||
@ -758,6 +920,7 @@ handle_curl_proof_finished (void *cls,
|
||||
* @param url_path rest of the URL after `/kyc-webhook/`
|
||||
* @param connection MHD connection object (for HTTP headers)
|
||||
* @param account_id which account to trigger process for
|
||||
* @param legi_row row in the table the legitimization is for
|
||||
* @param provider_user_id user ID (or NULL) the proof is for
|
||||
* @param provider_legitimization_id legitimization ID the proof is for
|
||||
* @param cb function to call with the result
|
||||
@ -770,6 +933,7 @@ oauth2_proof (void *cls,
|
||||
const char *const url_path[],
|
||||
struct MHD_Connection *connection,
|
||||
const struct TALER_PaytoHashP *account_id,
|
||||
uint64_t legi_row,
|
||||
const char *provider_user_id,
|
||||
const char *provider_legitimization_id,
|
||||
TALER_KYCLOGIC_ProofCallback cb,
|
||||
@ -779,16 +943,20 @@ oauth2_proof (void *cls,
|
||||
struct TALER_KYCLOGIC_ProofHandle *ph;
|
||||
const char *code;
|
||||
|
||||
if (strlen (provider_legitimization_id) >=
|
||||
sizeof (ph->provider_legitimization_id))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
GNUNET_break (NULL == provider_user_id);
|
||||
ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
|
||||
strcpy (ph->provider_legitimization_id,
|
||||
provider_legitimization_id);
|
||||
GNUNET_snprintf (ph->provider_legitimization_id,
|
||||
sizeof (ph->provider_legitimization_id),
|
||||
"%llu",
|
||||
(unsigned long long) legi_row);
|
||||
if ( (NULL != provider_legitimization_id) &&
|
||||
(0 != strcmp (provider_legitimization_id,
|
||||
ph->provider_legitimization_id)))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (ph);
|
||||
return NULL;
|
||||
}
|
||||
ph->pd = pd;
|
||||
ph->connection = connection;
|
||||
ph->h_payto = *account_id;
|
||||
@ -891,7 +1059,7 @@ oauth2_proof (void *cls,
|
||||
|
||||
ph->job = GNUNET_CURL_job_add (ps->curl_ctx,
|
||||
ph->eh,
|
||||
&handle_curl_proof_finished,
|
||||
&handle_curl_login_finished,
|
||||
ph);
|
||||
return ph;
|
||||
}
|
||||
|
@ -253,6 +253,7 @@ template_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
|
||||
* @param url_path rest of the URL after `/kyc-webhook/`
|
||||
* @param connection MHD connection object (for HTTP headers)
|
||||
* @param account_id which account to trigger process for
|
||||
* @param legi_row row in the table the legitimization is for
|
||||
* @param provider_user_id user ID (or NULL) the proof is for
|
||||
* @param provider_legitimization_id legitimization ID the proof is for
|
||||
* @param cb function to call with the result
|
||||
@ -265,6 +266,7 @@ template_proof (void *cls,
|
||||
const char *const url_path[],
|
||||
struct MHD_Connection *connection,
|
||||
const struct TALER_PaytoHashP *account_id,
|
||||
uint64_t legi_row,
|
||||
const char *provider_user_id,
|
||||
const char *provider_legitimization_id,
|
||||
TALER_KYCLOGIC_ProofCallback cb,
|
||||
|
@ -754,6 +754,7 @@ handler_kyc_proof_get (
|
||||
&args[2],
|
||||
rc->connection,
|
||||
&h_payto,
|
||||
kyc_row_id,
|
||||
cmd_provider_user_id,
|
||||
cmd_provider_legitimization_id,
|
||||
&proof_cb,
|
||||
@ -1456,7 +1457,7 @@ main (int argc,
|
||||
"use the given provider user ID (overridden if -i is also used)",
|
||||
&cmd_provider_user_id),
|
||||
GNUNET_GETOPT_option_string (
|
||||
'l',
|
||||
'U',
|
||||
"legitimization",
|
||||
"ID",
|
||||
"use the given provider legitimization ID (overridden if -i is also used)",
|
||||
@ -1464,7 +1465,7 @@ main (int argc,
|
||||
GNUNET_GETOPT_option_base32_fixed_size (
|
||||
'p',
|
||||
"payto-hash",
|
||||
"URI",
|
||||
"HASH",
|
||||
"base32 encoding of the hash of a payto://-URI to use for the account (otherwise a random value will be used)",
|
||||
&cmd_line_h_payto,
|
||||
sizeof (cmd_line_h_payto)),
|
||||
|
@ -1458,50 +1458,6 @@ TALER_EXCHANGE_check_purse_create_conflict_ (
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
make_payto (const char *exchange_url,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub)
|
||||
{
|
||||
char pub_str[sizeof (*reserve_pub) * 2];
|
||||
char *end;
|
||||
bool is_http;
|
||||
char *reserve_url;
|
||||
|
||||
end = GNUNET_STRINGS_data_to_string (
|
||||
reserve_pub,
|
||||
sizeof (*reserve_pub),
|
||||
pub_str,
|
||||
sizeof (pub_str));
|
||||
*end = '\0';
|
||||
if (0 == strncmp (exchange_url,
|
||||
"http://",
|
||||
strlen ("http://")))
|
||||
{
|
||||
is_http = true;
|
||||
exchange_url = &exchange_url[strlen ("http://")];
|
||||
}
|
||||
else if (0 == strncmp (exchange_url,
|
||||
"https://",
|
||||
strlen ("https://")))
|
||||
{
|
||||
is_http = false;
|
||||
exchange_url = &exchange_url[strlen ("https://")];
|
||||
}
|
||||
else
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
/* exchange_url includes trailing '/' */
|
||||
GNUNET_asprintf (&reserve_url,
|
||||
"payto://%s/%s%s",
|
||||
is_http ? "taler+http" : "taler",
|
||||
exchange_url,
|
||||
pub_str);
|
||||
return reserve_url;
|
||||
}
|
||||
|
||||
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
||||
const struct TALER_PurseMergeSignatureP *cmerge_sig,
|
||||
@ -1539,8 +1495,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
|
||||
}
|
||||
if (NULL == partner_url)
|
||||
partner_url = exchange_url;
|
||||
payto_uri = make_payto (partner_url,
|
||||
&reserve_pub);
|
||||
payto_uri = TALER_reserve_make_payto (partner_url,
|
||||
&reserve_pub);
|
||||
if (GNUNET_OK !=
|
||||
TALER_wallet_purse_merge_verify (
|
||||
payto_uri,
|
||||
|
@ -176,11 +176,14 @@ handle_deposit_wtid_finished (void *cls,
|
||||
case MHD_HTTP_ACCEPTED:
|
||||
{
|
||||
/* Transaction known, but not executed yet */
|
||||
bool no_legi = false;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_timestamp ("execution_time",
|
||||
&dr.details.accepted.execution_time),
|
||||
GNUNET_JSON_spec_uint64 ("payment_target_uuid",
|
||||
&dr.details.accepted.payment_target_uuid),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_uint64 ("legitimization_uuid",
|
||||
&dr.details.accepted.payment_target_uuid),
|
||||
&no_legi),
|
||||
GNUNET_JSON_spec_bool ("kyc_ok",
|
||||
&dr.details.accepted.kyc_ok),
|
||||
GNUNET_JSON_spec_end ()
|
||||
@ -196,6 +199,8 @@ handle_deposit_wtid_finished (void *cls,
|
||||
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
if (no_legi)
|
||||
dr.details.accepted.payment_target_uuid = 0;
|
||||
dwh->cb (dwh->cb_cls,
|
||||
&dr);
|
||||
TALER_EXCHANGE_deposits_get_cancel (dwh);
|
||||
|
@ -736,6 +736,7 @@ decode_keys_json (const json_t *resp_obj,
|
||||
struct GNUNET_HashCode hash_xor = {0};
|
||||
struct TALER_ExchangePublicKeyP pub;
|
||||
const char *currency;
|
||||
json_t *wblwk = NULL;
|
||||
struct GNUNET_JSON_Specification mspec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("denominations_sig",
|
||||
&denominations_sig),
|
||||
@ -750,8 +751,8 @@ decode_keys_json (const json_t *resp_obj,
|
||||
GNUNET_JSON_spec_string ("currency",
|
||||
¤cy),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
TALER_JSON_spec_amount_any ("wallet_balance_limit_without_kyc",
|
||||
&key_data->wallet_balance_limit_without_kyc),
|
||||
GNUNET_JSON_spec_json ("wallet_balance_limit_without_kyc",
|
||||
&wblwk),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
@ -819,17 +820,6 @@ decode_keys_json (const json_t *resp_obj,
|
||||
NULL, NULL));
|
||||
key_data->currency = GNUNET_strdup (currency);
|
||||
|
||||
if (GNUNET_OK ==
|
||||
TALER_amount_is_valid (&key_data->wallet_balance_limit_without_kyc))
|
||||
{
|
||||
if (0 != strcasecmp (currency,
|
||||
key_data->wallet_balance_limit_without_kyc.currency))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the global fees */
|
||||
{
|
||||
json_t *global_fees;
|
||||
@ -882,6 +872,32 @@ decode_keys_json (const json_t *resp_obj,
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse balance limits */
|
||||
if (NULL != wblwk)
|
||||
{
|
||||
key_data->wblwk_length = json_array_size (wblwk);
|
||||
key_data->wallet_balance_limit_without_kyc
|
||||
= GNUNET_new_array (key_data->wblwk_length,
|
||||
struct TALER_Amount);
|
||||
for (unsigned int i = 0; i<key_data->wblwk_length; i++)
|
||||
{
|
||||
struct TALER_Amount *a = &key_data->wallet_balance_limit_without_kyc[i];
|
||||
const json_t *aj = json_array_get (wblwk,
|
||||
i);
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount (NULL,
|
||||
currency,
|
||||
a),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
EXITIF (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (aj,
|
||||
spec,
|
||||
NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the supported extension(s): age-restriction. */
|
||||
/* TODO: maybe lift all this into a FP in TALER_Extension ? */
|
||||
{
|
||||
@ -1210,6 +1226,7 @@ free_key_data (struct TALER_EXCHANGE_Keys *key_data)
|
||||
GNUNET_array_grow (key_data->auditors,
|
||||
key_data->auditors_size,
|
||||
0);
|
||||
GNUNET_free (key_data->wallet_balance_limit_without_kyc);
|
||||
GNUNET_free (key_data->version);
|
||||
GNUNET_free (key_data->currency);
|
||||
GNUNET_free (key_data->global_fees);
|
||||
|
@ -207,7 +207,7 @@ handle_kyc_check_finished (void *cls,
|
||||
|
||||
struct TALER_EXCHANGE_KycCheckHandle *
|
||||
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *exchange,
|
||||
uint64_t payment_target,
|
||||
uint64_t legitimization_uuid,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
struct GNUNET_TIME_Relative timeout,
|
||||
TALER_EXCHANGE_KycStatusCallback cb,
|
||||
@ -238,8 +238,8 @@ TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *exchange,
|
||||
timeout_ms = timeout.rel_value_us
|
||||
/ GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
|
||||
GNUNET_asprintf (&arg_str,
|
||||
"/kyc-check/%llu?h_payto=%s&timeout_ms=%llu",
|
||||
(unsigned long long) payment_target,
|
||||
"/kyc-check/%llu/%s?timeout_ms=%llu",
|
||||
(unsigned long long) legitimization_uuid,
|
||||
payto_str,
|
||||
timeout_ms);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2021 Taler Systems SA
|
||||
Copyright (C) 2021, 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
|
||||
@ -142,8 +142,8 @@ handle_kyc_proof_finished (void *cls,
|
||||
struct TALER_EXCHANGE_KycProofHandle *
|
||||
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
||||
const struct TALER_PaytoHashP *h_payto,
|
||||
const char *code,
|
||||
const char *state,
|
||||
const char *logic,
|
||||
const char *args,
|
||||
TALER_EXCHANGE_KycProofCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
@ -151,13 +151,17 @@ TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
||||
struct GNUNET_CURL_Context *ctx;
|
||||
char *arg_str;
|
||||
|
||||
if (NULL == args)
|
||||
args = "";
|
||||
else
|
||||
GNUNET_assert ( (args[0] == '?') ||
|
||||
(args[0] == '/') );
|
||||
if (GNUNET_YES !=
|
||||
TEAH_handle_is_ready (exchange))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
/* TODO: any escaping of code/state needed??? */
|
||||
{
|
||||
char hstr[sizeof (struct TALER_PaytoHashP) * 2];
|
||||
char *end;
|
||||
@ -168,10 +172,10 @@ TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
||||
sizeof (hstr));
|
||||
*end = '\0';
|
||||
GNUNET_asprintf (&arg_str,
|
||||
"/kyc-proof/%s?code=%s&state=%s",
|
||||
"/kyc-proof/%s/%s%s",
|
||||
hstr,
|
||||
code,
|
||||
state);
|
||||
logic,
|
||||
args);
|
||||
}
|
||||
kph = GNUNET_new (struct TALER_EXCHANGE_KycProofHandle);
|
||||
kph->exchange = exchange;
|
||||
|
@ -152,6 +152,7 @@ handle_kyc_wallet_finished (void *cls,
|
||||
struct TALER_EXCHANGE_KycWalletHandle *
|
||||
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
|
||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||
const struct TALER_Amount *balance,
|
||||
TALER_EXCHANGE_KycWalletCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
@ -167,6 +168,8 @@ TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
|
||||
TALER_wallet_account_setup_sign (reserve_priv,
|
||||
&reserve_sig);
|
||||
req = GNUNET_JSON_PACK (
|
||||
TALER_JSON_pack_amount ("balance",
|
||||
balance),
|
||||
GNUNET_JSON_pack_data_auto ("reserve_pub",
|
||||
&reserve_pub),
|
||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
||||
|
@ -232,7 +232,6 @@ handle_purse_merge_finished (void *cls,
|
||||
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&pch->merge_priv.eddsa_priv,
|
||||
&merge_pub.eddsa_pub);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
||||
&pch->merge_sig,
|
||||
|
@ -264,28 +264,6 @@ handle_reserve_withdraw_finished (void *cls,
|
||||
GNUNET_assert (NULL == wh->cb);
|
||||
TALER_EXCHANGE_withdraw2_cancel (wh);
|
||||
return;
|
||||
case MHD_HTTP_ACCEPTED:
|
||||
/* only validate reply is well-formed */
|
||||
{
|
||||
uint64_t ptu;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint64 ("payment_target_uuid",
|
||||
&ptu),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
@ -333,6 +311,28 @@ handle_reserve_withdraw_finished (void *cls,
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
||||
/* only validate reply is well-formed */
|
||||
{
|
||||
uint64_t ptu;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint64 ("payment_target_uuid",
|
||||
&ptu),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (j,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2021 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 General Public License as
|
||||
@ -124,11 +124,12 @@ run (void *cls,
|
||||
"EUR:5",
|
||||
0, /* age restriction off */
|
||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS),
|
||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc",
|
||||
"create-reserve-1",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc",
|
||||
"create-reserve-1",
|
||||
"kyc-provider-test-oauth2",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc",
|
||||
"create-reserve-1",
|
||||
"EUR:5",
|
||||
@ -158,26 +159,35 @@ run (void *cls,
|
||||
struct TALER_TESTING_Command track[] = {
|
||||
CMD_EXEC_AGGREGATOR ("run-aggregator-before-kyc"),
|
||||
TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-no-kyc"),
|
||||
TALER_TESTING_cmd_track_transaction (
|
||||
"track-deposit-kyc-ready",
|
||||
"deposit-simple",
|
||||
0,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
NULL),
|
||||
TALER_TESTING_cmd_check_kyc_get ("check-kyc-deposit",
|
||||
"track-deposit",
|
||||
"track-deposit-kyc-ready",
|
||||
MHD_HTTP_ACCEPTED),
|
||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-no-service",
|
||||
"track-deposit",
|
||||
"bad",
|
||||
"state",
|
||||
MHD_HTTP_BAD_GATEWAY),
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-no-service",
|
||||
"track-deposit-kyc-ready",
|
||||
"kyc-provider-test-oauth2",
|
||||
"bad",
|
||||
"state",
|
||||
MHD_HTTP_BAD_GATEWAY),
|
||||
TALER_TESTING_cmd_oauth ("start-oauth-service",
|
||||
6666),
|
||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-fail",
|
||||
"track-deposit",
|
||||
"bad",
|
||||
"state",
|
||||
MHD_HTTP_FORBIDDEN),
|
||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-fail",
|
||||
"track-deposit",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-fail",
|
||||
"track-deposit-kyc-ready",
|
||||
"kyc-provider-test-oauth2",
|
||||
"bad",
|
||||
"state",
|
||||
MHD_HTTP_FORBIDDEN),
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-fail",
|
||||
"track-deposit-kyc-ready",
|
||||
"kyc-provider-test-oauth2",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
CMD_EXEC_AGGREGATOR ("run-aggregator-after-kyc"),
|
||||
TALER_TESTING_cmd_check_bank_transfer (
|
||||
"check_bank_transfer-499c",
|
||||
@ -190,15 +200,19 @@ run (void *cls,
|
||||
};
|
||||
|
||||
struct TALER_TESTING_Command wallet_kyc[] = {
|
||||
TALER_TESTING_cmd_oauth ("start-oauth-service",
|
||||
6666),
|
||||
TALER_TESTING_cmd_wallet_kyc_get (
|
||||
"wallet-kyc-fail",
|
||||
NULL,
|
||||
"EUR:1000000",
|
||||
MHD_HTTP_OK),
|
||||
TALER_TESTING_cmd_proof_kyc ("proof-wallet-kyc",
|
||||
"wallet-kyc-fail",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-wallet-kyc",
|
||||
"wallet-kyc-fail",
|
||||
"kyc-provider-test-oauth2",
|
||||
"pass",
|
||||
"state",
|
||||
MHD_HTTP_SEE_OTHER),
|
||||
TALER_TESTING_cmd_check_kyc_get (
|
||||
"wallet-kyc-check",
|
||||
"wallet-kyc-fail",
|
||||
|
@ -46,16 +46,12 @@ DB = postgres
|
||||
BASE_URL = "http://localhost:8081/"
|
||||
|
||||
|
||||
# Obsolete options, migrate to withdraw once implemented...
|
||||
KYC_MODE = OAUTH2
|
||||
|
||||
KYC_WALLET_BALANCE_LIMIT = EUR:1
|
||||
|
||||
KYC_WITHDRAW_PERIOD = "31 days"
|
||||
|
||||
KYC_WITHDRAW_LIMIT = EUR:8
|
||||
|
||||
[exchange-kyc-oauth2]
|
||||
|
||||
KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token
|
||||
KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login
|
||||
KYC_INFO_URL = http://localhost:6666/api/user/me
|
||||
@ -63,6 +59,38 @@ KYC_OAUTH2_CLIENT_ID = taler-exchange
|
||||
KYC_OAUTH2_CLIENT_SECRET = exchange-secret
|
||||
KYC_OAUTH2_POST_URL = http://example.com/
|
||||
|
||||
# end of obsolete options...
|
||||
|
||||
[kyc-provider-test-oauth2]
|
||||
COST = 0
|
||||
LOGIC = oauth2
|
||||
USER_TYPE = INDIVIDUAL
|
||||
PROVIDED_CHECKS = DUMMY
|
||||
KYC_OAUTH2_VALIDITY = forever
|
||||
KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token
|
||||
KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login
|
||||
KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me
|
||||
KYC_OAUTH2_CLIENT_ID = taler-exchange
|
||||
KYC_OAUTH2_CLIENT_SECRET = exchange-secret
|
||||
KYC_OAUTH2_POST_URL = http://example.com/
|
||||
|
||||
[kyc-legitimization-balance-high]
|
||||
OPERATION_TYPE = BALANCE
|
||||
REQUIRED_CHECKS = DUMMY
|
||||
THRESHOLD = EUR:8
|
||||
|
||||
[kyc-legitimization-deposit-any]
|
||||
OPERATION_TYPE = DEPOSIT
|
||||
REQUIRED_CHECKS = DUMMY
|
||||
THRESHOLD = EUR:0
|
||||
TIMEFRAME = 1d
|
||||
|
||||
[kyc-legitimization-withdraw]
|
||||
OPERATION_TYPE = WITHDRAW
|
||||
REQUIRED_CHECKS = DUMMY
|
||||
THRESHOLD = EUR:8
|
||||
TIMEFRAME = 1d
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = "postgres:///talercheck"
|
||||
|
||||
|
@ -283,7 +283,7 @@ batch_withdraw_run (void *cls,
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
|
||||
&ws->reserve_pub.eddsa_pub);
|
||||
ws->reserve_payto_uri
|
||||
= TALER_payto_from_reserve (ws->exchange_url,
|
||||
= TALER_reserve_make_payto (ws->exchange_url,
|
||||
&ws->reserve_pub);
|
||||
|
||||
for (unsigned int i = 0; i<ws->num_coins; i++)
|
||||
|
@ -48,6 +48,11 @@ struct KycProofGetState
|
||||
*/
|
||||
const char *state;
|
||||
|
||||
/**
|
||||
* Logic section name to pass to `/kyc-proof/` handler.
|
||||
*/
|
||||
const char *logic;
|
||||
|
||||
/**
|
||||
* Expected HTTP response code.
|
||||
*/
|
||||
@ -133,6 +138,7 @@ proof_kyc_run (void *cls,
|
||||
const struct TALER_TESTING_Command *res_cmd;
|
||||
const char **payto_uri;
|
||||
struct TALER_PaytoHashP h_payto;
|
||||
char *uargs;
|
||||
|
||||
(void) cmd;
|
||||
kps->is = is;
|
||||
@ -169,12 +175,17 @@ proof_kyc_run (void *cls,
|
||||
TALER_payto_hash (*payto_uri,
|
||||
&h_payto);
|
||||
}
|
||||
GNUNET_asprintf (&uargs,
|
||||
"?code=%s&state=%s",
|
||||
kps->code,
|
||||
kps->state);
|
||||
kps->kph = TALER_EXCHANGE_kyc_proof (is->exchange,
|
||||
&h_payto,
|
||||
kps->code,
|
||||
kps->state,
|
||||
kps->logic,
|
||||
uargs,
|
||||
&proof_kyc_cb,
|
||||
kps);
|
||||
GNUNET_free (uargs);
|
||||
GNUNET_assert (NULL != kps->kph);
|
||||
}
|
||||
|
||||
@ -236,17 +247,20 @@ proof_kyc_traits (void *cls,
|
||||
|
||||
|
||||
struct TALER_TESTING_Command
|
||||
TALER_TESTING_cmd_proof_kyc (const char *label,
|
||||
const char *payment_target_reference,
|
||||
const char *code,
|
||||
const char *state,
|
||||
unsigned int expected_response_code)
|
||||
TALER_TESTING_cmd_proof_kyc_oauth2 (
|
||||
const char *label,
|
||||
const char *payment_target_reference,
|
||||
const char *logic_section,
|
||||
const char *code,
|
||||
const char *state,
|
||||
unsigned int expected_response_code)
|
||||
{
|
||||
struct KycProofGetState *kps;
|
||||
|
||||
kps = GNUNET_new (struct KycProofGetState);
|
||||
kps->code = code;
|
||||
kps->state = state;
|
||||
kps->logic = logic_section;
|
||||
kps->payment_target_reference = payment_target_reference;
|
||||
kps->expected_response_code = expected_response_code;
|
||||
{
|
||||
|
@ -69,6 +69,11 @@ struct KycWalletGetState
|
||||
*/
|
||||
struct TALER_EXCHANGE_KycWalletHandle *kwh;
|
||||
|
||||
/**
|
||||
* Balance to pass to the exchange.
|
||||
*/
|
||||
struct TALER_Amount balance;
|
||||
|
||||
/**
|
||||
* Interpreter state.
|
||||
*/
|
||||
@ -170,10 +175,11 @@ wallet_kyc_run (void *cls,
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&kwg->reserve_priv.eddsa_priv,
|
||||
&kwg->reserve_pub.eddsa_pub);
|
||||
kwg->reserve_payto_uri
|
||||
= TALER_payto_from_reserve (TALER_EXCHANGE_get_base_url (is->exchange),
|
||||
= TALER_reserve_make_payto (TALER_EXCHANGE_get_base_url (is->exchange),
|
||||
&kwg->reserve_pub);
|
||||
kwg->kwh = TALER_EXCHANGE_kyc_wallet (is->exchange,
|
||||
&kwg->reserve_priv,
|
||||
&kwg->balance,
|
||||
&wallet_kyc_cb,
|
||||
kwg);
|
||||
GNUNET_assert (NULL != kwg->kwh);
|
||||
@ -242,6 +248,7 @@ wallet_kyc_traits (void *cls,
|
||||
struct TALER_TESTING_Command
|
||||
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
||||
const char *reserve_reference,
|
||||
const char *threshold_balance,
|
||||
unsigned int expected_response_code)
|
||||
{
|
||||
struct KycWalletGetState *kwg;
|
||||
@ -249,6 +256,9 @@ TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
||||
kwg = GNUNET_new (struct KycWalletGetState);
|
||||
kwg->reserve_reference = reserve_reference;
|
||||
kwg->expected_response_code = expected_response_code;
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_string_to_amount (threshold_balance,
|
||||
&kwg->balance));
|
||||
{
|
||||
struct TALER_TESTING_Command cmd = {
|
||||
.cls = kwg,
|
||||
|
@ -320,6 +320,9 @@ reserve_withdraw_cb (void *cls,
|
||||
case MHD_HTTP_GONE:
|
||||
/* theoretically could check that the key was actually */
|
||||
break;
|
||||
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
||||
/* KYC required */
|
||||
break;
|
||||
default:
|
||||
/* Unsupported status code (by test harness) */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
@ -375,7 +378,7 @@ withdraw_run (void *cls,
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
|
||||
&ws->reserve_pub.eddsa_pub);
|
||||
ws->reserve_payto_uri
|
||||
= TALER_payto_from_reserve (ws->exchange_url,
|
||||
= TALER_reserve_make_payto (ws->exchange_url,
|
||||
&ws->reserve_pub);
|
||||
|
||||
if (NULL == ws->reuse_coin_key_ref)
|
||||
|
@ -273,45 +273,6 @@ TALER_payto_hash (const char *payto,
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
TALER_payto_from_reserve (const char *exchange_base_url,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub)
|
||||
{
|
||||
char *payto_uri;
|
||||
char *rps;
|
||||
unsigned int skip;
|
||||
const char *extra = "";
|
||||
int url_len;
|
||||
|
||||
rps = GNUNET_STRINGS_data_to_string_alloc (reserve_pub,
|
||||
sizeof (*reserve_pub));
|
||||
skip = 0;
|
||||
if (0 == strncasecmp (exchange_base_url,
|
||||
"http://",
|
||||
strlen ("http://")))
|
||||
{
|
||||
skip = strlen ("http://");
|
||||
extra = "+http";
|
||||
}
|
||||
if (0 == strncasecmp (exchange_base_url,
|
||||
"https://",
|
||||
strlen ("https://")))
|
||||
skip = strlen ("https://");
|
||||
url_len = strlen (exchange_base_url);
|
||||
if ('/' == exchange_base_url[url_len - 1])
|
||||
url_len--;
|
||||
url_len -= skip;
|
||||
GNUNET_asprintf (&payto_uri,
|
||||
"taler%s://reserve/%.*s/%s",
|
||||
extra,
|
||||
url_len,
|
||||
exchange_base_url + skip,
|
||||
rps);
|
||||
GNUNET_free (rps);
|
||||
return payto_uri;
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
TALER_reserve_make_payto (const char *exchange_url,
|
||||
const struct TALER_ReservePublicKeyP *reserve_pub)
|
||||
@ -349,7 +310,7 @@ TALER_reserve_make_payto (const char *exchange_url,
|
||||
/* exchange_url includes trailing '/' */
|
||||
GNUNET_asprintf (&reserve_url,
|
||||
"payto://%s/%s%s",
|
||||
is_http ? "taler+http" : "taler",
|
||||
is_http ? "taler-reserve+http" : "taler-reserve",
|
||||
exchange_url,
|
||||
pub_str);
|
||||
return reserve_url;
|
||||
|
Loading…
Reference in New Issue
Block a user