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.
|
NAME = Merchant Inc.
|
||||||
|
|
||||||
[exchange-account-1]
|
[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_debit = yes
|
||||||
enable_credit = yes
|
enable_credit = yes
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ HONOR_default = YES
|
|||||||
ACTIVE_default = YES
|
ACTIVE_default = YES
|
||||||
|
|
||||||
[merchant-exchange-default]
|
[merchant-exchange-default]
|
||||||
MASTER_KEY = BV7EQTVVH06981REGB1EZKNBTYK2WMAZWD0SDXK9RSBVNXPBCW9G
|
MASTER_KEY = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0
|
||||||
EXCHANGE_BASE_URL = http://localhost:8081/
|
EXCHANGE_BASE_URL = http://localhost:8081/
|
||||||
CURRENCY = TESTKUDOS
|
CURRENCY = TESTKUDOS
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
|
|||||||
CONFIG = postgres:///auditor-basedb
|
CONFIG = postgres:///auditor-basedb
|
||||||
|
|
||||||
[exchange]
|
[exchange]
|
||||||
MASTER_PUBLIC_KEY = BV7EQTVVH06981REGB1EZKNBTYK2WMAZWD0SDXK9RSBVNXPBCW9G
|
MASTER_PUBLIC_KEY = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0
|
||||||
SIGNKEY_DURATION = 4 weeks
|
SIGNKEY_DURATION = 4 weeks
|
||||||
LOOKAHEAD_SIGN = 32 weeks 1 day
|
LOOKAHEAD_SIGN = 32 weeks 1 day
|
||||||
SIGNKEY_LEGAL_DURATION = 4 weeks
|
SIGNKEY_LEGAL_DURATION = 4 weeks
|
||||||
@ -177,7 +177,7 @@ CONFIG = postgres:///auditor-basedb
|
|||||||
[auditor]
|
[auditor]
|
||||||
BASE_URL = http://localhost:8083/
|
BASE_URL = http://localhost:8083/
|
||||||
TINY_AMOUNT = TESTKUDOS:0.01
|
TINY_AMOUNT = TESTKUDOS:0.01
|
||||||
PUBLIC_KEY = EB8DHSV6EPXPVDEB3YN9X90MHJ9BEXD0KH5H4CQVC5HTK1AQJ1Y0
|
PUBLIC_KEY = JZYPE53YY23MQ0HTTV3DYHRABW4RM6SJS1Y0HF2HMSEPEPRJ77WG
|
||||||
|
|
||||||
[PATHS]
|
[PATHS]
|
||||||
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
|
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.c
|
||||||
taler_exchange_aggregator_LDADD = \
|
taler_exchange_aggregator_LDADD = \
|
||||||
$(LIBGCRYPT_LIBS) \
|
$(LIBGCRYPT_LIBS) \
|
||||||
|
$(top_builddir)/src/kyclogic/libtalerkyclogic.la \
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
$(top_builddir)/src/bank-lib/libtalerbank.la \
|
$(top_builddir)/src/bank-lib/libtalerbank.la \
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
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_lib.h"
|
||||||
#include "taler_exchangedb_plugin.h"
|
#include "taler_exchangedb_plugin.h"
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_bank_service.h"
|
#include "taler_bank_service.h"
|
||||||
|
|
||||||
|
|
||||||
@ -42,6 +43,12 @@ struct AggregationUnit
|
|||||||
*/
|
*/
|
||||||
struct TALER_MerchantPublicKeyP merchant_pub;
|
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.
|
* 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;
|
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;
|
static struct GNUNET_SCHEDULER_Task *task;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How long should we sleep when idle before trying to find more work?
|
* 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
|
* @param cls NULL
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
run_shard (void *cls);
|
drain_kyc_alerts (void *cls);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -226,6 +245,7 @@ shutdown_task (void *cls)
|
|||||||
GNUNET_SCHEDULER_cancel (task);
|
GNUNET_SCHEDULER_cancel (task);
|
||||||
task = NULL;
|
task = NULL;
|
||||||
}
|
}
|
||||||
|
TALER_KYCLOGIC_kyc_done ();
|
||||||
TALER_EXCHANGEDB_plugin_unload (db_plugin);
|
TALER_EXCHANGEDB_plugin_unload (db_plugin);
|
||||||
db_plugin = NULL;
|
db_plugin = NULL;
|
||||||
TALER_EXCHANGEDB_unload_accounts ();
|
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
|
static void
|
||||||
run_aggregation (void *cls)
|
run_aggregation (void *cls)
|
||||||
{
|
{
|
||||||
struct Shard *s = cls;
|
struct Shard *s = cls;
|
||||||
struct AggregationUnit au_active;
|
struct AggregationUnit au_active;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
struct TALER_Amount trans;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
bool have_transient = true; /* squash compiler warning */
|
|
||||||
|
|
||||||
task = NULL;
|
task = NULL;
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
@ -384,7 +720,6 @@ run_aggregation (void *cls)
|
|||||||
db_plugin->cls,
|
db_plugin->cls,
|
||||||
s->shard_start,
|
s->shard_start,
|
||||||
s->shard_end,
|
s->shard_end,
|
||||||
kyc_off ? true : false,
|
|
||||||
&au_active.merchant_pub,
|
&au_active.merchant_pub,
|
||||||
&au_active.payto_uri);
|
&au_active.payto_uri);
|
||||||
switch (qs)
|
switch (qs)
|
||||||
@ -432,10 +767,10 @@ run_aggregation (void *cls)
|
|||||||
/* If we ended up doing zero work, sleep a bit */
|
/* If we ended up doing zero work, sleep a bit */
|
||||||
if (0 == counter)
|
if (0 == counter)
|
||||||
task = GNUNET_SCHEDULER_add_delayed (aggregator_idle_sleep_interval,
|
task = GNUNET_SCHEDULER_add_delayed (aggregator_idle_sleep_interval,
|
||||||
&run_shard,
|
&drain_kyc_alerts,
|
||||||
NULL);
|
NULL);
|
||||||
else
|
else
|
||||||
task = GNUNET_SCHEDULER_add_now (&run_shard,
|
task = GNUNET_SCHEDULER_add_now (&drain_kyc_alerts,
|
||||||
NULL);
|
NULL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -444,254 +779,31 @@ run_aggregation (void *cls)
|
|||||||
/* continued below */
|
/* continued below */
|
||||||
break;
|
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,
|
TALER_payto_hash (au_active.payto_uri,
|
||||||
&au_active.h_payto);
|
&au_active.h_payto);
|
||||||
|
ret = do_aggregate (&au_active);
|
||||||
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);
|
|
||||||
cleanup_au (&au_active);
|
cleanup_au (&au_active);
|
||||||
|
switch (ret)
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
case GNUNET_SYSERR:
|
||||||
"Serialization issue for prepared wire data; trying again later!\n");
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
db_plugin->rollback (db_plugin->cls);
|
||||||
|
release_shard (s);
|
||||||
|
return;
|
||||||
|
case GNUNET_NO:
|
||||||
db_plugin->rollback (db_plugin->cls);
|
db_plugin->rollback (db_plugin->cls);
|
||||||
/* start again */
|
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||||
s);
|
s);
|
||||||
return;
|
return;
|
||||||
}
|
case GNUNET_OK:
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
/* continued below */
|
||||||
{
|
break;
|
||||||
GNUNET_break (0);
|
|
||||||
db_plugin->rollback (db_plugin->cls);
|
|
||||||
/* die hard */
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
GNUNET_SCHEDULER_shutdown ();
|
|
||||||
release_shard (s);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Stored wire transfer out instructions\n");
|
"Committing aggregation result\n");
|
||||||
|
|
||||||
/* Now we can finally commit the overall transaction, as we are
|
/* Now we can finally commit the overall transaction, as we are
|
||||||
again consistent if all of this passes. */
|
again consistent if all of this passes. */
|
||||||
@ -699,8 +811,8 @@ run_aggregation (void *cls)
|
|||||||
{
|
{
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
/* try again */
|
/* try again */
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Commit issue for prepared wire data; trying again later!\n");
|
"Serialization issue on commit; trying again later!\n");
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||||
s);
|
s);
|
||||||
@ -714,7 +826,7 @@ run_aggregation (void *cls)
|
|||||||
return;
|
return;
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Preparation complete, going again\n");
|
"Commit complete, going again\n");
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
|
||||||
s);
|
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.
|
* First task.
|
||||||
*
|
*
|
||||||
@ -811,7 +1114,8 @@ run (void *cls,
|
|||||||
(void) cfgfile;
|
(void) cfgfile;
|
||||||
|
|
||||||
cfg = c;
|
cfg = c;
|
||||||
if (GNUNET_OK != parse_aggregator_config ())
|
if (GNUNET_OK !=
|
||||||
|
parse_aggregator_config ())
|
||||||
{
|
{
|
||||||
cfg = NULL;
|
cfg = NULL;
|
||||||
global_ret = EXIT_NOTCONFIGURED;
|
global_ret = EXIT_NOTCONFIGURED;
|
||||||
@ -832,11 +1136,18 @@ run (void *cls,
|
|||||||
shard_size = 1U + INT32_MAX;
|
shard_size = 1U + INT32_MAX;
|
||||||
else
|
else
|
||||||
shard_size = (uint32_t) ass;
|
shard_size = (uint32_t) ass;
|
||||||
GNUNET_assert (NULL == task);
|
if (GNUNET_OK !=
|
||||||
task = GNUNET_SCHEDULER_add_now (&run_shard,
|
TALER_KYCLOGIC_kyc_init (cfg))
|
||||||
NULL);
|
{
|
||||||
|
cfg = NULL;
|
||||||
|
global_ret = EXIT_NOTCONFIGURED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
|
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 <sched.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_auditors.h"
|
#include "taler-exchange-httpd_auditors.h"
|
||||||
#include "taler-exchange-httpd_batch-deposit.h"
|
#include "taler-exchange-httpd_batch-deposit.h"
|
||||||
@ -1215,13 +1216,14 @@ handle_mhd_request (void *cls,
|
|||||||
.url = "kyc-check",
|
.url = "kyc-check",
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
.method = MHD_HTTP_METHOD_GET,
|
||||||
.handler.get = &TEH_handler_kyc_check,
|
.handler.get = &TEH_handler_kyc_check,
|
||||||
.nargs = 1
|
.nargs = 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.url = "kyc-proof",
|
.url = "kyc-proof",
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
.method = MHD_HTTP_METHOD_GET,
|
||||||
.handler.get = &TEH_handler_kyc_proof,
|
.handler.get = &TEH_handler_kyc_proof,
|
||||||
.nargs = 1
|
.nargs = 128,
|
||||||
|
.nargs_is_upper_bound = true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.url = "kyc-wallet",
|
.url = "kyc-wallet",
|
||||||
@ -1680,6 +1682,11 @@ parse_kyc_oauth_cfg (void)
|
|||||||
static enum GNUNET_GenericReturnValue
|
static enum GNUNET_GenericReturnValue
|
||||||
exchange_serve_process_config (void)
|
exchange_serve_process_config (void)
|
||||||
{
|
{
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TALER_KYCLOGIC_kyc_init (TEH_cfg))
|
||||||
|
{
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
{
|
{
|
||||||
char *kyc_mode;
|
char *kyc_mode;
|
||||||
|
|
||||||
@ -2094,8 +2101,12 @@ do_shutdown (void *cls)
|
|||||||
TEH_purses_get_cleanup ();
|
TEH_purses_get_cleanup ();
|
||||||
TEH_kyc_check_cleanup ();
|
TEH_kyc_check_cleanup ();
|
||||||
TEH_kyc_proof_cleanup ();
|
TEH_kyc_proof_cleanup ();
|
||||||
|
TALER_KYCLOGIC_kyc_done ();
|
||||||
if (NULL != mhd)
|
if (NULL != mhd)
|
||||||
|
{
|
||||||
MHD_stop_daemon (mhd);
|
MHD_stop_daemon (mhd);
|
||||||
|
mhd = NULL;
|
||||||
|
}
|
||||||
TEH_wire_done ();
|
TEH_wire_done ();
|
||||||
TEH_extensions_done ();
|
TEH_extensions_done ();
|
||||||
TEH_keys_finished ();
|
TEH_keys_finished ();
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_batch-withdraw.h"
|
#include "taler-exchange-httpd_batch-withdraw.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
@ -86,6 +87,17 @@ struct BatchWithdrawContext
|
|||||||
*/
|
*/
|
||||||
struct PlanchetContext *planchets;
|
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.
|
* 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
|
* Function implementing withdraw transaction. Runs the
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
* 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;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
bool balance_ok = false;
|
bool balance_ok = false;
|
||||||
bool found = 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,
|
qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
|
||||||
now,
|
now,
|
||||||
wc->reserve_pub,
|
wc->reserve_pub,
|
||||||
&wc->batch_total,
|
&wc->batch_total,
|
||||||
&found,
|
&found,
|
||||||
&balance_ok,
|
&balance_ok,
|
||||||
&wc->kyc,
|
|
||||||
&ruuid);
|
&ruuid);
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
@ -164,55 +246,6 @@ batch_withdraw_transaction (void *cls,
|
|||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
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 */
|
/* Add information about each planchet in the batch */
|
||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
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;
|
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 ();
|
sigs = json_array ();
|
||||||
GNUNET_assert (NULL != sigs);
|
GNUNET_assert (NULL != sigs);
|
||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
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,
|
TALER_amount_set_zero (TEH_currency,
|
||||||
&wc.batch_total));
|
&wc.batch_total));
|
||||||
wc.reserve_pub = reserve_pub;
|
wc.reserve_pub = reserve_pub;
|
||||||
|
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue res;
|
enum GNUNET_GenericReturnValue res;
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ deposit_transaction (void *cls,
|
|||||||
}
|
}
|
||||||
if (in_conflict)
|
if (in_conflict)
|
||||||
{
|
{
|
||||||
/* FIXME #7267: conficting contract != insufficient funds */
|
/* FIXME #7267: conflicting contract != insufficient funds */
|
||||||
*mhd_ret
|
*mhd_ret
|
||||||
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
= TEH_RESPONSE_reply_coin_insufficient_funds (
|
||||||
connection,
|
connection,
|
||||||
@ -426,7 +426,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
|||||||
&h_wire,
|
&h_wire,
|
||||||
&deposit.h_contract_terms,
|
&deposit.h_contract_terms,
|
||||||
&deposit.coin.h_age_commitment,
|
&deposit.coin.h_age_commitment,
|
||||||
NULL /* h_extensions! */,
|
NULL /* FIXME: h_extensions! */,
|
||||||
&deposit.coin.denom_pub_hash,
|
&deposit.coin.denom_pub_hash,
|
||||||
deposit.timestamp,
|
deposit.timestamp,
|
||||||
&deposit.merchant_pub,
|
&deposit.merchant_pub,
|
||||||
@ -481,7 +481,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
|
|||||||
res = reply_deposit_success (connection,
|
res = reply_deposit_success (connection,
|
||||||
&deposit.coin.coin_pub,
|
&deposit.coin.coin_pub,
|
||||||
&h_wire,
|
&h_wire,
|
||||||
NULL /* h_extensions! */,
|
NULL /* FIXME: h_extensions! */,
|
||||||
&deposit.h_contract_terms,
|
&deposit.h_contract_terms,
|
||||||
dc.exchange_timestamp,
|
dc.exchange_timestamp,
|
||||||
deposit.refund_deadline,
|
deposit.refund_deadline,
|
||||||
|
@ -40,12 +40,12 @@ struct DepositWtidContext
|
|||||||
/**
|
/**
|
||||||
* Hash over the proposal data of the contract for which this deposit is made.
|
* 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.
|
* 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
|
* 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 (
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
connection,
|
connection,
|
||||||
MHD_HTTP_ACCEPTED,
|
MHD_HTTP_ACCEPTED,
|
||||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
GNUNET_JSON_pack_allow_null (
|
||||||
ctx->kyc.payment_target_uuid),
|
(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",
|
GNUNET_JSON_pack_bool ("kyc_ok",
|
||||||
ctx->kyc.ok),
|
ctx->kyc.ok),
|
||||||
GNUNET_JSON_pack_timestamp ("execution_time",
|
GNUNET_JSON_pack_timestamp ("execution_time",
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_dbevents.h"
|
#include "taler_dbevents.h"
|
||||||
#include "taler-exchange-httpd.h"
|
#include "taler-exchange-httpd.h"
|
||||||
#include "taler-exchange-httpd_keys.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,
|
* Initialize @a krd using the given values for @a signkeys,
|
||||||
* @a recoup and @a denoms.
|
* @a recoup and @a denoms.
|
||||||
@ -1854,17 +1876,20 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
|||||||
GNUNET_assert (NULL != keys);
|
GNUNET_assert (NULL != keys);
|
||||||
|
|
||||||
/* Set wallet limit if KYC is configured */
|
/* 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 (
|
json_t *wblwk = NULL;
|
||||||
0 ==
|
|
||||||
json_object_set_new (
|
TALER_KYCLOGIC_kyc_iterate_thresholds (
|
||||||
keys,
|
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
||||||
"wallet_balance_limit_without_kyc",
|
&wallet_threshold_cb,
|
||||||
TALER_JSON_from_amount (
|
&wblwk);
|
||||||
&TEH_kyc_config.wallet_balance_limit)));
|
if (NULL != wblwk)
|
||||||
|
GNUNET_assert (
|
||||||
|
0 ==
|
||||||
|
json_object_set_new (
|
||||||
|
keys,
|
||||||
|
"wallet_balance_limit_without_kyc",
|
||||||
|
wblwk));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Signal support for the configured, enabled extensions. */
|
/* Signal support for the configured, enabled extensions. */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -25,6 +25,7 @@
|
|||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler_signatures.h"
|
#include "taler_signatures.h"
|
||||||
#include "taler_dbevents.h"
|
#include "taler_dbevents.h"
|
||||||
@ -53,6 +54,17 @@ struct KycPoller
|
|||||||
*/
|
*/
|
||||||
struct MHD_Connection *connection;
|
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
|
* Subscription for the database event we are
|
||||||
* waiting for.
|
* waiting for.
|
||||||
@ -62,12 +74,7 @@ struct KycPoller
|
|||||||
/**
|
/**
|
||||||
* UUID being checked.
|
* UUID being checked.
|
||||||
*/
|
*/
|
||||||
uint64_t auth_payment_target_uuid;
|
uint64_t legitimization_uuid;
|
||||||
|
|
||||||
/**
|
|
||||||
* Current KYC status.
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash of the payto:// URI we are confirming to
|
* Hash of the payto:// URI we are confirming to
|
||||||
@ -75,21 +82,46 @@ struct KycPoller
|
|||||||
*/
|
*/
|
||||||
struct TALER_PaytoHashP h_payto;
|
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?
|
* When will this request time out?
|
||||||
*/
|
*/
|
||||||
struct GNUNET_TIME_Absolute timeout;
|
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.
|
* True if we are still suspended.
|
||||||
*/
|
*/
|
||||||
bool 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,
|
GNUNET_CONTAINER_DLL_remove (kyp_head,
|
||||||
kyp_tail,
|
kyp_tail,
|
||||||
kyp);
|
kyp);
|
||||||
|
if (NULL != kyp->ih)
|
||||||
|
{
|
||||||
|
kyp->ih_logic->initiate_cancel (kyp->ih);
|
||||||
|
kyp->ih = NULL;
|
||||||
|
}
|
||||||
if (kyp->suspended)
|
if (kyp->suspended)
|
||||||
{
|
{
|
||||||
kyp->suspended = false;
|
kyp->suspended = false;
|
||||||
@ -143,10 +180,83 @@ kyp_cleanup (struct TEH_RequestContext *rc)
|
|||||||
kyp->eh);
|
kyp->eh);
|
||||||
kyp->eh = NULL;
|
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);
|
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.
|
* Function implementing database transaction to check wallet's KYC status.
|
||||||
* Runs the transaction logic; IF it returns a non-error code, the transaction
|
* 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;
|
struct KycPoller *kyp = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
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,
|
qs = TEH_plugin->lookup_kyc_requirement_by_row (
|
||||||
&kyp->h_payto,
|
TEH_plugin->cls,
|
||||||
&kyp->kyc);
|
kyp->legitimization_uuid,
|
||||||
if (qs < 0)
|
&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)
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
return qs;
|
"No KYC requirements open for %llu\n",
|
||||||
GNUNET_break (0);
|
(unsigned long long) kyp->legitimization_uuid);
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"inselect_wallet_status");
|
|
||||||
return qs;
|
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;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,7 +401,7 @@ db_event_cb (void *cls,
|
|||||||
MHD_RESULT
|
MHD_RESULT
|
||||||
TEH_handler_kyc_check (
|
TEH_handler_kyc_check (
|
||||||
struct TEH_RequestContext *rc,
|
struct TEH_RequestContext *rc,
|
||||||
const char *const args[])
|
const char *const args[2])
|
||||||
{
|
{
|
||||||
struct KycPoller *kyp = rc->rh_ctx;
|
struct KycPoller *kyp = rc->rh_ctx;
|
||||||
MHD_RESULT res;
|
MHD_RESULT res;
|
||||||
@ -245,24 +416,37 @@ TEH_handler_kyc_check (
|
|||||||
rc->rh_cleaner = &kyp_cleanup;
|
rc->rh_cleaner = &kyp_cleanup;
|
||||||
|
|
||||||
{
|
{
|
||||||
// FIXME: now 'legitimization_uuid'!
|
unsigned long long legitimization_uuid;
|
||||||
unsigned long long payment_target_uuid;
|
|
||||||
char dummy;
|
char dummy;
|
||||||
|
|
||||||
if (1 !=
|
if (1 !=
|
||||||
sscanf (args[0],
|
sscanf (args[0],
|
||||||
"%llu%c",
|
"%llu%c",
|
||||||
&payment_target_uuid,
|
&legitimization_uuid,
|
||||||
&dummy))
|
&dummy))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_BAD_REQUEST,
|
MHD_HTTP_BAD_REQUEST,
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
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;
|
const char *ts;
|
||||||
|
|
||||||
@ -291,41 +475,8 @@ TEH_handler_kyc_check (
|
|||||||
tms));
|
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) &&
|
if ( (NULL == kyp->eh) &&
|
||||||
GNUNET_TIME_absolute_is_future (kyp->timeout) )
|
GNUNET_TIME_absolute_is_future (kyp->timeout) )
|
||||||
{
|
{
|
||||||
@ -353,27 +504,48 @@ TEH_handler_kyc_check (
|
|||||||
&kyc_check,
|
&kyc_check,
|
||||||
kyp);
|
kyp);
|
||||||
if (GNUNET_SYSERR == ret)
|
if (GNUNET_SYSERR == ret)
|
||||||
return res;
|
|
||||||
|
|
||||||
if (kyp->auth_payment_target_uuid !=
|
|
||||||
kyp->kyc.payment_target_uuid)
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Account %llu provided, but payto %s is for %llu\n",
|
"Transaction failed.\n");
|
||||||
(unsigned long long) kyp->auth_payment_target_uuid,
|
return res;
|
||||||
kyp->hps,
|
}
|
||||||
(unsigned long long) kyp->kyc.payment_target_uuid);
|
|
||||||
GNUNET_break_op (0);
|
if ( (NULL == kyp->ih) &&
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
(! kyp->found) )
|
||||||
MHD_HTTP_FORBIDDEN,
|
{
|
||||||
TALER_EC_EXCHANGE_KYC_CHECK_AUTHORIZATION_FAILED,
|
/* KYC not required */
|
||||||
"h_payto");
|
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? */
|
/* long polling? */
|
||||||
if ( (! kyp->kyc.ok) &&
|
if ( (NULL != kyp->required) &&
|
||||||
GNUNET_TIME_absolute_is_future (kyp->timeout))
|
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);
|
GNUNET_assert (NULL != kyp->eh);
|
||||||
kyp->suspended = true;
|
kyp->suspended = true;
|
||||||
GNUNET_CONTAINER_DLL_insert (kyp_head,
|
GNUNET_CONTAINER_DLL_insert (kyp_head,
|
||||||
@ -383,37 +555,24 @@ TEH_handler_kyc_check (
|
|||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* KYC failed? */
|
/* KYC plugin generated reply? */
|
||||||
if (! kyp->kyc.ok)
|
if (NULL != kyp->kyc_url)
|
||||||
{
|
{
|
||||||
char *url;
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
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 (
|
|
||||||
rc->connection,
|
rc->connection,
|
||||||
MHD_HTTP_ACCEPTED,
|
MHD_HTTP_ACCEPTED,
|
||||||
GNUNET_JSON_pack_string ("kyc_url",
|
GNUNET_JSON_pack_string ("kyc_url",
|
||||||
url));
|
kyp->kyc_url));
|
||||||
GNUNET_free (url);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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_ExchangePublicKeyP pub;
|
||||||
struct TALER_ExchangeSignatureP sig;
|
struct TALER_ExchangeSignatureP sig;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -25,6 +25,7 @@
|
|||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_kyc-proof.h"
|
#include "taler-exchange-httpd_kyc-proof.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
@ -52,31 +53,37 @@ struct KycProofContext
|
|||||||
struct TEH_RequestContext *rc;
|
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.
|
* OAuth 2.0 authorization code.
|
||||||
*/
|
*/
|
||||||
const char *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.
|
* Hash of payment target URI this is about.
|
||||||
*/
|
*/
|
||||||
@ -87,17 +94,25 @@ struct KycProofContext
|
|||||||
*/
|
*/
|
||||||
struct MHD_Response *response;
|
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.
|
* HTTP response code to return.
|
||||||
*/
|
*/
|
||||||
unsigned int response_code;
|
unsigned int response_code;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #GNUNET_YES if we are suspended,
|
* True if we are suspended,
|
||||||
* #GNUNET_NO if not.
|
|
||||||
* #GNUNET_SYSERR if we had some error.
|
|
||||||
*/
|
*/
|
||||||
enum GNUNET_GenericReturnValue suspended;
|
bool suspended;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,7 +137,7 @@ static void
|
|||||||
kpc_resume (struct KycProofContext *kpc)
|
kpc_resume (struct KycProofContext *kpc)
|
||||||
{
|
{
|
||||||
GNUNET_assert (GNUNET_YES == kpc->suspended);
|
GNUNET_assert (GNUNET_YES == kpc->suspended);
|
||||||
kpc->suspended = GNUNET_NO;
|
kpc->suspended = false;
|
||||||
GNUNET_CONTAINER_DLL_remove (kpc_head,
|
GNUNET_CONTAINER_DLL_remove (kpc_head,
|
||||||
kpc_tail,
|
kpc_tail,
|
||||||
kpc);
|
kpc);
|
||||||
@ -138,10 +153,10 @@ TEH_kyc_proof_cleanup (void)
|
|||||||
|
|
||||||
while (NULL != (kpc = kpc_head))
|
while (NULL != (kpc = kpc_head))
|
||||||
{
|
{
|
||||||
if (NULL != kpc->job)
|
if (NULL != kpc->ph)
|
||||||
{
|
{
|
||||||
GNUNET_CURL_job_cancel (kpc->job);
|
kpc->logic->proof_cancel (kpc->ph);
|
||||||
kpc->job = NULL;
|
kpc->ph = NULL;
|
||||||
}
|
}
|
||||||
kpc_resume (kpc);
|
kpc_resume (kpc);
|
||||||
}
|
}
|
||||||
@ -149,348 +164,69 @@ TEH_kyc_proof_cleanup (void)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function implementing database transaction to check proof's KYC status.
|
* Function called with the result of a proof check operation.
|
||||||
* Runs the transaction logic; IF it returns a non-error code, the transaction
|
|
||||||
* logic MUST NOT queue a MHD response. IF it returns an hard error, the
|
|
||||||
* transaction logic MUST queue a MHD response and set @a mhd_ret. IF it
|
|
||||||
* returns the soft error code, the function MAY be called again to retry and
|
|
||||||
* MUST not queue a MHD response.
|
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct KycProofContext *`
|
* Note that the "decref" for the @a response
|
||||||
* @param connection MHD proof which triggered the transaction
|
* will be done by the callee and MUST NOT be done by the plugin.
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @param[in,out] kpc request that failed
|
* @param cls closure
|
||||||
* @param j reply from the server (or NULL)
|
* @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
|
static void
|
||||||
handle_error (struct KycProofContext *kpc,
|
proof_cb (
|
||||||
const json_t *j)
|
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;
|
struct KycProofContext *kpc = cls;
|
||||||
const char *desc;
|
struct TEH_RequestContext *rc = kpc->rc;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_AsyncScopeSave old_scope;
|
||||||
GNUNET_JSON_spec_string ("error",
|
|
||||||
&msg),
|
|
||||||
GNUNET_JSON_spec_string ("error_description",
|
|
||||||
&desc),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
|
|
||||||
|
kpc->ph = NULL;
|
||||||
|
GNUNET_async_scope_enter (&rc->async_scope_id,
|
||||||
|
&old_scope);
|
||||||
|
|
||||||
|
if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue res;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
const char *emsg;
|
|
||||||
unsigned int line;
|
|
||||||
|
|
||||||
res = GNUNET_JSON_parse (j,
|
qs = TEH_plugin->update_kyc_requirement_by_row (TEH_plugin->cls,
|
||||||
spec,
|
kpc->legi_row,
|
||||||
&emsg,
|
kpc->provider_section,
|
||||||
&line);
|
&kpc->h_payto,
|
||||||
if (GNUNET_OK != res)
|
provider_user_id,
|
||||||
|
provider_legitimization_id,
|
||||||
|
expiration);
|
||||||
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break (0);
|
||||||
kpc->response
|
kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
|
||||||
= TALER_MHD_make_error (
|
kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||||
TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE,
|
"set_kyc_ok");
|
||||||
"Unexpected response from KYC gateway");
|
GNUNET_async_scope_restore (&old_scope);
|
||||||
kpc->response_code
|
|
||||||
= MHD_HTTP_BAD_GATEWAY;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* case TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_AUTHORZATION_FAILED,
|
else
|
||||||
we MAY want to in the future look at the requested content type
|
|
||||||
and possibly respond in JSON if indicated. */
|
|
||||||
{
|
{
|
||||||
char *reply;
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"KYC logic #%llu failed with status %d\n",
|
||||||
GNUNET_asprintf (&reply,
|
(unsigned long long) kpc->legi_row,
|
||||||
"<html><head><title>%s</title></head><body><h1>%s</h1><p>%s</p></body></html>",
|
status);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
kpc->response_code = http_status;
|
||||||
|
kpc->response = response;
|
||||||
kpc_resume (kpc);
|
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;
|
struct KycProofContext *kpc = rc->rh_ctx;
|
||||||
|
|
||||||
if (NULL != kpc->job)
|
if (NULL != kpc->ph)
|
||||||
{
|
{
|
||||||
GNUNET_CURL_job_cancel (kpc->job);
|
kpc->logic->proof_cancel (kpc->ph);
|
||||||
kpc->job = NULL;
|
kpc->ph = NULL;
|
||||||
}
|
}
|
||||||
if (NULL != kpc->response)
|
if (NULL != kpc->response)
|
||||||
{
|
{
|
||||||
MHD_destroy_response (kpc->response);
|
MHD_destroy_response (kpc->response);
|
||||||
kpc->response = NULL;
|
kpc->response = NULL;
|
||||||
}
|
}
|
||||||
GNUNET_free (kpc->post_body);
|
GNUNET_free (kpc->provider_user_id);
|
||||||
GNUNET_free (kpc->token_url);
|
GNUNET_free (kpc->provider_legitimization_id);
|
||||||
GNUNET_free (kpc->id);
|
GNUNET_free (kpc->provider_section);
|
||||||
GNUNET_free (kpc);
|
GNUNET_free (kpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,7 +265,18 @@ TEH_handler_kyc_proof (
|
|||||||
struct KycProofContext *kpc = rc->rh_ctx;
|
struct KycProofContext *kpc = rc->rh_ctx;
|
||||||
|
|
||||||
if (NULL == kpc)
|
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 = GNUNET_new (struct KycProofContext);
|
||||||
kpc->rc = rc;
|
kpc->rc = rc;
|
||||||
rc->rh_ctx = kpc;
|
rc->rh_ctx = kpc;
|
||||||
@ -546,166 +293,98 @@ TEH_handler_kyc_proof (
|
|||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||||
"h_payto");
|
"h_payto");
|
||||||
}
|
}
|
||||||
kpc->authorization_code
|
kpc->provider_section = GNUNET_strdup (args[1]);
|
||||||
= MHD_lookup_connection_value (rc->connection,
|
if (GNUNET_OK !=
|
||||||
MHD_GET_ARGUMENT_KIND,
|
TALER_KYCLOGIC_kyc_get_logic (kpc->provider_section,
|
||||||
"code");
|
&kpc->logic,
|
||||||
if (NULL == kpc->authorization_code)
|
&kpc->pd))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_BAD_REQUEST,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
|
||||||
"code");
|
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 ();
|
qs = TEH_plugin->lookup_kyc_requirement_by_account (
|
||||||
if (NULL == eh)
|
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,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_GENERIC_ALLOCATION_FAILURE,
|
TALER_EC_EXCHANGE_KYC_PROOF_REQUEST_UNKNOWN,
|
||||||
"curl_easy_init");
|
kpc->provider_section);
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
GNUNET_asprintf (&kpc->token_url,
|
if (GNUNET_TIME_absolute_is_future (expiration))
|
||||||
"%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));
|
|
||||||
{
|
{
|
||||||
char *client_id;
|
/* KYC not required */
|
||||||
char *redirect_uri;
|
return TALER_MHD_reply_static (
|
||||||
char *client_secret;
|
rc->connection,
|
||||||
char *authorization_code;
|
MHD_HTTP_NO_CONTENT,
|
||||||
|
NULL,
|
||||||
client_id = curl_easy_escape (eh,
|
NULL,
|
||||||
TEH_kyc_config.details.oauth2.client_id,
|
0);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
kpc->ph = kpc->logic->proof (kpc->logic->cls,
|
||||||
|
kpc->pd,
|
||||||
if (NULL != kpc->response)
|
&args[2],
|
||||||
{
|
rc->connection,
|
||||||
/* handle _failed_ resumed cases */
|
&kpc->h_payto,
|
||||||
return MHD_queue_response (rc->connection,
|
kpc->legi_row,
|
||||||
kpc->response_code,
|
kpc->provider_user_id,
|
||||||
kpc->response);
|
kpc->provider_legitimization_id,
|
||||||
}
|
&proof_cb,
|
||||||
|
kpc);
|
||||||
/* _successfully_ resumed case */
|
if (NULL == kpc->ph)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
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,
|
kpc->suspended = true;
|
||||||
MHD_HTTP_HEADER_LOCATION,
|
GNUNET_CONTAINER_DLL_insert (kpc_head,
|
||||||
TEH_kyc_config.details.oauth2.post_kyc_redirect_url));
|
kpc_tail,
|
||||||
res = MHD_queue_response (rc->connection,
|
kpc);
|
||||||
MHD_HTTP_SEE_OTHER,
|
MHD_suspend_connection (rc->connection);
|
||||||
response);
|
return MHD_YES;
|
||||||
MHD_destroy_response (response);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
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
|
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
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -26,6 +26,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler-exchange-httpd_kyc-wallet.h"
|
#include "taler-exchange-httpd_kyc-wallet.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
|
|
||||||
@ -38,15 +39,54 @@ struct KycRequestContext
|
|||||||
/**
|
/**
|
||||||
* Public key of the reserve/wallet this is about.
|
* 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.
|
* Function implementing database transaction to check wallet's KYC status.
|
||||||
* Runs the transaction logic; IF it returns a non-error code, the transaction
|
* 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;
|
struct KycRequestContext *krc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
qs = TEH_plugin->inselect_wallet_kyc_status (TEH_plugin->cls,
|
krc->required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
&krc->reserve_pub,
|
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
||||||
&krc->kyc);
|
&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 (qs < 0)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
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_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"inselect_wallet_status");
|
"insert_kyc_requirement_for_account");
|
||||||
return qs;
|
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;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +154,17 @@ TEH_handler_kyc_wallet (
|
|||||||
{
|
{
|
||||||
struct TALER_ReserveSignatureP reserve_sig;
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
struct KycRequestContext krc;
|
struct KycRequestContext krc;
|
||||||
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||||
&reserve_sig),
|
&reserve_sig),
|
||||||
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
|
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 ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
MHD_RESULT res;
|
MHD_RESULT res;
|
||||||
@ -115,8 +180,10 @@ TEH_handler_kyc_wallet (
|
|||||||
return MHD_YES; /* failure */
|
return MHD_YES; /* failure */
|
||||||
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||||
|
// FIXME: add balance threshold crossed to
|
||||||
|
// what the wallet signs over!
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_wallet_account_setup_verify (&krc.reserve_pub,
|
TALER_wallet_account_setup_verify (&reserve_pub,
|
||||||
&reserve_sig))
|
&reserve_sig))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
@ -126,13 +193,19 @@ TEH_handler_kyc_wallet (
|
|||||||
TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
|
TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
if (TEH_KYC_NONE == TEH_kyc_config.mode)
|
{
|
||||||
return TALER_MHD_reply_static (
|
char *payto_uri;
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
payto_uri = TALER_reserve_make_payto (TEH_base_url,
|
||||||
NULL,
|
&reserve_pub);
|
||||||
NULL,
|
TALER_payto_hash (payto_uri,
|
||||||
0);
|
&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,
|
ret = TEH_DB_run_transaction (rc->connection,
|
||||||
"check wallet kyc",
|
"check wallet kyc",
|
||||||
TEH_MT_REQUEST_OTHER,
|
TEH_MT_REQUEST_OTHER,
|
||||||
@ -141,11 +214,21 @@ TEH_handler_kyc_wallet (
|
|||||||
&krc);
|
&krc);
|
||||||
if (GNUNET_SYSERR == ret)
|
if (GNUNET_SYSERR == ret)
|
||||||
return res;
|
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 (
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
|
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",
|
"Received payto: `%s'\n",
|
||||||
pcc.payto_uri);
|
pcc.payto_uri);
|
||||||
if ( (0 != strncmp (pcc.payto_uri,
|
if ( (0 != strncmp (pcc.payto_uri,
|
||||||
"payto://taler/",
|
"payto://taler-reserve/",
|
||||||
strlen ("payto://taler/"))) &&
|
strlen ("payto://taler-reserve/"))) &&
|
||||||
(0 != strncmp (pcc.payto_uri,
|
(0 != strncmp (pcc.payto_uri,
|
||||||
"payto://taler+http/",
|
"payto://taler-reserve+http/",
|
||||||
strlen ("payto://taler+http/"))) )
|
strlen ("payto://taler-reserve+http/"))) )
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (
|
return TALER_MHD_reply_with_error (
|
||||||
@ -436,13 +436,13 @@ TEH_handler_purses_merge (
|
|||||||
}
|
}
|
||||||
|
|
||||||
http = (0 == strncmp (pcc.payto_uri,
|
http = (0 == strncmp (pcc.payto_uri,
|
||||||
"payto://taler+http/",
|
"payto://taler-reserve+http/",
|
||||||
strlen ("payto://taler+http/")));
|
strlen ("payto://taler-reserve+http/")));
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *host = &pcc.payto_uri[http
|
const char *host = &pcc.payto_uri[http
|
||||||
? strlen ("payto://taler+http/")
|
? strlen ("payto://taler-reserve+http/")
|
||||||
: strlen ("payto://taler/")];
|
: strlen ("payto://taler-reserve/")];
|
||||||
const char *slash = strchr (host,
|
const char *slash = strchr (host,
|
||||||
'/');
|
'/');
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
@ -27,6 +27,7 @@
|
|||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_withdraw.h"
|
#include "taler-exchange-httpd_withdraw.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
@ -44,15 +45,6 @@ struct WithdrawContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_BlindedCoinHashP h_coin_envelope;
|
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.
|
* Blinded planchet.
|
||||||
*/
|
*/
|
||||||
@ -68,9 +60,66 @@ struct WithdrawContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
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
|
* Function implementing withdraw transaction. Runs the
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
@ -98,25 +147,55 @@ withdraw_transaction (void *cls,
|
|||||||
bool found = false;
|
bool found = false;
|
||||||
bool balance_ok = false;
|
bool balance_ok = false;
|
||||||
bool nonce_ok = false;
|
bool nonce_ok = false;
|
||||||
struct GNUNET_TIME_Timestamp now;
|
|
||||||
uint64_t ruuid;
|
uint64_t ruuid;
|
||||||
const struct TALER_CsNonce *nonce;
|
const struct TALER_CsNonce *nonce;
|
||||||
const struct TALER_BlindedPlanchet *bp;
|
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;
|
bp = &wc->blinded_planchet;
|
||||||
nonce =
|
nonce = (TALER_DENOMINATION_CS == bp->cipher)
|
||||||
(TALER_DENOMINATION_CS == bp->cipher)
|
|
||||||
? &bp->details.cs_blinded_planchet.nonce
|
? &bp->details.cs_blinded_planchet.nonce
|
||||||
: NULL;
|
: NULL;
|
||||||
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
qs = TEH_plugin->do_withdraw (TEH_plugin->cls,
|
||||||
nonce,
|
nonce,
|
||||||
&wc->collectable,
|
&wc->collectable,
|
||||||
now,
|
wc->now,
|
||||||
&found,
|
&found,
|
||||||
&balance_ok,
|
&balance_ok,
|
||||||
&nonce_ok,
|
&nonce_ok,
|
||||||
&wc->kyc,
|
|
||||||
&ruuid);
|
&ruuid);
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
@ -153,56 +232,8 @@ withdraw_transaction (void *cls,
|
|||||||
NULL);
|
NULL);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
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)
|
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;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +305,6 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
0,
|
0,
|
||||||
sizeof (wc));
|
sizeof (wc));
|
||||||
wc.collectable.reserve_pub = *reserve_pub;
|
wc.collectable.reserve_pub = *reserve_pub;
|
||||||
|
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue res;
|
enum GNUNET_GenericReturnValue res;
|
||||||
|
|
||||||
@ -459,6 +489,14 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
/* Clean up and send back final response */
|
/* Clean up and send back final response */
|
||||||
GNUNET_JSON_parse_free (spec);
|
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;
|
MHD_RESULT ret;
|
||||||
|
|
||||||
|
@ -99,11 +99,12 @@ BEGIN
|
|||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE IF NOT EXISTS %I'
|
'CREATE TABLE IF NOT EXISTS %I'
|
||||||
'(legitimization_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE'
|
'(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)'
|
',expiration_time INT8 NOT NULL DEFAULT (0)'
|
||||||
',provider_section VARCHAR NOT NULL'
|
',provider_section VARCHAR NOT NULL'
|
||||||
',provider_user_id VARCHAR DEFAULT NULL'
|
',provider_user_id VARCHAR DEFAULT NULL'
|
||||||
',provider_legitimization_id VARCHAR DEFAULT NULL'
|
',provider_legitimization_id VARCHAR DEFAULT NULL'
|
||||||
|
',UNIQUE (h_payto, provider_section)'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,'legitimizations'
|
,'legitimizations'
|
||||||
,'PARTITION BY HASH (h_payto)'
|
,'PARTITION BY HASH (h_payto)'
|
||||||
@ -898,6 +899,7 @@ BEGIN
|
|||||||
'(amount_val INT8 NOT NULL'
|
'(amount_val INT8 NOT NULL'
|
||||||
',amount_frac INT4 NOT NULL'
|
',amount_frac INT4 NOT NULL'
|
||||||
',wire_target_h_payto BYTEA CHECK (LENGTH(wire_target_h_payto)=32)'
|
',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'
|
',exchange_account_section TEXT NOT NULL'
|
||||||
',wtid_raw BYTEA NOT NULL CHECK (LENGTH(wtid_raw)=32)'
|
',wtid_raw BYTEA NOT NULL CHECK (LENGTH(wtid_raw)=32)'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
|
@ -63,6 +63,22 @@ COMMENT ON TABLE denomination_revocations
|
|||||||
IS 'remembering which denomination keys have been revoked';
|
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 ----------------------------------------
|
-- ------------------------------ profit drains ----------------------------------------
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS profit_drains
|
CREATE TABLE IF NOT EXISTS profit_drains
|
||||||
|
@ -589,6 +589,14 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" WHERE reserve_pub=$1"
|
" WHERE reserve_pub=$1"
|
||||||
" LIMIT 1;",
|
" LIMIT 1;",
|
||||||
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() */
|
/* Used in #postgres_set_kyc_ok() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"set_kyc_ok",
|
"set_kyc_ok",
|
||||||
@ -638,6 +646,18 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" FROM wire_targets"
|
" FROM wire_targets"
|
||||||
" WHERE wire_target_h_payto=$1;",
|
" WHERE wire_target_h_payto=$1;",
|
||||||
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() */
|
/* Used in #reserves_get() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"reserves_get",
|
"reserves_get",
|
||||||
@ -876,8 +896,6 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" reserve_found"
|
" reserve_found"
|
||||||
",balance_ok"
|
",balance_ok"
|
||||||
",nonce_ok"
|
",nonce_ok"
|
||||||
",kycok AS kyc_ok"
|
|
||||||
",account_uuid AS payment_target_uuid"
|
|
||||||
",ruuid"
|
",ruuid"
|
||||||
" FROM exchange_do_withdraw"
|
" FROM exchange_do_withdraw"
|
||||||
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
|
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
|
||||||
@ -889,8 +907,6 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
"SELECT "
|
"SELECT "
|
||||||
" reserve_found"
|
" reserve_found"
|
||||||
",balance_ok"
|
",balance_ok"
|
||||||
",kycok AS kyc_ok"
|
|
||||||
",account_uuid AS payment_target_uuid"
|
|
||||||
",ruuid"
|
",ruuid"
|
||||||
" FROM exchange_do_batch_withdraw"
|
" FROM exchange_do_batch_withdraw"
|
||||||
" ($1,$2,$3,$4,$5);",
|
" ($1,$2,$3,$4,$5);",
|
||||||
@ -1719,8 +1735,8 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"get_deposit_without_wtid",
|
"get_deposit_without_wtid",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" wt.kyc_ok"
|
" legi.expiration_time"
|
||||||
",wt.wire_target_serial_id AS payment_target_uuid"
|
",legi.legitimization_serial_id"
|
||||||
",dep.wire_salt"
|
",dep.wire_salt"
|
||||||
",wt.payto_uri"
|
",wt.payto_uri"
|
||||||
",dep.amount_with_fee_val"
|
",dep.amount_with_fee_val"
|
||||||
@ -1732,9 +1748,12 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" JOIN wire_targets wt USING (wire_target_h_payto)"
|
" JOIN wire_targets wt USING (wire_target_h_payto)"
|
||||||
" JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)"
|
" JOIN known_coins kc ON (kc.coin_pub = dep.coin_pub)"
|
||||||
" JOIN denominations denom USING (denominations_serial)"
|
" JOIN denominations denom USING (denominations_serial)"
|
||||||
|
" LEFT JOIN legitimizations legi ON (wt.wire_target_h_payto = legi.h_payto)"
|
||||||
" WHERE dep.coin_pub=$1"
|
" WHERE dep.coin_pub=$1"
|
||||||
" AND dep.merchant_pub=$3"
|
" 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),
|
3),
|
||||||
/* Used in #postgres_get_ready_deposit() */
|
/* Used in #postgres_get_ready_deposit() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
@ -1744,18 +1763,18 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
",merchant_pub"
|
",merchant_pub"
|
||||||
" FROM deposits_by_ready dbr"
|
" FROM deposits_by_ready dbr"
|
||||||
" JOIN deposits dep"
|
" 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"
|
" JOIN wire_targets wt"
|
||||||
" USING (wire_target_h_payto)"
|
" USING (wire_target_h_payto)"
|
||||||
" WHERE dbr.wire_deadline<=$1"
|
" WHERE dbr.wire_deadline<=$1"
|
||||||
" AND dbr.shard >= $2"
|
" AND dbr.shard >= $2"
|
||||||
" AND dbr.shard <= $3"
|
" AND dbr.shard <= $3"
|
||||||
" AND (wt.kyc_ok OR $4)"
|
|
||||||
" ORDER BY "
|
" ORDER BY "
|
||||||
" dbr.wire_deadline ASC"
|
" dbr.wire_deadline ASC"
|
||||||
" ,dbr.shard ASC"
|
" ,dbr.shard ASC"
|
||||||
" LIMIT 1;",
|
" LIMIT 1;",
|
||||||
4),
|
3),
|
||||||
/* Used in #postgres_aggregate() */
|
/* Used in #postgres_aggregate() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"aggregate",
|
"aggregate",
|
||||||
@ -1850,11 +1869,12 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
"INSERT INTO aggregation_transient"
|
"INSERT INTO aggregation_transient"
|
||||||
" (amount_val"
|
" (amount_val"
|
||||||
" ,amount_frac"
|
" ,amount_frac"
|
||||||
|
" ,merchant_pub"
|
||||||
" ,wire_target_h_payto"
|
" ,wire_target_h_payto"
|
||||||
" ,exchange_account_section"
|
" ,exchange_account_section"
|
||||||
" ,wtid_raw)"
|
" ,wtid_raw)"
|
||||||
" VALUES ($1, $2, $3, $4, $5);",
|
" VALUES ($1, $2, $3, $4, $5, $6);",
|
||||||
5),
|
6),
|
||||||
/* Used in #postgres_select_aggregation_transient() */
|
/* Used in #postgres_select_aggregation_transient() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"select_aggregation_transient",
|
"select_aggregation_transient",
|
||||||
@ -1864,8 +1884,22 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" ,wtid_raw"
|
" ,wtid_raw"
|
||||||
" FROM aggregation_transient"
|
" FROM aggregation_transient"
|
||||||
" WHERE wire_target_h_payto=$1"
|
" WHERE wire_target_h_payto=$1"
|
||||||
" AND exchange_account_section=$2;",
|
" AND merchant_pub=$2"
|
||||||
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() */
|
/* Used in #postgres_update_aggregation_transient() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"update_aggregation_transient",
|
"update_aggregation_transient",
|
||||||
@ -4525,6 +4559,8 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" ,provider_section"
|
" ,provider_section"
|
||||||
" ) VALUES "
|
" ) VALUES "
|
||||||
" ($1, $2)"
|
" ($1, $2)"
|
||||||
|
" ON CONFLICT (h_payto,provider_section) "
|
||||||
|
" DO UPDATE SET h_payto=$1" /* syntax requirement: dummy op */
|
||||||
" RETURNING legitimization_serial_id",
|
" RETURNING legitimization_serial_id",
|
||||||
2),
|
2),
|
||||||
/* Used in #postgres_update_kyc_requirement_by_row() */
|
/* Used in #postgres_update_kyc_requirement_by_row() */
|
||||||
@ -4533,12 +4569,20 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
"UPDATE legitimizations"
|
"UPDATE legitimizations"
|
||||||
" SET provider_user_id=$4"
|
" SET provider_user_id=$4"
|
||||||
" ,provider_legitimization_id=$5"
|
" ,provider_legitimization_id=$5"
|
||||||
" ,expiration_time=$6"
|
" ,expiration_time=GREATEST(expiration_time,$6)"
|
||||||
" WHERE"
|
" WHERE"
|
||||||
" h_payto=$3"
|
" h_payto=$3"
|
||||||
" AND legitimization_serial_id=$1"
|
" AND legitimization_serial_id=$1"
|
||||||
" AND provider_section=$2;",
|
" AND provider_section=$2;",
|
||||||
6),
|
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() */
|
/* Used in #postgres_lookup_kyc_requirement_by_row() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
"lookup_legitimization_by_row",
|
"lookup_legitimization_by_row",
|
||||||
@ -4598,7 +4642,6 @@ prepare_statements (struct PostgresClosure *pg)
|
|||||||
" AND ro.execution_date >= $2"
|
" AND ro.execution_date >= $2"
|
||||||
" ORDER BY ro.execution_date DESC",
|
" ORDER BY ro.execution_date DESC",
|
||||||
2),
|
2),
|
||||||
|
|
||||||
/* Used in #postgres_select_aggregation_amounts_for_kyc_check (
|
/* Used in #postgres_select_aggregation_amounts_for_kyc_check (
|
||||||
() */
|
() */
|
||||||
GNUNET_PQ_make_prepare (
|
GNUNET_PQ_make_prepare (
|
||||||
@ -5705,7 +5748,6 @@ postgres_reserves_get (void *cls,
|
|||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
|
||||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"reserves_get_with_kyc",
|
"reserves_get_with_kyc",
|
||||||
params,
|
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.
|
* 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.
|
* 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
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_UNKNOWN;
|
|
||||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"select_kyc_status_by_payto",
|
"select_kyc_status_by_payto",
|
||||||
params,
|
params,
|
||||||
@ -5863,7 +5967,6 @@ inselect_account_kyc_status (
|
|||||||
kyc->ok = false;
|
kyc->ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_BALANCE;
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5888,7 +5991,7 @@ postgres_inselect_wallet_kyc_status (
|
|||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
struct TALER_PaytoHashP h_payto;
|
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);
|
reserve_pub);
|
||||||
qs = inselect_account_kyc_status (pg,
|
qs = inselect_account_kyc_status (pg,
|
||||||
payto_uri,
|
payto_uri,
|
||||||
@ -6284,7 +6387,6 @@ postgres_get_withdraw_info (
|
|||||||
* @param[out] found set to true if the reserve was found
|
* @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] balance_ok set to true if the balance was sufficient
|
||||||
* @param[out] nonce_ok set to false if the nonce was reused
|
* @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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -6297,7 +6399,6 @@ postgres_do_withdraw (
|
|||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
bool *nonce_ok,
|
bool *nonce_ok,
|
||||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
|
||||||
uint64_t *ruuid)
|
uint64_t *ruuid)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
@ -6321,12 +6422,8 @@ postgres_do_withdraw (
|
|||||||
found),
|
found),
|
||||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||||
balance_ok),
|
balance_ok),
|
||||||
GNUNET_PQ_result_spec_bool ("kyc_ok",
|
|
||||||
&kyc->ok),
|
|
||||||
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
GNUNET_PQ_result_spec_bool ("nonce_ok",
|
||||||
nonce_ok),
|
nonce_ok),
|
||||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
|
||||||
&kyc->payment_target_uuid),
|
|
||||||
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||||
ruuid),
|
ruuid),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
@ -6335,7 +6432,6 @@ postgres_do_withdraw (
|
|||||||
gc = GNUNET_TIME_absolute_to_timestamp (
|
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||||
GNUNET_TIME_absolute_add (now.abs_time,
|
GNUNET_TIME_absolute_add (now.abs_time,
|
||||||
pg->legal_reserve_expiration_time));
|
pg->legal_reserve_expiration_time));
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
|
||||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"call_withdraw",
|
"call_withdraw",
|
||||||
params,
|
params,
|
||||||
@ -6354,7 +6450,6 @@ postgres_do_withdraw (
|
|||||||
* @param amount total amount to withdraw
|
* @param amount total amount to withdraw
|
||||||
* @param[out] found set to true if the reserve was found
|
* @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] 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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -6366,7 +6461,6 @@ postgres_do_batch_withdraw (
|
|||||||
const struct TALER_Amount *amount,
|
const struct TALER_Amount *amount,
|
||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
struct TALER_EXCHANGEDB_KycStatus *kyc,
|
|
||||||
uint64_t *ruuid)
|
uint64_t *ruuid)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
@ -6383,10 +6477,6 @@ postgres_do_batch_withdraw (
|
|||||||
found),
|
found),
|
||||||
GNUNET_PQ_result_spec_bool ("balance_ok",
|
GNUNET_PQ_result_spec_bool ("balance_ok",
|
||||||
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",
|
GNUNET_PQ_result_spec_uint64 ("ruuid",
|
||||||
ruuid),
|
ruuid),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
@ -6395,7 +6485,6 @@ postgres_do_batch_withdraw (
|
|||||||
gc = GNUNET_TIME_absolute_to_timestamp (
|
gc = GNUNET_TIME_absolute_to_timestamp (
|
||||||
GNUNET_TIME_absolute_add (now.abs_time,
|
GNUNET_TIME_absolute_add (now.abs_time,
|
||||||
pg->legal_reserve_expiration_time));
|
pg->legal_reserve_expiration_time));
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_WITHDRAW;
|
|
||||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"call_batch_withdraw",
|
"call_batch_withdraw",
|
||||||
params,
|
params,
|
||||||
@ -7743,12 +7832,14 @@ postgres_create_aggregation_transient (
|
|||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
const char *exchange_account_section,
|
const char *exchange_account_section,
|
||||||
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||||
const struct TALER_Amount *total)
|
const struct TALER_Amount *total)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
TALER_PQ_query_param_amount (total),
|
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_auto_from_type (h_payto),
|
||||||
GNUNET_PQ_query_param_string (exchange_account_section),
|
GNUNET_PQ_query_param_string (exchange_account_section),
|
||||||
GNUNET_PQ_query_param_auto_from_type (wtid),
|
GNUNET_PQ_query_param_auto_from_type (wtid),
|
||||||
@ -7775,6 +7866,7 @@ static enum GNUNET_DB_QueryStatus
|
|||||||
postgres_select_aggregation_transient (
|
postgres_select_aggregation_transient (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
const char *exchange_account_section,
|
const char *exchange_account_section,
|
||||||
struct TALER_WireTransferIdentifierRawP *wtid,
|
struct TALER_WireTransferIdentifierRawP *wtid,
|
||||||
struct TALER_Amount *total)
|
struct TALER_Amount *total)
|
||||||
@ -7782,6 +7874,7 @@ postgres_select_aggregation_transient (
|
|||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
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_string (exchange_account_section),
|
||||||
GNUNET_PQ_query_param_end
|
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.
|
* Update existing entry in the transient aggregation table.
|
||||||
* @a h_payto is only needed for query performance.
|
* @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 cls the @e cls of this struct with the plugin-specific state
|
||||||
* @param start_shard_row minimum shard row to select
|
* @param start_shard_row minimum shard row to select
|
||||||
* @param end_shard_row maximum shard row to select (inclusive)
|
* @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] 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
|
* @param[out] payto_uri set to the account of the merchant, to be freed by caller
|
||||||
* @return transaction status code
|
* @return transaction status code
|
||||||
@ -7877,7 +8091,6 @@ static enum GNUNET_DB_QueryStatus
|
|||||||
postgres_get_ready_deposit (void *cls,
|
postgres_get_ready_deposit (void *cls,
|
||||||
uint64_t start_shard_row,
|
uint64_t start_shard_row,
|
||||||
uint64_t end_shard_row,
|
uint64_t end_shard_row,
|
||||||
bool kyc_off,
|
|
||||||
struct TALER_MerchantPublicKeyP *merchant_pub,
|
struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
char **payto_uri)
|
char **payto_uri)
|
||||||
{
|
{
|
||||||
@ -7887,7 +8100,6 @@ postgres_get_ready_deposit (void *cls,
|
|||||||
GNUNET_PQ_query_param_absolute_time (&now),
|
GNUNET_PQ_query_param_absolute_time (&now),
|
||||||
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
GNUNET_PQ_query_param_uint64 (&start_shard_row),
|
||||||
GNUNET_PQ_query_param_uint64 (&end_shard_row),
|
GNUNET_PQ_query_param_uint64 (&end_shard_row),
|
||||||
GNUNET_PQ_query_param_bool (kyc_off),
|
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
@ -9644,7 +9856,11 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
deposit_fee),
|
deposit_fee),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
struct GNUNET_TIME_Absolute expiration;
|
||||||
|
|
||||||
|
memset (kyc,
|
||||||
|
0,
|
||||||
|
sizeof (*kyc));
|
||||||
/* check if the aggregation record exists and get it */
|
/* check if the aggregation record exists and get it */
|
||||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"lookup_deposit_wtid",
|
"lookup_deposit_wtid",
|
||||||
@ -9663,10 +9879,6 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
h_wire))
|
h_wire))
|
||||||
{
|
{
|
||||||
*pending = false;
|
*pending = false;
|
||||||
memset (kyc,
|
|
||||||
0,
|
|
||||||
sizeof (*kyc));
|
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
|
||||||
kyc->ok = true;
|
kyc->ok = true;
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
@ -9685,15 +9897,21 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
/* Check if transaction exists in deposits, so that we just
|
/* Check if transaction exists in deposits, so that we just
|
||||||
do not have a WTID yet. In that case, return without wtid
|
do not have a WTID yet. In that case, return without wtid
|
||||||
(by setting 'pending' true). */
|
(by setting 'pending' true). */
|
||||||
|
bool no_kyc = false;
|
||||||
struct GNUNET_PQ_ResultSpec rs2[] = {
|
struct GNUNET_PQ_ResultSpec rs2[] = {
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
GNUNET_PQ_result_spec_auto_from_type ("wire_salt",
|
||||||
&wire_salt),
|
&wire_salt),
|
||||||
GNUNET_PQ_result_spec_string ("payto_uri",
|
GNUNET_PQ_result_spec_string ("payto_uri",
|
||||||
&payto_uri),
|
&payto_uri),
|
||||||
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
|
GNUNET_PQ_result_spec_allow_null (
|
||||||
&kyc->payment_target_uuid),
|
GNUNET_PQ_result_spec_uint64 ("legitimization_serial_id",
|
||||||
GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
|
|
||||||
&kyc->ok),
|
&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",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
|
||||||
amount_with_fee),
|
amount_with_fee),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
|
||||||
@ -9711,6 +9929,10 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
{
|
{
|
||||||
struct TALER_MerchantWireHashP wh;
|
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,
|
TALER_merchant_wire_signature_hash (payto_uri,
|
||||||
&wire_salt,
|
&wire_salt,
|
||||||
&wh);
|
&wh);
|
||||||
@ -9720,7 +9942,6 @@ postgres_lookup_transfer_by_deposit (
|
|||||||
h_wire))
|
h_wire))
|
||||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||||
}
|
}
|
||||||
kyc->type = TALER_EXCHANGEDB_KYC_DEPOSIT;
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -16149,7 +16370,7 @@ postgres_do_purse_merge (
|
|||||||
{
|
{
|
||||||
char *payto_uri;
|
char *payto_uri;
|
||||||
|
|
||||||
payto_uri = TALER_payto_from_reserve (pg->exchange_url,
|
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
|
||||||
reserve_pub);
|
reserve_pub);
|
||||||
TALER_payto_hash (payto_uri,
|
TALER_payto_hash (payto_uri,
|
||||||
&h_payto);
|
&h_payto);
|
||||||
@ -16228,7 +16449,7 @@ postgres_do_reserve_purse (
|
|||||||
{
|
{
|
||||||
char *payto_uri;
|
char *payto_uri;
|
||||||
|
|
||||||
payto_uri = TALER_payto_from_reserve (pg->exchange_url,
|
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
|
||||||
reserve_pub);
|
reserve_pub);
|
||||||
TALER_payto_hash (payto_uri,
|
TALER_payto_hash (payto_uri,
|
||||||
&h_payto);
|
&h_payto);
|
||||||
@ -16612,16 +16833,57 @@ postgres_update_kyc_requirement_by_row (
|
|||||||
GNUNET_PQ_query_param_uint64 (&legi_row),
|
GNUNET_PQ_query_param_uint64 (&legi_row),
|
||||||
GNUNET_PQ_query_param_string (provider_section),
|
GNUNET_PQ_query_param_string (provider_section),
|
||||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||||
GNUNET_PQ_query_param_string (provider_account_id),
|
(NULL != provider_account_id)
|
||||||
GNUNET_PQ_query_param_string (provider_legitimization_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_absolute_time (&expiration),
|
||||||
GNUNET_PQ_query_param_end
|
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,
|
pg->conn,
|
||||||
"update_legitimization_requirement",
|
"update_legitimization_requirement",
|
||||||
params);
|
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;
|
ctx->status = GNUNET_SYSERR;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Found satisfied LEGI: %s\n",
|
||||||
|
provider_section);
|
||||||
ctx->cb (ctx->cb_cls,
|
ctx->cb (ctx->cb_cls,
|
||||||
provider_section);
|
provider_section);
|
||||||
GNUNET_PQ_cleanup_result (rs);
|
GNUNET_PQ_cleanup_result (rs);
|
||||||
@ -16877,6 +17142,9 @@ postgres_select_satisfied_kyc_processes (
|
|||||||
params,
|
params,
|
||||||
&get_legitimizations_cb,
|
&get_legitimizations_cb,
|
||||||
&ctx);
|
&ctx);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Satisfied LEGI check returned %d\n",
|
||||||
|
qs);
|
||||||
if (GNUNET_OK != ctx.status)
|
if (GNUNET_OK != ctx.status)
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
return qs;
|
return qs;
|
||||||
@ -17220,7 +17488,9 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
|||||||
&postgres_iterate_auditor_denominations;
|
&postgres_iterate_auditor_denominations;
|
||||||
plugin->select_kyc_status = &postgres_select_kyc_status;
|
plugin->select_kyc_status = &postgres_select_kyc_status;
|
||||||
plugin->reserves_get = &postgres_reserves_get;
|
plugin->reserves_get = &postgres_reserves_get;
|
||||||
|
plugin->reserves_get_origin = &postgres_reserves_get_origin;
|
||||||
plugin->set_kyc_ok = &postgres_set_kyc_ok;
|
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->inselect_wallet_kyc_status = &postgres_inselect_wallet_kyc_status;
|
||||||
plugin->reserves_in_insert = &postgres_reserves_in_insert;
|
plugin->reserves_in_insert = &postgres_reserves_in_insert;
|
||||||
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
plugin->get_withdraw_info = &postgres_get_withdraw_info;
|
||||||
@ -17247,6 +17517,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
|||||||
= &postgres_create_aggregation_transient;
|
= &postgres_create_aggregation_transient;
|
||||||
plugin->select_aggregation_transient
|
plugin->select_aggregation_transient
|
||||||
= &postgres_select_aggregation_transient;
|
= &postgres_select_aggregation_transient;
|
||||||
|
plugin->find_aggregation_transient
|
||||||
|
= &postgres_find_aggregation_transient;
|
||||||
plugin->update_aggregation_transient
|
plugin->update_aggregation_transient
|
||||||
= &postgres_update_aggregation_transient;
|
= &postgres_update_aggregation_transient;
|
||||||
plugin->delete_aggregation_transient
|
plugin->delete_aggregation_transient
|
||||||
|
@ -37,8 +37,6 @@ CREATE OR REPLACE FUNCTION exchange_do_withdraw(
|
|||||||
OUT reserve_found BOOLEAN,
|
OUT reserve_found BOOLEAN,
|
||||||
OUT balance_ok BOOLEAN,
|
OUT balance_ok BOOLEAN,
|
||||||
OUT nonce_ok BOOLEAN,
|
OUT nonce_ok BOOLEAN,
|
||||||
OUT kycok BOOLEAN,
|
|
||||||
OUT account_uuid INT8,
|
|
||||||
OUT ruuid INT8)
|
OUT ruuid INT8)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
@ -67,8 +65,6 @@ THEN
|
|||||||
-- denomination unknown, should be impossible!
|
-- denomination unknown, should be impossible!
|
||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
kycok=FALSE;
|
|
||||||
account_uuid=0;
|
|
||||||
ruuid=0;
|
ruuid=0;
|
||||||
ASSERT false, 'denomination unknown';
|
ASSERT false, 'denomination unknown';
|
||||||
RETURN;
|
RETURN;
|
||||||
@ -94,8 +90,6 @@ THEN
|
|||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
nonce_ok=TRUE;
|
nonce_ok=TRUE;
|
||||||
kycok=FALSE;
|
|
||||||
account_uuid=0;
|
|
||||||
ruuid=2;
|
ruuid=2;
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
@ -128,8 +122,6 @@ THEN
|
|||||||
reserve_found=TRUE;
|
reserve_found=TRUE;
|
||||||
balance_ok=TRUE;
|
balance_ok=TRUE;
|
||||||
nonce_ok=TRUE;
|
nonce_ok=TRUE;
|
||||||
kycok=TRUE;
|
|
||||||
account_uuid=0;
|
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
@ -153,8 +145,6 @@ ELSE
|
|||||||
reserve_found=TRUE;
|
reserve_found=TRUE;
|
||||||
nonce_ok=TRUE; -- we do not really know
|
nonce_ok=TRUE; -- we do not really know
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
kycok=FALSE; -- we do not really know or care
|
|
||||||
account_uuid=0;
|
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
@ -201,8 +191,6 @@ THEN
|
|||||||
THEN
|
THEN
|
||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
kycok=FALSE;
|
|
||||||
account_uuid=0;
|
|
||||||
nonce_ok=FALSE;
|
nonce_ok=FALSE;
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
@ -211,40 +199,9 @@ ELSE
|
|||||||
nonce_ok=TRUE; -- no nonce, hence OK!
|
nonce_ok=TRUE; -- no nonce, hence OK!
|
||||||
END IF;
|
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 $$;
|
END $$;
|
||||||
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION exchange_do_withdraw(BYTEA, INT8, INT4, BYTEA, BYTEA, BYTEA, BYTEA, BYTEA, INT8, INT8)
|
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';
|
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,
|
IN min_reserve_gc INT8,
|
||||||
OUT reserve_found BOOLEAN,
|
OUT reserve_found BOOLEAN,
|
||||||
OUT balance_ok BOOLEAN,
|
OUT balance_ok BOOLEAN,
|
||||||
OUT kycok BOOLEAN,
|
|
||||||
OUT account_uuid INT8,
|
|
||||||
OUT ruuid INT8)
|
OUT ruuid INT8)
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
@ -295,8 +250,6 @@ THEN
|
|||||||
-- reserve unknown
|
-- reserve unknown
|
||||||
reserve_found=FALSE;
|
reserve_found=FALSE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
kycok=FALSE;
|
|
||||||
account_uuid=0;
|
|
||||||
ruuid=2;
|
ruuid=2;
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
@ -320,8 +273,6 @@ ELSE
|
|||||||
ELSE
|
ELSE
|
||||||
reserve_found=TRUE;
|
reserve_found=TRUE;
|
||||||
balance_ok=FALSE;
|
balance_ok=FALSE;
|
||||||
kycok=FALSE; -- we do not really know or care
|
|
||||||
account_uuid=0;
|
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
END IF;
|
END IF;
|
||||||
@ -340,37 +291,6 @@ WHERE
|
|||||||
reserve_found=TRUE;
|
reserve_found=TRUE;
|
||||||
balance_ok=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 $$;
|
END $$;
|
||||||
|
|
||||||
COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8)
|
COMMENT ON FUNCTION exchange_do_batch_withdraw(INT8, INT4, BYTEA, INT8, INT8)
|
||||||
|
@ -1399,7 +1399,6 @@ run (void *cls)
|
|||||||
bool found;
|
bool found;
|
||||||
bool nonce_ok;
|
bool nonce_ok;
|
||||||
bool balance_ok;
|
bool balance_ok;
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
|
||||||
uint64_t ruuid;
|
uint64_t ruuid;
|
||||||
|
|
||||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||||
@ -1410,12 +1409,10 @@ run (void *cls)
|
|||||||
&found,
|
&found,
|
||||||
&balance_ok,
|
&balance_ok,
|
||||||
&nonce_ok,
|
&nonce_ok,
|
||||||
&kyc,
|
|
||||||
&ruuid));
|
&ruuid));
|
||||||
GNUNET_assert (found);
|
GNUNET_assert (found);
|
||||||
GNUNET_assert (nonce_ok);
|
GNUNET_assert (nonce_ok);
|
||||||
GNUNET_assert (balance_ok);
|
GNUNET_assert (balance_ok);
|
||||||
GNUNET_assert (! kyc.ok);
|
|
||||||
}
|
}
|
||||||
FAILIF (GNUNET_OK !=
|
FAILIF (GNUNET_OK !=
|
||||||
check_reserve (&reserve_pub,
|
check_reserve (&reserve_pub,
|
||||||
@ -2159,7 +2156,6 @@ run (void *cls)
|
|||||||
plugin->get_ready_deposit (plugin->cls,
|
plugin->get_ready_deposit (plugin->cls,
|
||||||
0,
|
0,
|
||||||
INT32_MAX,
|
INT32_MAX,
|
||||||
true,
|
|
||||||
&merchant_pub2,
|
&merchant_pub2,
|
||||||
&payto_uri2));
|
&payto_uri2));
|
||||||
FAILIF (0 != GNUNET_memcmp (&merchant_pub2,
|
FAILIF (0 != GNUNET_memcmp (&merchant_pub2,
|
||||||
@ -2205,6 +2201,7 @@ run (void *cls)
|
|||||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
||||||
plugin->select_aggregation_transient (plugin->cls,
|
plugin->select_aggregation_transient (plugin->cls,
|
||||||
&wire_target_h_payto,
|
&wire_target_h_payto,
|
||||||
|
&deposit.merchant_pub,
|
||||||
"x-bank",
|
"x-bank",
|
||||||
&wtid2,
|
&wtid2,
|
||||||
&total2));
|
&total2));
|
||||||
@ -2212,11 +2209,13 @@ run (void *cls)
|
|||||||
plugin->create_aggregation_transient (plugin->cls,
|
plugin->create_aggregation_transient (plugin->cls,
|
||||||
&wire_target_h_payto,
|
&wire_target_h_payto,
|
||||||
"x-bank",
|
"x-bank",
|
||||||
|
&deposit.merchant_pub,
|
||||||
&wtid,
|
&wtid,
|
||||||
&total));
|
&total));
|
||||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||||
plugin->select_aggregation_transient (plugin->cls,
|
plugin->select_aggregation_transient (plugin->cls,
|
||||||
&wire_target_h_payto,
|
&wire_target_h_payto,
|
||||||
|
&deposit.merchant_pub,
|
||||||
"x-bank",
|
"x-bank",
|
||||||
&wtid2,
|
&wtid2,
|
||||||
&total2));
|
&total2));
|
||||||
@ -2237,6 +2236,7 @@ run (void *cls)
|
|||||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
|
||||||
plugin->select_aggregation_transient (plugin->cls,
|
plugin->select_aggregation_transient (plugin->cls,
|
||||||
&wire_target_h_payto,
|
&wire_target_h_payto,
|
||||||
|
&deposit.merchant_pub,
|
||||||
"x-bank",
|
"x-bank",
|
||||||
&wtid2,
|
&wtid2,
|
||||||
&total2));
|
&total2));
|
||||||
@ -2253,6 +2253,7 @@ run (void *cls)
|
|||||||
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
|
||||||
plugin->select_aggregation_transient (plugin->cls,
|
plugin->select_aggregation_transient (plugin->cls,
|
||||||
&wire_target_h_payto,
|
&wire_target_h_payto,
|
||||||
|
&deposit.merchant_pub,
|
||||||
"x-bank",
|
"x-bank",
|
||||||
&wtid2,
|
&wtid2,
|
||||||
&total2));
|
&total2));
|
||||||
|
@ -304,10 +304,16 @@ struct TALER_EXCHANGE_Keys
|
|||||||
struct GNUNET_TIME_Relative reserve_closing_delay;
|
struct GNUNET_TIME_Relative reserve_closing_delay;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum amount a wallet is allowed to hold from
|
* Array of amounts a wallet is allowed to hold from
|
||||||
* this exchange before it must undergo a KYC check.
|
* 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.
|
* Timestamp indicating the /keys generation.
|
||||||
@ -3422,7 +3428,7 @@ typedef void
|
|||||||
* of a merchant.
|
* of a merchant.
|
||||||
*
|
*
|
||||||
* @param eh exchange handle to use
|
* @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 h_payto hash of the payto:// URI at @a payment_target
|
||||||
* @param timeout how long to wait for a positive KYC status
|
* @param timeout how long to wait for a positive KYC status
|
||||||
* @param cb function to call with the result
|
* @param cb function to call with the result
|
||||||
@ -3431,7 +3437,7 @@ typedef void
|
|||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_KycCheckHandle *
|
struct TALER_EXCHANGE_KycCheckHandle *
|
||||||
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *eh,
|
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *eh,
|
||||||
uint64_t payment_target,
|
uint64_t legitimization_uuid,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
struct GNUNET_TIME_Relative timeout,
|
struct GNUNET_TIME_Relative timeout,
|
||||||
TALER_EXCHANGE_KycStatusCallback cb,
|
TALER_EXCHANGE_KycStatusCallback cb,
|
||||||
@ -3500,8 +3506,10 @@ struct TALER_EXCHANGE_KycProofHandle;
|
|||||||
*
|
*
|
||||||
* @param eh exchange handle to use
|
* @param eh exchange handle to use
|
||||||
* @param h_payto hash of payto URI identifying the target account
|
* @param h_payto hash of payto URI identifying the target account
|
||||||
* @param code OAuth 2.0 code argument
|
* @param logic name of the KYC logic to run
|
||||||
* @param state OAuth 2.0 state argument
|
* @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 function to call with the result
|
||||||
* @param cb_cls closure for @a cb
|
* @param cb_cls closure for @a cb
|
||||||
* @return NULL on error
|
* @return NULL on error
|
||||||
@ -3509,8 +3517,8 @@ struct TALER_EXCHANGE_KycProofHandle;
|
|||||||
struct TALER_EXCHANGE_KycProofHandle *
|
struct TALER_EXCHANGE_KycProofHandle *
|
||||||
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *eh,
|
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *eh,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
const char *code,
|
const char *logic,
|
||||||
const char *state,
|
const char *args,
|
||||||
TALER_EXCHANGE_KycProofCallback cb,
|
TALER_EXCHANGE_KycProofCallback cb,
|
||||||
void *cb_cls);
|
void *cb_cls);
|
||||||
|
|
||||||
@ -3573,6 +3581,7 @@ typedef void
|
|||||||
*
|
*
|
||||||
* @param eh exchange handle to use
|
* @param eh exchange handle to use
|
||||||
* @param reserve_priv wallet private key to check
|
* @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 function to call with the result
|
||||||
* @param cb_cls closure for @a cb
|
* @param cb_cls closure for @a cb
|
||||||
* @return NULL on error
|
* @return NULL on error
|
||||||
@ -3580,6 +3589,7 @@ typedef void
|
|||||||
struct TALER_EXCHANGE_KycWalletHandle *
|
struct TALER_EXCHANGE_KycWalletHandle *
|
||||||
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *eh,
|
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *eh,
|
||||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||||
|
const struct TALER_Amount *balance,
|
||||||
TALER_EXCHANGE_KycWalletCallback cb,
|
TALER_EXCHANGE_KycWalletCallback cb,
|
||||||
void *cb_cls);
|
void *cb_cls);
|
||||||
|
|
||||||
|
@ -2327,11 +2327,13 @@ struct TALER_EXCHANGEDB_KycStatus
|
|||||||
* Number that identifies the KYC target the operation
|
* Number that identifies the KYC target the operation
|
||||||
* was about.
|
* was about.
|
||||||
*/
|
*/
|
||||||
|
// FIXME: rename to 'legitimization_uuid'
|
||||||
uint64_t payment_target_uuid;
|
uint64_t payment_target_uuid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What kind of KYC operation is this?
|
* What kind of KYC operation is this?
|
||||||
*/
|
*/
|
||||||
|
// FIXME: kill!
|
||||||
enum TALER_EXCHANGEDB_KycType type;
|
enum TALER_EXCHANGEDB_KycType type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2582,6 +2584,26 @@ typedef enum GNUNET_GenericReturnValue
|
|||||||
const struct TALER_Amount *amount);
|
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.
|
* Callback with data about a prepared wire transfer.
|
||||||
*
|
*
|
||||||
@ -3097,6 +3119,21 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
struct TALER_EXCHANGEDB_KycStatus *kyc);
|
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.
|
* Set the KYC status to "OK" for a bank account.
|
||||||
*
|
*
|
||||||
@ -3111,6 +3148,20 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
const char *id);
|
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.
|
* 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] found set to true if the reserve was found
|
||||||
* @param[out] balance_ok set to true if the balance was sufficient
|
* @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] 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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -3220,7 +3270,6 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
bool *nonce_ok,
|
bool *nonce_ok,
|
||||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
|
||||||
uint64_t *ruuid);
|
uint64_t *ruuid);
|
||||||
|
|
||||||
|
|
||||||
@ -3235,7 +3284,6 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
* @param amount total amount to withdraw
|
* @param amount total amount to withdraw
|
||||||
* @param[out] found set to true if the reserve was found
|
* @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] 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)
|
* @param[out] ruuid set to the reserve's UUID (reserves table row)
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
@ -3247,7 +3295,6 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
const struct TALER_Amount *amount,
|
const struct TALER_Amount *amount,
|
||||||
bool *found,
|
bool *found,
|
||||||
bool *balance_ok,
|
bool *balance_ok,
|
||||||
struct TALER_EXCHANGEDB_KycStatus *kyc_ok,
|
|
||||||
uint64_t *ruuid);
|
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 cls the @e cls of this struct with the plugin-specific state
|
||||||
* @param start_shard_row minimum shard row to select
|
* @param start_shard_row minimum shard row to select
|
||||||
* @param end_shard_row maximum shard row to select (inclusive)
|
* @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] 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
|
* @param[out] payto_uri set to the account of the merchant, to be freed by caller
|
||||||
* @return transaction status code
|
* @return transaction status code
|
||||||
@ -3702,7 +3747,6 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
(*get_ready_deposit)(void *cls,
|
(*get_ready_deposit)(void *cls,
|
||||||
uint64_t start_shard_row,
|
uint64_t start_shard_row,
|
||||||
uint64_t end_shard_row,
|
uint64_t end_shard_row,
|
||||||
bool kyc_off,
|
|
||||||
struct TALER_MerchantPublicKeyP *merchant_pub,
|
struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
char **payto_uri);
|
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 cls the @e cls of this struct with the plugin-specific state
|
||||||
* @param h_payto destination of the wire transfer
|
* @param h_payto destination of the wire transfer
|
||||||
* @param exchange_account_section exchange account to use
|
* @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 wtid the raw wire transfer identifier to be used
|
||||||
* @param total amount to be wired in the future
|
* @param total amount to be wired in the future
|
||||||
* @return transaction status
|
* @return transaction status
|
||||||
@ -3749,15 +3794,17 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
const char *exchange_account_section,
|
const char *exchange_account_section,
|
||||||
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
const struct TALER_WireTransferIdentifierRawP *wtid,
|
const struct TALER_WireTransferIdentifierRawP *wtid,
|
||||||
const struct TALER_Amount *total);
|
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 cls the @e cls of this struct with the plugin-specific state
|
||||||
* @param h_payto destination of the wire transfer
|
* @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 exchange_account_section exchange account to use
|
||||||
* @param[out] wtid set to the raw wire transfer identifier to be used
|
* @param[out] wtid set to the raw wire transfer identifier to be used
|
||||||
* @param[out] total existing amount to be wired in the future
|
* @param[out] total existing amount to be wired in the future
|
||||||
@ -3767,11 +3814,29 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
(*select_aggregation_transient)(
|
(*select_aggregation_transient)(
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
|
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||||
const char *exchange_account_section,
|
const char *exchange_account_section,
|
||||||
struct TALER_WireTransferIdentifierRawP *wtid,
|
struct TALER_WireTransferIdentifierRawP *wtid,
|
||||||
struct TALER_Amount *total);
|
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.
|
* Update existing entry in the transient aggregation table.
|
||||||
* @a h_payto is only needed for query performance.
|
* @a h_payto is only needed for query performance.
|
||||||
|
@ -162,6 +162,19 @@ typedef void
|
|||||||
void *cb_cls);
|
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
|
* Call us on KYC processes satisfied for the given
|
||||||
* account. Must match the ``select_satisfied_kyc_processes`` of the exchange database plugin.
|
* 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);
|
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.
|
* 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 url_path rest of the URL after `/kyc-webhook/$H_PAYTO/$LOGIC`
|
||||||
* @param connection MHD connection object (for HTTP headers)
|
* @param connection MHD connection object (for HTTP headers)
|
||||||
* @param account_id which account to trigger process for
|
* @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_user_id user ID (or NULL) the proof is for
|
||||||
* @param provider_legitimization_id legitimization ID the proof is for
|
* @param provider_legitimization_id legitimization ID the proof is for
|
||||||
* @param cb function to call with the result
|
* @param cb function to call with the result
|
||||||
@ -307,6 +308,7 @@ struct TALER_KYCLOGIC_Plugin
|
|||||||
const char *const url_path[],
|
const char *const url_path[],
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
const struct TALER_PaytoHashP *account_id,
|
const struct TALER_PaytoHashP *account_id,
|
||||||
|
uint64_t legi_row,
|
||||||
const char *provider_user_id,
|
const char *provider_user_id,
|
||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
TALER_KYCLOGIC_ProofCallback cb,
|
TALER_KYCLOGIC_ProofCallback cb,
|
||||||
|
@ -2421,12 +2421,14 @@ TALER_TESTING_cmd_revoke_sign_key (
|
|||||||
*
|
*
|
||||||
* @param label command label.
|
* @param label command label.
|
||||||
* @param reserve_reference command with reserve private key to use (or NULL to create a fresh reserve key).
|
* @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
|
* @param expected_response_code expected HTTP status
|
||||||
* @return the command
|
* @return the command
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
||||||
const char *reserve_reference,
|
const char *reserve_reference,
|
||||||
|
const char *threshold_balance,
|
||||||
unsigned int expected_response_code);
|
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 label command label.
|
||||||
* @param payment_target_reference command with a payment target to query
|
* @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 code OAuth 2.0 code to use
|
||||||
* @param state OAuth 2.0 state to use
|
* @param state OAuth 2.0 state to use
|
||||||
* @param expected_response_code expected HTTP status
|
* @param expected_response_code expected HTTP status
|
||||||
* @return the command
|
* @return the command
|
||||||
*/
|
*/
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_proof_kyc (const char *label,
|
TALER_TESTING_cmd_proof_kyc_oauth2 (
|
||||||
const char *payment_target_reference,
|
const char *label,
|
||||||
const char *code,
|
const char *payment_target_reference,
|
||||||
const char *state,
|
const char *logic_section,
|
||||||
unsigned int expected_response_code);
|
const char *code,
|
||||||
|
const char *state,
|
||||||
|
unsigned int expected_response_code);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -361,19 +361,6 @@ char *
|
|||||||
TALER_payto_get_method (const char *payto_uri);
|
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.
|
* Obtain the account name from a payto URL.
|
||||||
*
|
*
|
||||||
|
@ -279,12 +279,22 @@ load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
|||||||
GNUNET_asprintf (&lib_name,
|
GNUNET_asprintf (&lib_name,
|
||||||
"libtaler_plugin_kyclogic_%s",
|
"libtaler_plugin_kyclogic_%s",
|
||||||
name);
|
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,
|
plugin = GNUNET_PLUGIN_load (lib_name,
|
||||||
(void *) cfg);
|
(void *) cfg);
|
||||||
if (NULL != plugin)
|
if (NULL != plugin)
|
||||||
plugin->library_name = lib_name;
|
plugin->library_name = lib_name;
|
||||||
else
|
else
|
||||||
GNUNET_free (lib_name);
|
GNUNET_free (lib_name);
|
||||||
|
GNUNET_array_append (kyc_logics,
|
||||||
|
num_kyc_logics,
|
||||||
|
plugin);
|
||||||
return 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
|
static enum GNUNET_GenericReturnValue
|
||||||
add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
||||||
const char *section)
|
const char *section)
|
||||||
@ -797,6 +815,9 @@ eval_trigger (void *cls,
|
|||||||
struct GNUNET_TIME_Relative duration;
|
struct GNUNET_TIME_Relative duration;
|
||||||
bool bump = true;
|
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);
|
duration = GNUNET_TIME_absolute_get_duration (date);
|
||||||
if (ttc->have_total)
|
if (ttc->have_total)
|
||||||
{
|
{
|
||||||
@ -812,19 +833,31 @@ eval_trigger (void *cls,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ttc->total = *amount;
|
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++)
|
for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)
|
||||||
{
|
{
|
||||||
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
||||||
|
|
||||||
if (ttc->event != kt->trigger)
|
if (ttc->event != kt->trigger)
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"KYC check #%u: trigger type does not match\n",
|
||||||
|
i);
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
duration = GNUNET_TIME_relative_max (duration,
|
duration = GNUNET_TIME_relative_max (duration,
|
||||||
kt->timeframe);
|
kt->timeframe);
|
||||||
if (GNUNET_TIME_relative_cmp (kt->timeframe,
|
if (GNUNET_TIME_relative_cmp (kt->timeframe,
|
||||||
>,
|
>,
|
||||||
duration))
|
duration))
|
||||||
{
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"KYC check #%u: amount is beyond time limit\n",
|
||||||
|
i);
|
||||||
if (bump)
|
if (bump)
|
||||||
ttc->start = i;
|
ttc->start = i;
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
@ -833,6 +866,9 @@ eval_trigger (void *cls,
|
|||||||
TALER_amount_cmp (&ttc->total,
|
TALER_amount_cmp (&ttc->total,
|
||||||
&kt->threshold))
|
&kt->threshold))
|
||||||
{
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"KYC check #%u: amount is below treshold\n",
|
||||||
|
i);
|
||||||
if (bump)
|
if (bump)
|
||||||
ttc->start = i;
|
ttc->start = i;
|
||||||
bump = false;
|
bump = false;
|
||||||
@ -848,6 +884,9 @@ eval_trigger (void *cls,
|
|||||||
for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
|
for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
|
||||||
if (ttc->needed[k] == rc)
|
if (ttc->needed[k] == rc)
|
||||||
{
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"KYC rule #%u already listed\n",
|
||||||
|
j);
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -857,6 +896,11 @@ eval_trigger (void *cls,
|
|||||||
(*ttc->needed_cnt)++;
|
(*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)
|
if (bump)
|
||||||
return GNUNET_NO; /* we hit all possible triggers! */
|
return GNUNET_NO; /* we hit all possible triggers! */
|
||||||
@ -903,13 +947,22 @@ remove_satisfied (void *cls,
|
|||||||
if (0 != strcasecmp (provider_name,
|
if (0 != strcasecmp (provider_name,
|
||||||
kp->provider_section_name))
|
kp->provider_section_name))
|
||||||
continue;
|
continue;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Provider `%s' satisfied\n",
|
||||||
|
provider_name);
|
||||||
for (unsigned int j = 0; j<kp->num_checks; j++)
|
for (unsigned int j = 0; j<kp->num_checks; j++)
|
||||||
{
|
{
|
||||||
const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_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++)
|
for (unsigned int k = 0; k<*rc->needed_cnt; k++)
|
||||||
if (kc == rc->needed[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[k] = rc->needed[*rc->needed_cnt - 1];
|
||||||
(*rc->needed_cnt)--;
|
(*rc->needed_cnt)--;
|
||||||
if (0 == *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
|
/* Check what provider checks are already satisfied for h_payto (with
|
||||||
database), remove those from the 'needed' array. */
|
database), remove those from the 'needed' array. */
|
||||||
GNUNET_break (0);
|
|
||||||
// FIXME: do via callback!
|
|
||||||
qs = ki (ki_cls,
|
qs = ki (ki_cls,
|
||||||
h_payto,
|
h_payto,
|
||||||
&
|
&remove_satisfied,
|
||||||
remove_satisfied,
|
|
||||||
&rc);
|
&rc);
|
||||||
GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
|
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
|
/* Count maximum number of remaining checks covered by any
|
||||||
provider */
|
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 */
|
/* end of taler-exchange-httpd_kyc.c */
|
||||||
|
@ -68,6 +68,11 @@ struct TALER_KYCLOGIC_ProviderDetails
|
|||||||
*/
|
*/
|
||||||
struct PluginState *ps;
|
struct PluginState *ps;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration section that configured us.
|
||||||
|
*/
|
||||||
|
char *section;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL of the OAuth2.0 endpoint for KYC checks.
|
* URL of the OAuth2.0 endpoint for KYC checks.
|
||||||
* (token/auth)
|
* (token/auth)
|
||||||
@ -265,6 +270,7 @@ struct TALER_KYCLOGIC_WebhookHandle
|
|||||||
static void
|
static void
|
||||||
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
||||||
{
|
{
|
||||||
|
GNUNET_free (pd->section);
|
||||||
GNUNET_free (pd->auth_url);
|
GNUNET_free (pd->auth_url);
|
||||||
GNUNET_free (pd->login_url);
|
GNUNET_free (pd->login_url);
|
||||||
GNUNET_free (pd->info_url);
|
GNUNET_free (pd->info_url);
|
||||||
@ -292,6 +298,7 @@ oauth2_load_configuration (void *cls,
|
|||||||
|
|
||||||
pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
|
pd = GNUNET_new (struct TALER_KYCLOGIC_ProviderDetails);
|
||||||
pd->ps = ps;
|
pd->ps = ps;
|
||||||
|
pd->section = GNUNET_strdup (provider_section_name);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
|
GNUNET_CONFIGURATION_get_value_time (ps->cfg,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
@ -467,9 +474,10 @@ initiate_task (void *cls)
|
|||||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
||||||
sizeof (ih->h_payto));
|
sizeof (ih->h_payto));
|
||||||
GNUNET_asprintf (&redirect_uri,
|
GNUNET_asprintf (&redirect_uri,
|
||||||
"%s/kyc-proof/%s/oauth2/%s",
|
"%s/kyc-proof/%s/%s/%s",
|
||||||
ps->exchange_base_url,
|
ps->exchange_base_url,
|
||||||
hps,
|
hps,
|
||||||
|
pd->section,
|
||||||
legi_s);
|
legi_s);
|
||||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||||
GNUNET_free (redirect_uri);
|
GNUNET_free (redirect_uri);
|
||||||
@ -532,7 +540,11 @@ oauth2_initiate (void *cls,
|
|||||||
static void
|
static void
|
||||||
oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
|
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);
|
GNUNET_free (ih);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,6 +671,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
|
|||||||
if (GNUNET_OK != res)
|
if (GNUNET_OK != res)
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
|
json_dumpf (j,
|
||||||
|
stderr,
|
||||||
|
JSON_INDENT (2));
|
||||||
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
||||||
ph->response
|
ph->response
|
||||||
= TALER_MHD_make_error (
|
= TALER_MHD_make_error (
|
||||||
@ -691,6 +706,9 @@ parse_proof_success_reply (struct TALER_KYCLOGIC_ProofHandle *ph,
|
|||||||
if (GNUNET_OK != res)
|
if (GNUNET_OK != res)
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
|
json_dumpf (data,
|
||||||
|
stderr,
|
||||||
|
JSON_INDENT (2));
|
||||||
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
ph->status = TALER_KYCLOGIC_STATUS_PROVIDER_FAILED;
|
||||||
ph->response
|
ph->response
|
||||||
= TALER_MHD_make_error (
|
= TALER_MHD_make_error (
|
||||||
@ -741,6 +759,9 @@ handle_curl_proof_finished (void *cls,
|
|||||||
j);
|
j);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||||
|
"OAuth2.0 info URL returned HTTP status %u\n",
|
||||||
|
(unsigned int) response_code);
|
||||||
handle_proof_error (ph,
|
handle_proof_error (ph,
|
||||||
j);
|
j);
|
||||||
break;
|
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.
|
* 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 url_path rest of the URL after `/kyc-webhook/`
|
||||||
* @param connection MHD connection object (for HTTP headers)
|
* @param connection MHD connection object (for HTTP headers)
|
||||||
* @param account_id which account to trigger process for
|
* @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_user_id user ID (or NULL) the proof is for
|
||||||
* @param provider_legitimization_id legitimization ID the proof is for
|
* @param provider_legitimization_id legitimization ID the proof is for
|
||||||
* @param cb function to call with the result
|
* @param cb function to call with the result
|
||||||
@ -770,6 +933,7 @@ oauth2_proof (void *cls,
|
|||||||
const char *const url_path[],
|
const char *const url_path[],
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
const struct TALER_PaytoHashP *account_id,
|
const struct TALER_PaytoHashP *account_id,
|
||||||
|
uint64_t legi_row,
|
||||||
const char *provider_user_id,
|
const char *provider_user_id,
|
||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
TALER_KYCLOGIC_ProofCallback cb,
|
TALER_KYCLOGIC_ProofCallback cb,
|
||||||
@ -779,16 +943,20 @@ oauth2_proof (void *cls,
|
|||||||
struct TALER_KYCLOGIC_ProofHandle *ph;
|
struct TALER_KYCLOGIC_ProofHandle *ph;
|
||||||
const char *code;
|
const char *code;
|
||||||
|
|
||||||
if (strlen (provider_legitimization_id) >=
|
|
||||||
sizeof (ph->provider_legitimization_id))
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
GNUNET_break (NULL == provider_user_id);
|
GNUNET_break (NULL == provider_user_id);
|
||||||
ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
|
ph = GNUNET_new (struct TALER_KYCLOGIC_ProofHandle);
|
||||||
strcpy (ph->provider_legitimization_id,
|
GNUNET_snprintf (ph->provider_legitimization_id,
|
||||||
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->pd = pd;
|
||||||
ph->connection = connection;
|
ph->connection = connection;
|
||||||
ph->h_payto = *account_id;
|
ph->h_payto = *account_id;
|
||||||
@ -891,7 +1059,7 @@ oauth2_proof (void *cls,
|
|||||||
|
|
||||||
ph->job = GNUNET_CURL_job_add (ps->curl_ctx,
|
ph->job = GNUNET_CURL_job_add (ps->curl_ctx,
|
||||||
ph->eh,
|
ph->eh,
|
||||||
&handle_curl_proof_finished,
|
&handle_curl_login_finished,
|
||||||
ph);
|
ph);
|
||||||
return 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 url_path rest of the URL after `/kyc-webhook/`
|
||||||
* @param connection MHD connection object (for HTTP headers)
|
* @param connection MHD connection object (for HTTP headers)
|
||||||
* @param account_id which account to trigger process for
|
* @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_user_id user ID (or NULL) the proof is for
|
||||||
* @param provider_legitimization_id legitimization ID the proof is for
|
* @param provider_legitimization_id legitimization ID the proof is for
|
||||||
* @param cb function to call with the result
|
* @param cb function to call with the result
|
||||||
@ -265,6 +266,7 @@ template_proof (void *cls,
|
|||||||
const char *const url_path[],
|
const char *const url_path[],
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
const struct TALER_PaytoHashP *account_id,
|
const struct TALER_PaytoHashP *account_id,
|
||||||
|
uint64_t legi_row,
|
||||||
const char *provider_user_id,
|
const char *provider_user_id,
|
||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
TALER_KYCLOGIC_ProofCallback cb,
|
TALER_KYCLOGIC_ProofCallback cb,
|
||||||
|
@ -754,6 +754,7 @@ handler_kyc_proof_get (
|
|||||||
&args[2],
|
&args[2],
|
||||||
rc->connection,
|
rc->connection,
|
||||||
&h_payto,
|
&h_payto,
|
||||||
|
kyc_row_id,
|
||||||
cmd_provider_user_id,
|
cmd_provider_user_id,
|
||||||
cmd_provider_legitimization_id,
|
cmd_provider_legitimization_id,
|
||||||
&proof_cb,
|
&proof_cb,
|
||||||
@ -1456,7 +1457,7 @@ main (int argc,
|
|||||||
"use the given provider user ID (overridden if -i is also used)",
|
"use the given provider user ID (overridden if -i is also used)",
|
||||||
&cmd_provider_user_id),
|
&cmd_provider_user_id),
|
||||||
GNUNET_GETOPT_option_string (
|
GNUNET_GETOPT_option_string (
|
||||||
'l',
|
'U',
|
||||||
"legitimization",
|
"legitimization",
|
||||||
"ID",
|
"ID",
|
||||||
"use the given provider legitimization ID (overridden if -i is also used)",
|
"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 (
|
GNUNET_GETOPT_option_base32_fixed_size (
|
||||||
'p',
|
'p',
|
||||||
"payto-hash",
|
"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)",
|
"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,
|
&cmd_line_h_payto,
|
||||||
sizeof (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
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
||||||
const struct TALER_PurseMergeSignatureP *cmerge_sig,
|
const struct TALER_PurseMergeSignatureP *cmerge_sig,
|
||||||
@ -1539,8 +1495,8 @@ TALER_EXCHANGE_check_purse_merge_conflict_ (
|
|||||||
}
|
}
|
||||||
if (NULL == partner_url)
|
if (NULL == partner_url)
|
||||||
partner_url = exchange_url;
|
partner_url = exchange_url;
|
||||||
payto_uri = make_payto (partner_url,
|
payto_uri = TALER_reserve_make_payto (partner_url,
|
||||||
&reserve_pub);
|
&reserve_pub);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_wallet_purse_merge_verify (
|
TALER_wallet_purse_merge_verify (
|
||||||
payto_uri,
|
payto_uri,
|
||||||
|
@ -176,11 +176,14 @@ handle_deposit_wtid_finished (void *cls,
|
|||||||
case MHD_HTTP_ACCEPTED:
|
case MHD_HTTP_ACCEPTED:
|
||||||
{
|
{
|
||||||
/* Transaction known, but not executed yet */
|
/* Transaction known, but not executed yet */
|
||||||
|
bool no_legi = false;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_timestamp ("execution_time",
|
GNUNET_JSON_spec_timestamp ("execution_time",
|
||||||
&dr.details.accepted.execution_time),
|
&dr.details.accepted.execution_time),
|
||||||
GNUNET_JSON_spec_uint64 ("payment_target_uuid",
|
GNUNET_JSON_spec_mark_optional (
|
||||||
&dr.details.accepted.payment_target_uuid),
|
GNUNET_JSON_spec_uint64 ("legitimization_uuid",
|
||||||
|
&dr.details.accepted.payment_target_uuid),
|
||||||
|
&no_legi),
|
||||||
GNUNET_JSON_spec_bool ("kyc_ok",
|
GNUNET_JSON_spec_bool ("kyc_ok",
|
||||||
&dr.details.accepted.kyc_ok),
|
&dr.details.accepted.kyc_ok),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
@ -196,6 +199,8 @@ handle_deposit_wtid_finished (void *cls,
|
|||||||
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (no_legi)
|
||||||
|
dr.details.accepted.payment_target_uuid = 0;
|
||||||
dwh->cb (dwh->cb_cls,
|
dwh->cb (dwh->cb_cls,
|
||||||
&dr);
|
&dr);
|
||||||
TALER_EXCHANGE_deposits_get_cancel (dwh);
|
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 GNUNET_HashCode hash_xor = {0};
|
||||||
struct TALER_ExchangePublicKeyP pub;
|
struct TALER_ExchangePublicKeyP pub;
|
||||||
const char *currency;
|
const char *currency;
|
||||||
|
json_t *wblwk = NULL;
|
||||||
struct GNUNET_JSON_Specification mspec[] = {
|
struct GNUNET_JSON_Specification mspec[] = {
|
||||||
GNUNET_JSON_spec_fixed_auto ("denominations_sig",
|
GNUNET_JSON_spec_fixed_auto ("denominations_sig",
|
||||||
&denominations_sig),
|
&denominations_sig),
|
||||||
@ -750,8 +751,8 @@ decode_keys_json (const json_t *resp_obj,
|
|||||||
GNUNET_JSON_spec_string ("currency",
|
GNUNET_JSON_spec_string ("currency",
|
||||||
¤cy),
|
¤cy),
|
||||||
GNUNET_JSON_spec_mark_optional (
|
GNUNET_JSON_spec_mark_optional (
|
||||||
TALER_JSON_spec_amount_any ("wallet_balance_limit_without_kyc",
|
GNUNET_JSON_spec_json ("wallet_balance_limit_without_kyc",
|
||||||
&key_data->wallet_balance_limit_without_kyc),
|
&wblwk),
|
||||||
NULL),
|
NULL),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
@ -819,17 +820,6 @@ decode_keys_json (const json_t *resp_obj,
|
|||||||
NULL, NULL));
|
NULL, NULL));
|
||||||
key_data->currency = GNUNET_strdup (currency);
|
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 */
|
/* parse the global fees */
|
||||||
{
|
{
|
||||||
json_t *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. */
|
/* Parse the supported extension(s): age-restriction. */
|
||||||
/* TODO: maybe lift all this into a FP in TALER_Extension ? */
|
/* 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,
|
GNUNET_array_grow (key_data->auditors,
|
||||||
key_data->auditors_size,
|
key_data->auditors_size,
|
||||||
0);
|
0);
|
||||||
|
GNUNET_free (key_data->wallet_balance_limit_without_kyc);
|
||||||
GNUNET_free (key_data->version);
|
GNUNET_free (key_data->version);
|
||||||
GNUNET_free (key_data->currency);
|
GNUNET_free (key_data->currency);
|
||||||
GNUNET_free (key_data->global_fees);
|
GNUNET_free (key_data->global_fees);
|
||||||
|
@ -207,7 +207,7 @@ handle_kyc_check_finished (void *cls,
|
|||||||
|
|
||||||
struct TALER_EXCHANGE_KycCheckHandle *
|
struct TALER_EXCHANGE_KycCheckHandle *
|
||||||
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
uint64_t payment_target,
|
uint64_t legitimization_uuid,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
struct GNUNET_TIME_Relative timeout,
|
struct GNUNET_TIME_Relative timeout,
|
||||||
TALER_EXCHANGE_KycStatusCallback cb,
|
TALER_EXCHANGE_KycStatusCallback cb,
|
||||||
@ -238,8 +238,8 @@ TALER_EXCHANGE_kyc_check (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
timeout_ms = timeout.rel_value_us
|
timeout_ms = timeout.rel_value_us
|
||||||
/ GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
|
/ GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
|
||||||
GNUNET_asprintf (&arg_str,
|
GNUNET_asprintf (&arg_str,
|
||||||
"/kyc-check/%llu?h_payto=%s&timeout_ms=%llu",
|
"/kyc-check/%llu/%s?timeout_ms=%llu",
|
||||||
(unsigned long long) payment_target,
|
(unsigned long long) legitimization_uuid,
|
||||||
payto_str,
|
payto_str,
|
||||||
timeout_ms);
|
timeout_ms);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
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 *
|
struct TALER_EXCHANGE_KycProofHandle *
|
||||||
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
const struct TALER_PaytoHashP *h_payto,
|
||||||
const char *code,
|
const char *logic,
|
||||||
const char *state,
|
const char *args,
|
||||||
TALER_EXCHANGE_KycProofCallback cb,
|
TALER_EXCHANGE_KycProofCallback cb,
|
||||||
void *cb_cls)
|
void *cb_cls)
|
||||||
{
|
{
|
||||||
@ -151,13 +151,17 @@ TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
struct GNUNET_CURL_Context *ctx;
|
struct GNUNET_CURL_Context *ctx;
|
||||||
char *arg_str;
|
char *arg_str;
|
||||||
|
|
||||||
|
if (NULL == args)
|
||||||
|
args = "";
|
||||||
|
else
|
||||||
|
GNUNET_assert ( (args[0] == '?') ||
|
||||||
|
(args[0] == '/') );
|
||||||
if (GNUNET_YES !=
|
if (GNUNET_YES !=
|
||||||
TEAH_handle_is_ready (exchange))
|
TEAH_handle_is_ready (exchange))
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* TODO: any escaping of code/state needed??? */
|
|
||||||
{
|
{
|
||||||
char hstr[sizeof (struct TALER_PaytoHashP) * 2];
|
char hstr[sizeof (struct TALER_PaytoHashP) * 2];
|
||||||
char *end;
|
char *end;
|
||||||
@ -168,10 +172,10 @@ TALER_EXCHANGE_kyc_proof (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
sizeof (hstr));
|
sizeof (hstr));
|
||||||
*end = '\0';
|
*end = '\0';
|
||||||
GNUNET_asprintf (&arg_str,
|
GNUNET_asprintf (&arg_str,
|
||||||
"/kyc-proof/%s?code=%s&state=%s",
|
"/kyc-proof/%s/%s%s",
|
||||||
hstr,
|
hstr,
|
||||||
code,
|
logic,
|
||||||
state);
|
args);
|
||||||
}
|
}
|
||||||
kph = GNUNET_new (struct TALER_EXCHANGE_KycProofHandle);
|
kph = GNUNET_new (struct TALER_EXCHANGE_KycProofHandle);
|
||||||
kph->exchange = exchange;
|
kph->exchange = exchange;
|
||||||
|
@ -152,6 +152,7 @@ handle_kyc_wallet_finished (void *cls,
|
|||||||
struct TALER_EXCHANGE_KycWalletHandle *
|
struct TALER_EXCHANGE_KycWalletHandle *
|
||||||
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
|
TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
|
||||||
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
const struct TALER_ReservePrivateKeyP *reserve_priv,
|
||||||
|
const struct TALER_Amount *balance,
|
||||||
TALER_EXCHANGE_KycWalletCallback cb,
|
TALER_EXCHANGE_KycWalletCallback cb,
|
||||||
void *cb_cls)
|
void *cb_cls)
|
||||||
{
|
{
|
||||||
@ -167,6 +168,8 @@ TALER_EXCHANGE_kyc_wallet (struct TALER_EXCHANGE_Handle *exchange,
|
|||||||
TALER_wallet_account_setup_sign (reserve_priv,
|
TALER_wallet_account_setup_sign (reserve_priv,
|
||||||
&reserve_sig);
|
&reserve_sig);
|
||||||
req = GNUNET_JSON_PACK (
|
req = GNUNET_JSON_PACK (
|
||||||
|
TALER_JSON_pack_amount ("balance",
|
||||||
|
balance),
|
||||||
GNUNET_JSON_pack_data_auto ("reserve_pub",
|
GNUNET_JSON_pack_data_auto ("reserve_pub",
|
||||||
&reserve_pub),
|
&reserve_pub),
|
||||||
GNUNET_JSON_pack_data_auto ("reserve_sig",
|
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,
|
GNUNET_CRYPTO_eddsa_key_get_public (&pch->merge_priv.eddsa_priv,
|
||||||
&merge_pub.eddsa_pub);
|
&merge_pub.eddsa_pub);
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
TALER_EXCHANGE_check_purse_merge_conflict_ (
|
||||||
&pch->merge_sig,
|
&pch->merge_sig,
|
||||||
|
@ -264,28 +264,6 @@ handle_reserve_withdraw_finished (void *cls,
|
|||||||
GNUNET_assert (NULL == wh->cb);
|
GNUNET_assert (NULL == wh->cb);
|
||||||
TALER_EXCHANGE_withdraw2_cancel (wh);
|
TALER_EXCHANGE_withdraw2_cancel (wh);
|
||||||
return;
|
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:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(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.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
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:
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
/* Server had an internal issue; we should retry, but this API
|
/* Server had an internal issue; we should retry, but this API
|
||||||
leaves this to the application */
|
leaves this to the application */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as
|
it under the terms of the GNU General Public License as
|
||||||
@ -124,11 +124,12 @@ run (void *cls,
|
|||||||
"EUR:5",
|
"EUR:5",
|
||||||
0, /* age restriction off */
|
0, /* age restriction off */
|
||||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS),
|
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS),
|
||||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc",
|
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc",
|
||||||
"create-reserve-1",
|
"create-reserve-1",
|
||||||
"pass",
|
"kyc-provider-test-oauth2",
|
||||||
"state",
|
"pass",
|
||||||
MHD_HTTP_SEE_OTHER),
|
"state",
|
||||||
|
MHD_HTTP_SEE_OTHER),
|
||||||
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc",
|
TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1-with-kyc",
|
||||||
"create-reserve-1",
|
"create-reserve-1",
|
||||||
"EUR:5",
|
"EUR:5",
|
||||||
@ -158,26 +159,35 @@ run (void *cls,
|
|||||||
struct TALER_TESTING_Command track[] = {
|
struct TALER_TESTING_Command track[] = {
|
||||||
CMD_EXEC_AGGREGATOR ("run-aggregator-before-kyc"),
|
CMD_EXEC_AGGREGATOR ("run-aggregator-before-kyc"),
|
||||||
TALER_TESTING_cmd_check_bank_empty ("check_bank_empty-no-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",
|
TALER_TESTING_cmd_check_kyc_get ("check-kyc-deposit",
|
||||||
"track-deposit",
|
"track-deposit-kyc-ready",
|
||||||
MHD_HTTP_ACCEPTED),
|
MHD_HTTP_ACCEPTED),
|
||||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-no-service",
|
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-no-service",
|
||||||
"track-deposit",
|
"track-deposit-kyc-ready",
|
||||||
"bad",
|
"kyc-provider-test-oauth2",
|
||||||
"state",
|
"bad",
|
||||||
MHD_HTTP_BAD_GATEWAY),
|
"state",
|
||||||
|
MHD_HTTP_BAD_GATEWAY),
|
||||||
TALER_TESTING_cmd_oauth ("start-oauth-service",
|
TALER_TESTING_cmd_oauth ("start-oauth-service",
|
||||||
6666),
|
6666),
|
||||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-fail",
|
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-fail",
|
||||||
"track-deposit",
|
"track-deposit-kyc-ready",
|
||||||
"bad",
|
"kyc-provider-test-oauth2",
|
||||||
"state",
|
"bad",
|
||||||
MHD_HTTP_FORBIDDEN),
|
"state",
|
||||||
TALER_TESTING_cmd_proof_kyc ("proof-kyc-fail",
|
MHD_HTTP_FORBIDDEN),
|
||||||
"track-deposit",
|
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-kyc-fail",
|
||||||
"pass",
|
"track-deposit-kyc-ready",
|
||||||
"state",
|
"kyc-provider-test-oauth2",
|
||||||
MHD_HTTP_SEE_OTHER),
|
"pass",
|
||||||
|
"state",
|
||||||
|
MHD_HTTP_SEE_OTHER),
|
||||||
CMD_EXEC_AGGREGATOR ("run-aggregator-after-kyc"),
|
CMD_EXEC_AGGREGATOR ("run-aggregator-after-kyc"),
|
||||||
TALER_TESTING_cmd_check_bank_transfer (
|
TALER_TESTING_cmd_check_bank_transfer (
|
||||||
"check_bank_transfer-499c",
|
"check_bank_transfer-499c",
|
||||||
@ -190,15 +200,19 @@ run (void *cls,
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct TALER_TESTING_Command wallet_kyc[] = {
|
struct TALER_TESTING_Command wallet_kyc[] = {
|
||||||
|
TALER_TESTING_cmd_oauth ("start-oauth-service",
|
||||||
|
6666),
|
||||||
TALER_TESTING_cmd_wallet_kyc_get (
|
TALER_TESTING_cmd_wallet_kyc_get (
|
||||||
"wallet-kyc-fail",
|
"wallet-kyc-fail",
|
||||||
NULL,
|
NULL,
|
||||||
|
"EUR:1000000",
|
||||||
MHD_HTTP_OK),
|
MHD_HTTP_OK),
|
||||||
TALER_TESTING_cmd_proof_kyc ("proof-wallet-kyc",
|
TALER_TESTING_cmd_proof_kyc_oauth2 ("proof-wallet-kyc",
|
||||||
"wallet-kyc-fail",
|
"wallet-kyc-fail",
|
||||||
"pass",
|
"kyc-provider-test-oauth2",
|
||||||
"state",
|
"pass",
|
||||||
MHD_HTTP_SEE_OTHER),
|
"state",
|
||||||
|
MHD_HTTP_SEE_OTHER),
|
||||||
TALER_TESTING_cmd_check_kyc_get (
|
TALER_TESTING_cmd_check_kyc_get (
|
||||||
"wallet-kyc-check",
|
"wallet-kyc-check",
|
||||||
"wallet-kyc-fail",
|
"wallet-kyc-fail",
|
||||||
|
@ -46,16 +46,12 @@ DB = postgres
|
|||||||
BASE_URL = "http://localhost:8081/"
|
BASE_URL = "http://localhost:8081/"
|
||||||
|
|
||||||
|
|
||||||
|
# Obsolete options, migrate to withdraw once implemented...
|
||||||
KYC_MODE = OAUTH2
|
KYC_MODE = OAUTH2
|
||||||
|
|
||||||
KYC_WALLET_BALANCE_LIMIT = EUR:1
|
|
||||||
|
|
||||||
KYC_WITHDRAW_PERIOD = "31 days"
|
KYC_WITHDRAW_PERIOD = "31 days"
|
||||||
|
|
||||||
KYC_WITHDRAW_LIMIT = EUR:8
|
KYC_WITHDRAW_LIMIT = EUR:8
|
||||||
|
|
||||||
[exchange-kyc-oauth2]
|
[exchange-kyc-oauth2]
|
||||||
|
|
||||||
KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token
|
KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token
|
||||||
KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login
|
KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login
|
||||||
KYC_INFO_URL = http://localhost:6666/api/user/me
|
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_CLIENT_SECRET = exchange-secret
|
||||||
KYC_OAUTH2_POST_URL = http://example.com/
|
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]
|
[exchangedb-postgres]
|
||||||
CONFIG = "postgres:///talercheck"
|
CONFIG = "postgres:///talercheck"
|
||||||
|
|
||||||
|
@ -283,7 +283,7 @@ batch_withdraw_run (void *cls,
|
|||||||
GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
|
GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
|
||||||
&ws->reserve_pub.eddsa_pub);
|
&ws->reserve_pub.eddsa_pub);
|
||||||
ws->reserve_payto_uri
|
ws->reserve_payto_uri
|
||||||
= TALER_payto_from_reserve (ws->exchange_url,
|
= TALER_reserve_make_payto (ws->exchange_url,
|
||||||
&ws->reserve_pub);
|
&ws->reserve_pub);
|
||||||
|
|
||||||
for (unsigned int i = 0; i<ws->num_coins; i++)
|
for (unsigned int i = 0; i<ws->num_coins; i++)
|
||||||
|
@ -48,6 +48,11 @@ struct KycProofGetState
|
|||||||
*/
|
*/
|
||||||
const char *state;
|
const char *state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logic section name to pass to `/kyc-proof/` handler.
|
||||||
|
*/
|
||||||
|
const char *logic;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expected HTTP response code.
|
* Expected HTTP response code.
|
||||||
*/
|
*/
|
||||||
@ -133,6 +138,7 @@ proof_kyc_run (void *cls,
|
|||||||
const struct TALER_TESTING_Command *res_cmd;
|
const struct TALER_TESTING_Command *res_cmd;
|
||||||
const char **payto_uri;
|
const char **payto_uri;
|
||||||
struct TALER_PaytoHashP h_payto;
|
struct TALER_PaytoHashP h_payto;
|
||||||
|
char *uargs;
|
||||||
|
|
||||||
(void) cmd;
|
(void) cmd;
|
||||||
kps->is = is;
|
kps->is = is;
|
||||||
@ -169,12 +175,17 @@ proof_kyc_run (void *cls,
|
|||||||
TALER_payto_hash (*payto_uri,
|
TALER_payto_hash (*payto_uri,
|
||||||
&h_payto);
|
&h_payto);
|
||||||
}
|
}
|
||||||
|
GNUNET_asprintf (&uargs,
|
||||||
|
"?code=%s&state=%s",
|
||||||
|
kps->code,
|
||||||
|
kps->state);
|
||||||
kps->kph = TALER_EXCHANGE_kyc_proof (is->exchange,
|
kps->kph = TALER_EXCHANGE_kyc_proof (is->exchange,
|
||||||
&h_payto,
|
&h_payto,
|
||||||
kps->code,
|
kps->logic,
|
||||||
kps->state,
|
uargs,
|
||||||
&proof_kyc_cb,
|
&proof_kyc_cb,
|
||||||
kps);
|
kps);
|
||||||
|
GNUNET_free (uargs);
|
||||||
GNUNET_assert (NULL != kps->kph);
|
GNUNET_assert (NULL != kps->kph);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,17 +247,20 @@ proof_kyc_traits (void *cls,
|
|||||||
|
|
||||||
|
|
||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_proof_kyc (const char *label,
|
TALER_TESTING_cmd_proof_kyc_oauth2 (
|
||||||
const char *payment_target_reference,
|
const char *label,
|
||||||
const char *code,
|
const char *payment_target_reference,
|
||||||
const char *state,
|
const char *logic_section,
|
||||||
unsigned int expected_response_code)
|
const char *code,
|
||||||
|
const char *state,
|
||||||
|
unsigned int expected_response_code)
|
||||||
{
|
{
|
||||||
struct KycProofGetState *kps;
|
struct KycProofGetState *kps;
|
||||||
|
|
||||||
kps = GNUNET_new (struct KycProofGetState);
|
kps = GNUNET_new (struct KycProofGetState);
|
||||||
kps->code = code;
|
kps->code = code;
|
||||||
kps->state = state;
|
kps->state = state;
|
||||||
|
kps->logic = logic_section;
|
||||||
kps->payment_target_reference = payment_target_reference;
|
kps->payment_target_reference = payment_target_reference;
|
||||||
kps->expected_response_code = expected_response_code;
|
kps->expected_response_code = expected_response_code;
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,11 @@ struct KycWalletGetState
|
|||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGE_KycWalletHandle *kwh;
|
struct TALER_EXCHANGE_KycWalletHandle *kwh;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance to pass to the exchange.
|
||||||
|
*/
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interpreter state.
|
* Interpreter state.
|
||||||
*/
|
*/
|
||||||
@ -170,10 +175,11 @@ wallet_kyc_run (void *cls,
|
|||||||
GNUNET_CRYPTO_eddsa_key_get_public (&kwg->reserve_priv.eddsa_priv,
|
GNUNET_CRYPTO_eddsa_key_get_public (&kwg->reserve_priv.eddsa_priv,
|
||||||
&kwg->reserve_pub.eddsa_pub);
|
&kwg->reserve_pub.eddsa_pub);
|
||||||
kwg->reserve_payto_uri
|
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->reserve_pub);
|
||||||
kwg->kwh = TALER_EXCHANGE_kyc_wallet (is->exchange,
|
kwg->kwh = TALER_EXCHANGE_kyc_wallet (is->exchange,
|
||||||
&kwg->reserve_priv,
|
&kwg->reserve_priv,
|
||||||
|
&kwg->balance,
|
||||||
&wallet_kyc_cb,
|
&wallet_kyc_cb,
|
||||||
kwg);
|
kwg);
|
||||||
GNUNET_assert (NULL != kwg->kwh);
|
GNUNET_assert (NULL != kwg->kwh);
|
||||||
@ -242,6 +248,7 @@ wallet_kyc_traits (void *cls,
|
|||||||
struct TALER_TESTING_Command
|
struct TALER_TESTING_Command
|
||||||
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
||||||
const char *reserve_reference,
|
const char *reserve_reference,
|
||||||
|
const char *threshold_balance,
|
||||||
unsigned int expected_response_code)
|
unsigned int expected_response_code)
|
||||||
{
|
{
|
||||||
struct KycWalletGetState *kwg;
|
struct KycWalletGetState *kwg;
|
||||||
@ -249,6 +256,9 @@ TALER_TESTING_cmd_wallet_kyc_get (const char *label,
|
|||||||
kwg = GNUNET_new (struct KycWalletGetState);
|
kwg = GNUNET_new (struct KycWalletGetState);
|
||||||
kwg->reserve_reference = reserve_reference;
|
kwg->reserve_reference = reserve_reference;
|
||||||
kwg->expected_response_code = expected_response_code;
|
kwg->expected_response_code = expected_response_code;
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_string_to_amount (threshold_balance,
|
||||||
|
&kwg->balance));
|
||||||
{
|
{
|
||||||
struct TALER_TESTING_Command cmd = {
|
struct TALER_TESTING_Command cmd = {
|
||||||
.cls = kwg,
|
.cls = kwg,
|
||||||
|
@ -320,6 +320,9 @@ reserve_withdraw_cb (void *cls,
|
|||||||
case MHD_HTTP_GONE:
|
case MHD_HTTP_GONE:
|
||||||
/* theoretically could check that the key was actually */
|
/* theoretically could check that the key was actually */
|
||||||
break;
|
break;
|
||||||
|
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
||||||
|
/* KYC required */
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* Unsupported status code (by test harness) */
|
/* Unsupported status code (by test harness) */
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
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,
|
GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
|
||||||
&ws->reserve_pub.eddsa_pub);
|
&ws->reserve_pub.eddsa_pub);
|
||||||
ws->reserve_payto_uri
|
ws->reserve_payto_uri
|
||||||
= TALER_payto_from_reserve (ws->exchange_url,
|
= TALER_reserve_make_payto (ws->exchange_url,
|
||||||
&ws->reserve_pub);
|
&ws->reserve_pub);
|
||||||
|
|
||||||
if (NULL == ws->reuse_coin_key_ref)
|
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 *
|
char *
|
||||||
TALER_reserve_make_payto (const char *exchange_url,
|
TALER_reserve_make_payto (const char *exchange_url,
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub)
|
const struct TALER_ReservePublicKeyP *reserve_pub)
|
||||||
@ -349,7 +310,7 @@ TALER_reserve_make_payto (const char *exchange_url,
|
|||||||
/* exchange_url includes trailing '/' */
|
/* exchange_url includes trailing '/' */
|
||||||
GNUNET_asprintf (&reserve_url,
|
GNUNET_asprintf (&reserve_url,
|
||||||
"payto://%s/%s%s",
|
"payto://%s/%s%s",
|
||||||
is_http ? "taler+http" : "taler",
|
is_http ? "taler-reserve+http" : "taler-reserve",
|
||||||
exchange_url,
|
exchange_url,
|
||||||
pub_str);
|
pub_str);
|
||||||
return reserve_url;
|
return reserve_url;
|
||||||
|
Loading…
Reference in New Issue
Block a user