-work on new KYC logic: tests pass again

This commit is contained in:
Christian Grothoff 2022-08-14 18:04:09 +02:00
parent 913eacf506
commit 74ba46db39
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
38 changed files with 2650 additions and 2735 deletions

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
1660251795 1660491075

View File

@ -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/DE546854?receiver-name=Exchange+Company PAYTO_URI = payto://iban/SANDBOXX/DE128580?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 = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0 MASTER_KEY = EHBD7SW56Y5KD1VT7YVSWJ3MMB7ZNASXE4NMEGE82D8RZ413GWR0
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 = SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0 MASTER_PUBLIC_KEY = EHBD7SW56Y5KD1VT7YVSWJ3MMB7ZNASXE4NMEGE82D8RZ413GWR0
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 = JZYPE53YY23MQ0HTTV3DYHRABW4RM6SJS1Y0HF2HMSEPEPRJ77WG PUBLIC_KEY = RPBANQQH936J5S6Q61A58WA9R92WKXPBZNYWFZ5YXNZB0NFV78NG
[PATHS] [PATHS]
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/ TALER_CACHE_HOME = $TALER_HOME/.cache/taler/

View File

@ -1 +1 @@
SA4PMGHM403V1F2TQVFRVKH9BTZ2FBG3V6R7FFVVTYFEFDYG3AX0 EHBD7SW56Y5KD1VT7YVSWJ3MMB7ZNASXE4NMEGE82D8RZ413GWR0

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
1660252041 1660491749

View File

@ -1 +1 @@
QKEQ98KRJ7RGKBZ2FSW0S0Y07G1AVJMBDHPZ23098JC5731MNYY0 78C7KGQ7780CDXG1MKTFVHHWQ32DNJ7TDFVJX4FB8ZVB6ZXK1Y6G

File diff suppressed because it is too large Load Diff

View File

@ -1020,13 +1020,10 @@ verify_reserve_balance (void *cls,
internal audit, as otherwise the balance of the 'reserves' table internal audit, as otherwise the balance of the 'reserves' table
is not replicated at the auditor. */ is not replicated at the auditor. */
struct TALER_EXCHANGEDB_Reserve reserve; struct TALER_EXCHANGEDB_Reserve reserve;
struct TALER_EXCHANGEDB_KycStatus kyc;
reserve.pub = rs->reserve_pub; reserve.pub = rs->reserve_pub;
qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls, qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
&reserve, &reserve);
&kyc);
// FIXME: figure out what to do with KYC status!
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
{ {
/* If the exchange doesn't have this reserve in the summary, it /* If the exchange doesn't have this reserve in the summary, it

View File

@ -327,11 +327,8 @@ generate_reply_success (const struct TEH_RequestContext *rc,
if (! wc->kyc.ok) if (! wc->kyc.ok)
{ {
/* KYC required */ /* KYC required */
return TALER_MHD_REPLY_JSON_PACK ( return TEH_RESPONSE_reply_kyc_required (rc->connection,
rc->connection, &wc->kyc);
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
GNUNET_JSON_pack_uint64 ("payment_target_uuid",
wc->kyc.payment_target_uuid));
} }
sigs = json_array (); sigs = json_array ();

View File

@ -247,7 +247,7 @@ handle_track_transaction_request (
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_INVARIANT_FAILURE, TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
"wire fees exceed aggregate in database"); "wire fees exceed aggregate in database");
if (GNUNET_YES == ctx->pending) if (ctx->pending)
return TALER_MHD_REPLY_JSON_PACK ( return TALER_MHD_REPLY_JSON_PACK (
connection, connection,
MHD_HTTP_ACCEPTED, MHD_HTTP_ACCEPTED,

View File

@ -227,7 +227,7 @@ TEH_handler_kyc_wallet (
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 ("legitimization_uuid",
krc.legi_row)); krc.legi_row));
} }

View File

@ -28,6 +28,7 @@
#include <pthread.h> #include <pthread.h>
#include "taler_dbevents.h" #include "taler_dbevents.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_purses_merge.h" #include "taler-exchange-httpd_purses_merge.h"
#include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_responses.h"
@ -103,10 +104,20 @@ struct PurseMergeContext
/** /**
* URI of the account the purse is to be merged into. * URI of the account the purse is to be merged into.
* Must be of the form 'payto://taler/$EXCHANGE_URL/RESERVE_PUB'. * Must be of the form 'payto://taler-reserve/$EXCHANGE_URL/RESERVE_PUB'.
*/ */
const char *payto_uri; const char *payto_uri;
/**
* Hash of the @e payto_uri.
*/
struct TALER_PaytoHashP h_payto;
/**
* KYC status of the operation.
*/
struct TALER_EXCHANGEDB_KycStatus kyc;
/** /**
* Base URL of the exchange provider hosting the reserve. * Base URL of the exchange provider hosting the reserve.
*/ */
@ -201,6 +212,46 @@ reply_merge_success (struct MHD_Connection *connection,
} }
/**
* 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 a `struct PurseMergeContext`
* @param limit maximum time-range for which events
* should be fetched (timestamp in the past)
* @param cb function to call on each event found,
* events must be returned in reverse chronological
* order
* @param cb_cls closure for @a cb
*/
static void
amount_iterator (void *cls,
struct GNUNET_TIME_Absolute limit,
TALER_EXCHANGEDB_KycAmountCallback cb,
void *cb_cls)
{
struct PurseMergeContext *pcc = cls;
enum GNUNET_DB_QueryStatus qs;
cb (cb_cls,
&pcc->target_amount,
GNUNET_TIME_absolute_get ());
qs = TEH_plugin->select_merge_amounts_for_kyc_check (
TEH_plugin->cls,
&pcc->h_payto,
limit,
cb,
cb_cls);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got %d additional transactions for this merge and limit %llu\n",
qs,
(unsigned long long) limit.abs_value_us);
GNUNET_break (qs >= 0);
}
/** /**
* Execute database transaction for /purses/$PID/merge. Runs the transaction * Execute database transaction for /purses/$PID/merge. Runs the transaction
* logic; IF it returns a non-error code, the transaction logic MUST NOT queue * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
@ -224,9 +275,26 @@ merge_transaction (void *cls,
bool in_conflict = true; bool in_conflict = true;
bool no_balance = true; bool no_balance = true;
bool no_partner = true; bool no_partner = true;
bool no_kyc = true;
bool no_reserve = true; bool no_reserve = true;
const char *required;
required = TALER_KYCLOGIC_kyc_test_required (
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
&pcc->h_payto,
TEH_plugin->select_satisfied_kyc_processes,
TEH_plugin->cls,
&amount_iterator,
pcc);
if (NULL != required)
{
pcc->kyc.ok = false;
return TEH_plugin->insert_kyc_requirement_for_account (
TEH_plugin->cls,
required,
&pcc->h_payto,
&pcc->kyc.payment_target_uuid);
}
pcc->kyc.ok = true;
qs = TEH_plugin->do_purse_merge ( qs = TEH_plugin->do_purse_merge (
TEH_plugin->cls, TEH_plugin->cls,
pcc->purse_pub, pcc->purse_pub,
@ -235,11 +303,9 @@ merge_transaction (void *cls,
&pcc->reserve_sig, &pcc->reserve_sig,
pcc->provider_url, pcc->provider_url,
&pcc->reserve_pub, &pcc->reserve_pub,
TEH_KYC_NONE != TEH_kyc_config.mode,
&no_partner, &no_partner,
&no_balance, &no_balance,
&no_reserve, &no_reserve,
&no_kyc,
&in_conflict); &in_conflict);
if (qs < 0) if (qs < 0)
{ {
@ -272,17 +338,6 @@ merge_transaction (void *cls,
NULL); NULL);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if ( (no_kyc) &&
(TEH_KYC_NONE != TEH_kyc_config.mode) )
{
*mhd_ret
= TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
TALER_JSON_pack_ec (
TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED));
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (no_balance) if (no_balance)
{ {
*mhd_ret = *mhd_ret =
@ -333,6 +388,7 @@ merge_transaction (void *cls,
GNUNET_free (partner_url); GNUNET_free (partner_url);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
return qs; return qs;
} }
@ -434,7 +490,6 @@ TEH_handler_purses_merge (
TALER_EC_GENERIC_PARAMETER_MALFORMED, TALER_EC_GENERIC_PARAMETER_MALFORMED,
"payto_uri"); "payto_uri");
} }
http = (0 == strncmp (pcc.payto_uri, http = (0 == strncmp (pcc.payto_uri,
"payto://taler-reserve+http/", "payto://taler-reserve+http/",
strlen ("payto://taler-reserve+http/"))); strlen ("payto://taler-reserve+http/")));
@ -477,6 +532,8 @@ TEH_handler_purses_merge (
} }
slash++; slash++;
} }
TALER_payto_hash (pcc.payto_uri,
&pcc.h_payto);
if (0 == strcmp (pcc.provider_url, if (0 == strcmp (pcc.provider_url,
TEH_base_url)) TEH_base_url))
{ {
@ -615,6 +672,12 @@ TEH_handler_purses_merge (
} }
} }
GNUNET_free (pcc.provider_url);
if (! pcc.kyc.ok)
return TEH_RESPONSE_reply_kyc_required (connection,
&pcc.kyc);
{ {
struct TALER_PurseEventP rep = { struct TALER_PurseEventP rep = {
.header.size = htons (sizeof (rep)), .header.size = htons (sizeof (rep)),
@ -630,7 +693,6 @@ TEH_handler_purses_merge (
0); 0);
} }
GNUNET_free (pcc.provider_url);
/* generate regular response */ /* generate regular response */
return reply_merge_success (connection, return reply_merge_success (connection,
&pcc); &pcc);

