diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_reserves_purse.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_purse.c | 190 |
1 files changed, 153 insertions, 37 deletions
diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c b/src/exchange/taler-exchange-httpd_reserves_purse.c index d377323e4..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" @@ -101,6 +102,16 @@ struct ReservePurseContext 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. */ uint32_t min_age; @@ -119,6 +130,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 * logic; IF it returns a non-error code, the transaction logic MUST NOT queue * a MHD response. IF it returns an hard error, the transaction logic MUST @@ -138,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; @@ -159,8 +254,7 @@ purse_transaction (void *cls, { 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, @@ -181,7 +275,7 @@ 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->pd.purse_pub, &merge_pub, @@ -230,7 +324,6 @@ 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", @@ -246,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) { @@ -272,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 ( @@ -280,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); @@ -293,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, @@ -312,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 ( @@ -322,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 @@ -472,6 +569,35 @@ 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)); @@ -524,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 { @@ -562,21 +689,6 @@ TEH_handler_reserves_purse ( NULL); } if (GNUNET_OK != - TALER_wallet_purse_merge_verify (TEH_base_url, - rpc.merge_timestamp, - &rpc.pd.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.pd.purse_pub, rpc.pd.purse_expiration, @@ -593,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) && @@ -641,6 +753,10 @@ 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; |