exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit eff2f2cca6b14db6b983e7235a7b5379a88636a1
parent f18c25828e6f7175b0a26f76048fb86611cadd08
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun,  5 May 2024 10:51:24 +0200

get exchange_api_kyc_check.c to build again

Diffstat:
Msrc/include/taler_crypto_lib.h | 29+++++++++++++++++++++++++++++
Msrc/include/taler_exchangedb_plugin.h | 2+-
Msrc/include/taler_extensions_policy.h | 1-
Msrc/include/taler_json_lib.h | 44++++++++++++++++----------------------------
Msrc/include/taler_kyclogic_lib.h | 17+++++++++++------
Msrc/json/json_helper.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/exchange_api_kyc_check.c | 199++++++++++++++++++++++++++++++++++---------------------------------------------
7 files changed, 238 insertions(+), 150 deletions(-)

diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h @@ -1822,6 +1822,35 @@ struct TALER_FreshCoin }; +/** + * Details about an encrypted contract. + */ +struct TALER_EncryptedContract +{ + + /** + * Signature of the client affiming this encrypted contract. + */ + struct TALER_PurseContractSignatureP econtract_sig; + + /** + * Contract decryption key for the purse. + */ + struct TALER_ContractDiffiePublicP contract_pub; + + /** + * Encrypted contract, can be NULL. + */ + void *econtract; + + /** + * Number of bytes in @e econtract. + */ + size_t econtract_size; + +}; + + GNUNET_NETWORK_STRUCT_BEGIN /** diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h @@ -25,7 +25,7 @@ #include <jansson.h> #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_db_lib.h> -#include "taler_json_lib.h" +#include "taler_util.h" #include "taler_signatures.h" #include "taler_extensions_policy.h" diff --git a/src/include/taler_extensions_policy.h b/src/include/taler_extensions_policy.h @@ -23,7 +23,6 @@ #include <gnunet/gnunet_util_lib.h> #include "taler_util.h" -#include "taler_json_lib.h" #include "taler_mhd_lib.h" /* diff --git a/src/include/taler_json_lib.h b/src/include/taler_json_lib.h @@ -26,6 +26,7 @@ #include <gnunet/gnunet_json_lib.h> #include <gnunet/gnunet_curl_lib.h> #include "taler_util.h" +#include "taler_kyclogic_lib.h" #include "taler_error_codes.h" /** @@ -33,34 +34,6 @@ */ #define TALER_JSON_LIB_VERSION 0x00020000 -/** - * Details about an encrypted contract. - */ -struct TALER_EncryptedContract -{ - - /** - * Signature of the client affiming this encrypted contract. - */ - struct TALER_PurseContractSignatureP econtract_sig; - - /** - * Contract decryption key for the purse. - */ - struct TALER_ContractDiffiePublicP contract_pub; - - /** - * Encrypted contract, can be NULL. - */ - void *econtract; - - /** - * Number of bytes in @e econtract. - */ - size_t econtract_size; - -}; - /** * Print JSON parsing related error information @@ -341,6 +314,21 @@ TALER_JSON_spec_otp_type (const char *name, /** + * Provide specification to parse a KYC trigger + * event type. + * The value could be provided as an integer or + * as a descriptive string. + * + * @param name name of the OTP method type in the JSON + * @param[out] kte where to store the operation type + * @return spec for parsing trigger event type + */ +struct GNUNET_JSON_Specification +TALER_JSON_spec_kycte (const char *name, + enum TALER_KYCLOGIC_KycTriggerEvent *kte); + + +/** * Generate specification to parse all fees for * a denomination under a prefix @a pfx. * diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h @@ -34,34 +34,39 @@ enum TALER_KYCLOGIC_KycTriggerEvent { /** + * Reserved value for invalid event types. + */ + TALER_KYCLOGIC_KYC_TRIGGER_NONE = 0, + + /** * Customer withdraws coins. */ - TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW = 0, + TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW = 1, /** * Merchant deposits coins. */ - TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT = 1, + TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT = 2, /** * Wallet receives P2P payment. */ - TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE = 2, + TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE = 3, /** * Wallet balance exceeds threshold. */ - TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE = 3, + TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE = 4, /** * Reserve is being closed by force. */ - TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE = 4, + TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE = 5, /** * Customer withdraws coins via age-withdraw. */ - TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW = 5, + TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW = 6, }; diff --git a/src/json/json_helper.c b/src/json/json_helper.c @@ -1441,4 +1441,100 @@ TALER_JSON_spec_otp_type (const char *name, } +/** + * Parse given JSON object to `enum TALER_KYCLOGIC_KycTriggerEvent` + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static enum GNUNET_GenericReturnValue +parse_kycte (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + static const struct Entry + { + const char *name; + enum TALER_KYCLOGIC_KycTriggerEvent val; + } lt [] = { + { .name = "NONE", + .val = TALER_KYCLOGIC_KYC_TRIGGER_NONE }, + { .name = "WITHDRAW", + .val = TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW }, + { .name = "DEPOSIT", + .val = TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT }, + { .name = "P2P-RECEIVE", + .val = TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE }, + { .name = "WALLET-BALANCE", + .val = TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE }, + { .name = "RESERVE-CLOSE", + .val = TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE }, + { .name = "AGE-WITHDRAW", + .val = TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW }, + { .name = NULL, + .val = TALER_KYCLOGIC_KYC_TRIGGER_NONE }, + }; + enum TALER_KYCLOGIC_KycTriggerEvent *res + = (enum TALER_KYCLOGIC_KycTriggerEvent *) spec->ptr; + + (void) cls; + if (json_is_string (root)) + { + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (0 == strcasecmp (str, + lt[i].name)) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + } + if (json_is_integer (root)) + { + json_int_t val; + + val = json_integer_value (root); + for (unsigned int i = 0; NULL != lt[i].name; i++) + { + if (val == lt[i].val) + { + *res = lt[i].val; + return GNUNET_OK; + } + } + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + GNUNET_break_op (0); + return GNUNET_SYSERR; +} + + +struct GNUNET_JSON_Specification +TALER_JSON_spec_kycte (const char *name, + enum TALER_KYCLOGIC_KycTriggerEvent *kte) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_kycte, + .field = name, + .ptr = kte + }; + + *kte = TALER_KYCLOGIC_KYC_TRIGGER_NONE; + return ret; +} + + /* end of json/json_helper.c */ diff --git a/src/lib/exchange_api_kyc_check.c b/src/lib/exchange_api_kyc_check.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2021-2023 Taler Systems SA + Copyright (C) 2021-2024 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -42,11 +42,6 @@ struct TALER_EXCHANGE_KycCheckHandle char *url; /** - * Keys of the exchange. - */ - struct TALER_EXCHANGE_Keys *keys; - - /** * Handle for the request. */ struct GNUNET_CURL_Job *job; @@ -61,14 +56,83 @@ struct TALER_EXCHANGE_KycCheckHandle */ void *cb_cls; - /** - * Hash of the payto:// URL that is being KYC'ed. - */ - struct TALER_PaytoHashP h_payto; - }; +static enum GNUNET_GenericReturnValue +parse_account_status (struct TALER_EXCHANGE_KycCheckHandle *kch, + const json_t *j, + struct TALER_EXCHANGE_KycStatus *ks, + struct TALER_EXCHANGE_AccountKycStatus *aks) +{ + const json_t *limits = NULL; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_bool ("aml_review", + &aks->aml_review), + GNUNET_JSON_spec_fixed_auto ("access_token", + &aks->access_token), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_array_const ("limits", + &limits), + NULL), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (j, + spec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (NULL != limits) && + (0 != json_array_size (limits)) ) + { + size_t als = json_array_size (limits); + struct TALER_EXCHANGE_AccountLimit ala[GNUNET_NZL (als)]; + size_t i; + json_t *limit; + + json_array_foreach (limits, i, limit) + { + struct TALER_EXCHANGE_AccountLimit *al = &ala[i]; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_bool ("soft_limit", + &al->soft_limit), + GNUNET_JSON_spec_relative_time ("timeframe", + &al->timeframe), + TALER_JSON_spec_kycte ("operation_type", + &al->operation_type), + TALER_JSON_spec_amount_any ("threshold", + &al->threshold), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (limit, + ispec, + NULL, NULL)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + aks->limits = ala; + aks->limits_length = als; + kch->cb (kch->cb_cls, + ks); + } + else + { + kch->cb (kch->cb_cls, + ks); + } + GNUNET_JSON_parse_free (spec); + return GNUNET_OK; +} + + /** * Function called when we're done processing the * HTTP /kyc-check request. @@ -96,86 +160,33 @@ handle_kyc_check_finished (void *cls, break; case MHD_HTTP_OK: { - const json_t *kyc_details; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("exchange_sig", - &ks.details.ok.exchange_sig), - GNUNET_JSON_spec_fixed_auto ("exchange_pub", - &ks.details.ok.exchange_pub), - GNUNET_JSON_spec_timestamp ("now", - &ks.details.ok.timestamp), - GNUNET_JSON_spec_object_const ("kyc_details", - &kyc_details), - TALER_JSON_spec_aml_decision ("aml_status", - &ks.details.ok.aml_status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - ks.details.ok.kyc_details = kyc_details; if (GNUNET_OK != - TALER_EXCHANGE_test_signing_key (kch->keys, - &ks.details.ok.exchange_pub)) + parse_account_status (kch, + j, + &ks, + &ks.details.ok)) { GNUNET_break_op (0); ks.http_status = 0; ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); break; } - - if (GNUNET_OK != - TALER_exchange_online_account_setup_success_verify ( - &kch->h_payto, - ks.details.ok.kyc_details, - ks.details.ok.timestamp, - &ks.details.ok.exchange_pub, - &ks.details.ok.exchange_sig)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - GNUNET_JSON_parse_free (spec); - break; - } - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); TALER_EXCHANGE_kyc_check_cancel (kch); return; } case MHD_HTTP_ACCEPTED: { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_web_url ("kyc_url", - &ks.details.accepted.kyc_url), - TALER_JSON_spec_aml_decision ("aml_status", - &ks.details.accepted.aml_status), - GNUNET_JSON_spec_end () - }; - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) + parse_account_status (kch, + j, + &ks, + &ks.details.accepted)) { GNUNET_break_op (0); ks.http_status = 0; ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; break; } - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); TALER_EXCHANGE_kyc_check_cancel (kch); return; } @@ -192,31 +203,6 @@ handle_kyc_check_finished (void *cls, case MHD_HTTP_NOT_FOUND: ks.ec = TALER_JSON_get_error_code (j); break; - case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS: - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_aml_decision ( - "aml_status", - &ks.details.unavailable_for_legal_reasons.aml_status), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (j, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - ks.http_status = 0; - ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE; - break; - } - kch->cb (kch->cb_cls, - &ks); - GNUNET_JSON_parse_free (spec); - TALER_EXCHANGE_kyc_check_cancel (kch); - return; - } case MHD_HTTP_INTERNAL_SERVER_ERROR: ks.ec = TALER_JSON_get_error_code (j); /* Server had an internal issue; we should retry, but this API @@ -242,10 +228,8 @@ struct TALER_EXCHANGE_KycCheckHandle * TALER_EXCHANGE_kyc_check ( struct GNUNET_CURL_Context *ctx, const char *url, - struct TALER_EXCHANGE_Keys *keys, uint64_t requirement_row, - const struct TALER_PaytoHashP *h_payto, - enum TALER_KYCLOGIC_KycUserType ut, + const struct GNUNET_CRYPTO_EddsaPrivateKey *pk, struct GNUNET_TIME_Relative timeout, TALER_EXCHANGE_KycStatusCallback cb, void *cb_cls) @@ -255,27 +239,16 @@ TALER_EXCHANGE_kyc_check ( char *arg_str; { - char payto_str[sizeof (*h_payto) * 2]; - char *end; unsigned long long timeout_ms; - end = GNUNET_STRINGS_data_to_string ( - h_payto, - sizeof (*h_payto), - payto_str, - sizeof (payto_str) - 1); - *end = '\0'; timeout_ms = timeout.rel_value_us / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us; GNUNET_asprintf (&arg_str, - "kyc-check/%llu/%s/%s?timeout_ms=%llu", + "kyc-check/%llu?timeout_ms=%llu", (unsigned long long) requirement_row, - payto_str, - TALER_KYCLOGIC_kyc_user_type2s (ut), timeout_ms); } kch = GNUNET_new (struct TALER_EXCHANGE_KycCheckHandle); - kch->h_payto = *h_payto; kch->cb = cb; kch->cb_cls = cb_cls; kch->url = TALER_url_join (url, @@ -295,7 +268,6 @@ TALER_EXCHANGE_kyc_check ( GNUNET_free (kch); return NULL; } - kch->keys = TALER_EXCHANGE_keys_incref (keys); kch->job = GNUNET_CURL_job_add_with_ct_json (ctx, eh, &handle_kyc_check_finished, @@ -312,7 +284,6 @@ TALER_EXCHANGE_kyc_check_cancel (struct TALER_EXCHANGE_KycCheckHandle *kch) GNUNET_CURL_job_cancel (kch->job); kch->job = NULL; } - TALER_EXCHANGE_keys_decref (kch->keys); GNUNET_free (kch->url); GNUNET_free (kch); }