View File

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

View File

@ -27,6 +27,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_reserves_purse.h" #include "taler-exchange-httpd_reserves_purse.h"
#include "taler-exchange-httpd_responses.h" #include "taler-exchange-httpd_responses.h"
@ -100,6 +101,16 @@ struct ReservePurseContext
*/ */
struct TEH_PurseDetails pd; struct TEH_PurseDetails pd;
/**
* Hash of the @e payto_uri.
*/
struct TALER_PaytoHashP h_payto;
/**
* KYC status of the operation.
*/
struct TALER_EXCHANGEDB_KycStatus kyc;
/** /**
* Minimum age for deposits into this purse. * Minimum age for deposits into this purse.
*/ */
@ -118,6 +129,46 @@ struct ReservePurseContext
}; };
/**
* 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 a `struct ReservePurseContext`
* @param limit maximum time-range for which events
* should be fetched (timestamp in the past)
* @param cb function to call on each event found,
* events must be returned in reverse chronological
* order
* @param cb_cls closure for @a cb
*/
static void
amount_iterator (void *cls,
struct GNUNET_TIME_Absolute limit,
TALER_EXCHANGEDB_KycAmountCallback cb,
void *cb_cls)
{
struct ReservePurseContext *rpc = cls;
enum GNUNET_DB_QueryStatus qs;
cb (cb_cls,
&rpc->deposit_total,
GNUNET_TIME_absolute_get ());
qs = TEH_plugin->select_merge_amounts_for_kyc_check (
TEH_plugin->cls,
&rpc->h_payto,
limit,
cb,
cb_cls);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Got %d additional transactions for this merge and limit %llu\n",
qs,
(unsigned long long) limit.abs_value_us);
GNUNET_break (qs >= 0);
}
/** /**
* Execute database transaction for /reserves/$PID/purse. Runs the transaction * Execute database transaction for /reserves/$PID/purse. Runs the transaction
* logic; IF it returns a non-error code, the transaction logic MUST NOT queue * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
@ -139,6 +190,26 @@ purse_transaction (void *cls,
struct ReservePurseContext *rpc = cls; struct ReservePurseContext *rpc = cls;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
const char *required;
required = TALER_KYCLOGIC_kyc_test_required (
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
&rpc->h_payto,
TEH_plugin->select_satisfied_kyc_processes,
TEH_plugin->cls,
&amount_iterator,
rpc);
if (NULL != required)
{
rpc->kyc.ok = false;
return TEH_plugin->insert_kyc_requirement_for_account (
TEH_plugin->cls,
required,
&rpc->h_payto,
&rpc->kyc.payment_target_uuid);
}
rpc->kyc.ok = true;
{ {
bool in_conflict = true; bool in_conflict = true;
@ -230,7 +301,6 @@ purse_transaction (void *cls,
bool in_conflict = true; bool in_conflict = true;
bool insufficient_funds = true; bool insufficient_funds = true;
bool no_reserve = true; bool no_reserve = true;
bool no_kyc = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating purse with flags %d\n", "Creating purse with flags %d\n",
@ -246,10 +316,8 @@ purse_transaction (void *cls,
? NULL ? NULL
: &rpc->gf->fees.purse, : &rpc->gf->fees.purse,
rpc->reserve_pub, rpc->reserve_pub,
TEH_KYC_NONE != TEH_kyc_config.mode,
&in_conflict, &in_conflict,
&no_reserve, &no_reserve,
&no_kyc,
&insufficient_funds); &insufficient_funds);
if (qs < 0) if (qs < 0)
{ {
@ -322,17 +390,6 @@ purse_transaction (void *cls,
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN)); TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN));
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
} }
if ( (no_kyc) &&
(TEH_KYC_NONE != TEH_kyc_config.mode) )
{
*mhd_ret
= TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
TALER_JSON_pack_ec (
TALER_EC_EXCHANGE_GENERIC_KYC_REQUIRED));
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (insufficient_funds) if (insufficient_funds)
{ {
*mhd_ret *mhd_ret
@ -472,6 +529,15 @@ TEH_handler_reserves_purse (
return MHD_YES; /* failure */ return MHD_YES; /* failure */
} }
} }
{
char *payto_uri;
payto_uri = TALER_reserve_make_payto (TEH_base_url,
reserve_pub);
TALER_payto_hash (payto_uri,
&rpc.h_payto);
GNUNET_free (payto_uri);
}
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TEH_currency, TALER_amount_set_zero (TEH_currency,
&rpc.deposit_total)); &rpc.deposit_total));
@ -641,6 +707,9 @@ TEH_handler_reserves_purse (
} }
} }
if (! rpc.kyc.ok)
return TEH_RESPONSE_reply_kyc_required (connection,
&rpc.kyc);
/* generate regular response */ /* generate regular response */
{ {
MHD_RESULT res; MHD_RESULT res;

View File

@ -53,11 +53,6 @@ struct ReserveStatusContext
*/ */
struct TALER_EXCHANGEDB_ReserveHistory *rh; struct TALER_EXCHANGEDB_ReserveHistory *rh;
/**
* Current KYC status.
*/
struct TALER_EXCHANGEDB_KycStatus kyc;
/** /**
* Sum of incoming transactions within the returned history. * Sum of incoming transactions within the returned history.
* (currently not used). * (currently not used).
@ -102,8 +97,6 @@ reply_reserve_status_success (struct MHD_Connection *connection,
MHD_HTTP_OK, MHD_HTTP_OK,
TALER_JSON_pack_amount ("balance", TALER_JSON_pack_amount ("balance",
&rhc->balance), &rhc->balance),
GNUNET_JSON_pack_bool ("kyc_passed",
rhc->kyc.ok),
GNUNET_JSON_pack_array_steal ("history", GNUNET_JSON_pack_array_steal ("history",
json_history)); json_history));
} }
@ -133,20 +126,6 @@ reserve_status_transaction (void *cls,
struct ReserveStatusContext *rsc = cls; struct ReserveStatusContext *rsc = cls;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->inselect_wallet_kyc_status (TEH_plugin->cls,
rsc->reserve_pub,
&rsc->kyc);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
GNUNET_break (0);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_FETCH_FAILED,
"inselect_wallet_status");
return qs;
}
qs = TEH_plugin->get_reserve_status (TEH_plugin->cls, qs = TEH_plugin->get_reserve_status (TEH_plugin->cls,
rsc->reserve_pub, rsc->reserve_pub,
&rsc->balance_in, &rsc->balance_in,

View File

@ -977,4 +977,16 @@ TEH_RESPONSE_reply_purse_created (
} }
MHD_RESULT
TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_KycStatus *kyc)
{
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
GNUNET_JSON_pack_uint64 ("legitimization_uuid",
kyc->payment_target_uuid));
}
/* end of taler-exchange-httpd_responses.c */ /* end of taler-exchange-httpd_responses.c */

View File

@ -74,6 +74,19 @@ TEH_RESPONSE_reply_reserve_insufficient_balance (
const struct TALER_ReservePublicKeyP *reserve_pub); const struct TALER_ReservePublicKeyP *reserve_pub);
/**
* Send information that a KYC check must be
* satisfied to proceed to client.
*
* @param connection connection to the client
* @param pcc details about the request that succeeded
* @return MHD result code
*/
MHD_RESULT
TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_KycStatus *kyc);
/** /**
* Send assertion that the given denomination key hash * Send assertion that the given denomination key hash
* is not usable (typically expired) at this time. * is not usable (typically expired) at this time.

View File

@ -490,13 +490,8 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
if (! wc.kyc.ok) if (! wc.kyc.ok)
{ return TEH_RESPONSE_reply_kyc_required (rc->connection,
return TALER_MHD_REPLY_JSON_PACK ( &wc.kyc);
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;

View File

@ -572,23 +572,6 @@ prepare_statements (struct PostgresClosure *pg)
" FROM denominations" " FROM denominations"
" WHERE denom_pub_hash=$1);", " WHERE denom_pub_hash=$1);",
1), 1),
/* Used in #postgres_reserves_get() */
GNUNET_PQ_make_prepare (
"reserves_get_with_kyc",
"SELECT"
" current_balance_val"
",current_balance_frac"
",expiration_date"
",gc_date"
",kyc_ok"
",wire_target_serial_id AS payment_target_uuid"
" FROM reserves"
" JOIN reserves_in ri USING (reserve_pub)"
" JOIN wire_targets wt "
" ON (ri.wire_source_h_payto = wt.wire_target_h_payto)"
" WHERE reserve_pub=$1"
" LIMIT 1;",
1),
/* Used in #postgres_reserves_get_origin() */ /* Used in #postgres_reserves_get_origin() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"get_h_wire_source_of_reserve", "get_h_wire_source_of_reserve",
@ -658,7 +641,7 @@ prepare_statements (struct PostgresClosure *pg)
" LIMIT 1)" " LIMIT 1)"
" RETURNING h_payto;", " RETURNING h_payto;",
1), 1),
/* Used in #reserves_get() */ /* Used in #postgres_reserves_get() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"reserves_get", "reserves_get",
"SELECT" "SELECT"
@ -4497,11 +4480,10 @@ prepare_statements (struct PostgresClosure *pg)
"SELECT" "SELECT"
" out_no_partner AS no_partner" " out_no_partner AS no_partner"
",out_no_balance AS no_balance" ",out_no_balance AS no_balance"
",out_no_kyc AS no_kyc"
",out_no_reserve AS no_reserve" ",out_no_reserve AS no_reserve"
",out_conflict AS conflict" ",out_conflict AS conflict"
" FROM exchange_do_purse_merge" " FROM exchange_do_purse_merge"
" ($1, $2, $3, $4, $5, $6, $7, $8);", " ($1, $2, $3, $4, $5, $6, $7);",
7), 7),
/* Used in #postgres_do_reserve_purse() */ /* Used in #postgres_do_reserve_purse() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
@ -4509,11 +4491,10 @@ prepare_statements (struct PostgresClosure *pg)
"SELECT" "SELECT"
" out_no_funds AS insufficient_funds" " out_no_funds AS insufficient_funds"
",out_no_reserve AS no_reserve" ",out_no_reserve AS no_reserve"
",out_no_kyc AS no_kyc"
",out_conflict AS conflict" ",out_conflict AS conflict"
" FROM exchange_do_reserve_purse" " FROM exchange_do_reserve_purse"
" ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);", " ($1, $2, $3, $4, $5, $6, $7, $8, $9);",
10), 9),
/* Used in #postgres_select_purse_merge */ /* Used in #postgres_select_purse_merge */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"select_purse_merge", "select_purse_merge",
@ -5721,13 +5702,11 @@ postgres_iterate_auditor_denominations (
* @param[in,out] reserve the reserve data. The public key of the reserve should be * @param[in,out] reserve the reserve data. The public key of the reserve should be
* set in this structure; it is used to query the database. The balance * set in this structure; it is used to query the database. The balance
* and expiration are then filled accordingly. * and expiration are then filled accordingly.
* @param[out] kyc set to the KYC status of the reserve
* @return transaction status * @return transaction status
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
postgres_reserves_get (void *cls, postgres_reserves_get (void *cls,
struct TALER_EXCHANGEDB_Reserve *reserve, struct TALER_EXCHANGEDB_Reserve *reserve)
struct TALER_EXCHANGEDB_KycStatus *kyc)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
@ -5741,15 +5720,11 @@ postgres_reserves_get (void *cls,
&reserve->expiry), &reserve->expiry),
GNUNET_PQ_result_spec_timestamp ("gc_date", GNUNET_PQ_result_spec_timestamp ("gc_date",
&reserve->gc), &reserve->gc),
GNUNET_PQ_result_spec_uint64 ("payment_target_uuid",
&kyc->payment_target_uuid),
GNUNET_PQ_result_spec_bool ("kyc_ok",
&kyc->ok),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
}; };
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserves_get_with_kyc", "reserves_get",
params, params,
rs); rs);
} }
@ -5871,39 +5846,6 @@ postgres_drain_kyc_alert (void *cls,
} }
/**
* Get the @a kyc status and @a h_payto by UUID.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param h_payto set to the hash of the account's payto URI (unsalted)
* @param[out] kyc set to the KYC status of the account
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_select_kyc_status (void *cls,
const struct TALER_PaytoHashP *h_payto,
struct TALER_EXCHANGEDB_KycStatus *kyc)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (h_payto),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("wire_target_serial_id",
&kyc->payment_target_uuid),
GNUNET_PQ_result_spec_bool ("kyc_ok",
&kyc->ok),
GNUNET_PQ_result_spec_end
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"select_kyc_status_by_payto",
params,
rs);
}
/** /**
* Compute the hash of the @a payto_uri and use it to get the KYC status for a * Compute the hash of the @a payto_uri and use it to get the KYC status for a
* wallet. If the status is unknown, inserts a new status record (hence * wallet. If the status is unknown, inserts a new status record (hence
@ -5971,76 +5913,6 @@ inselect_account_kyc_status (
} }
/**
* Get the KYC status for a wallet. If the status is unknown,
* inserts a new status record (hence INsertSELECT).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the wallet
* @param[out] kyc set to the KYC status of the wallet
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
postgres_inselect_wallet_kyc_status (
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct TALER_EXCHANGEDB_KycStatus *kyc)
{
struct PostgresClosure *pg = cls;
char *payto_uri;
enum GNUNET_DB_QueryStatus qs;
struct TALER_PaytoHashP h_payto;
payto_uri = TALER_reserve_make_payto (pg->exchange_url,
reserve_pub);
qs = inselect_account_kyc_status (pg,
payto_uri,
&h_payto,
kyc);
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Wire account for `%s' is %llu\n",
payto_uri,
(unsigned long long) kyc->payment_target_uuid);
GNUNET_free (payto_uri);
return qs;
}
/**
* Get the summary of a reserve.
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param[in,out] reserve the reserve data. The public key of the reserve should be
* set in this structure; it is used to query the database. The balance
* and expiration are then filled accordingly.
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
reserves_get_internal (void *cls,
struct TALER_EXCHANGEDB_Reserve *reserve)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
GNUNET_PQ_query_param_end
};
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("current_balance",
&reserve->balance),
GNUNET_PQ_result_spec_timestamp ("expiration_date",
&reserve->expiry),
GNUNET_PQ_result_spec_timestamp ("gc_date",
&reserve->gc),
GNUNET_PQ_result_spec_end
};
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
"reserves_get",
params,
rs);
}
/** /**
* Updates a reserve with the data from the given reserve structure. * Updates a reserve with the data from the given reserve structure.
* *
@ -6248,7 +6120,7 @@ postgres_reserves_in_insert (void *cls,
{ {
enum GNUNET_DB_QueryStatus reserve_exists; enum GNUNET_DB_QueryStatus reserve_exists;
reserve_exists = reserves_get_internal (pg, reserve_exists = postgres_reserves_get (pg,
&reserve); &reserve);
switch (reserve_exists) switch (reserve_exists)
{ {
@ -10610,7 +10482,7 @@ postgres_insert_reserve_closed (
/* update reserve balance */ /* update reserve balance */
reserve.pub = *reserve_pub; reserve.pub = *reserve_pub;
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
(qs = reserves_get_internal (cls, (qs = postgres_reserves_get (cls,
&reserve))) &reserve)))
{ {
/* Existence should have been checked before we got here... */ /* Existence should have been checked before we got here... */
@ -16316,11 +16188,9 @@ postgres_get_purse_deposit (
* @param reserve_sig signature of the reserve affirming the merge * @param reserve_sig signature of the reserve affirming the merge
* @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us * @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us
* @param reserve_pub public key of the reserve to credit * @param reserve_pub public key of the reserve to credit
* @param require_kyc true if we should check for KYC
* @param[out] no_partner set to true if @a partner_url is unknown * @param[out] no_partner set to true if @a partner_url is unknown
* @param[out] no_balance set to true if the @a purse_pub is not paid up yet * @param[out] no_balance set to true if the @a purse_pub is not paid up yet
* @param[out] no_reserve set to true if the @a reserve_pub is not known * @param[out] no_reserve set to true if the @a reserve_pub is not known
* @param[out] no_kyc set to true if the @a reserve_pub lacks KYC
* @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
* @return transaction status code * @return transaction status code
*/ */
@ -16333,11 +16203,9 @@ postgres_do_purse_merge (
const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_ReserveSignatureP *reserve_sig,
const char *partner_url, const char *partner_url,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
bool require_kyc,
bool *no_partner, bool *no_partner,
bool *no_balance, bool *no_balance,
bool *no_reserve, bool *no_reserve,
bool *no_kyc,
bool *in_conflict) bool *in_conflict)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
@ -16352,7 +16220,6 @@ postgres_do_purse_merge (
: GNUNET_PQ_query_param_string (partner_url), : GNUNET_PQ_query_param_string (partner_url),
GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_auto_from_type (&h_payto), GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_bool (require_kyc),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
@ -16360,8 +16227,6 @@ postgres_do_purse_merge (
no_partner), no_partner),
GNUNET_PQ_result_spec_bool ("no_balance", GNUNET_PQ_result_spec_bool ("no_balance",
no_balance), no_balance),
GNUNET_PQ_result_spec_bool ("no_kyc",
no_kyc),
GNUNET_PQ_result_spec_bool ("no_reserve", GNUNET_PQ_result_spec_bool ("no_reserve",
no_reserve), no_reserve),
GNUNET_PQ_result_spec_bool ("conflict", GNUNET_PQ_result_spec_bool ("conflict",
@ -16397,10 +16262,8 @@ postgres_do_purse_merge (
* @param reserve_sig signature of the reserve affirming the merge * @param reserve_sig signature of the reserve affirming the merge
* @param purse_fee amount to charge the reserve for the purse creation, NULL to use the quota * @param purse_fee amount to charge the reserve for the purse creation, NULL to use the quota
* @param reserve_pub public key of the reserve to credit * @param reserve_pub public key of the reserve to credit
* @param require_kyc true if we should check for KYC
* @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
* @param[out] no_reserve set to true if @a reserve_pub is not a known reserve * @param[out] no_reserve set to true if @a reserve_pub is not a known reserve
* @param[out] no_kyc set to true if @a reserve_pub has not passed KYC checks
* @param[out] insufficient_funds set to true if @a reserve_pub has insufficient capacity to create another purse * @param[out] insufficient_funds set to true if @a reserve_pub has insufficient capacity to create another purse
* @return transaction status code * @return transaction status code
*/ */
@ -16413,10 +16276,8 @@ postgres_do_reserve_purse (
const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_Amount *purse_fee, const struct TALER_Amount *purse_fee,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
bool require_kyc,
bool *in_conflict, bool *in_conflict,
bool *no_reserve, bool *no_reserve,
bool *no_kyc,
bool *insufficient_funds) bool *insufficient_funds)
{ {
struct PostgresClosure *pg = cls; struct PostgresClosure *pg = cls;
@ -16433,7 +16294,6 @@ postgres_do_reserve_purse (
: purse_fee), : purse_fee),
GNUNET_PQ_query_param_auto_from_type (reserve_pub), GNUNET_PQ_query_param_auto_from_type (reserve_pub),
GNUNET_PQ_query_param_auto_from_type (&h_payto), GNUNET_PQ_query_param_auto_from_type (&h_payto),
GNUNET_PQ_query_param_bool (require_kyc),
GNUNET_PQ_query_param_end GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
@ -16441,8 +16301,6 @@ postgres_do_reserve_purse (
insufficient_funds), insufficient_funds),
GNUNET_PQ_result_spec_bool ("conflict", GNUNET_PQ_result_spec_bool ("conflict",
in_conflict), in_conflict),
GNUNET_PQ_result_spec_bool ("no_kyc",
no_kyc),
GNUNET_PQ_result_spec_bool ("no_reserve", GNUNET_PQ_result_spec_bool ("no_reserve",
no_reserve), no_reserve),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
@ -17488,12 +17346,10 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
plugin->iterate_active_auditors = &postgres_iterate_active_auditors; plugin->iterate_active_auditors = &postgres_iterate_active_auditors;
plugin->iterate_auditor_denominations = plugin->iterate_auditor_denominations =
&postgres_iterate_auditor_denominations; &postgres_iterate_auditor_denominations;
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->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->drain_kyc_alert = &postgres_drain_kyc_alert;
plugin->inselect_wallet_kyc_status = &postgres_inselect_wallet_kyc_status;
plugin->reserves_in_insert = &postgres_reserves_in_insert; plugin->reserves_in_insert = &postgres_reserves_in_insert;
plugin->get_withdraw_info = &postgres_get_withdraw_info; plugin->get_withdraw_info = &postgres_get_withdraw_info;
plugin->do_withdraw = &postgres_do_withdraw; plugin->do_withdraw = &postgres_do_withdraw;

View File

@ -1648,10 +1648,8 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_merge(
IN in_partner_url VARCHAR, IN in_partner_url VARCHAR,
IN in_reserve_pub BYTEA, IN in_reserve_pub BYTEA,
IN in_wallet_h_payto BYTEA, IN in_wallet_h_payto BYTEA,
IN in_require_kyc BOOLEAN,
OUT out_no_partner BOOLEAN, OUT out_no_partner BOOLEAN,
OUT out_no_balance BOOLEAN, OUT out_no_balance BOOLEAN,
OUT out_no_kyc BOOLEAN,
OUT out_no_reserve BOOLEAN, OUT out_no_reserve BOOLEAN,
OUT out_conflict BOOLEAN) OUT out_conflict BOOLEAN)
LANGUAGE plpgsql LANGUAGE plpgsql
@ -1686,7 +1684,6 @@ ELSE
THEN THEN
out_no_partner=TRUE; out_no_partner=TRUE;
out_conflict=FALSE; out_conflict=FALSE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
RETURN; RETURN;
END IF; END IF;
@ -1715,7 +1712,6 @@ IF NOT FOUND
THEN THEN
out_no_balance=TRUE; out_no_balance=TRUE;
out_conflict=FALSE; out_conflict=FALSE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
RETURN; RETURN;
END IF; END IF;
@ -1749,14 +1745,12 @@ THEN
THEN THEN
-- Purse was merged, but to some other reserve. Not allowed. -- Purse was merged, but to some other reserve. Not allowed.
out_conflict=TRUE; out_conflict=TRUE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
RETURN; RETURN;
END IF; END IF;
-- "success" -- "success"
out_conflict=FALSE; out_conflict=FALSE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
RETURN; RETURN;
END IF; END IF;
@ -1764,33 +1758,16 @@ out_conflict=FALSE;
ASSERT NOT my_finished, 'internal invariant failed'; ASSERT NOT my_finished, 'internal invariant failed';
IF ( (in_partner_url IS NULL) AND PERFORM
(in_require_kyc) )
THEN
-- Need to do KYC check.
SELECT NOT kyc_passed
INTO out_no_kyc
FROM exchange.reserves FROM exchange.reserves
WHERE reserve_pub=in_reserve_pub; WHERE reserve_pub=in_reserve_pub;
IF NOT FOUND IF NOT FOUND
THEN THEN
out_no_kyc=TRUE;
out_no_reserve=TRUE; out_no_reserve=TRUE;
RETURN; RETURN;
END IF;
out_no_reserve=FALSE;
IF (out_no_kyc)
THEN
RETURN;
END IF;
ELSE
-- KYC is not our responsibility
out_no_reserve=FALSE;
out_no_kyc=FALSE;
END IF; END IF;
out_no_reserve=FALSE;
-- Store account merge signature. -- Store account merge signature.
@ -1850,7 +1827,7 @@ RETURN;
END $$; END $$;
COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, VARCHAR, BYTEA, BYTEA, BOOLEAN) COMMENT ON FUNCTION exchange_do_purse_merge(BYTEA, BYTEA, INT8, BYTEA, VARCHAR, BYTEA, BYTEA)
IS 'Checks that the partner exists, the purse has not been merged with a different reserve and that the purse is full. If so, persists the merge data and either merges the purse with the reserve or marks it as ready for the taler-exchange-router. Caller MUST abort the transaction on failures so as to not persist data by accident.'; IS 'Checks that the partner exists, the purse has not been merged with a different reserve and that the purse is full. If so, persists the merge data and either merges the purse with the reserve or marks it as ready for the taler-exchange-router. Caller MUST abort the transaction on failures so as to not persist data by accident.';
@ -1864,9 +1841,7 @@ CREATE OR REPLACE FUNCTION exchange_do_reserve_purse(
IN in_purse_fee_frac INT4, IN in_purse_fee_frac INT4,
IN in_reserve_pub BYTEA, IN in_reserve_pub BYTEA,
IN in_wallet_h_payto BYTEA, IN in_wallet_h_payto BYTEA,
IN in_require_kyc BOOLEAN,
OUT out_no_funds BOOLEAN, OUT out_no_funds BOOLEAN,
OUT out_no_kyc BOOLEAN,
OUT out_no_reserve BOOLEAN, OUT out_no_reserve BOOLEAN,
OUT out_conflict BOOLEAN) OUT out_conflict BOOLEAN)
LANGUAGE plpgsql LANGUAGE plpgsql
@ -1901,7 +1876,6 @@ THEN
THEN THEN
-- Purse was merged, but to some other reserve. Not allowed. -- Purse was merged, but to some other reserve. Not allowed.
out_conflict=TRUE; out_conflict=TRUE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
out_no_funds=FALSE; out_no_funds=FALSE;
RETURN; RETURN;
@ -1910,38 +1884,28 @@ THEN
-- "success" -- "success"
out_conflict=FALSE; out_conflict=FALSE;
out_no_funds=FALSE; out_no_funds=FALSE;
out_no_kyc=FALSE;
out_no_reserve=FALSE; out_no_reserve=FALSE;
RETURN; RETURN;
END IF; END IF;
out_conflict=FALSE; out_conflict=FALSE;
SELECT NOT kyc_passed PERFORM
INTO out_no_kyc
FROM exchange.reserves FROM exchange.reserves
WHERE reserve_pub=in_reserve_pub; WHERE reserve_pub=in_reserve_pub;
IF NOT FOUND IF NOT FOUND
THEN THEN
out_no_kyc=TRUE;
out_no_reserve=TRUE; out_no_reserve=TRUE;
out_no_funds=TRUE; out_no_funds=TRUE;
RETURN; RETURN;
END IF; END IF;
out_no_reserve=FALSE; out_no_reserve=FALSE;
IF (out_no_kyc AND in_require_kyc)
THEN
out_no_funds=FALSE;
RETURN;
END IF;
IF (in_reserve_quota) IF (in_reserve_quota)
THEN THEN
-- Increment active purses per reserve (and check this is allowed) -- Increment active purses per reserve (and check this is allowed)
UPDATE reserves UPDATE reserves
SET purses_active=purses_active+1 SET purses_active=purses_active+1
,kyc_required=TRUE
WHERE reserve_pub=in_reserve_pub WHERE reserve_pub=in_reserve_pub
AND purses_active < purses_allowed; AND purses_active < purses_allowed;
IF NOT FOUND IF NOT FOUND
@ -1965,7 +1929,6 @@ ELSE
THEN 1 THEN 1
ELSE 0 ELSE 0
END END
,kyc_required=TRUE
WHERE reserve_pub=in_reserve_pub WHERE reserve_pub=in_reserve_pub
AND ( (current_balance_val > in_purse_fee_val) OR AND ( (current_balance_val > in_purse_fee_val) OR
( (current_balance_frac >= in_purse_fee_frac) AND ( (current_balance_frac >= in_purse_fee_frac) AND
@ -1994,7 +1957,7 @@ INSERT INTO exchange.account_merges
END $$; END $$;
COMMENT ON FUNCTION exchange_do_reserve_purse(BYTEA, BYTEA, INT8, BYTEA, BOOLEAN, INT8, INT4, BYTEA, BYTEA, BOOLEAN) COMMENT ON FUNCTION exchange_do_reserve_purse(BYTEA, BYTEA, INT8, BYTEA, BOOLEAN, INT8, INT4, BYTEA, BYTEA)
IS 'Create a purse for a reserve.'; IS 'Create a purse for a reserve.';

View File

@ -220,18 +220,15 @@ check_reserve (const struct TALER_ReservePublicKeyP *pub,
const char *currency) const char *currency)
{ {
struct TALER_EXCHANGEDB_Reserve reserve; struct TALER_EXCHANGEDB_Reserve reserve;
struct TALER_EXCHANGEDB_KycStatus kyc;
reserve.pub = *pub; reserve.pub = *pub;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserves_get (plugin->cls, plugin->reserves_get (plugin->cls,
&reserve, &reserve));
&kyc));
FAILIF (value != reserve.balance.value); FAILIF (value != reserve.balance.value);
FAILIF (fraction != reserve.balance.fraction); FAILIF (fraction != reserve.balance.fraction);
FAILIF (0 != strcmp (currency, FAILIF (0 != strcmp (currency,
reserve.balance.currency)); reserve.balance.currency));
FAILIF (kyc.ok);
return GNUNET_OK; return GNUNET_OK;
drop: drop:
return GNUNET_SYSERR; return GNUNET_SYSERR;
@ -1001,15 +998,10 @@ test_wire_out (const struct TALER_EXCHANGEDB_Deposit *deposit)
} }
{ {
struct TALER_ReservePublicKeyP rpub; struct TALER_ReservePublicKeyP rpub;
struct TALER_EXCHANGEDB_KycStatus kyc;
memset (&rpub, memset (&rpub,
44, 44,
sizeof (rpub)); sizeof (rpub));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->inselect_wallet_kyc_status (plugin->cls,
&rpub,
&kyc));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->store_wire_transfer_out (plugin->cls, plugin->store_wire_transfer_out (plugin->cls,
wire_out_date, wire_out_date,
@ -1755,7 +1747,6 @@ run (void *cls)
struct TALER_EXCHANGEDB_Reserve pre_reserve; struct TALER_EXCHANGEDB_Reserve pre_reserve;
struct TALER_EXCHANGEDB_Reserve post_reserve; struct TALER_EXCHANGEDB_Reserve post_reserve;
struct TALER_Amount delta; struct TALER_Amount delta;
struct TALER_EXCHANGEDB_KycStatus kyc;
bool recoup_ok; bool recoup_ok;
bool internal_failure; bool internal_failure;
struct GNUNET_TIME_Timestamp recoup_timestamp struct GNUNET_TIME_Timestamp recoup_timestamp
@ -1764,8 +1755,7 @@ run (void *cls)
pre_reserve.pub = reserve_pub; pre_reserve.pub = reserve_pub;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserves_get (plugin->cls, plugin->reserves_get (plugin->cls,
&pre_reserve, &pre_reserve));
&kyc));
FAILIF (! TALER_amount_is_zero (&pre_reserve.balance)); FAILIF (! TALER_amount_is_zero (&pre_reserve.balance));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->do_recoup (plugin->cls, plugin->do_recoup (plugin->cls,
@ -1783,8 +1773,7 @@ run (void *cls)
post_reserve.pub = reserve_pub; post_reserve.pub = reserve_pub;
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->reserves_get (plugin->cls, plugin->reserves_get (plugin->cls,
&post_reserve, &post_reserve));
&kyc));
FAILIF (0 >= FAILIF (0 >=
TALER_amount_subtract (&delta, TALER_amount_subtract (&delta,
&post_reserve.balance, &post_reserve.balance,

View File

@ -1883,11 +1883,6 @@ struct TALER_EXCHANGE_ReserveStatus
*/ */
unsigned int history_len; unsigned int history_len;
/**
* KYC passed?
*/
bool kyc_ok;
} ok; } ok;
} details; } details;
@ -2138,7 +2133,7 @@ struct TALER_EXCHANGE_WithdrawResponse
struct TALER_EXCHANGE_PrivateCoinDetails success; struct TALER_EXCHANGE_PrivateCoinDetails success;
/** /**
* Details if the status is #MHD_HTTP_ACCEPTED. * Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
*/ */
struct struct
{ {
@ -2147,7 +2142,7 @@ struct TALER_EXCHANGE_WithdrawResponse
* to check for its KYC status. * to check for its KYC status.
*/ */
uint64_t payment_target_uuid; uint64_t payment_target_uuid;
} accepted; } unavailable_for_legal_reasons;
/** /**
* Details if the status is #MHD_HTTP_CONFLICT. * Details if the status is #MHD_HTTP_CONFLICT.
@ -4875,6 +4870,19 @@ struct TALER_EXCHANGE_AccountMergeResponse
} success; } success;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
*/
struct
{
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} unavailable_for_legal_reasons;
} details; } details;
}; };
@ -4970,6 +4978,19 @@ struct TALER_EXCHANGE_PurseCreateMergeResponse
{ {
} success; } success;
/**
* Details if the status is #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS.
*/
struct
{
/**
* Payment target that the merchant should use
* to check for its KYC status.
*/
uint64_t payment_target_uuid;
} unavailable_for_legal_reasons;
} details; } details;
}; };

