2022-08-01 18:09:06 +02:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
|
|
|
Copyright (C) 2022 Taler Systems SA
|
|
|
|
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU Affero General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License along with
|
|
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
|
|
*/
|
|
|
|
/**
|
2022-08-08 15:22:45 +02:00
|
|
|
* @file kyclogic_api.c
|
|
|
|
* @brief server-side KYC API
|
2022-08-01 18:09:06 +02:00
|
|
|
* @author Christian Grothoff
|
|
|
|
*/
|
|
|
|
#include "platform.h"
|
2022-08-08 15:22:45 +02:00
|
|
|
#include "taler_kyclogic_lib.h"
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Information about a KYC provider.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Abstract representation of a KYC check.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Human-readable name given to the KYC check.
|
|
|
|
*/
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of @e num_providers providers that offer this type of KYC check.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider **providers;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the @e providers array.
|
|
|
|
*/
|
|
|
|
unsigned int num_providers;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Name of the provider (configuration section name).
|
|
|
|
*/
|
|
|
|
const char *provider_section_name;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of @e num_checks checks performed by this provider.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck **provided_checks;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Logic to run for this provider.
|
|
|
|
*/
|
2022-08-04 12:52:30 +02:00
|
|
|
struct TALER_KYCLOGIC_Plugin *logic;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @e provider_section_name specific details to
|
|
|
|
* pass to the @e logic functions.
|
|
|
|
*/
|
2022-08-04 12:52:30 +02:00
|
|
|
struct TALER_KYCLOGIC_ProviderDetails *pd;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
2022-08-04 11:36:05 +02:00
|
|
|
/**
|
|
|
|
* Cost of running this provider's KYC.
|
|
|
|
*/
|
|
|
|
unsigned long long cost;
|
|
|
|
|
2022-08-01 18:09:06 +02:00
|
|
|
/**
|
|
|
|
* Length of the @e checks array.
|
|
|
|
*/
|
|
|
|
unsigned int num_checks;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Type of user this provider supports.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycUserType user_type;
|
2022-08-01 18:09:06 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Condition that triggers a need to perform KYC.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycTrigger
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Timeframe to consider for computing the amount
|
|
|
|
* to compare against the @e limit. Zero for the
|
|
|
|
* wallet balance trigger (as not applicable).
|
|
|
|
*/
|
|
|
|
struct GNUNET_TIME_Relative timeframe;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum amount that can be transacted until
|
|
|
|
* the rule triggers.
|
|
|
|
*/
|
2022-08-02 12:05:57 +02:00
|
|
|
struct TALER_Amount threshold;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of @e num_checks checks to apply on this trigger.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck **required_checks;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the @e checks array.
|
|
|
|
*/
|
|
|
|
unsigned int num_checks;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* What event is this trigger for?
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycTriggerEvent trigger;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of @e num_kyc_logics KYC logic plugins we have loaded.
|
|
|
|
*/
|
2022-08-04 12:52:30 +02:00
|
|
|
static struct TALER_KYCLOGIC_Plugin **kyc_logics;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the #kyc_logics array.
|
|
|
|
*/
|
2022-08-04 12:52:30 +02:00
|
|
|
static unsigned int num_kyc_logics;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of @e num_kyc_checks known types of
|
|
|
|
* KYC checks.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
static struct TALER_KYCLOGIC_KycCheck **kyc_checks;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the #kyc_checks array.
|
|
|
|
*/
|
|
|
|
static unsigned int num_kyc_checks;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of configured triggers.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
static struct TALER_KYCLOGIC_KycTrigger **kyc_triggers;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the #kyc_triggers array.
|
|
|
|
*/
|
|
|
|
static unsigned int num_kyc_triggers;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of configured providers.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
static struct TALER_KYCLOGIC_KycProvider **kyc_providers;
|
2022-08-01 18:09:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Length of the #kyc_providers array.
|
|
|
|
*/
|
|
|
|
static unsigned int num_kyc_providers;
|
|
|
|
|
|
|
|
|
|
|
|
enum GNUNET_GenericReturnValue
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s,
|
|
|
|
enum TALER_KYCLOGIC_KycTriggerEvent *
|
|
|
|
trigger)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-02 12:05:57 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
const char *in;
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycTriggerEvent out;
|
2022-08-02 12:05:57 +02:00
|
|
|
} map [] = {
|
2022-08-08 15:22:45 +02:00
|
|
|
{ "withdraw", TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW },
|
|
|
|
{ "deposit", TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT },
|
|
|
|
{ "merge", TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE },
|
|
|
|
{ "balance", TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE },
|
2022-08-02 12:05:57 +02:00
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (unsigned int i = 0; NULL != map[i].in; i++)
|
|
|
|
if (0 == strcasecmp (map[i].in,
|
|
|
|
trigger_s))
|
|
|
|
{
|
|
|
|
*trigger = map[i].out;
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Invalid KYC trigger `%s'\n",
|
|
|
|
trigger_s);
|
2022-08-01 18:09:06 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-02 12:05:57 +02:00
|
|
|
switch (trigger)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "withdraw";
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "deposit";
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "merge";
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "balance";
|
|
|
|
}
|
2022-08-01 18:09:06 +02:00
|
|
|
GNUNET_break (0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum GNUNET_GenericReturnValue
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_user_type_from_string (const char *ut_s,
|
|
|
|
enum TALER_KYCLOGIC_KycUserType *ut)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-02 12:05:57 +02:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
const char *in;
|
2022-08-19 09:31:51 +02:00
|
|
|
enum TALER_KYCLOGIC_KycUserType out;
|
2022-08-02 12:05:57 +02:00
|
|
|
} map [] = {
|
2022-08-08 15:22:45 +02:00
|
|
|
{ "individual", TALER_KYCLOGIC_KYC_UT_INDIVIDUAL },
|
|
|
|
{ "business", TALER_KYCLOGIC_KYC_UT_BUSINESS },
|
2022-08-02 12:05:57 +02:00
|
|
|
{ NULL, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
for (unsigned int i = 0; NULL != map[i].in; i++)
|
|
|
|
if (0 == strcasecmp (map[i].in,
|
|
|
|
ut_s))
|
|
|
|
{
|
|
|
|
*ut = map[i].out;
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Invalid user type `%s'\n",
|
|
|
|
ut_s);
|
2022-08-01 18:09:06 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_user_type2s (enum TALER_KYCLOGIC_KycUserType ut)
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
switch (ut)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_UT_INDIVIDUAL:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "individual";
|
2022-08-08 15:22:45 +02:00
|
|
|
case TALER_KYCLOGIC_KYC_UT_BUSINESS:
|
2022-08-02 12:05:57 +02:00
|
|
|
return "business";
|
|
|
|
}
|
|
|
|
GNUNET_break (0);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load KYC logic plugin.
|
|
|
|
*
|
2022-08-08 15:22:45 +02:00
|
|
|
* @param cfg configuration to use
|
2022-08-02 12:05:57 +02:00
|
|
|
* @param name name of the plugin
|
|
|
|
* @return NULL on error
|
|
|
|
*/
|
2022-08-04 12:52:30 +02:00
|
|
|
static struct TALER_KYCLOGIC_Plugin *
|
2022-08-08 15:22:45 +02:00
|
|
|
load_logic (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
|
|
|
const char *name)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-04 12:52:30 +02:00
|
|
|
char *lib_name;
|
|
|
|
struct TALER_KYCLOGIC_Plugin *plugin;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&lib_name,
|
|
|
|
"libtaler_plugin_kyclogic_%s",
|
|
|
|
name);
|
2022-08-11 23:35:33 +02:00
|
|
|
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];
|
|
|
|
}
|
2022-08-04 12:52:30 +02:00
|
|
|
plugin = GNUNET_PLUGIN_load (lib_name,
|
2022-08-08 15:22:45 +02:00
|
|
|
(void *) cfg);
|
2022-08-15 13:48:13 +02:00
|
|
|
if (NULL == plugin)
|
|
|
|
{
|
2022-08-04 12:52:30 +02:00
|
|
|
GNUNET_free (lib_name);
|
2022-08-15 13:48:13 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
plugin->library_name = lib_name;
|
2022-08-18 15:39:28 +02:00
|
|
|
plugin->name = GNUNET_strdup (name);
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_array_append (kyc_logics,
|
|
|
|
num_kyc_logics,
|
|
|
|
plugin);
|
2022-08-04 12:52:30 +02:00
|
|
|
return plugin;
|
2022-08-01 18:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-02 12:05:57 +02:00
|
|
|
/**
|
|
|
|
* Add check type to global array of checks.
|
|
|
|
* First checks if the type already exists, otherwise
|
|
|
|
* adds a new one.
|
|
|
|
*
|
|
|
|
* @param check name of the check
|
|
|
|
* @return pointer into the global list
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
static struct TALER_KYCLOGIC_KycCheck *
|
2022-08-02 12:05:57 +02:00
|
|
|
add_check (const char *check)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *kc;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_kyc_checks; i++)
|
|
|
|
if (0 == strcasecmp (check,
|
|
|
|
kyc_checks[i]->name))
|
|
|
|
return kyc_checks[i];
|
2022-08-08 15:22:45 +02:00
|
|
|
kc = GNUNET_new (struct TALER_KYCLOGIC_KycCheck);
|
2022-08-02 12:05:57 +02:00
|
|
|
kc->name = GNUNET_strdup (check);
|
|
|
|
GNUNET_array_append (kyc_checks,
|
|
|
|
num_kyc_checks,
|
|
|
|
kc);
|
|
|
|
return kc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse list of checks from @a checks and build an
|
|
|
|
* array of aliases into the global checks array
|
|
|
|
* in @a provided_checks.
|
|
|
|
*
|
|
|
|
* @param[in,out] checks list of checks; clobbered
|
|
|
|
* @param[out] p_checks where to put array of aliases
|
|
|
|
* @param[out] num_p_checks set to length of @a p_checks array
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
add_checks (char *checks,
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck ***p_checks,
|
2022-08-02 12:05:57 +02:00
|
|
|
unsigned int *num_p_checks)
|
|
|
|
{
|
|
|
|
char *sptr;
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck **rchecks = NULL;
|
2022-08-02 12:05:57 +02:00
|
|
|
unsigned int num_rchecks = 0;
|
|
|
|
|
|
|
|
for (char *tok = strtok_r (checks, " ", &sptr);
|
|
|
|
NULL != tok;
|
2022-08-04 12:52:30 +02:00
|
|
|
tok = strtok_r (NULL, " ", &sptr))
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *kc;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
kc = add_check (tok);
|
|
|
|
GNUNET_array_append (rchecks,
|
|
|
|
num_rchecks,
|
|
|
|
kc);
|
|
|
|
}
|
|
|
|
*p_checks = rchecks;
|
|
|
|
*num_p_checks = num_rchecks;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parse configuration of a KYC provider.
|
|
|
|
*
|
2022-08-08 15:22:45 +02:00
|
|
|
* @param cfg configuration to parse
|
|
|
|
* @param section name of the section to analyze
|
2022-08-02 12:05:57 +02:00
|
|
|
* @return #GNUNET_OK on success
|
|
|
|
*/
|
|
|
|
static enum GNUNET_GenericReturnValue
|
2022-08-08 15:22:45 +02:00
|
|
|
add_provider (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
|
|
|
const char *section)
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
unsigned long long cost;
|
|
|
|
char *logic;
|
|
|
|
char *ut_s;
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycUserType ut;
|
2022-08-02 12:05:57 +02:00
|
|
|
char *checks;
|
2022-08-04 12:52:30 +02:00
|
|
|
struct TALER_KYCLOGIC_Plugin *lp;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_number (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"COST",
|
|
|
|
&cost))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"COST",
|
|
|
|
"number required");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"USER_TYPE",
|
|
|
|
&ut_s))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"USER_TYPE");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_user_type_from_string (ut_s,
|
|
|
|
&ut))
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"USER_TYPE",
|
|
|
|
"valid user type required");
|
|
|
|
GNUNET_free (ut_s);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_free (ut_s);
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"LOGIC",
|
|
|
|
&logic))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"LOGIC");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2022-08-08 15:22:45 +02:00
|
|
|
lp = load_logic (cfg,
|
|
|
|
logic);
|
2022-08-02 12:05:57 +02:00
|
|
|
if (NULL == lp)
|
|
|
|
{
|
|
|
|
GNUNET_free (logic);
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"LOGIC",
|
|
|
|
"logic plugin could not be loaded");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_free (logic);
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"PROVIDED_CHECKS",
|
|
|
|
&checks))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"PROVIDED_CHECKS");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider *kp;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
kp = GNUNET_new (struct TALER_KYCLOGIC_KycProvider);
|
2022-08-02 12:05:57 +02:00
|
|
|
kp->provider_section_name = section;
|
|
|
|
kp->user_type = ut;
|
|
|
|
kp->logic = lp;
|
2022-08-04 11:36:05 +02:00
|
|
|
kp->cost = cost;
|
2022-08-02 12:05:57 +02:00
|
|
|
add_checks (checks,
|
|
|
|
&kp->provided_checks,
|
|
|
|
&kp->num_checks);
|
|
|
|
GNUNET_free (checks);
|
2022-08-04 12:52:30 +02:00
|
|
|
kp->pd = lp->load_configuration (lp->cls,
|
|
|
|
section);
|
2022-08-02 12:05:57 +02:00
|
|
|
if (NULL == kp->pd)
|
|
|
|
{
|
|
|
|
GNUNET_free (kp);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_array_append (kyc_providers,
|
|
|
|
num_kyc_providers,
|
|
|
|
kp);
|
|
|
|
for (unsigned int i = 0; i<kp->num_checks; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[i];
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
GNUNET_array_append (kc->providers,
|
|
|
|
kc->num_providers,
|
|
|
|
kp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-11 23:35:33 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2022-08-02 12:05:57 +02:00
|
|
|
static enum GNUNET_GenericReturnValue
|
2022-08-08 15:22:45 +02:00
|
|
|
add_trigger (const struct GNUNET_CONFIGURATION_Handle *cfg,
|
|
|
|
const char *section)
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
char *ot_s;
|
|
|
|
struct TALER_Amount threshold;
|
|
|
|
struct GNUNET_TIME_Relative timeframe;
|
|
|
|
char *checks;
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycTriggerEvent ot;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_config_get_amount (cfg,
|
2022-08-04 12:52:30 +02:00
|
|
|
section,
|
|
|
|
"THRESHOLD",
|
|
|
|
&threshold))
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"THRESHOLD",
|
|
|
|
"amount required");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"OPERATION_TYPE",
|
|
|
|
&ot_s))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"OPERATION_TYPE");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_trigger_from_string (ot_s,
|
|
|
|
&ot))
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"OPERATION_TYPE",
|
|
|
|
"valid trigger type required");
|
|
|
|
GNUNET_free (ot_s);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_free (ot_s);
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_time (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"TIMEFRAME",
|
|
|
|
&timeframe))
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
if (TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE == ot)
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
|
|
|
timeframe = GNUNET_TIME_UNIT_ZERO;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"TIMEFRAME",
|
|
|
|
"duration required");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_get_value_string (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
section,
|
|
|
|
"REQUIRED_CHECKS",
|
|
|
|
&checks))
|
|
|
|
{
|
|
|
|
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
section,
|
|
|
|
"REQUIRED_CHECKS");
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycTrigger *kt;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
kt = GNUNET_new (struct TALER_KYCLOGIC_KycTrigger);
|
2022-08-02 12:05:57 +02:00
|
|
|
kt->timeframe = timeframe;
|
|
|
|
kt->threshold = threshold;
|
|
|
|
kt->trigger = ot;
|
|
|
|
add_checks (checks,
|
|
|
|
&kt->required_checks,
|
|
|
|
&kt->num_checks);
|
|
|
|
GNUNET_free (checks);
|
2022-08-04 12:52:30 +02:00
|
|
|
GNUNET_array_append (kyc_triggers,
|
|
|
|
num_kyc_triggers,
|
2022-08-02 12:05:57 +02:00
|
|
|
kt);
|
|
|
|
}
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
/**
|
|
|
|
* Closure for #handle_section().
|
|
|
|
*/
|
|
|
|
struct SectionContext
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Configuration to handle.
|
|
|
|
*/
|
|
|
|
const struct GNUNET_CONFIGURATION_Handle *cfg;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Result to return, set to false on failures.
|
|
|
|
*/
|
|
|
|
bool result;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2022-08-02 12:05:57 +02:00
|
|
|
/**
|
|
|
|
* Function to iterate over configuration sections.
|
|
|
|
*
|
2022-08-08 15:22:45 +02:00
|
|
|
* @param cls a `struct SectionContext *`
|
2022-08-02 12:05:57 +02:00
|
|
|
* @param section name of the section
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
handle_section (void *cls,
|
|
|
|
const char *section)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct SectionContext *sc = cls;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
if (0 == strncasecmp (section,
|
|
|
|
"kyc-provider-",
|
|
|
|
strlen ("kyc-provider-")))
|
|
|
|
{
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
add_provider (sc->cfg,
|
|
|
|
section))
|
|
|
|
sc->result = false;
|
2022-08-02 12:05:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (0 == strncasecmp (section,
|
|
|
|
"kyc-legitimization-",
|
|
|
|
strlen ("kyc-legitimization-")))
|
|
|
|
{
|
|
|
|
if (GNUNET_OK !=
|
2022-08-08 15:22:45 +02:00
|
|
|
add_trigger (sc->cfg,
|
|
|
|
section))
|
|
|
|
sc->result = false;
|
2022-08-02 12:05:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-04 11:36:05 +02:00
|
|
|
/**
|
|
|
|
* Comparator for qsort. Compares two triggers
|
|
|
|
* by timeframe to sort triggers by time.
|
|
|
|
*
|
|
|
|
* @param p1 first trigger to compare
|
|
|
|
* @param p2 second trigger to compare
|
|
|
|
* @return -1 if p1 < p2, 0 if p1==p2, 1 if p1 > p2.
|
|
|
|
*/
|
|
|
|
static int
|
2022-08-04 12:52:30 +02:00
|
|
|
sort_by_timeframe (const void *p1,
|
|
|
|
const void *p2)
|
2022-08-04 11:36:05 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycTrigger **t1 = (struct
|
|
|
|
TALER_KYCLOGIC_KycTrigger **) p1;
|
|
|
|
struct TALER_KYCLOGIC_KycTrigger **t2 = (struct
|
|
|
|
TALER_KYCLOGIC_KycTrigger **) p2;
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
if (GNUNET_TIME_relative_cmp ((*t1)->timeframe,
|
|
|
|
<,
|
|
|
|
(*t2)->timeframe))
|
|
|
|
return -1;
|
|
|
|
if (GNUNET_TIME_relative_cmp ((*t1)->timeframe,
|
|
|
|
>,
|
|
|
|
(*t2)->timeframe))
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-01 18:09:06 +02:00
|
|
|
enum GNUNET_GenericReturnValue
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct SectionContext sc = {
|
|
|
|
.cfg = cfg,
|
|
|
|
.result = true
|
|
|
|
};
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
GNUNET_CONFIGURATION_iterate_sections (cfg,
|
2022-08-02 12:05:57 +02:00
|
|
|
&handle_section,
|
2022-08-08 15:22:45 +02:00
|
|
|
&sc);
|
|
|
|
if (! sc.result)
|
2022-08-02 12:05:57 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_done ();
|
2022-08-02 12:05:57 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sanity check: ensure at least one provider exists
|
|
|
|
for any trigger and indidivual or business. */
|
|
|
|
for (unsigned int i = 0; i<num_kyc_checks; i++)
|
|
|
|
if (0 == kyc_checks[i]->num_providers)
|
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"No provider available for required KYC check `%s'\n",
|
|
|
|
kyc_checks[i]->name);
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_done ();
|
2022-08-02 12:05:57 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2022-08-04 11:36:05 +02:00
|
|
|
qsort (kyc_triggers,
|
|
|
|
num_kyc_triggers,
|
2022-08-08 15:22:45 +02:00
|
|
|
sizeof (struct TALER_KYCLOGIC_KycTrigger *),
|
2022-08-04 11:36:05 +02:00
|
|
|
&sort_by_timeframe);
|
2022-08-01 18:09:06 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_done (void)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-02 12:05:57 +02:00
|
|
|
for (unsigned int i = 0; i<num_kyc_triggers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
GNUNET_array_grow (kt->required_checks,
|
|
|
|
kt->num_checks,
|
|
|
|
0);
|
|
|
|
GNUNET_free (kt);
|
|
|
|
}
|
|
|
|
GNUNET_array_grow (kyc_triggers,
|
|
|
|
num_kyc_triggers,
|
|
|
|
0);
|
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-04 12:52:30 +02:00
|
|
|
kp->logic->unload_configuration (kp->pd);
|
2022-08-02 12:05:57 +02:00
|
|
|
GNUNET_array_grow (kp->provided_checks,
|
|
|
|
kp->num_checks,
|
|
|
|
0);
|
|
|
|
GNUNET_free (kp);
|
|
|
|
}
|
|
|
|
GNUNET_array_grow (kyc_providers,
|
|
|
|
num_kyc_providers,
|
|
|
|
0);
|
|
|
|
for (unsigned int i = 0; i<num_kyc_logics; i++)
|
|
|
|
{
|
2022-08-04 12:52:30 +02:00
|
|
|
struct TALER_KYCLOGIC_Plugin *lp = kyc_logics[i];
|
|
|
|
char *lib_name = lp->library_name;
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-18 15:39:28 +02:00
|
|
|
GNUNET_free (lp->name);
|
2022-08-04 12:52:30 +02:00
|
|
|
GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
|
|
|
|
lp));
|
|
|
|
GNUNET_free (lib_name);
|
2022-08-02 12:05:57 +02:00
|
|
|
}
|
|
|
|
GNUNET_array_grow (kyc_logics,
|
|
|
|
num_kyc_logics,
|
|
|
|
0);
|
|
|
|
for (unsigned int i = 0; i<num_kyc_checks; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *kc = kyc_checks[i];
|
2022-08-02 12:05:57 +02:00
|
|
|
|
2022-08-17 14:36:16 +02:00
|
|
|
GNUNET_array_grow (kc->providers,
|
|
|
|
kc->num_providers,
|
|
|
|
0);
|
2022-08-02 12:05:57 +02:00
|
|
|
GNUNET_free (kc->name);
|
|
|
|
GNUNET_free (kc);
|
|
|
|
}
|
|
|
|
GNUNET_array_grow (kyc_checks,
|
|
|
|
num_kyc_checks,
|
|
|
|
0);
|
2022-08-01 18:09:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-04 11:36:05 +02:00
|
|
|
/**
|
|
|
|
* Closure for the #eval_trigger().
|
|
|
|
*/
|
|
|
|
struct ThresholdTestContext
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* Total amount so far.
|
|
|
|
*/
|
|
|
|
struct TALER_Amount total;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger event to evaluate triggers of.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
enum TALER_KYCLOGIC_KycTriggerEvent event;
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Offset in the triggers array where we need to start
|
|
|
|
* checking for triggers. All trigges below this
|
|
|
|
* offset were already hit.
|
|
|
|
*/
|
|
|
|
unsigned int start;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of checks needed so far.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck **needed;
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Pointer to number of entries used in @a needed.
|
|
|
|
*/
|
|
|
|
unsigned int *needed_cnt;
|
|
|
|
|
2022-08-08 15:22:45 +02:00
|
|
|
/**
|
|
|
|
* Has @e total been initialized yet?
|
|
|
|
*/
|
|
|
|
bool have_total;
|
2022-08-04 11:36:05 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function called on each @a amount that was found to
|
|
|
|
* be relevant for a KYC check.
|
|
|
|
*
|
|
|
|
* @param cls closure to allow the KYC module to
|
|
|
|
* total up amounts and evaluate rules
|
|
|
|
* @param amount encountered transaction amount
|
|
|
|
* @param date when was the amount encountered
|
|
|
|
* @return #GNUNET_OK to continue to iterate,
|
|
|
|
* #GNUNET_NO to abort iteration
|
|
|
|
* #GNUNET_SYSERR on internal error (also abort itaration)
|
|
|
|
*/
|
|
|
|
static enum GNUNET_GenericReturnValue
|
|
|
|
eval_trigger (void *cls,
|
|
|
|
const struct TALER_Amount *amount,
|
|
|
|
struct GNUNET_TIME_Absolute date)
|
|
|
|
{
|
|
|
|
struct ThresholdTestContext *ttc = cls;
|
|
|
|
struct GNUNET_TIME_Relative duration;
|
|
|
|
bool bump = true;
|
|
|
|
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"KYC check with new amount %s\n",
|
|
|
|
TALER_amount2s (amount));
|
2022-08-04 11:36:05 +02:00
|
|
|
duration = GNUNET_TIME_absolute_get_duration (date);
|
2022-08-08 15:22:45 +02:00
|
|
|
if (ttc->have_total)
|
2022-08-04 11:36:05 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
if (0 >
|
|
|
|
TALER_amount_add (&ttc->total,
|
|
|
|
&ttc->total,
|
|
|
|
amount))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ttc->total = *amount;
|
2022-08-11 23:35:33 +02:00
|
|
|
ttc->have_total = true;
|
2022-08-04 11:36:05 +02:00
|
|
|
}
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"KYC check: new total is %s\n",
|
|
|
|
TALER_amount2s (&ttc->total));
|
2022-08-04 11:36:05 +02:00
|
|
|
for (unsigned int i = ttc->start; i<num_kyc_triggers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
2022-08-04 12:52:30 +02:00
|
|
|
if (ttc->event != kt->trigger)
|
2022-08-11 23:35:33 +02:00
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"KYC check #%u: trigger type does not match\n",
|
|
|
|
i);
|
2022-08-04 11:36:05 +02:00
|
|
|
continue;
|
2022-08-11 23:35:33 +02:00
|
|
|
}
|
2022-08-04 12:52:30 +02:00
|
|
|
duration = GNUNET_TIME_relative_max (duration,
|
|
|
|
kt->timeframe);
|
2022-08-04 11:36:05 +02:00
|
|
|
if (GNUNET_TIME_relative_cmp (kt->timeframe,
|
|
|
|
>,
|
|
|
|
duration))
|
|
|
|
{
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"KYC check #%u: amount is beyond time limit\n",
|
|
|
|
i);
|
2022-08-04 11:36:05 +02:00
|
|
|
if (bump)
|
|
|
|
ttc->start = i;
|
2022-08-04 12:52:30 +02:00
|
|
|
return GNUNET_OK;
|
2022-08-04 11:36:05 +02:00
|
|
|
}
|
|
|
|
if (-1 ==
|
|
|
|
TALER_amount_cmp (&ttc->total,
|
|
|
|
&kt->threshold))
|
|
|
|
{
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
2022-08-11 23:53:25 +02:00
|
|
|
"KYC check #%u: amount is below threshold\n",
|
2022-08-11 23:35:33 +02:00
|
|
|
i);
|
2022-08-04 11:36:05 +02:00
|
|
|
if (bump)
|
|
|
|
ttc->start = i;
|
|
|
|
bump = false;
|
|
|
|
continue; /* amount too low to trigger */
|
|
|
|
}
|
|
|
|
/* add check to list of required checks, unless
|
|
|
|
already present... */
|
|
|
|
for (unsigned int j = 0; j<kt->num_checks; j++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *rc = kt->required_checks[j];
|
2022-08-04 11:36:05 +02:00
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
for (unsigned int k = 0; k<*ttc->needed_cnt; k++)
|
|
|
|
if (ttc->needed[k] == rc)
|
|
|
|
{
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"KYC rule #%u already listed\n",
|
|
|
|
j);
|
2022-08-04 11:36:05 +02:00
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (! found)
|
|
|
|
{
|
|
|
|
ttc->needed[*ttc->needed_cnt] = rc;
|
|
|
|
(*ttc->needed_cnt)++;
|
|
|
|
}
|
|
|
|
}
|
2022-08-11 23:35:33 +02:00
|
|
|
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);
|
2022-08-04 11:36:05 +02:00
|
|
|
}
|
|
|
|
if (bump)
|
|
|
|
return GNUNET_NO; /* we hit all possible triggers! */
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Closure for the #remove_satisfied().
|
|
|
|
*/
|
|
|
|
struct RemoveContext
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Array of checks needed so far.
|
|
|
|
*/
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck **needed;
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Pointer to number of entries used in @a needed.
|
|
|
|
*/
|
|
|
|
unsigned int *needed_cnt;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove all checks satisfied by @a provider_name from
|
|
|
|
* our list of checks.
|
|
|
|
*
|
|
|
|
* @param cls a `struct RemoveContext`
|
|
|
|
* @param provider_name section name of provider that was already run previously
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
remove_satisfied (void *cls,
|
|
|
|
const char *provider_name)
|
|
|
|
{
|
|
|
|
struct RemoveContext *rc = cls;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
if (0 != strcasecmp (provider_name,
|
|
|
|
kp->provider_section_name))
|
|
|
|
continue;
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Provider `%s' satisfied\n",
|
|
|
|
provider_name);
|
2022-08-04 11:36:05 +02:00
|
|
|
for (unsigned int j = 0; j<kp->num_checks; j++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Provider satisfies check `%s'\n",
|
|
|
|
kc->name);
|
2022-08-04 11:36:05 +02:00
|
|
|
for (unsigned int k = 0; k<*rc->needed_cnt; k++)
|
|
|
|
if (kc == rc->needed[k])
|
|
|
|
{
|
2022-08-11 23:35:33 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
|
|
"Removing check `%s' from list\n",
|
|
|
|
kc->name);
|
2022-08-04 11:36:05 +02:00
|
|
|
rc->needed[k] = rc->needed[*rc->needed_cnt - 1];
|
|
|
|
(*rc->needed_cnt)--;
|
|
|
|
if (0 == *rc->needed_cnt)
|
|
|
|
return; /* for sure finished */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-01 18:09:06 +02:00
|
|
|
const char *
|
2022-08-08 15:22:45 +02:00
|
|
|
TALER_KYCLOGIC_kyc_test_required (enum TALER_KYCLOGIC_KycTriggerEvent event,
|
|
|
|
const struct TALER_PaytoHashP *h_payto,
|
|
|
|
TALER_KYCLOGIC_KycSatisfiedIterator ki,
|
|
|
|
void *ki_cls,
|
|
|
|
TALER_KYCLOGIC_KycAmountIterator ai,
|
|
|
|
void *ai_cls)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
|
2022-08-04 11:36:05 +02:00
|
|
|
unsigned int needed_cnt = 0;
|
2022-08-20 21:29:29 +02:00
|
|
|
char *ret;
|
2022-08-04 11:36:05 +02:00
|
|
|
struct GNUNET_TIME_Relative timeframe;
|
|
|
|
|
|
|
|
timeframe = GNUNET_TIME_UNIT_ZERO;
|
|
|
|
for (unsigned int i = 0; i<num_kyc_triggers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
if (event != kt->trigger)
|
|
|
|
continue;
|
|
|
|
timeframe = GNUNET_TIME_relative_max (timeframe,
|
|
|
|
kt->timeframe);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute now;
|
|
|
|
struct ThresholdTestContext ttc = {
|
|
|
|
.event = event,
|
|
|
|
.needed = needed,
|
|
|
|
.needed_cnt = &needed_cnt
|
|
|
|
};
|
|
|
|
|
|
|
|
now = GNUNET_TIME_absolute_get ();
|
|
|
|
ai (ai_cls,
|
|
|
|
GNUNET_TIME_absolute_subtract (now,
|
|
|
|
timeframe),
|
|
|
|
&eval_trigger,
|
|
|
|
&ttc);
|
|
|
|
}
|
2022-08-20 21:29:29 +02:00
|
|
|
if (0 == needed_cnt)
|
|
|
|
return NULL;
|
|
|
|
timeframe = GNUNET_TIME_UNIT_ZERO;
|
|
|
|
for (unsigned int i = 0; i<num_kyc_triggers; i++)
|
|
|
|
{
|
|
|
|
const struct TALER_KYCLOGIC_KycTrigger *kt = kyc_triggers[i];
|
|
|
|
|
|
|
|
if (event != kt->trigger)
|
|
|
|
continue;
|
|
|
|
timeframe = GNUNET_TIME_relative_max (timeframe,
|
|
|
|
kt->timeframe);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
struct GNUNET_TIME_Absolute now;
|
|
|
|
struct ThresholdTestContext ttc = {
|
|
|
|
.event = event,
|
|
|
|
.needed = needed,
|
|
|
|
.needed_cnt = &needed_cnt
|
|
|
|
};
|
|
|
|
|
|
|
|
now = GNUNET_TIME_absolute_get ();
|
|
|
|
ai (ai_cls,
|
|
|
|
GNUNET_TIME_absolute_subtract (now,
|
|
|
|
timeframe),
|
|
|
|
&eval_trigger,
|
|
|
|
&ttc);
|
|
|
|
}
|
|
|
|
if (0 == needed_cnt)
|
|
|
|
return NULL;
|
|
|
|
{
|
|
|
|
struct RemoveContext rc = {
|
|
|
|
.needed = needed,
|
|
|
|
.needed_cnt = &needed_cnt
|
|
|
|
};
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
|
|
|
|
/* Check what provider checks are already satisfied for h_payto (with
|
|
|
|
database), remove those from the 'needed' array. */
|
|
|
|
qs = ki (ki_cls,
|
|
|
|
h_payto,
|
|
|
|
&remove_satisfied,
|
|
|
|
&rc);
|
|
|
|
GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
|
|
|
|
}
|
2022-08-04 11:36:05 +02:00
|
|
|
if (0 == needed_cnt)
|
|
|
|
return NULL;
|
|
|
|
{
|
|
|
|
struct RemoveContext rc = {
|
|
|
|
.needed = needed,
|
|
|
|
.needed_cnt = &needed_cnt
|
|
|
|
};
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
|
|
|
|
/* Check what provider checks are already satisfied for h_payto (with
|
|
|
|
database), remove those from the 'needed' array. */
|
2022-08-08 15:22:45 +02:00
|
|
|
qs = ki (ki_cls,
|
|
|
|
h_payto,
|
2022-08-11 23:35:33 +02:00
|
|
|
&remove_satisfied,
|
2022-08-08 15:22:45 +02:00
|
|
|
&rc);
|
2022-08-04 11:36:05 +02:00
|
|
|
GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
|
|
|
|
}
|
2022-08-11 23:35:33 +02:00
|
|
|
if (0 == needed_cnt)
|
|
|
|
return NULL;
|
2022-08-20 21:29:29 +02:00
|
|
|
ret = NULL;
|
|
|
|
for (unsigned int k = 0; k<needed_cnt; k++)
|
|
|
|
{
|
|
|
|
const struct TALER_KYCLOGIC_KycCheck *kc = needed[k];
|
|
|
|
|
|
|
|
if (NULL == ret)
|
|
|
|
{
|
|
|
|
ret = GNUNET_strdup (kc->name);
|
|
|
|
}
|
|
|
|
else /* append */
|
|
|
|
{
|
|
|
|
char *tmp = ret;
|
|
|
|
|
|
|
|
GNUNET_asprintf (&ret,
|
|
|
|
"%s %s",
|
|
|
|
tmp,
|
|
|
|
kc->name);
|
|
|
|
GNUNET_free (tmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
TALER_KYCLOGIC_kyc_get_details (
|
|
|
|
const char *logic_name,
|
|
|
|
TALER_KYCLOGIC_DetailsCallback cb,
|
|
|
|
void *cb_cls)
|
|
|
|
{
|
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
|
|
|
struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
|
|
|
|
|
|
|
if (0 !=
|
|
|
|
strcmp (kp->logic->name,
|
|
|
|
logic_name))
|
|
|
|
continue;
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
cb (cb_cls,
|
|
|
|
kp->pd,
|
|
|
|
kp->logic->cls))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
TALER_KYCLOGIC_check_satisfied (const char *requirements,
|
|
|
|
const struct TALER_PaytoHashP *h_payto,
|
|
|
|
TALER_KYCLOGIC_KycSatisfiedIterator ki,
|
|
|
|
void *ki_cls)
|
|
|
|
{
|
|
|
|
struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
|
|
|
|
unsigned int needed_cnt = 0;
|
|
|
|
|
|
|
|
if (NULL == requirements)
|
|
|
|
return true;
|
|
|
|
{
|
|
|
|
char *req = GNUNET_strdup (requirements);
|
|
|
|
|
|
|
|
for (const char *tok = strtok (req, " ");
|
|
|
|
NULL != tok;
|
|
|
|
tok = strtok (NULL, " "))
|
|
|
|
needed[needed_cnt++] = add_check (tok);
|
|
|
|
GNUNET_free (req);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
struct RemoveContext rc = {
|
|
|
|
.needed = needed,
|
|
|
|
.needed_cnt = &needed_cnt
|
|
|
|
};
|
|
|
|
enum GNUNET_DB_QueryStatus qs;
|
|
|
|
|
|
|
|
/* Check what provider checks are already satisfied for h_payto (with
|
|
|
|
database), remove those from the 'needed' array. */
|
|
|
|
qs = ki (ki_cls,
|
|
|
|
h_payto,
|
|
|
|
&remove_satisfied,
|
|
|
|
&rc);
|
|
|
|
GNUNET_break (qs >= 0); // FIXME: handle DB failure more nicely?
|
|
|
|
}
|
|
|
|
return (0 == needed_cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum GNUNET_GenericReturnValue
|
|
|
|
TALER_KYCLOGIC_requirements_to_logic (const char *requirements,
|
|
|
|
enum TALER_KYCLOGIC_KycUserType ut,
|
|
|
|
struct TALER_KYCLOGIC_Plugin **plugin,
|
|
|
|
struct TALER_KYCLOGIC_ProviderDetails **pd,
|
|
|
|
const char **configuration_section)
|
|
|
|
{
|
|
|
|
struct TALER_KYCLOGIC_KycCheck *needed[num_kyc_checks];
|
|
|
|
unsigned int needed_cnt = 0;
|
2022-08-24 03:18:06 +02:00
|
|
|
unsigned long long min_cost = ULLONG_MAX;
|
2022-08-20 21:29:29 +02:00
|
|
|
unsigned int max_checks = 0;
|
|
|
|
const struct TALER_KYCLOGIC_KycProvider *kp_best = NULL;
|
|
|
|
|
2022-08-22 22:45:41 +02:00
|
|
|
// FIXME: use 'ut' to filter providers!
|
2022-08-20 21:29:29 +02:00
|
|
|
if (NULL == requirements)
|
|
|
|
return GNUNET_NO;
|
|
|
|
{
|
|
|
|
char *req = GNUNET_strdup (requirements);
|
|
|
|
|
|
|
|
for (const char *tok = strtok (req, " ");
|
|
|
|
NULL != tok;
|
|
|
|
tok = strtok (NULL, " "))
|
|
|
|
needed[needed_cnt++] = add_check (tok);
|
|
|
|
GNUNET_free (req);
|
|
|
|
}
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
/* Count maximum number of remaining checks covered by any
|
|
|
|
provider */
|
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
2022-08-04 11:36:05 +02:00
|
|
|
unsigned int matched = 0;
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j<kp->num_checks; j++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
for (unsigned int k = 0; k<needed_cnt; k++)
|
|
|
|
if (kc == needed[k])
|
|
|
|
{
|
|
|
|
matched++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
max_checks = GNUNET_MAX (max_checks,
|
|
|
|
matched);
|
|
|
|
}
|
2022-08-20 21:29:29 +02:00
|
|
|
if (0 == max_checks)
|
|
|
|
return GNUNET_SYSERR;
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
/* Find min-cost provider covering max_checks. */
|
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
2022-08-04 11:36:05 +02:00
|
|
|
unsigned int matched = 0;
|
|
|
|
|
|
|
|
for (unsigned int j = 0; j<kp->num_checks; j++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
const struct TALER_KYCLOGIC_KycCheck *kc = kp->provided_checks[j];
|
2022-08-04 11:36:05 +02:00
|
|
|
|
|
|
|
for (unsigned int k = 0; k<needed_cnt; k++)
|
|
|
|
if (kc == needed[k])
|
|
|
|
{
|
|
|
|
matched++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( (max_checks == matched) &&
|
|
|
|
(kp->cost < min_cost) )
|
|
|
|
{
|
|
|
|
min_cost = kp->cost;
|
|
|
|
kp_best = kp;
|
|
|
|
}
|
|
|
|
}
|
2022-08-20 21:29:29 +02:00
|
|
|
*plugin = kp_best->logic;
|
|
|
|
*pd = kp_best->pd;
|
|
|
|
*configuration_section = kp_best->provider_section_name;
|
|
|
|
return GNUNET_OK;
|
2022-08-18 15:39:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-01 18:09:06 +02:00
|
|
|
enum GNUNET_GenericReturnValue
|
2022-08-20 21:29:29 +02:00
|
|
|
TALER_KYCLOGIC_lookup_logic (const char *name,
|
|
|
|
struct TALER_KYCLOGIC_Plugin **plugin,
|
|
|
|
struct TALER_KYCLOGIC_ProviderDetails **pd,
|
|
|
|
const char **provider_section)
|
2022-08-01 18:09:06 +02:00
|
|
|
{
|
2022-08-02 12:05:57 +02:00
|
|
|
for (unsigned int i = 0; i<num_kyc_providers; i++)
|
|
|
|
{
|
2022-08-08 15:22:45 +02:00
|
|
|
struct TALER_KYCLOGIC_KycProvider *kp = kyc_providers[i];
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
if (0 !=
|
2022-08-20 21:29:29 +02:00
|
|
|
strcasecmp (name,
|
2022-08-02 12:05:57 +02:00
|
|
|
kp->provider_section_name))
|
|
|
|
continue;
|
|
|
|
*plugin = kp->logic;
|
|
|
|
*pd = kp->pd;
|
2022-08-20 21:29:29 +02:00
|
|
|
*provider_section = kp->provider_section_name;
|
2022-08-02 12:05:57 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2022-08-18 15:39:28 +02:00
|
|
|
for (unsigned int i = 0; i<num_kyc_logics; i++)
|
|
|
|
{
|
|
|
|
struct TALER_KYCLOGIC_Plugin *logic = kyc_logics[i];
|
|
|
|
|
|
|
|
if (0 !=
|
|
|
|
strcasecmp (logic->name,
|
2022-08-20 21:29:29 +02:00
|
|
|
name))
|
2022-08-18 15:39:28 +02:00
|
|
|
continue;
|
|
|
|
*plugin = logic;
|
|
|
|
*pd = NULL;
|
2022-08-20 21:29:29 +02:00
|
|
|
*provider_section = NULL;
|
2022-08-18 15:39:28 +02:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2022-08-02 12:05:57 +02:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
|
|
"Provider `%s' unknown\n",
|
2022-08-20 21:29:29 +02:00
|
|
|
name);
|
2022-08-01 18:09:06 +02:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2022-08-02 12:05:57 +02:00
|
|
|
|
|
|
|
|
2022-08-11 23:35:33 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-08-02 12:05:57 +02:00
|
|
|
/* end of taler-exchange-httpd_kyc.c */
|