Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2021, 2022, 2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-exchange-httpd_kyc-wallet.c
18 : * @brief Handle request for wallet for KYC check.
19 : * @author Christian Grothoff
20 : */
21 : #include "taler/platform.h"
22 : #include <gnunet/gnunet_util_lib.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <microhttpd.h>
26 : #include <pthread.h>
27 : #include "taler/taler_json_lib.h"
28 : #include "taler/taler_mhd_lib.h"
29 : #include "taler/taler_kyclogic_lib.h"
30 : #include "taler-exchange-httpd_common_kyc.h"
31 : #include "taler-exchange-httpd_kyc-wallet.h"
32 : #include "taler-exchange-httpd_responses.h"
33 :
34 :
35 : /**
36 : * Context for the request.
37 : */
38 : struct KycRequestContext
39 : {
40 :
41 : /**
42 : * Kept in a DLL.
43 : */
44 : struct KycRequestContext *next;
45 :
46 : /**
47 : * Kept in a DLL.
48 : */
49 : struct KycRequestContext *prev;
50 :
51 : /**
52 : * Handle for legitimization check.
53 : */
54 : struct TEH_LegitimizationCheckHandle *lch;
55 :
56 : /**
57 : * Payto URI of the reserve.
58 : */
59 : struct TALER_NormalizedPayto payto_uri;
60 :
61 : /**
62 : * Request context.
63 : */
64 : struct TEH_RequestContext *rc;
65 :
66 : /**
67 : * Response to return. Note that the response must
68 : * be queued or destroyed by the callee. NULL
69 : * if the legitimization check was successful and the handler should return
70 : * a handler-specific result.
71 : */
72 : struct MHD_Response *response;
73 :
74 : /**
75 : * Public key of the reserve/wallet this is about.
76 : */
77 : struct TALER_NormalizedPaytoHashP h_payto;
78 :
79 : /**
80 : * The wallet's public key
81 : */
82 : union TALER_AccountPublicKeyP wallet_pub;
83 :
84 : /**
85 : * Balance threshold crossed by the wallet.
86 : */
87 : struct TALER_Amount balance;
88 :
89 : /**
90 : * KYC status, with row with the legitimization requirement.
91 : */
92 : struct TALER_EXCHANGEDB_KycStatus kyc;
93 :
94 : /**
95 : * Smallest amount (over any timeframe) that may
96 : * require additional KYC checks (if @a kyc.ok).
97 : */
98 : struct TALER_Amount next_threshold;
99 :
100 : /**
101 : * When do the current KYC rules possibly expire.
102 : * Only valid if @a kyc.ok.
103 : */
104 : struct GNUNET_TIME_Timestamp expiration_date;
105 :
106 : /**
107 : * HTTP status code for @a response, or 0
108 : */
109 : unsigned int http_status;
110 :
111 : };
112 :
113 :
114 : /**
115 : * Kept in a DLL.
116 : */
117 : static struct KycRequestContext *krc_head;
118 :
119 : /**
120 : * Kept in a DLL.
121 : */
122 : static struct KycRequestContext *krc_tail;
123 :
124 :
125 : void
126 21 : TEH_kyc_wallet_cleanup ()
127 : {
128 : struct KycRequestContext *krc;
129 :
130 21 : while (NULL != (krc = krc_head))
131 : {
132 0 : GNUNET_CONTAINER_DLL_remove (krc_head,
133 : krc_tail,
134 : krc);
135 0 : MHD_resume_connection (krc->rc->connection);
136 : }
137 21 : }
138 :
139 :
140 : /**
141 : * Function called to iterate over KYC-relevant
142 : * transaction amounts for a particular time range.
143 : * Returns the wallet balance.
144 : *
145 : * @param cls closure, a `struct KycRequestContext`
146 : * @param limit maximum time-range for which events
147 : * should be fetched (timestamp in the past)
148 : * @param cb function to call on each event found,
149 : * events must be returned in reverse chronological
150 : * order
151 : * @param cb_cls closure for @a cb
152 : */
153 : static enum GNUNET_DB_QueryStatus
154 7 : balance_iterator (void *cls,
155 : struct GNUNET_TIME_Absolute limit,
156 : TALER_EXCHANGEDB_KycAmountCallback cb,
157 : void *cb_cls)
158 : {
159 7 : struct KycRequestContext *krc = cls;
160 : enum GNUNET_GenericReturnValue ret;
161 :
162 : (void) limit;
163 7 : ret = cb (cb_cls,
164 7 : &krc->balance,
165 : GNUNET_TIME_absolute_get ());
166 7 : GNUNET_break (GNUNET_SYSERR != ret);
167 7 : if (GNUNET_OK != ret)
168 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
169 7 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
170 : }
171 :
172 :
173 : /**
174 : * Function called with the result of a legitimization
175 : * check.
176 : *
177 : * @param cls must be a `struct KycRequestContext *`
178 : * @param lcr legitimization check result
179 : */
180 : static void
181 7 : legi_result_cb (
182 : void *cls,
183 : const struct TEH_LegitimizationCheckResult *lcr)
184 : {
185 7 : struct KycRequestContext *krc = cls;
186 :
187 7 : TEH_plugin->preflight (TEH_plugin->cls);
188 7 : krc->lch = NULL;
189 7 : krc->http_status = lcr->http_status;
190 7 : krc->response = lcr->response;
191 7 : krc->kyc = lcr->kyc;
192 7 : krc->next_threshold = lcr->next_threshold;
193 7 : krc->expiration_date = lcr->expiration_date;
194 7 : GNUNET_CONTAINER_DLL_remove (krc_head,
195 : krc_tail,
196 : krc);
197 7 : MHD_resume_connection (krc->rc->connection);
198 7 : TALER_MHD_daemon_trigger ();
199 7 : }
200 :
201 :
202 : /**
203 : * Function to clean up our rh_ctx in @a rc
204 : *
205 : * @param[in,out] rc context to clean up
206 : */
207 : static void
208 7 : krc_cleaner (struct TEH_RequestContext *rc)
209 : {
210 7 : struct KycRequestContext *krc = rc->rh_ctx;
211 :
212 7 : if (NULL != krc->lch)
213 : {
214 0 : TEH_legitimization_check_cancel (krc->lch);
215 0 : krc->lch = NULL;
216 : }
217 7 : GNUNET_free (krc->payto_uri.normalized_payto);
218 7 : GNUNET_free (krc);
219 7 : }
220 :
221 :
222 : MHD_RESULT
223 14 : TEH_handler_kyc_wallet (
224 : struct TEH_RequestContext *rc,
225 : const json_t *root,
226 : const char *const args[])
227 : {
228 14 : struct KycRequestContext *krc = rc->rh_ctx;
229 :
230 14 : if (NULL == krc)
231 : {
232 7 : krc = GNUNET_new (struct KycRequestContext);
233 7 : krc->rc = rc;
234 7 : rc->rh_ctx = krc;
235 7 : rc->rh_cleaner = &krc_cleaner;
236 : {
237 : struct TALER_ReserveSignatureP reserve_sig;
238 : struct GNUNET_JSON_Specification spec[] = {
239 7 : GNUNET_JSON_spec_fixed_auto ("reserve_sig",
240 : &reserve_sig),
241 7 : GNUNET_JSON_spec_fixed_auto ("reserve_pub",
242 : &krc->wallet_pub.reserve_pub),
243 7 : TALER_JSON_spec_amount ("balance",
244 : TEH_currency,
245 : &krc->balance),
246 7 : GNUNET_JSON_spec_end ()
247 : };
248 : enum GNUNET_GenericReturnValue ret;
249 :
250 : (void) args;
251 7 : ret = TALER_MHD_parse_json_data (rc->connection,
252 : root,
253 : spec);
254 7 : if (GNUNET_SYSERR == ret)
255 0 : return MHD_NO; /* hard failure */
256 7 : if (GNUNET_NO == ret)
257 0 : return MHD_YES; /* failure */
258 :
259 7 : TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
260 7 : if (GNUNET_OK !=
261 7 : TALER_wallet_account_setup_verify (
262 7 : &krc->wallet_pub.reserve_pub,
263 7 : &krc->balance,
264 : &reserve_sig))
265 : {
266 0 : GNUNET_break_op (0);
267 0 : return TALER_MHD_reply_with_error (
268 : rc->connection,
269 : MHD_HTTP_FORBIDDEN,
270 : TALER_EC_EXCHANGE_KYC_WALLET_SIGNATURE_INVALID,
271 : NULL);
272 : }
273 : }
274 : krc->payto_uri
275 7 : = TALER_reserve_make_payto (TEH_base_url,
276 7 : &krc->wallet_pub.reserve_pub);
277 7 : TALER_normalized_payto_hash (krc->payto_uri,
278 : &krc->h_payto);
279 7 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
280 : "h_payto of wallet %s is %s\n",
281 : krc->payto_uri.normalized_payto,
282 : TALER_B2S (&krc->h_payto));
283 : {
284 : struct TALER_FullPayto fake_full_payto;
285 :
286 7 : GNUNET_asprintf (&fake_full_payto.full_payto,
287 : "%s?receiver-name=wallet",
288 : krc->payto_uri.normalized_payto);
289 14 : krc->lch = TEH_legitimization_check (
290 7 : &rc->async_scope_id,
291 : TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
292 : fake_full_payto,
293 7 : &krc->h_payto,
294 7 : &krc->wallet_pub,
295 : &balance_iterator,
296 : krc,
297 : &legi_result_cb,
298 : krc);
299 7 : GNUNET_free (fake_full_payto.full_payto);
300 : }
301 7 : GNUNET_assert (NULL != krc->lch);
302 7 : MHD_suspend_connection (rc->connection);
303 7 : GNUNET_CONTAINER_DLL_insert (krc_head,
304 : krc_tail,
305 : krc);
306 7 : return MHD_YES;
307 : }
308 7 : if (NULL != krc->response)
309 0 : return MHD_queue_response (rc->connection,
310 : krc->http_status,
311 : krc->response);
312 7 : if (krc->kyc.ok)
313 : {
314 2 : bool have_ts
315 2 : = TALER_amount_is_valid (&krc->next_threshold);
316 :
317 : /* KYC not required or already satisfied */
318 2 : return TALER_MHD_REPLY_JSON_PACK (
319 : rc->connection,
320 : MHD_HTTP_OK,
321 : GNUNET_JSON_pack_timestamp ("expiration_time",
322 : krc->expiration_date),
323 : GNUNET_JSON_pack_allow_null (
324 : TALER_JSON_pack_amount ("next_threshold",
325 : have_ts
326 : ? &krc->next_threshold
327 : : NULL)));
328 : }
329 5 : return TEH_RESPONSE_reply_kyc_required (rc->connection,
330 5 : &krc->h_payto,
331 5 : &krc->kyc,
332 : false);
333 : }
334 :
335 :
336 : /* end of taler-exchange-httpd_kyc-wallet.c */
|