View File

@ -2273,50 +2273,6 @@ struct TALER_EXCHANGEDB_CsRevealFreshCoinData
}; };
/**
* Types of operations that require KYC checks.
* @deprecated FIXME - remove with new KYC logic
*/
enum TALER_EXCHANGEDB_KycType
{
/**
* It is unclear for which type of KYC operation
* this information is.
*/
TALER_EXCHANGEDB_KYC_UNKNOWN = 0,
/**
* KYC to be applied for simple withdraws without
* the involvement of wallet-to-wallet payments.
* Tied to the payto:// of the debited account.
*/
TALER_EXCHANGEDB_KYC_WITHDRAW = 1,
/**
* KYC to be applied for simple deposits to a
* merchant's bank account. Tied to the payto://
* of the credited account.
*/
TALER_EXCHANGEDB_KYC_DEPOSIT = 2,
/**
* KYC that is self-applied by a wallet that is exceeding the amount
* threshold. Tied to the reserve-account public key that identifies the
* funds-holding wallet.
*/
TALER_EXCHANGEDB_KYC_BALANCE = 3,
/**
* KYC that is triggered upon wallet-to-wallet
* payments for the recipient of funds. Tied to the
* reserve public key that identifies the receiving
* wallet.
*/
TALER_EXCHANGEDB_KYC_W2W = 4
};
/** /**
* Generic KYC status for some operation. * Generic KYC status for some operation.
* @deprecated FIXME - remove with new KYC logic * @deprecated FIXME - remove with new KYC logic
@ -2330,12 +2286,6 @@ struct TALER_EXCHANGEDB_KycStatus
// FIXME: rename to 'legitimization_uuid' // FIXME: rename to 'legitimization_uuid'
uint64_t payment_target_uuid; uint64_t payment_target_uuid;
/**
* What kind of KYC operation is this?
*/
// FIXME: kill!
enum TALER_EXCHANGEDB_KycType type;
/** /**
* True if the KYC status is "satisfied". * True if the KYC status is "satisfied".
*/ */
@ -3110,13 +3060,11 @@ struct TALER_EXCHANGEDB_Plugin
* @param[in,out] reserve the reserve data. The public key of the reserve should be set * @param[in,out] reserve the reserve data. The public key of the reserve should be set
* in this structure; it is used to query the database. The balance * in this structure; it is used to query the database. The balance
* and expiration are then filled accordingly. * and expiration are then filled accordingly.
* @param[out] kyc set to the KYC status of the reserve
* @return transaction status * @return transaction status
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
(*reserves_get)(void *cls, (*reserves_get)(void *cls,
struct TALER_EXCHANGEDB_Reserve *reserve, struct TALER_EXCHANGEDB_Reserve *reserve);
struct TALER_EXCHANGEDB_KycStatus *kyc);
/** /**
@ -3162,36 +3110,6 @@ struct TALER_EXCHANGEDB_Plugin
struct TALER_PaytoHashP *h_payto); struct TALER_PaytoHashP *h_payto);
/**
* Get the @a kyc status and @a h_payto by UUID.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param h_payto set to the hash of the account's payto URI (unsalted)
* @param[out] kyc set to the KYC status of the account
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
(*select_kyc_status)(void *cls,
const struct TALER_PaytoHashP *h_payto,
struct TALER_EXCHANGEDB_KycStatus *kyc);
/**
* Get the KYC status for a wallet. If the status is unknown,
* inserts a new status record (hence INsertSELECT).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param reserve_pub public key of the wallet
* @param[out] kyc set to the KYC status of the wallet
* @return transaction status
*/
enum GNUNET_DB_QueryStatus
(*inselect_wallet_kyc_status)(
void *cls,
const struct TALER_ReservePublicKeyP *reserve_pub,
struct TALER_EXCHANGEDB_KycStatus *kyc);
/** /**
* Insert a incoming transaction into reserves. New reserves are * Insert a incoming transaction into reserves. New reserves are
* also created through this function. * also created through this function.
@ -5496,11 +5414,9 @@ struct TALER_EXCHANGEDB_Plugin
* @param reserve_sig signature of the reserve affirming the merge * @param reserve_sig signature of the reserve affirming the merge
* @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us * @param partner_url URL of the partner exchange, can be NULL if the reserves lives with us
* @param reserve_pub public key of the reserve to credit * @param reserve_pub public key of the reserve to credit
* @param require_kyc true if we should check for KYC
* @param[out] no_partner set to true if @a partner_url is unknown * @param[out] no_partner set to true if @a partner_url is unknown
* @param[out] no_balance set to true if the @a purse_pub is not paid up yet * @param[out] no_balance set to true if the @a purse_pub is not paid up yet
* @param[out] no_reserve set to true if the @a reserve_pub is not known * @param[out] no_reserve set to true if the @a reserve_pub is not known
* @param[out] no_kyc set to true if the @a reserve_pub lacks KYC
* @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
* @return transaction status code * @return transaction status code
*/ */
@ -5513,11 +5429,9 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_ReserveSignatureP *reserve_sig,
const char *partner_url, const char *partner_url,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
bool require_kyc,
bool *no_partner, bool *no_partner,
bool *no_balance, bool *no_balance,
bool *no_reserve, bool *no_reserve,
bool *no_kyc,
bool *in_conflict); bool *in_conflict);
@ -5533,10 +5447,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param reserve_sig signature of the reserve affirming the merge * @param reserve_sig signature of the reserve affirming the merge
* @param purse_fee amount to charge the reserve for the purse creation, NULL to use the quota * @param purse_fee amount to charge the reserve for the purse creation, NULL to use the quota
* @param reserve_pub public key of the reserve to credit * @param reserve_pub public key of the reserve to credit
* @param require_kyc true if we should check for KYC
* @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already * @param[out] in_conflict set to true if @a purse_pub was merged into a different reserve already
* @param[out] no_reserve set to true if @a reserve_pub is not a known reserve * @param[out] no_reserve set to true if @a reserve_pub is not a known reserve
* @param[out] no_kyc set to true if @a reserve_pub has not passed KYC checks
* @param[out] insufficient_funds set to true if @a reserve_pub has insufficient capacity to create another purse * @param[out] insufficient_funds set to true if @a reserve_pub has insufficient capacity to create another purse
* @return transaction status code * @return transaction status code
*/ */
@ -5549,10 +5461,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_ReserveSignatureP *reserve_sig,
const struct TALER_Amount *purse_fee, const struct TALER_Amount *purse_fee,
const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReservePublicKeyP *reserve_pub,
bool require_kyc,
bool *in_conflict, bool *in_conflict,
bool *no_reserve, bool *no_reserve,
bool *no_kyc,
bool *insufficient_funds); bool *insufficient_funds);

