diff options
Diffstat (limited to 'src/exchange/taler-exchange-httpd_reserves_purse.c')
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_purse.c | 292 |
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; } |