summaryrefslogtreecommitdiff
path: root/src/exchange/taler-exchange-httpd_reserves_purse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/exchange/taler-exchange-httpd_reserves_purse.c')
-rw-r--r--src/exchange/taler-exchange-httpd_reserves_purse.c292
1 files changed, 174 insertions, 118 deletions
diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c b/src/exchange/taler-exchange-httpd_reserves_purse.c
index fcf688d42..5e06db206 100644
--- a/src/exchange/taler-exchange-httpd_reserves_purse.c
+++ b/src/exchange/taler-exchange-httpd_reserves_purse.c
@@ -27,6 +27,7 @@
#include <microhttpd.h>
#include <pthread.h>
#include "taler_json_lib.h"
+#include "taler_kyclogic_lib.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_reserves_purse.h"
#include "taler-exchange-httpd_responses.h"
@@ -56,11 +57,6 @@ struct ReservePurseContext
struct TALER_ReserveSignatureP reserve_sig;
/**
- * Total amount to be put into the purse.
- */
- struct TALER_Amount amount;
-
- /**
* Purse fee the client is willing to pay.
*/
struct TALER_Amount purse_fee;
@@ -71,11 +67,6 @@ struct ReservePurseContext
struct TALER_Amount deposit_total;
/**
- * When should the purse expire.
- */
- struct GNUNET_TIME_Timestamp purse_expiration;
-
- /**
* Merge time.
*/
struct GNUNET_TIME_Timestamp merge_timestamp;
@@ -101,19 +92,24 @@ struct ReservePurseContext
struct TALER_PurseMergeSignatureP merge_sig;
/**
- * Public key of the purse we are creating.
+ * Signature of the client affiming this request.
+ */
+ struct TALER_PurseContractSignatureP purse_sig;
+
+ /**
+ * Fundamental details about the purse.
*/
- struct TALER_PurseContractPublicKeyP purse_pub;
+ struct TEH_PurseDetails pd;
/**
- * Signature of the client affiming this request.
+ * Hash of the @e payto_uri.
*/
- struct TALER_PurseContractSignatureP purse_sig;
+ struct TALER_PaytoHashP h_payto;
/**
- * Hash of the contract terms of the purse.
+ * KYC status of the operation.
*/
- struct TALER_PrivateContractHashP h_contract_terms;
+ struct TALER_EXCHANGEDB_KycStatus kyc;
/**
* Minimum age for deposits into this purse.
@@ -134,49 +130,42 @@ struct ReservePurseContext
/**
- * Send confirmation of purse creation success to client.
+ * 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 connection connection to the client
- * @param rpc details about the request that succeeded
- * @return MHD result code
+ * @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 MHD_RESULT
-reply_purse_success (struct MHD_Connection *connection,
- const struct ReservePurseContext *rpc)
+static void
+amount_iterator (void *cls,
+ struct GNUNET_TIME_Absolute limit,
+ TALER_EXCHANGEDB_KycAmountCallback cb,
+ void *cb_cls)
{
- struct TALER_ExchangePublicKeyP pub;
- struct TALER_ExchangeSignatureP sig;
- enum TALER_ErrorCode ec;
-
- if (TALER_EC_NONE !=
- (ec = TALER_exchange_online_purse_created_sign (
- &TEH_keys_exchange_sign_,
- rpc->exchange_timestamp,
- rpc->purse_expiration,
- &rpc->amount,
- &rpc->deposit_total,
- &rpc->purse_pub,
- &rpc->h_contract_terms,
- &pub,
- &sig)))
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_ec (connection,
- ec,
- NULL);
- }
- // FIXME: share logic with /purses/$PID/create API!
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- TALER_JSON_pack_amount ("total_deposited",
- &rpc->deposit_total),
- GNUNET_JSON_pack_timestamp ("exchange_timestamp",
- rpc->exchange_timestamp),
- GNUNET_JSON_pack_data_auto ("exchange_sig",
- &sig),
- GNUNET_JSON_pack_data_auto ("exchange_pub",
- &pub));
+ 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);
}
@@ -200,6 +189,50 @@ purse_transaction (void *cls,
{
struct ReservePurseContext *rpc = cls;
enum GNUNET_DB_QueryStatus qs;
+ char *required;
+
+ qs = 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,
+ &required);
+ 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,
+ "kyc_test_required");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (NULL != required)
+ {
+ rpc->kyc.ok = false;
+ qs = TEH_plugin->insert_kyc_requirement_for_account (
+ TEH_plugin->cls,
+ required,
+ &rpc->h_payto,
+ rpc->reserve_pub,
+ &rpc->kyc.requirement_row);
+ GNUNET_free (required);
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+ {
+ GNUNET_break (0);
+ *mhd_ret
+ = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "insert_kyc_requirement_for_account");
+ }
+ return qs;
+ }
+ rpc->kyc.ok = true;
{
bool in_conflict = true;
@@ -207,22 +240,21 @@ purse_transaction (void *cls,
/* 1) store purse */
qs = TEH_plugin->insert_purse_request (
TEH_plugin->cls,
- &rpc->purse_pub,
+ &rpc->pd.purse_pub,
&rpc->merge_pub,
- rpc->purse_expiration,
- &rpc->h_contract_terms,
+ rpc->pd.purse_expiration,
+ &rpc->pd.h_contract_terms,
rpc->min_age,
rpc->flags,
&rpc->purse_fee,
- &rpc->amount,
+ &rpc->pd.target_amount,
&rpc->purse_sig,
&in_conflict);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
- TALER_LOG_WARNING (
- "Failed to store purse purse information in database\n");
+ GNUNET_break (0);
*mhd_ret =
TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
@@ -243,9 +275,9 @@ purse_transaction (void *cls,
uint32_t min_age;
TEH_plugin->rollback (TEH_plugin->cls);
- qs = TEH_plugin->select_purse_request (
+ qs = TEH_plugin->get_purse_request (
TEH_plugin->cls,
- &rpc->purse_pub,
+ &rpc->pd.purse_pub,
&merge_pub,
&purse_expiration,
&h_contract_terms,
@@ -292,14 +324,13 @@ purse_transaction (void *cls,
bool in_conflict = true;
bool insufficient_funds = true;
bool no_reserve = true;
- bool no_kyc = true;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating purse with flags %d\n",
rpc->flags);
qs = TEH_plugin->do_reserve_purse (
TEH_plugin->cls,
- &rpc->purse_pub,
+ &rpc->pd.purse_pub,
&rpc->merge_sig,
rpc->merge_timestamp,
&rpc->reserve_sig,
@@ -308,10 +339,8 @@ purse_transaction (void *cls,
? NULL
: &rpc->gf->fees.purse,
rpc->reserve_pub,
- TEH_KYC_NONE != TEH_kyc_config.mode,
&in_conflict,
&no_reserve,
- &no_kyc,
&insufficient_funds);
if (qs < 0)
{
@@ -334,6 +363,7 @@ purse_transaction (void *cls,
struct GNUNET_TIME_Timestamp merge_timestamp;
char *partner_url;
struct TALER_ReservePublicKeyP reserve_pub;
+ bool refunded;
TEH_plugin->rollback (TEH_plugin->cls);
qs = TEH_plugin->select_purse_merge (
@@ -342,7 +372,8 @@ purse_transaction (void *cls,
&merge_sig,
&merge_timestamp,
&partner_url,
- &reserve_pub);
+ &reserve_pub,
+ &refunded);
if (qs <= 0)
{
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
@@ -355,6 +386,18 @@ purse_transaction (void *cls,
"select purse merge");
return GNUNET_DB_STATUS_HARD_ERROR;
}
+ if (refunded)
+ {
+ /* This is a bit of a strange case ... */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Purse was already refunded\n");
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_GONE,
+ TALER_EC_EXCHANGE_GENERIC_PURSE_EXPIRED,
+ NULL);
+ GNUNET_free (partner_url);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
*mhd_ret
= TALER_MHD_REPLY_JSON_PACK (
connection,
@@ -374,7 +417,10 @@ purse_transaction (void *cls,
GNUNET_free (partner_url);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- if (no_reserve)
+ if ( (no_reserve) &&
+ ( (TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA
+ == rpc->flags) ||
+ (! TALER_amount_is_zero (&rpc->gf->fees.purse)) ) )
{
*mhd_ret
= TALER_MHD_REPLY_JSON_PACK (
@@ -384,17 +430,6 @@ purse_transaction (void *cls,
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN));
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)
{
*mhd_ret
@@ -412,7 +447,7 @@ purse_transaction (void *cls,
bool in_conflict = true;
qs = TEH_plugin->insert_contract (TEH_plugin->cls,
- &rpc->purse_pub,
+ &rpc->pd.purse_pub,
&rpc->econtract,
&in_conflict);
if (qs < 0)
@@ -433,7 +468,7 @@ purse_transaction (void *cls,
qs = TEH_plugin->select_contract_by_purse (
TEH_plugin->cls,
- &rpc->purse_pub,
+ &rpc->pd.purse_pub,
&econtract);
if (qs <= 0)
{
@@ -486,7 +521,7 @@ TEH_handler_reserves_purse (
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("purse_value",
TEH_currency,
- &rpc.amount),
+ &rpc.pd.target_amount),
GNUNET_JSON_spec_uint32 ("min_age",
&rpc.min_age),
GNUNET_JSON_spec_mark_optional (
@@ -505,15 +540,15 @@ TEH_handler_reserves_purse (
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
&rpc.reserve_sig),
GNUNET_JSON_spec_fixed_auto ("purse_pub",
- &rpc.purse_pub),
+ &rpc.pd.purse_pub),
GNUNET_JSON_spec_fixed_auto ("purse_sig",
&rpc.purse_sig),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
- &rpc.h_contract_terms),
+ &rpc.pd.h_contract_terms),
GNUNET_JSON_spec_timestamp ("merge_timestamp",
&rpc.merge_timestamp),
GNUNET_JSON_spec_timestamp ("purse_expiration",
- &rpc.purse_expiration),
+ &rpc.pd.purse_expiration),
GNUNET_JSON_spec_end ()
};
@@ -534,10 +569,39 @@ TEH_handler_reserves_purse (
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);
+ TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
+ if (GNUNET_OK !=
+ TALER_wallet_purse_merge_verify (payto_uri,
+ rpc.merge_timestamp,
+ &rpc.pd.purse_pub,
+ &rpc.merge_pub,
+ &rpc.merge_sig))
+ {
+ MHD_RESULT ret;
+
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ ret = TALER_MHD_reply_with_error (
+ connection,
+ MHD_HTTP_FORBIDDEN,
+ TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_SIGNATURE_INVALID,
+ payto_uri);
+ GNUNET_free (payto_uri);
+ return ret;
+ }
+ GNUNET_free (payto_uri);
+ }
GNUNET_assert (GNUNET_OK ==
TALER_amount_set_zero (TEH_currency,
&rpc.deposit_total));
- if (GNUNET_TIME_timestamp_cmp (rpc.purse_expiration,
+ if (GNUNET_TIME_timestamp_cmp (rpc.pd.purse_expiration,
<,
rpc.exchange_timestamp))
{
@@ -548,7 +612,7 @@ TEH_handler_reserves_purse (
TALER_EC_EXCHANGE_RESERVES_PURSE_EXPIRATION_BEFORE_NOW,
NULL);
}
- if (GNUNET_TIME_absolute_is_never (rpc.purse_expiration.abs_time))
+ if (GNUNET_TIME_absolute_is_never (rpc.pd.purse_expiration.abs_time))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
@@ -586,8 +650,9 @@ TEH_handler_reserves_purse (
if (no_purse_fee)
{
rpc.flags = TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA;
- TALER_amount_set_zero (TEH_currency,
- &rpc.purse_fee);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (TEH_currency,
+ &rpc.purse_fee));
}
else
{
@@ -607,12 +672,12 @@ TEH_handler_reserves_purse (
}
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
- TALER_wallet_purse_create_verify (rpc.purse_expiration,
- &rpc.h_contract_terms,
+ TALER_wallet_purse_create_verify (rpc.pd.purse_expiration,
+ &rpc.pd.h_contract_terms,
&rpc.merge_pub,
rpc.min_age,
- &rpc.amount,
- &rpc.purse_pub,
+ &rpc.pd.target_amount,
+ &rpc.pd.purse_pub,
&rpc.purse_sig))
{
GNUNET_break_op (0);
@@ -624,26 +689,11 @@ TEH_handler_reserves_purse (
NULL);
}
if (GNUNET_OK !=
- TALER_wallet_purse_merge_verify (TEH_base_url,
- rpc.merge_timestamp,
- &rpc.purse_pub,
- &rpc.merge_pub,
- &rpc.merge_sig))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_SIGNATURE_INVALID,
- NULL);
- }
- if (GNUNET_OK !=
TALER_wallet_account_merge_verify (rpc.merge_timestamp,
- &rpc.purse_pub,
- rpc.purse_expiration,
- &rpc.h_contract_terms,
- &rpc.amount,
+ &rpc.pd.purse_pub,
+ rpc.pd.purse_expiration,
+ &rpc.pd.h_contract_terms,
+ &rpc.pd.target_amount,
&rpc.purse_fee,
rpc.min_age,
rpc.flags,
@@ -655,7 +705,7 @@ TEH_handler_reserves_purse (
return TALER_MHD_reply_with_error (
connection,
MHD_HTTP_FORBIDDEN,
- TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_SIGNATURE_INVALID,
+ TALER_EC_EXCHANGE_RESERVES_RESERVE_MERGE_SIGNATURE_INVALID,
NULL);
}
if ( (! rpc.no_econtract) &&
@@ -663,7 +713,7 @@ TEH_handler_reserves_purse (
TALER_wallet_econtract_upload_verify (rpc.econtract.econtract,
rpc.econtract.econtract_size,
&rpc.econtract.contract_pub,
- &rpc.purse_pub,
+ &rpc.pd.purse_pub,
&rpc.econtract.econtract_sig)) )
{
TALER_LOG_WARNING ("Invalid signature on /reserves/$PID/purse request\n");
@@ -703,12 +753,18 @@ TEH_handler_reserves_purse (
}
}
+ if (! rpc.kyc.ok)
+ return TEH_RESPONSE_reply_kyc_required (connection,
+ &rpc.h_payto,
+ &rpc.kyc);
/* generate regular response */
{
MHD_RESULT res;
- res = reply_purse_success (connection,
- &rpc);
+ res = TEH_RESPONSE_reply_purse_created (connection,
+ rpc.exchange_timestamp,
+ &rpc.deposit_total,
+ &rpc.pd);
GNUNET_JSON_parse_free (spec);
return res;
}