View File

@ -207,7 +207,7 @@ handle_reserve_batch_withdraw_finished (
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("payment_target_uuid", GNUNET_JSON_spec_uint64 ("legitimization_uuid",
&wr.details.accepted.payment_target_uuid), &wr.details.accepted.payment_target_uuid),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };

View File

@ -288,12 +288,12 @@ handle_reserve_batch_withdraw_finished (void *cls,
GNUNET_assert (NULL == wh->cb); GNUNET_assert (NULL == wh->cb);
TALER_EXCHANGE_batch_withdraw2_cancel (wh); TALER_EXCHANGE_batch_withdraw2_cancel (wh);
return; return;
case MHD_HTTP_ACCEPTED: case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* only validate reply is well-formed */ /* only validate reply is well-formed */
{ {
uint64_t ptu; uint64_t ptu;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("payment_target_uuid", GNUNET_JSON_spec_uint64 ("legitimization_uuid",
&ptu), &ptu),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };

View File

@ -98,7 +98,7 @@ handle_kyc_wallet_finished (void *cls,
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("payment_target_uuid", GNUNET_JSON_spec_uint64 ("legitimization_uuid",
&ks.payment_target_uuid), &ks.payment_target_uuid),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };

View File

@ -295,9 +295,25 @@ handle_purse_create_with_merge_finished (void *cls,
dr.hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* aka KYC required */ {
dr.hr.ec = TALER_JSON_get_error_code (j); struct GNUNET_JSON_Specification spec[] = {
dr.hr.hint = TALER_JSON_get_error_hint (j); GNUNET_JSON_spec_uint64 (
"legitimization_uuid",
&dr.details.unavailable_for_legal_reasons.payment_target_uuid),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (j,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
}
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);

View File

@ -256,6 +256,27 @@ handle_purse_merge_finished (void *cls,
dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
dr.hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
{
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 (
"legitimization_uuid",
&dr.details.unavailable_for_legal_reasons.payment_target_uuid),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (j,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
dr.hr.http_status = 0;
dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break;
}
}
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.ec = TALER_JSON_get_error_code (j);
dr.hr.hint = TALER_JSON_get_error_hint (j); dr.hr.hint = TALER_JSON_get_error_hint (j);

View File

@ -98,8 +98,6 @@ handle_reserves_status_ok (struct TALER_EXCHANGE_ReservesStatusHandle *rsh,
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount_any ("balance", TALER_JSON_spec_amount_any ("balance",
&rs.details.ok.balance), &rs.details.ok.balance),
GNUNET_JSON_spec_bool ("kyc_passed",
&rs.details.ok.kyc_ok),
GNUNET_JSON_spec_json ("history", GNUNET_JSON_spec_json ("history",
&history), &history),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()

View File

@ -160,8 +160,9 @@ handle_reserve_withdraw_finished (
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("payment_target_uuid", GNUNET_JSON_spec_uint64 (
&wr.details.accepted.payment_target_uuid), "legitimization_uuid",
&wr.details.unavailable_for_legal_reasons.payment_target_uuid),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };

View File

@ -316,7 +316,7 @@ handle_reserve_withdraw_finished (void *cls,
{ {
uint64_t ptu; uint64_t ptu;
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint64 ("payment_target_uuid", GNUNET_JSON_spec_uint64 ("legitimization_uuid",
&ptu), &ptu),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };

View File

@ -231,6 +231,8 @@ run (void *cls,
"EUR:5.04"), "EUR:5.04"),
CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-2", CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-2",
"EUR:5.01"), "EUR:5.01"),
CMD_TRANSFER_TO_EXCHANGE ("p2p_create-reserve-3",
"EUR:0.03"),
TALER_TESTING_cmd_reserve_poll ("p2p_poll-reserve-1", TALER_TESTING_cmd_reserve_poll ("p2p_poll-reserve-1",
"p2p_create-reserve-1", "p2p_create-reserve-1",
"EUR:5.04", "EUR:5.04",
@ -293,6 +295,17 @@ run (void *cls,
MHD_HTTP_OK, MHD_HTTP_OK,
true, /* for merge */ true, /* for merge */
"purse-with-deposit"), "purse-with-deposit"),
TALER_TESTING_cmd_purse_merge (
"purse-merge-into-reserve",
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
"push-get-contract",
"p2p_create-reserve-1"),
TALER_TESTING_cmd_proof_kyc_oauth2 ("p2p_proof-kyc",
"purse-merge-into-reserve",
"kyc-provider-test-oauth2",
"pass",
"state",
MHD_HTTP_SEE_OTHER),
TALER_TESTING_cmd_purse_merge ( TALER_TESTING_cmd_purse_merge (
"purse-merge-into-reserve", "purse-merge-into-reserve",
MHD_HTTP_OK, MHD_HTTP_OK,
@ -318,13 +331,26 @@ run (void *cls,
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
struct TALER_TESTING_Command pull[] = { struct TALER_TESTING_Command pull[] = {
TALER_TESTING_cmd_purse_create_with_reserve (
"purse-create-with-reserve",
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
"{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
true /* upload contract */,
GNUNET_TIME_UNIT_MINUTES, /* expiration */
"p2p_create-reserve-3"),
TALER_TESTING_cmd_proof_kyc_oauth2 ("p2p_proof-kyc-pull",
"purse-create-with-reserve",
"kyc-provider-test-oauth2",
"pass",
"state",
MHD_HTTP_SEE_OTHER),
TALER_TESTING_cmd_purse_create_with_reserve ( TALER_TESTING_cmd_purse_create_with_reserve (
"purse-create-with-reserve", "purse-create-with-reserve",
MHD_HTTP_OK, MHD_HTTP_OK,
"{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}", "{\"amount\":\"EUR:1\",\"summary\":\"ice cream\"}",
true /* upload contract */, true /* upload contract */,
GNUNET_TIME_UNIT_MINUTES, /* expiration */ GNUNET_TIME_UNIT_MINUTES, /* expiration */
"p2p_create-reserve-1"), "p2p_create-reserve-3"),
TALER_TESTING_cmd_contract_get ( TALER_TESTING_cmd_contract_get (
"pull-get-contract", "pull-get-contract",
MHD_HTTP_OK, MHD_HTTP_OK,
@ -353,23 +379,14 @@ run (void *cls,
"pull-poll-purse-before-deposit"), "pull-poll-purse-before-deposit"),
TALER_TESTING_cmd_status ( TALER_TESTING_cmd_status (
"pull-check-post-merge-reserve-balance-get", "pull-check-post-merge-reserve-balance-get",
"p2p_create-reserve-1", "p2p_create-reserve-3",
"EUR:2.02", "EUR:1.02",
MHD_HTTP_OK), MHD_HTTP_OK),
/* POST history doesn't yet support P2P transfers */
TALER_TESTING_cmd_reserve_status ( TALER_TESTING_cmd_reserve_status (
"push-check-post-merge-reserve-balance-post", "push-check-post-merge-reserve-balance-post",
"p2p_create-reserve-1", "p2p_create-reserve-3",
"EUR:2.02", "EUR:1.02",
MHD_HTTP_OK), MHD_HTTP_OK),
/* create 2nd purse for a deposit conflict */
TALER_TESTING_cmd_purse_create_with_reserve (
"purse-create-with-reserve-2",
MHD_HTTP_OK,
"{\"amount\":\"EUR:4\",\"summary\":\"beer\"}",
true /* upload contract */,
GNUNET_TIME_UNIT_MINUTES, /* expiration */
"p2p_create-reserve-1"),
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };
@ -401,7 +418,6 @@ run (void *cls,
CONFIG_FILE), CONFIG_FILE),
TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys", TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
2), 2),
#if 1
TALER_TESTING_cmd_batch ("withdraw", TALER_TESTING_cmd_batch ("withdraw",
withdraw), withdraw),
TALER_TESTING_cmd_batch ("spend", TALER_TESTING_cmd_batch ("spend",
@ -412,15 +428,12 @@ run (void *cls,
withdraw_kyc), withdraw_kyc),
TALER_TESTING_cmd_batch ("wallet-kyc", TALER_TESTING_cmd_batch ("wallet-kyc",
wallet_kyc), wallet_kyc),
#endif
TALER_TESTING_cmd_batch ("p2p_withdraw", TALER_TESTING_cmd_batch ("p2p_withdraw",
p2p_withdraw), p2p_withdraw),
#if 0
TALER_TESTING_cmd_batch ("push", TALER_TESTING_cmd_batch ("push",
push), push),
TALER_TESTING_cmd_batch ("pull", TALER_TESTING_cmd_batch ("pull",
pull), pull),
#endif
TALER_TESTING_cmd_end () TALER_TESTING_cmd_end ()
}; };

View File

@ -91,6 +91,12 @@ REQUIRED_CHECKS = DUMMY
THRESHOLD = EUR:8 THRESHOLD = EUR:8
TIMEFRAME = 1d TIMEFRAME = 1d
[kyc-legitimization-merge]
OPERATION_TYPE = MERGE
REQUIRED_CHECKS = DUMMY
THRESHOLD = EUR:0
TIMEFRAME = 1d
[exchangedb-postgres] [exchangedb-postgres]
CONFIG = "postgres:///talercheck" CONFIG = "postgres:///talercheck"

View File

@ -71,6 +71,18 @@ struct PurseMergeState
*/ */
struct TALER_TESTING_Interpreter *is; struct TALER_TESTING_Interpreter *is;
/**
* Hash of the payto://-URI for the reserve we are
* merging into.
*/
struct TALER_PaytoHashP h_payto;
/**
* Set to the KYC UUID *if* the exchange replied with
* a request for KYC.
*/
uint64_t kyc_uuid;
/** /**
* Reserve history entry that corresponds to this operation. * Reserve history entry that corresponds to this operation.
* Will be of type #TALER_EXCHANGE_RTT_MERGE. * Will be of type #TALER_EXCHANGE_RTT_MERGE.
@ -129,8 +141,9 @@ merge_cb (void *cls,
struct PurseMergeState *ds = cls; struct PurseMergeState *ds = cls;
ds->dh = NULL; ds->dh = NULL;
if (MHD_HTTP_OK == dr->hr.http_status) switch (dr->hr.http_status)
{ {
case MHD_HTTP_OK:
ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE; ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE;
ds->reserve_history.amount = ds->value_after_fees; ds->reserve_history.amount = ds->value_after_fees;
GNUNET_assert (GNUNET_OK == GNUNET_assert (GNUNET_OK ==
@ -153,6 +166,12 @@ merge_cb (void *cls,
= ds->min_age; = ds->min_age;
ds->reserve_history.details.merge_details.flags ds->reserve_history.details.merge_details.flags
= TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE; = TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE;
break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* KYC required */
ds->kyc_uuid =
dr->details.unavailable_for_legal_reasons.payment_target_uuid;
break;
} }
@ -281,6 +300,15 @@ merge_run (void *cls,
} }
GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&ds->reserve_priv.eddsa_priv,
&ds->reserve_pub.eddsa_pub); &ds->reserve_pub.eddsa_pub);
{
char *payto_uri;
payto_uri = TALER_reserve_make_payto (is->exchange_url,
&ds->reserve_pub);
TALER_payto_hash (payto_uri,
&ds->h_payto);
GNUNET_free (payto_uri);
}
GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
&ds->merge_pub.eddsa_pub); &ds->merge_pub.eddsa_pub);
ds->merge_timestamp = GNUNET_TIME_timestamp_get (); ds->merge_timestamp = GNUNET_TIME_timestamp_get ();
@ -357,6 +385,8 @@ merge_traits (void *cls,
TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub), TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub),
TALER_TESTING_make_trait_timestamp (0, TALER_TESTING_make_trait_timestamp (0,
&ds->merge_timestamp), &ds->merge_timestamp),
TALER_TESTING_make_trait_payment_target_uuid (&ds->kyc_uuid),
TALER_TESTING_make_trait_h_payto (&ds->h_payto),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };

