diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-06-04 15:19:57 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-06-04 15:19:57 +0200 |
commit | 93943bdb5b6ab43b6bcfdb721f67674cc89250d6 (patch) | |
tree | 17e98a062ba886f1389ce99f5cb2fecb242a5f76 | |
parent | 3e99c50c0f940fda34585225c38c9014cc1110d0 (diff) |
add KYC errors for p2p payments
m--------- | contrib/gana | 0 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_batch-withdraw.c | 6 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_purses_merge.c | 23 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_reserves_purse.c | 28 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_withdraw.c | 6 | ||||
-rw-r--r-- | src/exchangedb/exchange-0001-part.sql | 66 | ||||
-rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 18 | ||||
-rw-r--r-- | src/include/taler_exchangedb_plugin.h | 8 | ||||
-rw-r--r-- | src/lib/exchange_api_batch_withdraw.c | 2 | ||||
-rw-r--r-- | src/lib/exchange_api_purse_create_with_merge.c | 5 | ||||
-rw-r--r-- | src/lib/exchange_api_withdraw.c | 2 | ||||
-rw-r--r-- | src/testing/test_kyc_api.c | 4 | ||||
-rw-r--r-- | src/testing/testing_api_cmd_withdraw.c | 6 |
13 files changed, 157 insertions, 17 deletions
diff --git a/contrib/gana b/contrib/gana -Subproject aebd5420308d7599aadb8818a82d9ffc8949233 +Subproject d4234f340c6e7261de36ab5fad3d53597ea8ecd diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c index f4276ef67..d1311f8ad 100644 --- a/src/exchange/taler-exchange-httpd_batch-withdraw.c +++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -150,7 +150,7 @@ batch_withdraw_transaction (void *cls, { *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN, + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, NULL); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -171,7 +171,7 @@ batch_withdraw_transaction (void *cls, /* Wallet-to-wallet payments _always_ require KYC */ *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( connection, - MHD_HTTP_ACCEPTED, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, GNUNET_JSON_pack_uint64 ("payment_target_uuid", wc->kyc.payment_target_uuid)); return GNUNET_DB_STATUS_HARD_ERROR; @@ -206,7 +206,7 @@ batch_withdraw_transaction (void *cls, { *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( connection, - MHD_HTTP_ACCEPTED, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, GNUNET_JSON_pack_uint64 ("payment_target_uuid", wc->kyc.payment_target_uuid)); return GNUNET_DB_STATUS_HARD_ERROR; diff --git a/src/exchange/taler-exchange-httpd_purses_merge.c b/src/exchange/taler-exchange-httpd_purses_merge.c index b0055edab..14dba5232 100644 --- a/src/exchange/taler-exchange-httpd_purses_merge.c +++ b/src/exchange/taler-exchange-httpd_purses_merge.c @@ -223,6 +223,8 @@ merge_transaction (void *cls, bool in_conflict = true; bool no_balance = true; bool no_partner = true; + bool no_kyc = true; + bool no_reserve = true; // FIXME: add KYC-check logic! qs = TEH_plugin->do_purse_merge (TEH_plugin->cls, @@ -234,6 +236,8 @@ merge_transaction (void *cls, &pcc->reserve_pub, &no_partner, &no_balance, + &no_reserve, + &no_kyc, &in_conflict); if (qs < 0) { @@ -257,6 +261,25 @@ merge_transaction (void *cls, pcc->provider_url); return GNUNET_DB_STATUS_HARD_ERROR; } + if (no_reserve) + { + *mhd_ret = + TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, + NULL); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (no_kyc) + { + *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) { *mhd_ret = diff --git a/src/exchange/taler-exchange-httpd_reserves_purse.c b/src/exchange/taler-exchange-httpd_reserves_purse.c index 09ef3b373..f56852d53 100644 --- a/src/exchange/taler-exchange-httpd_reserves_purse.c +++ b/src/exchange/taler-exchange-httpd_reserves_purse.c @@ -213,8 +213,6 @@ purse_transaction (void *cls, { bool in_conflict = true; - // FIXME: also check KYC state of the account - // FIXME: distinguish reserve-not-found! /* 1) store purse */ qs = TEH_plugin->insert_purse_request (TEH_plugin->cls, &rpc->purse_pub, @@ -294,14 +292,14 @@ purse_transaction (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } - // FIXME: return 404 if reserve-not-found! - // FIXME: if KYC check failed, generate 451 response! } /* 2) create purse with reserve (and debit reserve for purse creation!) */ { bool in_conflict = true; bool insufficient_funds = true; + bool no_reserve = true; + bool no_kyc = true; qs = TEH_plugin->do_reserve_purse ( TEH_plugin->cls, @@ -315,6 +313,8 @@ purse_transaction (void *cls, : &rpc->gf->fees.purse, rpc->reserve_pub, &in_conflict, + &no_reserve, + &no_kyc, &insufficient_funds); if (qs < 0) { @@ -377,6 +377,26 @@ purse_transaction (void *cls, GNUNET_free (partner_url); return GNUNET_DB_STATUS_HARD_ERROR; } + if (no_reserve) + { + *mhd_ret + = TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_NOT_FOUND, + TALER_JSON_pack_ec ( + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN)); + return GNUNET_DB_STATUS_HARD_ERROR; + } + if (no_kyc) + { + *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 diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c index ca4ed9a71..1d48045b6 100644 --- a/src/exchange/taler-exchange-httpd_withdraw.c +++ b/src/exchange/taler-exchange-httpd_withdraw.c @@ -133,7 +133,7 @@ withdraw_transaction (void *cls, { *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN, + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, NULL); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -153,7 +153,7 @@ withdraw_transaction (void *cls, /* Wallet-to-wallet payments _always_ require KYC */ *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( connection, - MHD_HTTP_ACCEPTED, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, GNUNET_JSON_pack_uint64 ("payment_target_uuid", wc->kyc.payment_target_uuid)); return GNUNET_DB_STATUS_HARD_ERROR; @@ -188,7 +188,7 @@ withdraw_transaction (void *cls, { *mhd_ret = TALER_MHD_REPLY_JSON_PACK ( connection, - MHD_HTTP_ACCEPTED, + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS, GNUNET_JSON_pack_uint64 ("payment_target_uuid", wc->kyc.payment_target_uuid)); return GNUNET_DB_STATUS_HARD_ERROR; diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index 52a16cd9a..0b99e25f9 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -3092,6 +3092,8 @@ CREATE OR REPLACE FUNCTION exchange_do_purse_merge( IN in_reserve_pub BYTEA, OUT out_no_partner BOOLEAN, OUT out_no_balance BOOLEAN, + OUT out_no_kyc BOOLEAN, + OUT out_no_reserve BOOLEAN, OUT out_conflict BOOLEAN) LANGUAGE plpgsql AS $$ @@ -3121,6 +3123,8 @@ ELSE THEN out_no_partner=TRUE; out_conflict=FALSE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; RETURN; END IF; END IF; @@ -3144,6 +3148,8 @@ IF NOT FOUND THEN out_no_balance=TRUE; out_conflict=FALSE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; RETURN; END IF; out_no_balance=FALSE; @@ -3176,17 +3182,49 @@ THEN THEN -- Purse was merged, but to some other reserve. Not allowed. out_conflict=TRUE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; RETURN; END IF; -- "success" out_conflict=FALSE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; RETURN; END IF; out_conflict=FALSE; ASSERT NOT my_finished, 'internal invariant failed'; +IF in_partner_url IS NULL +THEN + -- Need to do KYC check. + SELECT NOT kyc_passed + INTO out_no_kyc + FROM reserves + WHERE reserve_pub=in_reserve_pub; + + IF NOT FOUND + THEN + out_no_kyc=TRUE; + out_no_reserve=TRUE; + 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; + + + -- Store account merge signature. INSERT INTO account_merges (reserve_pub @@ -3248,6 +3286,8 @@ CREATE OR REPLACE FUNCTION exchange_do_reserve_purse( IN in_purse_fee_frac INT4, IN in_reserve_pub BYTEA, OUT out_no_funds BOOLEAN, + OUT out_no_kyc BOOLEAN, + OUT out_no_reserve BOOLEAN, OUT out_conflict BOOLEAN) LANGUAGE plpgsql AS $$ @@ -3281,16 +3321,40 @@ THEN THEN -- Purse was merged, but to some other reserve. Not allowed. out_conflict=TRUE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; + out_no_funds=FALSE; RETURN; END IF; -- "success" out_conflict=FALSE; out_no_funds=FALSE; + out_no_kyc=FALSE; + out_no_reserve=FALSE; RETURN; END IF; out_conflict=FALSE; +SELECT NOT kyc_passed + INTO out_no_kyc + FROM reserves + WHERE reserve_pub=in_reserve_pub; + +IF NOT FOUND +THEN + out_no_kyc=TRUE; + out_no_reserve=TRUE; + out_no_funds=TRUE; + RETURN; +END IF; +out_no_reserve=FALSE; + +IF (out_no_kyc) +THEN + out_no_funds=FALSE; + RETURN; +END IF; IF (in_reserve_quota) THEN @@ -3303,6 +3367,7 @@ THEN IF NOT FOUND THEN out_no_funds=TRUE; + RETURN; END IF; ELSE -- UPDATE reserves balance (and check if balance is enough to pay the fee) @@ -3328,6 +3393,7 @@ ELSE IF NOT FOUND THEN out_no_funds=TRUE; + RETURN; END IF; END IF; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 3a269c6de..01869d592 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -3973,6 +3973,8 @@ prepare_statements (struct PostgresClosure *pg) "SELECT" " out_no_partner AS no_partner" ",out_no_balance AS no_balance" + ",out_no_kyc AS no_kyc" + ",out_no_reserve AS no_reserve" ",out_conflict AS conflict" " FROM exchange_do_purse_merge" " ($1, $2, $3, $4, $5, $6);", @@ -3982,6 +3984,8 @@ prepare_statements (struct PostgresClosure *pg) "call_reserve_purse", "SELECT" " out_no_funds AS insufficient_funds" + ",out_no_reserve AS no_reserve" + ",out_no_kyc AS no_kyc" ",out_conflict AS conflict" " FROM exchange_do_reserve_purse" " ($1, $2, $3, $4, $5, $6, $7, $8);", @@ -14490,6 +14494,8 @@ postgres_get_purse_deposit ( * @param reserve_pub public key of the reserve to credit * @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_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 * @return transaction status code */ @@ -14504,6 +14510,8 @@ postgres_do_purse_merge ( const struct TALER_ReservePublicKeyP *reserve_pub, bool *no_partner, bool *no_balance, + bool *no_reserve, + bool *no_kyc, bool *in_conflict) { struct PostgresClosure *pg = cls; @@ -14523,6 +14531,10 @@ postgres_do_purse_merge ( no_partner), GNUNET_PQ_result_spec_bool ("no_balance", no_balance), + GNUNET_PQ_result_spec_bool ("no_kyc", + no_kyc), + GNUNET_PQ_result_spec_bool ("no_reserve", + no_reserve), GNUNET_PQ_result_spec_bool ("conflict", in_conflict), GNUNET_PQ_result_spec_end @@ -14561,6 +14573,8 @@ postgres_do_reserve_purse ( const struct TALER_Amount *purse_fee, const struct TALER_ReservePublicKeyP *reserve_pub, bool *in_conflict, + bool *no_reserve, + bool *no_kyc, bool *insufficient_funds) { struct PostgresClosure *pg = cls; @@ -14582,6 +14596,10 @@ postgres_do_reserve_purse ( insufficient_funds), GNUNET_PQ_result_spec_bool ("conflict", in_conflict), + GNUNET_PQ_result_spec_bool ("no_kyc", + no_kyc), + GNUNET_PQ_result_spec_bool ("no_reserve", + no_reserve), GNUNET_PQ_result_spec_end }; diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 707ce311f..a88f34b01 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -5039,6 +5039,8 @@ struct TALER_EXCHANGEDB_Plugin * @param reserve_pub public key of the reserve to credit * @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_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 * @return transaction status code */ @@ -5053,6 +5055,8 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_ReservePublicKeyP *reserve_pub, bool *no_partner, bool *no_balance, + bool *no_reserve, + bool *no_kyc, bool *in_conflict); @@ -5069,6 +5073,8 @@ struct TALER_EXCHANGEDB_Plugin * @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[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_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 * @return transaction status code */ @@ -5082,6 +5088,8 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_Amount *purse_fee, const struct TALER_ReservePublicKeyP *reserve_pub, bool *in_conflict, + bool *no_reserve, + bool *no_kyc, bool *insufficient_funds); diff --git a/src/lib/exchange_api_batch_withdraw.c b/src/lib/exchange_api_batch_withdraw.c index 295695d8f..e5d3c7f9c 100644 --- a/src/lib/exchange_api_batch_withdraw.c +++ b/src/lib/exchange_api_batch_withdraw.c @@ -203,7 +203,7 @@ handle_reserve_batch_withdraw_finished ( wr.details.success.num_coins = wh->num_coins; break; } - case MHD_HTTP_ACCEPTED: + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_uint64 ("payment_target_uuid", diff --git a/src/lib/exchange_api_purse_create_with_merge.c b/src/lib/exchange_api_purse_create_with_merge.c index 32b88c597..af5b554e4 100644 --- a/src/lib/exchange_api_purse_create_with_merge.c +++ b/src/lib/exchange_api_purse_create_with_merge.c @@ -224,6 +224,11 @@ handle_purse_create_with_merge_finished (void *cls, dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.hint = TALER_JSON_get_error_hint (j); break; + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: + /* aka KYC required */ + dr.hr.ec = TALER_JSON_get_error_code (j); + dr.hr.hint = TALER_JSON_get_error_hint (j); + break; case MHD_HTTP_INTERNAL_SERVER_ERROR: dr.hr.ec = TALER_JSON_get_error_code (j); dr.hr.hint = TALER_JSON_get_error_hint (j); diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c index 6bb579c2c..b8ab1df44 100644 --- a/src/lib/exchange_api_withdraw.c +++ b/src/lib/exchange_api_withdraw.c @@ -157,7 +157,7 @@ handle_reserve_withdraw_finished ( wr.details.success.exchange_vals = wh->alg_values; break; } - case MHD_HTTP_ACCEPTED: + case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_uint64 ("payment_target_uuid", diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c index 13244993b..303c7e11a 100644 --- a/src/testing/test_kyc_api.c +++ b/src/testing/test_kyc_api.c @@ -106,7 +106,7 @@ run (void *cls, "create-reserve-1", "EUR:10", 0, /* age restriction off */ - MHD_HTTP_ACCEPTED), + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), TALER_TESTING_cmd_withdraw_amount ("withdraw-coin-1", "create-reserve-1", "EUR:5", @@ -123,7 +123,7 @@ run (void *cls, "create-reserve-1", "EUR:5", 0, /* age restriction off */ - MHD_HTTP_ACCEPTED), + MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS), TALER_TESTING_cmd_proof_kyc ("proof-kyc", "create-reserve-1", "pass", diff --git a/src/testing/testing_api_cmd_withdraw.c b/src/testing/testing_api_cmd_withdraw.c index 019c08de4..44e295338 100644 --- a/src/testing/testing_api_cmd_withdraw.c +++ b/src/testing/testing_api_cmd_withdraw.c @@ -242,12 +242,12 @@ reserve_withdraw_cb (void *cls, { if (0 != ws->do_retry) { - if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec) + if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec) ws->do_retry--; /* we don't count reserve unknown as failures here */ if ( (0 == wr->hr.http_status) || (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) || (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == wr->hr.ec) || - (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN == wr->hr.ec) || + (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN == wr->hr.ec) || (MHD_HTTP_INTERNAL_SERVER_ERROR == wr->hr.http_status) ) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, @@ -257,7 +257,7 @@ reserve_withdraw_cb (void *cls, /* on DB conflicts, do not use backoff */ if (TALER_EC_GENERIC_DB_SOFT_FAILURE == wr->hr.ec) ws->backoff = GNUNET_TIME_UNIT_ZERO; - else if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != wr->hr.ec) + else if (TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN != wr->hr.ec) ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff); else ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF, |