View File

@ -97,6 +97,18 @@ struct ReservePurseState
*/ */
struct GNUNET_TIME_Timestamp purse_expiration; struct GNUNET_TIME_Timestamp purse_expiration;
/**
* Hash of the payto://-URI for the reserve we are
* merging into.
*/
struct TALER_PaytoHashP h_payto;
/**
* Set to the KYC UUID *if* the exchange replied with
* a request for KYC.
*/
uint64_t kyc_uuid;
/** /**
* Contract terms for the purse. * Contract terms for the purse.
*/ */
@ -149,6 +161,14 @@ purse_cb (void *cls,
TALER_TESTING_interpreter_fail (ds->is); TALER_TESTING_interpreter_fail (ds->is);
return; return;
} }
switch (dr->hr.http_status)
{
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* KYC required */
ds->kyc_uuid =
dr->details.unavailable_for_legal_reasons.payment_target_uuid;
break;
}
TALER_TESTING_interpreter_next (ds->is); TALER_TESTING_interpreter_next (ds->is);
} }
@ -194,6 +214,17 @@ purse_run (void *cls,
GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv); GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv);
ds->purse_expiration = GNUNET_TIME_absolute_to_timestamp ( ds->purse_expiration = GNUNET_TIME_absolute_to_timestamp (
GNUNET_TIME_relative_to_absolute (ds->expiration_rel)); GNUNET_TIME_relative_to_absolute (ds->expiration_rel));
{
char *payto_uri;
payto_uri = TALER_reserve_make_payto (is->exchange_url,
&ds->reserve_pub);
TALER_payto_hash (payto_uri,
&ds->h_payto);
GNUNET_free (payto_uri);
}
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set_new ( json_object_set_new (
ds->contract_terms, ds->contract_terms,
@ -278,6 +309,8 @@ purse_traits (void *cls,
TALER_TESTING_make_trait_reserve_priv (&ds->reserve_priv), TALER_TESTING_make_trait_reserve_priv (&ds->reserve_priv),
TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub), TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub),
TALER_TESTING_make_trait_reserve_sig (&ds->reserve_sig), TALER_TESTING_make_trait_reserve_sig (&ds->reserve_sig),
TALER_TESTING_make_trait_payment_target_uuid (&ds->kyc_uuid),
TALER_TESTING_make_trait_h_payto (&ds->h_payto),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };

View File

@ -304,10 +304,6 @@ reserve_withdraw_cb (void *cls,
GNUNET_YES)); GNUNET_YES));
} }
break; break;
case MHD_HTTP_ACCEPTED:
/* nothing to check */
ws->kyc_uuid = wr->details.accepted.payment_target_uuid;
break;
case MHD_HTTP_FORBIDDEN: case MHD_HTTP_FORBIDDEN:
/* nothing to check */ /* nothing to check */
break; break;
@ -322,6 +318,8 @@ reserve_withdraw_cb (void *cls,
break; break;
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
/* KYC required */ /* KYC required */
ws->kyc_uuid =
wr->details.unavailable_for_legal_reasons.payment_target_uuid;
break; break;
default: default:
/* Unsupported status code (by test harness) */ /* Unsupported status code (by test harness) */
@ -545,8 +543,10 @@ withdraw_traits (void *cls,
(const char **) &ws->reserve_payto_uri), (const char **) &ws->reserve_payto_uri),
TALER_TESTING_make_trait_exchange_url ( TALER_TESTING_make_trait_exchange_url (
(const char **) &ws->exchange_url), (const char **) &ws->exchange_url),
TALER_TESTING_make_trait_age_commitment_proof (0, ws->age_commitment_proof), TALER_TESTING_make_trait_age_commitment_proof (0,
TALER_TESTING_make_trait_h_age_commitment (0, ws->h_age_commitment), ws->age_commitment_proof),
TALER_TESTING_make_trait_h_age_commitment (0,
ws->h_age_commitment),
TALER_TESTING_trait_end () TALER_TESTING_trait_end ()
}; };