diff options
Diffstat (limited to 'src/stasis/plugin_anastasis_postgres.c')
-rw-r--r-- | src/stasis/plugin_anastasis_postgres.c | 2301 |
1 files changed, 2301 insertions, 0 deletions
diff --git a/src/stasis/plugin_anastasis_postgres.c b/src/stasis/plugin_anastasis_postgres.c new file mode 100644 index 0000000..4aba97c --- /dev/null +++ b/src/stasis/plugin_anastasis_postgres.c | |||
@@ -0,0 +1,2301 @@ | |||
1 | /* | ||
2 | This file is part of Anastasis | ||
3 | Copyright (C) 2020, 2021 Taler Systems SA | ||
4 | |||
5 | Anastasis is free software; you can redistribute it and/or modify it under the | ||
6 | terms of the GNU Lesser General Public License as published by the Free Software | ||
7 | Foundation; either version 3, or (at your option) any later version. | ||
8 | |||
9 | Anastasis 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 General Public License for more details. | ||
12 | |||
13 | You should have received a copy of the GNU General Public License along with | ||
14 | Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> | ||
15 | */ | ||
16 | /** | ||
17 | * @file anastasis/plugin_anastasisdb_postgres.c | ||
18 | * @brief database helper functions for postgres used by the anastasis | ||
19 | * @author Sree Harsha Totakura <sreeharsha@totakura.in> | ||
20 | * @author Christian Grothoff | ||
21 | * @author Marcello Stanisci | ||
22 | */ | ||
23 | #include "platform.h" | ||
24 | #include "anastasis_database_plugin.h" | ||
25 | #include "anastasis_database_lib.h" | ||
26 | #include <taler/taler_pq_lib.h> | ||
27 | |||
28 | /** | ||
29 | * How long do we keep transient accounts open (those that have | ||
30 | * not been paid at all, but are awaiting payment). This puts | ||
31 | * a cap on how long users have to make a payment after a payment | ||
32 | * request was generated. | ||
33 | */ | ||
34 | #define TRANSIENT_LIFETIME GNUNET_TIME_UNIT_WEEKS | ||
35 | |||
36 | /** | ||
37 | * How often do we re-try if we run into a DB serialization error? | ||
38 | */ | ||
39 | #define MAX_RETRIES 3 | ||
40 | |||
41 | |||
42 | /** | ||
43 | * Type of the "cls" argument given to each of the functions in | ||
44 | * our API. | ||
45 | */ | ||
46 | struct PostgresClosure | ||
47 | { | ||
48 | |||
49 | /** | ||
50 | * Postgres connection handle. | ||
51 | */ | ||
52 | struct GNUNET_PQ_Context *conn; | ||
53 | |||
54 | /** | ||
55 | * Underlying configuration. | ||
56 | */ | ||
57 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
58 | |||
59 | /** | ||
60 | * Name of the currently active transaction, NULL if none is active. | ||
61 | */ | ||
62 | const char *transaction_name; | ||
63 | |||
64 | /** | ||
65 | * Currency we accept payments in. | ||
66 | */ | ||
67 | char *currency; | ||
68 | |||
69 | }; | ||
70 | |||
71 | |||
72 | /** | ||
73 | * Drop anastasis tables | ||
74 | * | ||
75 | * @param cls closure our `struct Plugin` | ||
76 | * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure | ||
77 | */ | ||
78 | static int | ||
79 | postgres_drop_tables (void *cls) | ||
80 | { | ||
81 | struct PostgresClosure *pg = cls; | ||
82 | struct GNUNET_PQ_Context *conn; | ||
83 | |||
84 | conn = GNUNET_PQ_connect_with_cfg (pg->cfg, | ||
85 | "stasis-postgres", | ||
86 | "drop", | ||
87 | NULL, | ||
88 | NULL); | ||
89 | if (NULL == conn) | ||
90 | return GNUNET_SYSERR; | ||
91 | GNUNET_PQ_disconnect (conn); | ||
92 | return GNUNET_OK; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Check that the database connection is still up. | ||
98 | * | ||
99 | * @param pg connection to check | ||
100 | */ | ||
101 | static void | ||
102 | check_connection (void *cls) | ||
103 | { | ||
104 | struct PostgresClosure *pg = cls; | ||
105 | |||
106 | GNUNET_PQ_reconnect_if_down (pg->conn); | ||
107 | } | ||
108 | |||
109 | |||
110 | /** | ||
111 | * Do a pre-flight check that we are not in an uncommitted transaction. | ||
112 | * If we are, try to commit the previous transaction and output a warning. | ||
113 | * Does not return anything, as we will continue regardless of the outcome. | ||
114 | * | ||
115 | * @param cls the `struct PostgresClosure` with the plugin-specific state | ||
116 | */ | ||
117 | static void | ||
118 | postgres_preflight (void *cls) | ||
119 | { | ||
120 | struct PostgresClosure *pg = cls; | ||
121 | struct GNUNET_PQ_ExecuteStatement es[] = { | ||
122 | GNUNET_PQ_make_execute ("COMMIT"), | ||
123 | GNUNET_PQ_EXECUTE_STATEMENT_END | ||
124 | }; | ||
125 | |||
126 | if (NULL == pg->transaction_name) | ||
127 | return; /* all good */ | ||
128 | if (GNUNET_OK == | ||
129 | GNUNET_PQ_exec_statements (pg->conn, | ||
130 | es)) | ||
131 | { | ||
132 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
133 | "BUG: Preflight check committed transaction `%s'!\n", | ||
134 | pg->transaction_name); | ||
135 | } | ||
136 | else | ||
137 | { | ||
138 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
139 | "BUG: Preflight check failed to commit transaction `%s'!\n", | ||
140 | pg->transaction_name); | ||
141 | } | ||
142 | pg->transaction_name = NULL; | ||
143 | } | ||
144 | |||
145 | |||
146 | /** | ||
147 | * Start a transaction. | ||
148 | * | ||
149 | * @param cls the `struct PostgresClosure` with the plugin-specific state | ||
150 | * @param name unique name identifying the transaction (for debugging), | ||
151 | * must point to a constant | ||
152 | * @return #GNUNET_OK on success | ||
153 | */ | ||
154 | static int | ||
155 | begin_transaction (void *cls, | ||
156 | const char *name) | ||
157 | { | ||
158 | struct PostgresClosure *pg = cls; | ||
159 | struct GNUNET_PQ_ExecuteStatement es[] = { | ||
160 | GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"), | ||
161 | GNUNET_PQ_EXECUTE_STATEMENT_END | ||
162 | }; | ||
163 | |||
164 | check_connection (pg); | ||
165 | postgres_preflight (pg); | ||
166 | pg->transaction_name = name; | ||
167 | if (GNUNET_OK != | ||
168 | GNUNET_PQ_exec_statements (pg->conn, | ||
169 | es)) | ||
170 | { | ||
171 | TALER_LOG_ERROR ("Failed to start transaction\n"); | ||
172 | GNUNET_break (0); | ||
173 | return GNUNET_SYSERR; | ||
174 | } | ||
175 | return GNUNET_OK; | ||
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * Roll back the current transaction of a database connection. | ||
181 | * | ||
182 | * @param cls the `struct PostgresClosure` with the plugin-specific state | ||
183 | * @return #GNUNET_OK on success | ||
184 | */ | ||
185 | static void | ||
186 | rollback (void *cls) | ||
187 | { | ||
188 | struct PostgresClosure *pg = cls; | ||
189 | struct GNUNET_PQ_ExecuteStatement es[] = { | ||
190 | GNUNET_PQ_make_execute ("ROLLBACK"), | ||
191 | GNUNET_PQ_EXECUTE_STATEMENT_END | ||
192 | }; | ||
193 | |||
194 | if (GNUNET_OK != | ||
195 | GNUNET_PQ_exec_statements (pg->conn, | ||
196 | es)) | ||
197 | { | ||
198 | TALER_LOG_ERROR ("Failed to rollback transaction\n"); | ||
199 | GNUNET_break (0); | ||
200 | } | ||
201 | pg->transaction_name = NULL; | ||
202 | } | ||
203 | |||
204 | |||
205 | /** | ||
206 | * Commit the current transaction of a database connection. | ||
207 | * | ||
208 | * @param cls the `struct PostgresClosure` with the plugin-specific state | ||
209 | * @return transaction status code | ||
210 | */ | ||
211 | static enum GNUNET_DB_QueryStatus | ||
212 | commit_transaction (void *cls) | ||
213 | { | ||
214 | struct PostgresClosure *pg = cls; | ||
215 | enum GNUNET_DB_QueryStatus qs; | ||
216 | struct GNUNET_PQ_QueryParam no_params[] = { | ||
217 | GNUNET_PQ_query_param_end | ||
218 | }; | ||
219 | |||
220 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
221 | "do_commit", | ||
222 | no_params); | ||
223 | pg->transaction_name = NULL; | ||
224 | return qs; | ||
225 | } | ||
226 | |||
227 | |||
228 | /** | ||
229 | * Function called to perform "garbage collection" on the | ||
230 | * database, expiring records we no longer require. Deletes | ||
231 | * all user records that are not paid up (and by cascade deletes | ||
232 | * the associated recovery documents). Also deletes expired | ||
233 | * truth and financial records older than @a fin_expire. | ||
234 | * | ||
235 | * @param cls closure | ||
236 | * @param expire_backups backups older than the given time stamp should be garbage collected | ||
237 | * @param expire_pending_payments payments still pending from since before | ||
238 | * this value should be garbage collected | ||
239 | * @return transaction status | ||
240 | */ | ||
241 | static enum GNUNET_DB_QueryStatus | ||
242 | postgres_gc (void *cls, | ||
243 | struct GNUNET_TIME_Absolute expire_backups, | ||
244 | struct GNUNET_TIME_Absolute expire_pending_payments) | ||
245 | { | ||
246 | struct PostgresClosure *pg = cls; | ||
247 | struct GNUNET_PQ_QueryParam params[] = { | ||
248 | GNUNET_PQ_query_param_absolute_time (&expire_backups), | ||
249 | GNUNET_PQ_query_param_end | ||
250 | }; | ||
251 | struct GNUNET_PQ_QueryParam params2[] = { | ||
252 | GNUNET_PQ_query_param_absolute_time (&expire_pending_payments), | ||
253 | GNUNET_PQ_query_param_end | ||
254 | }; | ||
255 | enum GNUNET_DB_QueryStatus qs; | ||
256 | |||
257 | check_connection (pg); | ||
258 | postgres_preflight (pg); | ||
259 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
260 | "gc_accounts", | ||
261 | params); | ||
262 | if (qs < 0) | ||
263 | return qs; | ||
264 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
265 | "gc_recdoc_pending_payments", | ||
266 | params2); | ||
267 | } | ||
268 | |||
269 | |||
270 | /** | ||
271 | * Store encrypted recovery document. | ||
272 | * | ||
273 | * @param cls closure | ||
274 | * @param anastasis_pub public key of the user's account | ||
275 | * @param account_sig signature affirming storage request | ||
276 | * @param data_hash hash of @a data | ||
277 | * @param data contains encrypted_recovery_document | ||
278 | * @param data_size size of data blob | ||
279 | * @param payment_secret identifier for the payment, used to later charge on uploads | ||
280 | * @param[out] version set to the version assigned to the document by the database | ||
281 | * @return transaction status, 0 if upload could not be finished because @a payment_secret | ||
282 | * did not have enough upload left; HARD error if @a payment_secret is unknown, ... | ||
283 | */ | ||
284 | static enum ANASTASIS_DB_StoreStatus | ||
285 | postgres_store_recovery_document ( | ||
286 | void *cls, | ||
287 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
288 | const struct ANASTASIS_AccountSignatureP *account_sig, | ||
289 | const struct GNUNET_HashCode *recovery_data_hash, | ||
290 | const void *recovery_data, | ||
291 | size_t recovery_data_size, | ||
292 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
293 | uint32_t *version) | ||
294 | { | ||
295 | struct PostgresClosure *pg = cls; | ||
296 | enum GNUNET_DB_QueryStatus qs; | ||
297 | |||
298 | check_connection (pg); | ||
299 | postgres_preflight (pg); | ||
300 | for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) | ||
301 | { | ||
302 | if (GNUNET_OK != | ||
303 | begin_transaction (pg, | ||
304 | "store_recovery_document")) | ||
305 | { | ||
306 | GNUNET_break (0); | ||
307 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
308 | } | ||
309 | /* get the current version and hash of the latest recovery document | ||
310 | for this account */ | ||
311 | { | ||
312 | struct GNUNET_HashCode dh; | ||
313 | struct GNUNET_PQ_QueryParam params[] = { | ||
314 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
315 | GNUNET_PQ_query_param_end | ||
316 | }; | ||
317 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
318 | GNUNET_PQ_result_spec_uint32 ("version", | ||
319 | version), | ||
320 | GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", | ||
321 | &dh), | ||
322 | GNUNET_PQ_result_spec_end | ||
323 | }; | ||
324 | |||
325 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
326 | "latest_recovery_version_select", | ||
327 | params, | ||
328 | rs); | ||
329 | switch (qs) | ||
330 | { | ||
331 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
332 | rollback (pg); | ||
333 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
334 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
335 | goto retry; | ||
336 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
337 | *version = 1; | ||
338 | break; | ||
339 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
340 | /* had an existing recovery_data, is it identical? */ | ||
341 | if (0 == GNUNET_memcmp (&dh, | ||
342 | recovery_data_hash)) | ||
343 | { | ||
344 | /* Yes. Previous identical recovery data exists */ | ||
345 | rollback (pg); | ||
346 | return ANASTASIS_DB_STORE_STATUS_NO_RESULTS; | ||
347 | } | ||
348 | (*version)++; | ||
349 | break; | ||
350 | default: | ||
351 | rollback (pg); | ||
352 | return qs; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | /* First, check if account exists */ | ||
357 | { | ||
358 | struct GNUNET_PQ_QueryParam params[] = { | ||
359 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
360 | GNUNET_PQ_query_param_end | ||
361 | }; | ||
362 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
363 | GNUNET_PQ_result_spec_end | ||
364 | }; | ||
365 | |||
366 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
367 | "user_select", | ||
368 | params, | ||
369 | rs); | ||
370 | } | ||
371 | switch (qs) | ||
372 | { | ||
373 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
374 | rollback (pg); | ||
375 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
376 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
377 | goto retry; | ||
378 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
379 | rollback (pg); | ||
380 | return ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED; | ||
381 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
382 | /* handle interesting case below */ | ||
383 | break; | ||
384 | } | ||
385 | |||
386 | { | ||
387 | uint32_t postcounter; | ||
388 | |||
389 | /* lookup if the user has enough uploads left and decrement */ | ||
390 | { | ||
391 | struct GNUNET_PQ_QueryParam params[] = { | ||
392 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
393 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
394 | GNUNET_PQ_query_param_end | ||
395 | }; | ||
396 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
397 | GNUNET_PQ_result_spec_uint32 ("post_counter", | ||
398 | &postcounter), | ||
399 | GNUNET_PQ_result_spec_end | ||
400 | }; | ||
401 | |||
402 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
403 | "postcounter_select", | ||
404 | params, | ||
405 | rs); | ||
406 | switch (qs) | ||
407 | { | ||
408 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
409 | rollback (pg); | ||
410 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
411 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
412 | goto retry; | ||
413 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
414 | rollback (pg); | ||
415 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
416 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
417 | break; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | if (0 == postcounter) | ||
422 | { | ||
423 | rollback (pg); | ||
424 | return ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED; | ||
425 | } | ||
426 | /* Decrement the postcounter by one */ | ||
427 | postcounter--; | ||
428 | |||
429 | /* Update the postcounter in the Database */ | ||
430 | { | ||
431 | struct GNUNET_PQ_QueryParam params[] = { | ||
432 | GNUNET_PQ_query_param_uint32 (&postcounter), | ||
433 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
434 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
435 | GNUNET_PQ_query_param_end | ||
436 | }; | ||
437 | |||
438 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
439 | "postcounter_update", | ||
440 | params); | ||
441 | switch (qs) | ||
442 | { | ||
443 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
444 | rollback (pg); | ||
445 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
446 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
447 | goto retry; | ||
448 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
449 | GNUNET_break (0); | ||
450 | rollback (pg); | ||
451 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
452 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
453 | break; | ||
454 | default: | ||
455 | rollback (pg); | ||
456 | return qs; | ||
457 | } | ||
458 | } | ||
459 | } | ||
460 | |||
461 | /* finally, actually insert the recovery document */ | ||
462 | { | ||
463 | struct GNUNET_PQ_QueryParam params[] = { | ||
464 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
465 | GNUNET_PQ_query_param_uint32 (version), | ||
466 | GNUNET_PQ_query_param_auto_from_type (account_sig), | ||
467 | GNUNET_PQ_query_param_auto_from_type (recovery_data_hash), | ||
468 | GNUNET_PQ_query_param_fixed_size (recovery_data, | ||
469 | recovery_data_size), | ||
470 | GNUNET_PQ_query_param_end | ||
471 | }; | ||
472 | |||
473 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
474 | "recovery_document_insert", | ||
475 | params); | ||
476 | switch (qs) | ||
477 | { | ||
478 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
479 | rollback (pg); | ||
480 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
481 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
482 | goto retry; | ||
483 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
484 | GNUNET_break (0); | ||
485 | rollback (pg); | ||
486 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
487 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
488 | qs = commit_transaction (pg); | ||
489 | if (GNUNET_DB_STATUS_SOFT_ERROR == qs) | ||
490 | goto retry; | ||
491 | if (qs < 0) | ||
492 | return ANASTASIS_DB_STORE_STATUS_HARD_ERROR; | ||
493 | return ANASTASIS_DB_STORE_STATUS_SUCCESS; | ||
494 | } | ||
495 | } | ||
496 | retry: | ||
497 | rollback (pg); | ||
498 | } | ||
499 | return ANASTASIS_DB_STORE_STATUS_SOFT_ERROR; | ||
500 | } | ||
501 | |||
502 | |||
503 | /** | ||
504 | * Increment account lifetime. | ||
505 | * | ||
506 | * @param cls closure | ||
507 | * @param anastasis_pub which account received a payment | ||
508 | * @param payment_identifier proof of payment, must be unique and match pending payment | ||
509 | * @param lifetime for how long is the account now paid (increment) | ||
510 | * @param[out] paid_until set to the end of the lifetime after the operation | ||
511 | * @return transaction status | ||
512 | */ | ||
513 | static enum GNUNET_DB_QueryStatus | ||
514 | postgres_increment_lifetime ( | ||
515 | void *cls, | ||
516 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
517 | const struct ANASTASIS_PaymentSecretP *payment_identifier, | ||
518 | struct GNUNET_TIME_Relative lifetime, | ||
519 | struct GNUNET_TIME_Absolute *paid_until) | ||
520 | { | ||
521 | struct PostgresClosure *pg = cls; | ||
522 | enum GNUNET_DB_QueryStatus qs; | ||
523 | |||
524 | check_connection (pg); | ||
525 | for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) | ||
526 | { | ||
527 | if (GNUNET_OK != | ||
528 | begin_transaction (pg, | ||
529 | "increment lifetime")) | ||
530 | { | ||
531 | GNUNET_break (0); | ||
532 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
533 | } | ||
534 | |||
535 | { | ||
536 | struct GNUNET_PQ_QueryParam params[] = { | ||
537 | GNUNET_PQ_query_param_auto_from_type (payment_identifier), | ||
538 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
539 | GNUNET_PQ_query_param_end | ||
540 | }; | ||
541 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
542 | "recdoc_payment_done", | ||
543 | params); | ||
544 | switch (qs) | ||
545 | { | ||
546 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
547 | rollback (pg); | ||
548 | *paid_until = GNUNET_TIME_UNIT_ZERO_ABS; | ||
549 | return qs; | ||
550 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
551 | goto retry; | ||
552 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
553 | /* continued below */ | ||
554 | break; | ||
555 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
556 | /* continued below */ | ||
557 | break; | ||
558 | } | ||
559 | } | ||
560 | |||
561 | { | ||
562 | enum GNUNET_DB_QueryStatus qs2; | ||
563 | struct GNUNET_PQ_QueryParam params[] = { | ||
564 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
565 | GNUNET_PQ_query_param_end | ||
566 | }; | ||
567 | struct GNUNET_TIME_Absolute expiration; | ||
568 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
569 | GNUNET_PQ_result_spec_absolute_time ("expiration_date", | ||
570 | &expiration), | ||
571 | GNUNET_PQ_result_spec_end | ||
572 | }; | ||
573 | |||
574 | qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
575 | "user_select", | ||
576 | params, | ||
577 | rs); | ||
578 | switch (qs2) | ||
579 | { | ||
580 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
581 | rollback (pg); | ||
582 | return qs2; | ||
583 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
584 | goto retry; | ||
585 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
586 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
587 | { | ||
588 | /* inconsistent, cannot have recdoc payment but no user!? */ | ||
589 | GNUNET_break (0); | ||
590 | rollback (pg); | ||
591 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
592 | } | ||
593 | else | ||
594 | { | ||
595 | /* user does not exist, create new one */ | ||
596 | struct GNUNET_PQ_QueryParam params[] = { | ||
597 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
598 | GNUNET_PQ_query_param_absolute_time (&expiration), | ||
599 | GNUNET_PQ_query_param_end | ||
600 | }; | ||
601 | |||
602 | expiration = GNUNET_TIME_relative_to_absolute (lifetime); | ||
603 | GNUNET_break (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != | ||
604 | expiration.abs_value_us); | ||
605 | *paid_until = expiration; | ||
606 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
607 | "user_insert", | ||
608 | params); | ||
609 | } | ||
610 | break; | ||
611 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
612 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
613 | { | ||
614 | /* existing rec doc payment, return expiration */ | ||
615 | *paid_until = expiration; | ||
616 | rollback (pg); | ||
617 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
618 | "Payment existed, lifetime of account %s unchanged at %s\n", | ||
619 | TALER_B2S (anastasis_pub), | ||
620 | GNUNET_STRINGS_absolute_time_to_string (*paid_until)); | ||
621 | return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; | ||
622 | } | ||
623 | else | ||
624 | { | ||
625 | /* user exists, update expiration_date */ | ||
626 | struct GNUNET_PQ_QueryParam params[] = { | ||
627 | GNUNET_PQ_query_param_absolute_time (&expiration), | ||
628 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
629 | GNUNET_PQ_query_param_end | ||
630 | }; | ||
631 | |||
632 | expiration = GNUNET_TIME_absolute_add (expiration, | ||
633 | lifetime); | ||
634 | GNUNET_break (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != | ||
635 | expiration.abs_value_us); | ||
636 | *paid_until = expiration; | ||
637 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
638 | "user_update", | ||
639 | params); | ||
640 | } | ||
641 | break; | ||
642 | } | ||
643 | } | ||
644 | |||
645 | switch (qs) | ||
646 | { | ||
647 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
648 | rollback (pg); | ||
649 | return qs; | ||
650 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
651 | goto retry; | ||
652 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
653 | GNUNET_break (0); | ||
654 | rollback (pg); | ||
655 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
656 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
657 | break; | ||
658 | } | ||
659 | qs = commit_transaction (pg); | ||
660 | if (GNUNET_DB_STATUS_SOFT_ERROR == qs) | ||
661 | goto retry; | ||
662 | if (qs < 0) | ||
663 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
664 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
665 | "Incremented lifetime of account %s to %s\n", | ||
666 | TALER_B2S (anastasis_pub), | ||
667 | GNUNET_STRINGS_absolute_time_to_string (*paid_until)); | ||
668 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; | ||
669 | retry: | ||
670 | rollback (pg); | ||
671 | } | ||
672 | return GNUNET_DB_STATUS_SOFT_ERROR; | ||
673 | } | ||
674 | |||
675 | |||
676 | /** | ||
677 | * Update account lifetime to the maximum of the current | ||
678 | * value and @a eol. | ||
679 | * | ||
680 | * @param cls closure | ||
681 | * @param account_pub which account received a payment | ||
682 | * @param payment_identifier proof of payment, must be unique and match pending payment | ||
683 | * @param eol for how long is the account now paid (absolute) | ||
684 | * @return transaction status | ||
685 | */ | ||
686 | static enum GNUNET_DB_QueryStatus | ||
687 | postgres_update_lifetime ( | ||
688 | void *cls, | ||
689 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
690 | const struct ANASTASIS_PaymentSecretP *payment_identifier, | ||
691 | struct GNUNET_TIME_Absolute eol) | ||
692 | { | ||
693 | struct PostgresClosure *pg = cls; | ||
694 | enum GNUNET_DB_QueryStatus qs; | ||
695 | |||
696 | check_connection (pg); | ||
697 | for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) | ||
698 | { | ||
699 | if (GNUNET_OK != | ||
700 | begin_transaction (pg, | ||
701 | "update lifetime")) | ||
702 | { | ||
703 | GNUNET_break (0); | ||
704 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
705 | } | ||
706 | |||
707 | { | ||
708 | struct GNUNET_PQ_QueryParam params[] = { | ||
709 | GNUNET_PQ_query_param_auto_from_type (payment_identifier), | ||
710 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
711 | GNUNET_PQ_query_param_end | ||
712 | }; | ||
713 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
714 | "recdoc_payment_done", | ||
715 | params); | ||
716 | if (GNUNET_DB_STATUS_SOFT_ERROR == qs) | ||
717 | goto retry; | ||
718 | if (0 >= qs) | ||
719 | { | ||
720 | /* same payment made before, or unknown, or error | ||
721 | => no further action! */ | ||
722 | rollback (pg); | ||
723 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
724 | "Payment existed, lifetime of account %s unchanged\n", | ||
725 | TALER_B2S (anastasis_pub)); | ||
726 | return qs; | ||
727 | } | ||
728 | } | ||
729 | |||
730 | { | ||
731 | struct GNUNET_PQ_QueryParam params[] = { | ||
732 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
733 | GNUNET_PQ_query_param_end | ||
734 | }; | ||
735 | struct GNUNET_TIME_Absolute expiration; | ||
736 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
737 | GNUNET_PQ_result_spec_absolute_time ("expiration_date", | ||
738 | &expiration), | ||
739 | GNUNET_PQ_result_spec_end | ||
740 | }; | ||
741 | |||
742 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
743 | "user_select", | ||
744 | params, | ||
745 | rs); | ||
746 | switch (qs) | ||
747 | { | ||
748 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
749 | rollback (pg); | ||
750 | return qs; | ||
751 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
752 | goto retry; | ||
753 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
754 | { | ||
755 | /* user does not exist, create new one */ | ||
756 | struct GNUNET_PQ_QueryParam params[] = { | ||
757 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
758 | GNUNET_PQ_query_param_absolute_time (&eol), | ||
759 | GNUNET_PQ_query_param_end | ||
760 | }; | ||
761 | |||
762 | GNUNET_break (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != | ||
763 | eol.abs_value_us); | ||
764 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
765 | "user_insert", | ||
766 | params); | ||
767 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
768 | "Created new account %s with expiration %s\n", | ||
769 | TALER_B2S (anastasis_pub), | ||
770 | GNUNET_STRINGS_absolute_time_to_string (eol)); | ||
771 | } | ||
772 | break; | ||
773 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
774 | { | ||
775 | /* user exists, update expiration_date */ | ||
776 | struct GNUNET_PQ_QueryParam params[] = { | ||
777 | GNUNET_PQ_query_param_absolute_time (&expiration), | ||
778 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
779 | GNUNET_PQ_query_param_end | ||
780 | }; | ||
781 | |||
782 | expiration = GNUNET_TIME_absolute_max (expiration, | ||
783 | eol); | ||
784 | GNUNET_break (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us != | ||
785 | expiration.abs_value_us); | ||
786 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
787 | "user_update", | ||
788 | params); | ||
789 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
790 | "Updated account %s to new expiration %s\n", | ||
791 | TALER_B2S (anastasis_pub), | ||
792 | GNUNET_STRINGS_absolute_time_to_string (expiration)); | ||
793 | } | ||
794 | break; | ||
795 | } | ||
796 | } | ||
797 | |||
798 | switch (qs) | ||
799 | { | ||
800 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
801 | rollback (pg); | ||
802 | return qs; | ||
803 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
804 | goto retry; | ||
805 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
806 | GNUNET_break (0); | ||
807 | rollback (pg); | ||
808 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
809 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
810 | break; | ||
811 | } | ||
812 | qs = commit_transaction (pg); | ||
813 | if (GNUNET_DB_STATUS_SOFT_ERROR == qs) | ||
814 | goto retry; | ||
815 | if (qs < 0) | ||
816 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
817 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; | ||
818 | retry: | ||
819 | rollback (pg); | ||
820 | } | ||
821 | return GNUNET_DB_STATUS_SOFT_ERROR; | ||
822 | } | ||
823 | |||
824 | |||
825 | /** | ||
826 | * Store payment. Used to begin a payment, not indicative | ||
827 | * that the payment actually was made. (That is done | ||
828 | * when we increment the account's lifetime.) | ||
829 | * | ||
830 | * @param cls closure | ||
831 | * @param anastasis_pub anastasis's public key | ||
832 | * @param post_counter how many uploads does @a amount pay for | ||
833 | * @param payment_secret payment secret which the user must provide with every upload | ||
834 | * @param amount how much we asked for | ||
835 | * @return transaction status | ||
836 | */ | ||
837 | static enum GNUNET_DB_QueryStatus | ||
838 | postgres_record_recdoc_payment ( | ||
839 | void *cls, | ||
840 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
841 | uint32_t post_counter, | ||
842 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
843 | const struct TALER_Amount *amount) | ||
844 | { | ||
845 | struct PostgresClosure *pg = cls; | ||
846 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
847 | struct GNUNET_TIME_Absolute expiration; | ||
848 | struct GNUNET_PQ_QueryParam params[] = { | ||
849 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
850 | GNUNET_PQ_query_param_uint32 (&post_counter), | ||
851 | TALER_PQ_query_param_amount (amount), | ||
852 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
853 | GNUNET_PQ_query_param_absolute_time (&now), | ||
854 | GNUNET_PQ_query_param_end | ||
855 | }; | ||
856 | enum GNUNET_DB_QueryStatus qs; | ||
857 | |||
858 | check_connection (pg); | ||
859 | postgres_preflight (pg); | ||
860 | |||
861 | /* because of constraint at user_id, first we have to verify | ||
862 | if user exists, and if not, create one */ | ||
863 | { | ||
864 | struct GNUNET_PQ_QueryParam params[] = { | ||
865 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
866 | GNUNET_PQ_query_param_end | ||
867 | }; | ||
868 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
869 | GNUNET_PQ_result_spec_absolute_time ("expiration_date", | ||
870 | &expiration), | ||
871 | GNUNET_PQ_result_spec_end | ||
872 | }; | ||
873 | |||
874 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
875 | "user_select", | ||
876 | params, | ||
877 | rs); | ||
878 | } | ||
879 | switch (qs) | ||
880 | { | ||
881 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
882 | return qs; | ||
883 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
884 | GNUNET_break (0); | ||
885 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
886 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
887 | { | ||
888 | /* create new user with short lifetime */ | ||
889 | struct GNUNET_TIME_Absolute exp | ||
890 | = GNUNET_TIME_relative_to_absolute (TRANSIENT_LIFETIME); | ||
891 | struct GNUNET_PQ_QueryParam params[] = { | ||
892 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
893 | GNUNET_PQ_query_param_absolute_time (&exp), | ||
894 | GNUNET_PQ_query_param_end | ||
895 | }; | ||
896 | |||
897 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
898 | "user_insert", | ||
899 | params); | ||
900 | switch (qs) | ||
901 | { | ||
902 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
903 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
904 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
905 | GNUNET_break (0); | ||
906 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
907 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
908 | GNUNET_break (0); | ||
909 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
910 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
911 | /* successful, continue below */ | ||
912 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
913 | "Created new account %s with transient life until %s\n", | ||
914 | TALER_B2S (anastasis_pub), | ||
915 | GNUNET_STRINGS_absolute_time_to_string (exp)); | ||
916 | break; | ||
917 | } | ||
918 | } | ||
919 | /* continue below */ | ||
920 | break; | ||
921 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
922 | /* handle case below */ | ||
923 | break; | ||
924 | } | ||
925 | |||
926 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
927 | "recdoc_payment_insert", | ||
928 | params); | ||
929 | } | ||
930 | |||
931 | |||
932 | /** | ||
933 | * Record truth upload payment was made. | ||
934 | * | ||
935 | * @param cls closure | ||
936 | * @param uuid the truth's UUID | ||
937 | * @param amount the amount that was paid | ||
938 | * @param duration how long is the truth paid for | ||
939 | * @return transaction status | ||
940 | */ | ||
941 | static enum GNUNET_DB_QueryStatus | ||
942 | postgres_record_truth_upload_payment ( | ||
943 | void *cls, | ||
944 | const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, | ||
945 | const struct TALER_Amount *amount, | ||
946 | struct GNUNET_TIME_Relative duration) | ||
947 | { | ||
948 | struct PostgresClosure *pg = cls; | ||
949 | struct GNUNET_TIME_Absolute exp = GNUNET_TIME_relative_to_absolute (duration); | ||
950 | struct GNUNET_PQ_QueryParam params[] = { | ||
951 | GNUNET_PQ_query_param_auto_from_type (uuid), | ||
952 | TALER_PQ_query_param_amount (amount), | ||
953 | GNUNET_PQ_query_param_absolute_time (&exp), | ||
954 | GNUNET_PQ_query_param_end | ||
955 | }; | ||
956 | |||
957 | check_connection (pg); | ||
958 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
959 | "truth_payment_insert", | ||
960 | params); | ||
961 | } | ||
962 | |||
963 | |||
964 | /** | ||
965 | * Inquire whether truth upload payment was made. | ||
966 | * | ||
967 | * @param cls closure | ||
968 | * @param uuid the truth's UUID | ||
969 | * @param[out] paid_until set for how long this truth is paid for | ||
970 | * @return transaction status | ||
971 | */ | ||
972 | static enum GNUNET_DB_QueryStatus | ||
973 | postgres_check_truth_upload_paid ( | ||
974 | void *cls, | ||
975 | const struct ANASTASIS_CRYPTO_TruthUUIDP *uuid, | ||
976 | struct GNUNET_TIME_Absolute *paid_until) | ||
977 | { | ||
978 | struct PostgresClosure *pg = cls; | ||
979 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
980 | struct GNUNET_PQ_QueryParam params[] = { | ||
981 | GNUNET_PQ_query_param_auto_from_type (uuid), | ||
982 | GNUNET_PQ_query_param_absolute_time (&now), | ||
983 | GNUNET_PQ_query_param_end | ||
984 | }; | ||
985 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
986 | GNUNET_PQ_result_spec_absolute_time ("expiration", | ||
987 | paid_until), | ||
988 | GNUNET_PQ_result_spec_end | ||
989 | }; | ||
990 | |||
991 | check_connection (pg); | ||
992 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
993 | "truth_payment_select", | ||
994 | params, | ||
995 | rs); | ||
996 | } | ||
997 | |||
998 | |||
999 | /** | ||
1000 | * Store payment for challenge. | ||
1001 | * | ||
1002 | * @param cls closure | ||
1003 | * @param truth_key identifier of the challenge to pay | ||
1004 | * @param payment_secret payment secret which the user must provide with every upload | ||
1005 | * @param amount how much we asked for | ||
1006 | * @return transaction status | ||
1007 | */ | ||
1008 | static enum GNUNET_DB_QueryStatus | ||
1009 | postgres_record_challenge_payment ( | ||
1010 | void *cls, | ||
1011 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1012 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
1013 | const struct TALER_Amount *amount) | ||
1014 | { | ||
1015 | struct PostgresClosure *pg = cls; | ||
1016 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
1017 | struct GNUNET_PQ_QueryParam params[] = { | ||
1018 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1019 | TALER_PQ_query_param_amount (amount), | ||
1020 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
1021 | GNUNET_PQ_query_param_absolute_time (&now), | ||
1022 | GNUNET_PQ_query_param_end | ||
1023 | }; | ||
1024 | |||
1025 | check_connection (pg); | ||
1026 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1027 | "challenge_payment_insert", | ||
1028 | params); | ||
1029 | } | ||
1030 | |||
1031 | |||
1032 | /** | ||
1033 | * Store refund granted for challenge. | ||
1034 | * | ||
1035 | * @param cls closure | ||
1036 | * @param truth_key identifier of the challenge to pay | ||
1037 | * @param payment_secret payment secret which the user must provide with every upload | ||
1038 | * @return transaction status | ||
1039 | */ | ||
1040 | static enum GNUNET_DB_QueryStatus | ||
1041 | postgres_record_challenge_refund ( | ||
1042 | void *cls, | ||
1043 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1044 | const struct ANASTASIS_PaymentSecretP *payment_secret) | ||
1045 | { | ||
1046 | struct PostgresClosure *pg = cls; | ||
1047 | struct GNUNET_PQ_QueryParam params[] = { | ||
1048 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
1049 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1050 | GNUNET_PQ_query_param_end | ||
1051 | }; | ||
1052 | |||
1053 | check_connection (pg); | ||
1054 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1055 | "challenge_refund_update", | ||
1056 | params); | ||
1057 | } | ||
1058 | |||
1059 | |||
1060 | /** | ||
1061 | * Check payment identifier. Used to check if a payment identifier given by | ||
1062 | * the user is valid (existing and paid). | ||
1063 | * | ||
1064 | * @param cls closure | ||
1065 | * @param payment_secret payment secret which the user must provide with every upload | ||
1066 | * @param[out] paid bool value to show if payment is paid | ||
1067 | * @param[out] valid_counter bool value to show if post_counter is > 0 | ||
1068 | * @return transaction status | ||
1069 | */ | ||
1070 | static enum GNUNET_DB_QueryStatus | ||
1071 | postgres_check_challenge_payment ( | ||
1072 | void *cls, | ||
1073 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
1074 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1075 | bool *paid) | ||
1076 | { | ||
1077 | struct PostgresClosure *pg = cls; | ||
1078 | uint8_t paid8; | ||
1079 | struct GNUNET_PQ_QueryParam params[] = { | ||
1080 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
1081 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1082 | GNUNET_PQ_query_param_end | ||
1083 | }; | ||
1084 | enum GNUNET_DB_QueryStatus qs; | ||
1085 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1086 | GNUNET_PQ_result_spec_auto_from_type ("paid", | ||
1087 | &paid8), | ||
1088 | GNUNET_PQ_result_spec_end | ||
1089 | }; | ||
1090 | |||
1091 | check_connection (pg); | ||
1092 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1093 | "challenge_payment_select", | ||
1094 | params, | ||
1095 | rs); | ||
1096 | *paid = (0 != paid8); | ||
1097 | return qs; | ||
1098 | } | ||
1099 | |||
1100 | |||
1101 | /** | ||
1102 | * Check payment identifier. Used to check if a payment identifier given by | ||
1103 | * the user is valid (existing and paid). | ||
1104 | * | ||
1105 | * @param cls closure | ||
1106 | * @param payment_secret payment secret which the user must provide with every upload | ||
1107 | * @param[out] paid bool value to show if payment is paid | ||
1108 | * @param[out] valid_counter bool value to show if post_counter is > 0 | ||
1109 | * @return transaction status | ||
1110 | */ | ||
1111 | static enum GNUNET_DB_QueryStatus | ||
1112 | postgres_check_payment_identifier ( | ||
1113 | void *cls, | ||
1114 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
1115 | bool *paid, | ||
1116 | bool *valid_counter) | ||
1117 | { | ||
1118 | struct PostgresClosure *pg = cls; | ||
1119 | uint32_t counter; | ||
1120 | uint8_t paid8; | ||
1121 | struct GNUNET_PQ_QueryParam params[] = { | ||
1122 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
1123 | GNUNET_PQ_query_param_end | ||
1124 | }; | ||
1125 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1126 | GNUNET_PQ_result_spec_auto_from_type ("paid", | ||
1127 | &paid8), | ||
1128 | GNUNET_PQ_result_spec_uint32 ("post_counter", | ||
1129 | &counter), | ||
1130 | GNUNET_PQ_result_spec_end | ||
1131 | }; | ||
1132 | enum GNUNET_DB_QueryStatus qs; | ||
1133 | |||
1134 | check_connection (pg); | ||
1135 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1136 | "recdoc_payment_select", | ||
1137 | params, | ||
1138 | rs); | ||
1139 | |||
1140 | if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) | ||
1141 | { | ||
1142 | if (counter > 0) | ||
1143 | *valid_counter = true; | ||
1144 | else | ||
1145 | *valid_counter = false; | ||
1146 | *paid = (0 != paid8); | ||
1147 | } | ||
1148 | return qs; | ||
1149 | } | ||
1150 | |||
1151 | |||
1152 | /** | ||
1153 | * Upload Truth, which contains the Truth and the KeyShare. | ||
1154 | * | ||
1155 | * @param cls closure | ||
1156 | * @param truth_uuid the identifier for the Truth | ||
1157 | * @param key_share_data contains information of an EncryptedKeyShare | ||
1158 | * @param method name of method | ||
1159 | * @param nonce nonce used to compute encryption key for encrypted_truth | ||
1160 | * @param aes_gcm_tag authentication tag of encrypted_truth | ||
1161 | * @param encrypted_truth contains the encrypted Truth which includes the ground truth i.e. H(challenge answer), phonenumber, SMS | ||
1162 | * @param encrypted_truth_size the size of the Truth | ||
1163 | * @param truth_expiration time till the according data will be stored | ||
1164 | * @return transaction status | ||
1165 | */ | ||
1166 | static enum GNUNET_DB_QueryStatus | ||
1167 | postgres_store_truth ( | ||
1168 | void *cls, | ||
1169 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1170 | const struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share_data, | ||
1171 | const char *mime_type, | ||
1172 | const void *encrypted_truth, | ||
1173 | size_t encrypted_truth_size, | ||
1174 | const char *method, | ||
1175 | struct GNUNET_TIME_Relative truth_expiration) | ||
1176 | { | ||
1177 | struct PostgresClosure *pg = cls; | ||
1178 | struct GNUNET_TIME_Absolute expiration = GNUNET_TIME_absolute_get (); | ||
1179 | struct GNUNET_PQ_QueryParam params[] = { | ||
1180 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1181 | GNUNET_PQ_query_param_auto_from_type (key_share_data), | ||
1182 | GNUNET_PQ_query_param_string (method), | ||
1183 | GNUNET_PQ_query_param_fixed_size (encrypted_truth, | ||
1184 | encrypted_truth_size), | ||
1185 | GNUNET_PQ_query_param_string (mime_type), | ||
1186 | TALER_PQ_query_param_absolute_time (&expiration), | ||
1187 | GNUNET_PQ_query_param_end | ||
1188 | }; | ||
1189 | |||
1190 | |||
1191 | expiration = GNUNET_TIME_absolute_add (expiration, | ||
1192 | truth_expiration); | ||
1193 | GNUNET_TIME_round_abs (&expiration); | ||
1194 | check_connection (pg); | ||
1195 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1196 | "truth_insert", | ||
1197 | params); | ||
1198 | } | ||
1199 | |||
1200 | |||
1201 | /** | ||
1202 | * Get the encrypted truth to validate the challenge response | ||
1203 | * | ||
1204 | * @param cls closure | ||
1205 | * @param truth_uuid the identifier for the Truth | ||
1206 | * @param[out] truth contains the encrypted truth | ||
1207 | * @param[out] truth_size size of the encrypted truth | ||
1208 | * @param[out] truth_mime mime type of truth | ||
1209 | * @param[out] method type of the challenge | ||
1210 | * @return transaction status | ||
1211 | */ | ||
1212 | enum GNUNET_DB_QueryStatus | ||
1213 | postgres_get_escrow_challenge ( | ||
1214 | void *cls, | ||
1215 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1216 | void **truth, | ||
1217 | size_t *truth_size, | ||
1218 | char **truth_mime, | ||
1219 | char **method) | ||
1220 | { | ||
1221 | struct PostgresClosure *pg = cls; | ||
1222 | struct GNUNET_PQ_QueryParam params[] = { | ||
1223 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1224 | GNUNET_PQ_query_param_end | ||
1225 | }; | ||
1226 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1227 | GNUNET_PQ_result_spec_variable_size ("encrypted_truth", | ||
1228 | truth, | ||
1229 | truth_size), | ||
1230 | GNUNET_PQ_result_spec_string ("truth_mime", | ||
1231 | truth_mime), | ||
1232 | GNUNET_PQ_result_spec_string ("method_name", | ||
1233 | method), | ||
1234 | GNUNET_PQ_result_spec_end | ||
1235 | }; | ||
1236 | |||
1237 | check_connection (pg); | ||
1238 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1239 | "truth_select", | ||
1240 | params, | ||
1241 | rs); | ||
1242 | } | ||
1243 | |||
1244 | |||
1245 | /** | ||
1246 | * Lookup (encrypted) key share by @a truth_uuid. | ||
1247 | * | ||
1248 | * @param cls closure | ||
1249 | * @param truth_uuid the identifier for the Truth | ||
1250 | * @param[out] key_share contains the encrypted Keyshare | ||
1251 | * @return transaction status | ||
1252 | */ | ||
1253 | enum GNUNET_DB_QueryStatus | ||
1254 | postgres_get_key_share ( | ||
1255 | void *cls, | ||
1256 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1257 | struct ANASTASIS_CRYPTO_EncryptedKeyShareP *key_share) | ||
1258 | { | ||
1259 | struct PostgresClosure *pg = cls; | ||
1260 | struct GNUNET_PQ_QueryParam params[] = { | ||
1261 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1262 | GNUNET_PQ_query_param_end | ||
1263 | }; | ||
1264 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1265 | GNUNET_PQ_result_spec_auto_from_type ("key_share_data", | ||
1266 | key_share), | ||
1267 | GNUNET_PQ_result_spec_end | ||
1268 | }; | ||
1269 | |||
1270 | check_connection (pg); | ||
1271 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1272 | "key_share_select", | ||
1273 | params, | ||
1274 | rs); | ||
1275 | } | ||
1276 | |||
1277 | |||
1278 | /** | ||
1279 | * Check if an account exists, and if so, return the | ||
1280 | * current @a recovery_document_hash. | ||
1281 | * | ||
1282 | * @param cls closure | ||
1283 | * @param anastasis_pub account identifier | ||
1284 | * @param[out] paid_until until when is the account paid up? | ||
1285 | * @param[out] recovery_data_hash set to hash of @a recovery document | ||
1286 | * @param[out] version set to the recovery policy version | ||
1287 | * @return transaction status | ||
1288 | */ | ||
1289 | enum ANASTASIS_DB_AccountStatus | ||
1290 | postgres_lookup_account ( | ||
1291 | void *cls, | ||
1292 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
1293 | struct GNUNET_TIME_Absolute *paid_until, | ||
1294 | struct GNUNET_HashCode *recovery_data_hash, | ||
1295 | uint32_t *version) | ||
1296 | { | ||
1297 | struct PostgresClosure *pg = cls; | ||
1298 | struct GNUNET_PQ_QueryParam params[] = { | ||
1299 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
1300 | GNUNET_PQ_query_param_end | ||
1301 | }; | ||
1302 | enum GNUNET_DB_QueryStatus qs; | ||
1303 | |||
1304 | check_connection (pg); | ||
1305 | postgres_preflight (pg); | ||
1306 | { | ||
1307 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1308 | GNUNET_PQ_result_spec_absolute_time ("expiration_date", | ||
1309 | paid_until), | ||
1310 | GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", | ||
1311 | recovery_data_hash), | ||
1312 | GNUNET_PQ_result_spec_uint32 ("version", | ||
1313 | version), | ||
1314 | GNUNET_PQ_result_spec_end | ||
1315 | }; | ||
1316 | |||
1317 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1318 | "latest_recovery_version_select", | ||
1319 | params, | ||
1320 | rs); | ||
1321 | } | ||
1322 | switch (qs) | ||
1323 | { | ||
1324 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
1325 | return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; | ||
1326 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
1327 | GNUNET_break (0); | ||
1328 | return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; | ||
1329 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
1330 | break; /* handle interesting case below */ | ||
1331 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
1332 | return ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED; | ||
1333 | } | ||
1334 | |||
1335 | /* check if account exists */ | ||
1336 | { | ||
1337 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1338 | GNUNET_PQ_result_spec_absolute_time ("expiration_date", | ||
1339 | paid_until), | ||
1340 | GNUNET_PQ_result_spec_end | ||
1341 | }; | ||
1342 | |||
1343 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1344 | "user_select", | ||
1345 | params, | ||
1346 | rs); | ||
1347 | } | ||
1348 | switch (qs) | ||
1349 | { | ||
1350 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
1351 | return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; | ||
1352 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
1353 | GNUNET_break (0); | ||
1354 | return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; | ||
1355 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
1356 | /* indicates: no account */ | ||
1357 | return ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; | ||
1358 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
1359 | /* indicates: no backup */ | ||
1360 | *version = UINT32_MAX; | ||
1361 | memset (recovery_data_hash, | ||
1362 | 0, | ||
1363 | sizeof (*recovery_data_hash)); | ||
1364 | return ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS; | ||
1365 | default: | ||
1366 | GNUNET_break (0); | ||
1367 | return ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR; | ||
1368 | } | ||
1369 | } | ||
1370 | |||
1371 | |||
1372 | /** | ||
1373 | * Fetch latest recovery document for user. | ||
1374 | * | ||
1375 | * @param cls closure | ||
1376 | * @param anastasis_pub public key of the user's account | ||
1377 | * @param account_sig signature | ||
1378 | * @param recovery_data_hash hash of the current recovery data | ||
1379 | * @param data_size size of data blob | ||
1380 | * @param data blob which contains the recovery document | ||
1381 | * @param version[OUT] set to the version number of the policy being returned | ||
1382 | * @return transaction status | ||
1383 | */ | ||
1384 | enum GNUNET_DB_QueryStatus | ||
1385 | postgres_get_latest_recovery_document ( | ||
1386 | void *cls, | ||
1387 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
1388 | struct ANASTASIS_AccountSignatureP *account_sig, | ||
1389 | struct GNUNET_HashCode *recovery_data_hash, | ||
1390 | size_t *data_size, | ||
1391 | void **data, | ||
1392 | uint32_t *version) | ||
1393 | { | ||
1394 | struct PostgresClosure *pg = cls; | ||
1395 | struct GNUNET_PQ_QueryParam params[] = { | ||
1396 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
1397 | GNUNET_PQ_query_param_end | ||
1398 | }; | ||
1399 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1400 | GNUNET_PQ_result_spec_uint32 ("version", | ||
1401 | version), | ||
1402 | GNUNET_PQ_result_spec_auto_from_type ("account_sig", | ||
1403 | account_sig), | ||
1404 | GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", | ||
1405 | recovery_data_hash), | ||
1406 | GNUNET_PQ_result_spec_variable_size ("recovery_data", | ||
1407 | data, | ||
1408 | data_size), | ||
1409 | GNUNET_PQ_result_spec_end | ||
1410 | }; | ||
1411 | |||
1412 | check_connection (pg); | ||
1413 | postgres_preflight (pg); | ||
1414 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1415 | "latest_recoverydocument_select", | ||
1416 | params, | ||
1417 | rs); | ||
1418 | } | ||
1419 | |||
1420 | |||
1421 | /** | ||
1422 | * Fetch recovery document for user according given version. | ||
1423 | * | ||
1424 | * @param cls closure | ||
1425 | * @param anastasis_pub public key of the user's account | ||
1426 | * @param version the version number of the policy the user requests | ||
1427 | * @param[out] account_sig signature | ||
1428 | * @param[out] recovery_data_hash hash of the current recovery data | ||
1429 | * @param[out] data_size size of data blob | ||
1430 | * @param[out] data blob which contains the recovery document | ||
1431 | * @return transaction status | ||
1432 | */ | ||
1433 | enum GNUNET_DB_QueryStatus | ||
1434 | postgres_get_recovery_document ( | ||
1435 | void *cls, | ||
1436 | const struct ANASTASIS_CRYPTO_AccountPublicKeyP *anastasis_pub, | ||
1437 | uint32_t version, | ||
1438 | struct ANASTASIS_AccountSignatureP *account_sig, | ||
1439 | struct GNUNET_HashCode *recovery_data_hash, | ||
1440 | size_t *data_size, | ||
1441 | void **data) | ||
1442 | { | ||
1443 | struct PostgresClosure *pg = cls; | ||
1444 | struct GNUNET_PQ_QueryParam params[] = { | ||
1445 | GNUNET_PQ_query_param_auto_from_type (anastasis_pub), | ||
1446 | GNUNET_PQ_query_param_uint32 (&version), | ||
1447 | GNUNET_PQ_query_param_end | ||
1448 | }; | ||
1449 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1450 | GNUNET_PQ_result_spec_auto_from_type ("account_sig", | ||
1451 | account_sig), | ||
1452 | GNUNET_PQ_result_spec_auto_from_type ("recovery_data_hash", | ||
1453 | recovery_data_hash), | ||
1454 | GNUNET_PQ_result_spec_variable_size ("recovery_data", | ||
1455 | data, | ||
1456 | data_size), | ||
1457 | GNUNET_PQ_result_spec_end | ||
1458 | }; | ||
1459 | |||
1460 | check_connection (pg); | ||
1461 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1462 | "recoverydocument_select", | ||
1463 | params, | ||
1464 | rs); | ||
1465 | } | ||
1466 | |||
1467 | |||
1468 | /** | ||
1469 | * Closure for check_valid_code(). | ||
1470 | */ | ||
1471 | struct CheckValidityContext | ||
1472 | { | ||
1473 | /** | ||
1474 | * Code to check for. | ||
1475 | */ | ||
1476 | const struct GNUNET_HashCode *hashed_code; | ||
1477 | |||
1478 | /** | ||
1479 | * Truth we are processing. | ||
1480 | */ | ||
1481 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid; | ||
1482 | |||
1483 | /** | ||
1484 | * Database context. | ||
1485 | */ | ||
1486 | struct PostgresClosure *pg; | ||
1487 | |||
1488 | /** | ||
1489 | * Set to true if a code matching @e hashed_code was found. | ||
1490 | */ | ||
1491 | bool valid; | ||
1492 | |||
1493 | /** | ||
1494 | * Set to true if we had a database failure. | ||
1495 | */ | ||
1496 | bool db_failure; | ||
1497 | |||
1498 | }; | ||
1499 | |||
1500 | |||
1501 | /** | ||
1502 | * Helper function for #postgres_verify_challenge_code(). | ||
1503 | * To be called with the results of a SELECT statement | ||
1504 | * that has returned @a num_results results. | ||
1505 | * | ||
1506 | * @param cls closure of type `struct CheckValidityContext *` | ||
1507 | * @param result the postgres result | ||
1508 | * @param num_result the number of results in @a result | ||
1509 | */ | ||
1510 | static void | ||
1511 | check_valid_code (void *cls, | ||
1512 | PGresult *result, | ||
1513 | unsigned int num_results) | ||
1514 | { | ||
1515 | struct CheckValidityContext *cvc = cls; | ||
1516 | struct PostgresClosure *pg = cvc->pg; | ||
1517 | |||
1518 | for (unsigned int i = 0; i < num_results; i++) | ||
1519 | { | ||
1520 | uint64_t server_code; | ||
1521 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1522 | GNUNET_PQ_result_spec_uint64 ("code", | ||
1523 | &server_code), | ||
1524 | GNUNET_PQ_result_spec_end | ||
1525 | }; | ||
1526 | |||
1527 | if (GNUNET_OK != | ||
1528 | GNUNET_PQ_extract_result (result, | ||
1529 | rs, | ||
1530 | i)) | ||
1531 | { | ||
1532 | GNUNET_break (0); | ||
1533 | cvc->db_failure = true; | ||
1534 | return; | ||
1535 | } | ||
1536 | { | ||
1537 | struct GNUNET_HashCode shashed_code; | ||
1538 | |||
1539 | ANASTASIS_hash_answer (server_code, | ||
1540 | &shashed_code); | ||
1541 | if (0 == | ||
1542 | GNUNET_memcmp (&shashed_code, | ||
1543 | cvc->hashed_code)) | ||
1544 | { | ||
1545 | cvc->valid = true; | ||
1546 | } | ||
1547 | else | ||
1548 | { | ||
1549 | /* count failures to prevent brute-force attacks */ | ||
1550 | struct GNUNET_PQ_QueryParam params[] = { | ||
1551 | GNUNET_PQ_query_param_auto_from_type (cvc->truth_uuid), | ||
1552 | GNUNET_PQ_query_param_uint64 (&server_code), | ||
1553 | GNUNET_PQ_query_param_end | ||
1554 | }; | ||
1555 | enum GNUNET_DB_QueryStatus qs; | ||
1556 | |||
1557 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1558 | "challengecode_update_retry", | ||
1559 | params); | ||
1560 | if (qs <= 0) | ||
1561 | { | ||
1562 | GNUNET_break (0); | ||
1563 | cvc->db_failure = true; | ||
1564 | } | ||
1565 | } | ||
1566 | } | ||
1567 | } | ||
1568 | } | ||
1569 | |||
1570 | |||
1571 | /** | ||
1572 | * Verify the provided code with the code on the server. | ||
1573 | * If the code matches the function will return with success, if the code | ||
1574 | * does not match, the retry counter will be decreased by one. | ||
1575 | * | ||
1576 | * @param cls closure | ||
1577 | * @param truth_pub identification of the challenge which the code corresponds to | ||
1578 | * @param hashed_code code which the user provided and wants to verify | ||
1579 | * @return code validity status | ||
1580 | */ | ||
1581 | enum ANASTASIS_DB_CodeStatus | ||
1582 | postgres_verify_challenge_code ( | ||
1583 | void *cls, | ||
1584 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1585 | const struct GNUNET_HashCode *hashed_code) | ||
1586 | { | ||
1587 | struct PostgresClosure *pg = cls; | ||
1588 | struct CheckValidityContext cvc = { | ||
1589 | .truth_uuid = truth_uuid, | ||
1590 | .hashed_code = hashed_code, | ||
1591 | .pg = pg | ||
1592 | }; | ||
1593 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
1594 | struct GNUNET_PQ_QueryParam params[] = { | ||
1595 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1596 | TALER_PQ_query_param_absolute_time (&now), | ||
1597 | GNUNET_PQ_query_param_end | ||
1598 | }; | ||
1599 | enum GNUNET_DB_QueryStatus qs; | ||
1600 | |||
1601 | check_connection (pg); | ||
1602 | GNUNET_TIME_round_abs (&now); | ||
1603 | qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, | ||
1604 | "challengecode_select", | ||
1605 | params, | ||
1606 | &check_valid_code, | ||
1607 | &cvc); | ||
1608 | if ( (qs < 0) || | ||
1609 | (cvc.db_failure) ) | ||
1610 | return ANASTASIS_DB_CODE_STATUS_HARD_ERROR; | ||
1611 | if (cvc.valid) | ||
1612 | return ANASTASIS_DB_CODE_STATUS_VALID_CODE_STORED; | ||
1613 | if (0 == qs) | ||
1614 | return ANASTASIS_DB_CODE_STATUS_NO_RESULTS; | ||
1615 | return ANASTASIS_DB_CODE_STATUS_CHALLENGE_CODE_MISMATCH; | ||
1616 | } | ||
1617 | |||
1618 | |||
1619 | /** | ||
1620 | * Lookup pending payment for a certain challenge. | ||
1621 | * | ||
1622 | * @param cls closure | ||
1623 | * @param truth_uuid identification of the challenge | ||
1624 | * @param[out] payment_secret set to the challenge payment secret | ||
1625 | * @return transaction status | ||
1626 | */ | ||
1627 | enum GNUNET_DB_QueryStatus | ||
1628 | postgres_lookup_challenge_payment ( | ||
1629 | void *cls, | ||
1630 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1631 | struct ANASTASIS_PaymentSecretP *payment_secret) | ||
1632 | { | ||
1633 | struct PostgresClosure *pg = cls; | ||
1634 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
1635 | struct GNUNET_TIME_Absolute recent | ||
1636 | = GNUNET_TIME_absolute_subtract (now, | ||
1637 | ANASTASIS_CHALLENGE_OFFER_LIFETIME); | ||
1638 | struct GNUNET_PQ_QueryParam params[] = { | ||
1639 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1640 | GNUNET_PQ_query_param_absolute_time (&recent), | ||
1641 | GNUNET_PQ_query_param_end | ||
1642 | }; | ||
1643 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1644 | GNUNET_PQ_result_spec_auto_from_type ("payment_identifier", | ||
1645 | payment_secret), | ||
1646 | GNUNET_PQ_result_spec_end | ||
1647 | }; | ||
1648 | |||
1649 | return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1650 | "challenge_pending_payment_select", | ||
1651 | params, | ||
1652 | rs); | ||
1653 | } | ||
1654 | |||
1655 | |||
1656 | /** | ||
1657 | * Update payment status of challenge | ||
1658 | * | ||
1659 | * @param cls closure | ||
1660 | * @param truth_uuid which challenge received a payment | ||
1661 | * @param payment_identifier proof of payment, must be unique and match pending payment | ||
1662 | * @return transaction status | ||
1663 | */ | ||
1664 | enum GNUNET_DB_QueryStatus | ||
1665 | postgres_update_challenge_payment ( | ||
1666 | void *cls, | ||
1667 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1668 | const struct ANASTASIS_PaymentSecretP *payment_identifier) | ||
1669 | { | ||
1670 | struct PostgresClosure *pg = cls; | ||
1671 | struct GNUNET_PQ_QueryParam params[] = { | ||
1672 | GNUNET_PQ_query_param_auto_from_type (payment_identifier), | ||
1673 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1674 | GNUNET_PQ_query_param_end | ||
1675 | }; | ||
1676 | |||
1677 | check_connection (pg); | ||
1678 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1679 | "challenge_payment_done", | ||
1680 | params); | ||
1681 | } | ||
1682 | |||
1683 | |||
1684 | /** | ||
1685 | * Create a new challenge code for a given challenge identified by the challenge | ||
1686 | * public key. The function will first check if there is already a valid code | ||
1687 | * for this challenge present and won't insert a new one in this case. | ||
1688 | * | ||
1689 | * @param cls closure | ||
1690 | * @param truth_uuid the identifier for the challenge | ||
1691 | * @param rotation_period for how long is the code available | ||
1692 | * @param validity_period for how long is the code available | ||
1693 | * @param retry_counter amount of retries allowed | ||
1694 | * @param[out] retransmission_date when to next retransmit | ||
1695 | * @param[out] code set to the code which will be checked for later | ||
1696 | * @return transaction status, | ||
1697 | * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if we are out of valid tries, | ||
1698 | * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if @a code is now in the DB | ||
1699 | */ | ||
1700 | enum GNUNET_DB_QueryStatus | ||
1701 | postgres_create_challenge_code ( | ||
1702 | void *cls, | ||
1703 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1704 | struct GNUNET_TIME_Relative rotation_period, | ||
1705 | struct GNUNET_TIME_Relative validity_period, | ||
1706 | unsigned int retry_counter, | ||
1707 | struct GNUNET_TIME_Absolute *retransmission_date, | ||
1708 | uint64_t *code) | ||
1709 | { | ||
1710 | struct PostgresClosure *pg = cls; | ||
1711 | enum GNUNET_DB_QueryStatus qs; | ||
1712 | struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get (); | ||
1713 | struct GNUNET_TIME_Absolute expiration_date; | ||
1714 | struct GNUNET_TIME_Absolute ex_rot; | ||
1715 | |||
1716 | check_connection (pg); | ||
1717 | GNUNET_TIME_round_abs (&now); | ||
1718 | expiration_date = GNUNET_TIME_absolute_add (now, | ||
1719 | validity_period); | ||
1720 | ex_rot = GNUNET_TIME_absolute_subtract (now, | ||
1721 | rotation_period); | ||
1722 | for (unsigned int retries = 0; retries<MAX_RETRIES; retries++) | ||
1723 | { | ||
1724 | if (GNUNET_OK != | ||
1725 | begin_transaction (pg, | ||
1726 | "create_challenge_code")) | ||
1727 | { | ||
1728 | GNUNET_break (0); | ||
1729 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
1730 | } | ||
1731 | |||
1732 | { | ||
1733 | uint32_t old_retry_counter; | ||
1734 | struct GNUNET_PQ_QueryParam params[] = { | ||
1735 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1736 | TALER_PQ_query_param_absolute_time (&now), | ||
1737 | TALER_PQ_query_param_absolute_time (&ex_rot), | ||
1738 | GNUNET_PQ_query_param_end | ||
1739 | }; | ||
1740 | struct GNUNET_PQ_ResultSpec rs[] = { | ||
1741 | GNUNET_PQ_result_spec_uint64 ("code", | ||
1742 | code), | ||
1743 | GNUNET_PQ_result_spec_uint32 ("retry_counter", | ||
1744 | &old_retry_counter), | ||
1745 | GNUNET_PQ_result_spec_absolute_time ("retransmission_date", | ||
1746 | retransmission_date), | ||
1747 | GNUNET_PQ_result_spec_end | ||
1748 | }; | ||
1749 | enum GNUNET_DB_QueryStatus qs; | ||
1750 | |||
1751 | qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn, | ||
1752 | "challengecode_select_meta", | ||
1753 | params, | ||
1754 | rs); | ||
1755 | switch (qs) | ||
1756 | { | ||
1757 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
1758 | GNUNET_break (0); | ||
1759 | rollback (pg); | ||
1760 | return qs; | ||
1761 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
1762 | goto retry; | ||
1763 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
1764 | /* no active challenge, create fresh one (below) */ | ||
1765 | break; | ||
1766 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
1767 | if (0 == old_retry_counter) | ||
1768 | { | ||
1769 | rollback (pg); | ||
1770 | return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; | ||
1771 | } | ||
1772 | rollback (pg); | ||
1773 | return qs; | ||
1774 | } | ||
1775 | } | ||
1776 | |||
1777 | *code = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
1778 | INT64_MAX); | ||
1779 | *retransmission_date = GNUNET_TIME_UNIT_ZERO_ABS; | ||
1780 | { | ||
1781 | struct GNUNET_PQ_QueryParam params[] = { | ||
1782 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1783 | GNUNET_PQ_query_param_uint64 (code), | ||
1784 | TALER_PQ_query_param_absolute_time (&now), | ||
1785 | TALER_PQ_query_param_absolute_time (&expiration_date), | ||
1786 | GNUNET_PQ_query_param_uint32 (&retry_counter), | ||
1787 | GNUNET_PQ_query_param_end | ||
1788 | }; | ||
1789 | |||
1790 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1791 | "challengecode_insert", | ||
1792 | params); | ||
1793 | switch (qs) | ||
1794 | { | ||
1795 | case GNUNET_DB_STATUS_HARD_ERROR: | ||
1796 | rollback (pg); | ||
1797 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
1798 | case GNUNET_DB_STATUS_SOFT_ERROR: | ||
1799 | goto retry; | ||
1800 | case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: | ||
1801 | GNUNET_break (0); | ||
1802 | rollback (pg); | ||
1803 | return GNUNET_DB_STATUS_HARD_ERROR; | ||
1804 | case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: | ||
1805 | break; | ||
1806 | } | ||
1807 | } | ||
1808 | qs = commit_transaction (pg); | ||
1809 | if (GNUNET_DB_STATUS_SOFT_ERROR == qs) | ||
1810 | goto retry; | ||
1811 | if (qs < 0) | ||
1812 | return qs; | ||
1813 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; | ||
1814 | retry: | ||
1815 | rollback (pg); | ||
1816 | } | ||
1817 | return GNUNET_DB_STATUS_SOFT_ERROR; | ||
1818 | } | ||
1819 | |||
1820 | |||
1821 | /** | ||
1822 | * Remember in the database that we successfully sent a challenge. | ||
1823 | * | ||
1824 | * @param cls closure | ||
1825 | * @param payment_secret payment secret which the user must provide with every upload | ||
1826 | * @param truth_uuid the identifier for the challenge | ||
1827 | * @param code the challenge that was sent | ||
1828 | */ | ||
1829 | static enum GNUNET_DB_QueryStatus | ||
1830 | postgres_mark_challenge_sent ( | ||
1831 | void *cls, | ||
1832 | const struct ANASTASIS_PaymentSecretP *payment_secret, | ||
1833 | const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, | ||
1834 | uint64_t code) | ||
1835 | { | ||
1836 | struct PostgresClosure *pg = cls; | ||
1837 | enum GNUNET_DB_QueryStatus qs; | ||
1838 | |||
1839 | check_connection (pg); | ||
1840 | { | ||
1841 | struct GNUNET_TIME_Absolute now; | ||
1842 | struct GNUNET_PQ_QueryParam params[] = { | ||
1843 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1844 | GNUNET_PQ_query_param_uint64 (&code), | ||
1845 | TALER_PQ_query_param_absolute_time (&now), | ||
1846 | GNUNET_PQ_query_param_end | ||
1847 | }; | ||
1848 | |||
1849 | now = GNUNET_TIME_absolute_get (); | ||
1850 | GNUNET_TIME_round_abs (&now); | ||
1851 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1852 | "challengecode_mark_sent", | ||
1853 | params); | ||
1854 | if (qs <= 0) | ||
1855 | return qs; | ||
1856 | } | ||
1857 | { | ||
1858 | struct GNUNET_PQ_QueryParam params[] = { | ||
1859 | GNUNET_PQ_query_param_auto_from_type (truth_uuid), | ||
1860 | GNUNET_PQ_query_param_auto_from_type (payment_secret), | ||
1861 | GNUNET_PQ_query_param_end | ||
1862 | }; | ||
1863 | |||
1864 | qs = GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1865 | "challengepayment_dec_counter", | ||
1866 | params); | ||
1867 | if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) | ||
1868 | return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* probably was free */ | ||
1869 | return qs; | ||
1870 | } | ||
1871 | } | ||
1872 | |||
1873 | |||
1874 | /** | ||
1875 | * Function called to remove all expired codes from the database. | ||
1876 | * FIXME maybe implemented as part of postgres_gc() in the future. | ||
1877 | * | ||
1878 | * @return transaction status | ||
1879 | */ | ||
1880 | enum GNUNET_DB_QueryStatus | ||
1881 | postgres_challenge_gc (void *cls) | ||
1882 | { | ||
1883 | struct PostgresClosure *pg = cls; | ||
1884 | struct GNUNET_TIME_Absolute time_now = GNUNET_TIME_absolute_get (); | ||
1885 | struct GNUNET_PQ_QueryParam params[] = { | ||
1886 | GNUNET_PQ_query_param_absolute_time (&time_now), | ||
1887 | GNUNET_PQ_query_param_end | ||
1888 | }; | ||
1889 | |||
1890 | check_connection (pg); | ||
1891 | postgres_preflight (pg); | ||
1892 | return GNUNET_PQ_eval_prepared_non_select (pg->conn, | ||
1893 | "gc_challengecodes", | ||
1894 | params); | ||
1895 | } | ||
1896 | |||
1897 | |||
1898 | /** | ||
1899 | * Initialize Postgres database subsystem. | ||
1900 | * | ||
1901 | * @param cls a configuration instance | ||
1902 | * @return NULL on error, otherwise a `struct TALER_ANASTASISDB_Plugin` | ||
1903 | */ | ||
1904 | void * | ||
1905 | libanastasis_plugin_db_postgres_init (void *cls) | ||
1906 | { | ||
1907 | struct GNUNET_CONFIGURATION_Handle *cfg = cls; | ||
1908 | struct PostgresClosure *pg; | ||
1909 | struct ANASTASIS_DatabasePlugin *plugin; | ||
1910 | struct GNUNET_PQ_PreparedStatement ps[] = { | ||
1911 | GNUNET_PQ_make_prepare ("user_insert", | ||
1912 | "INSERT INTO anastasis_user " | ||
1913 | "(user_id" | ||
1914 | ",expiration_date" | ||
1915 | ") VALUES " | ||
1916 | "($1, $2);", | ||
1917 | 2), | ||
1918 | GNUNET_PQ_make_prepare ("do_commit", | ||
1919 | "COMMIT", | ||
1920 | 0), | ||
1921 | GNUNET_PQ_make_prepare ("user_select", | ||
1922 | "SELECT" | ||
1923 | " expiration_date " | ||
1924 | "FROM anastasis_user" | ||
1925 | " WHERE user_id=$1" | ||
1926 | " FOR UPDATE;", | ||
1927 | 1), | ||
1928 | GNUNET_PQ_make_prepare ("user_update", | ||
1929 | "UPDATE anastasis_user" | ||
1930 | " SET " | ||
1931 | " expiration_date=$1" | ||
1932 | " WHERE user_id=$2;", | ||
1933 | 2), | ||
1934 | GNUNET_PQ_make_prepare ("recdoc_payment_insert", | ||
1935 | "INSERT INTO anastasis_recdoc_payment " | ||
1936 | "(user_id" | ||
1937 | ",post_counter" | ||
1938 | ",amount_val" | ||
1939 | ",amount_frac" | ||
1940 | ",payment_identifier" | ||
1941 | ",creation_date" | ||
1942 | ") VALUES " | ||
1943 | "($1, $2, $3, $4, $5, $6);", | ||
1944 | 6), | ||
1945 | GNUNET_PQ_make_prepare ("challenge_payment_insert", | ||
1946 | "INSERT INTO anastasis_challenge_payment " | ||
1947 | "(truth_uuid" | ||
1948 | ",amount_val" | ||
1949 | ",amount_frac" | ||
1950 | ",payment_identifier" | ||
1951 | ",creation_date" | ||
1952 | ") VALUES " | ||
1953 | "($1, $2, $3, $4, $5);", | ||
1954 | 5), | ||
1955 | GNUNET_PQ_make_prepare ("truth_payment_insert", | ||
1956 | "INSERT INTO anastasis_truth_payment " | ||
1957 | "(truth_uuid" | ||
1958 | ",amount_val" | ||
1959 | ",amount_frac" | ||
1960 | ",expiration" | ||
1961 | ") VALUES " | ||
1962 | "($1, $2, $3, $4);", | ||
1963 | 4), | ||
1964 | GNUNET_PQ_make_prepare ("recdoc_payment_done", | ||
1965 | "UPDATE anastasis_recdoc_payment " | ||
1966 | "SET" | ||
1967 | " paid=TRUE " | ||
1968 | "WHERE" | ||
1969 | " payment_identifier=$1" | ||
1970 | " AND" | ||
1971 | " user_id=$2" | ||
1972 | " AND" | ||
1973 | " paid=FALSE;", | ||
1974 | 2), | ||
1975 | GNUNET_PQ_make_prepare ("challenge_refund_update", | ||
1976 | "UPDATE anastasis_challenge_payment " | ||
1977 | "SET" | ||
1978 | " refunded=TRUE " | ||
1979 | "WHERE" | ||
1980 | " payment_identifier=$1" | ||
1981 | " AND" | ||
1982 | " paid=TRUE" | ||
1983 | " AND" | ||
1984 | " truth_uuid=$2;", | ||
1985 | 2), | ||
1986 | GNUNET_PQ_make_prepare ("challenge_payment_done", | ||
1987 | "UPDATE anastasis_challenge_payment " | ||
1988 | "SET" | ||
1989 | " paid=TRUE " | ||
1990 | "WHERE" | ||
1991 | " payment_identifier=$1" | ||
1992 | " AND" | ||
1993 | " refunded=FALSE" | ||
1994 | " AND" | ||
1995 | " truth_uuid=$2" | ||
1996 | " AND" | ||
1997 | " paid=FALSE;", | ||
1998 | 2), | ||
1999 | GNUNET_PQ_make_prepare ("recdoc_payment_select", | ||
2000 | "SELECT" | ||
2001 | " creation_date" | ||
2002 | ",post_counter" | ||
2003 | ",amount_val" | ||
2004 | ",amount_frac" | ||
2005 | ",paid" | ||
2006 | " FROM anastasis_recdoc_payment" | ||
2007 | " WHERE payment_identifier=$1;", | ||
2008 | 1), | ||
2009 | GNUNET_PQ_make_prepare ("truth_payment_select", | ||
2010 | "SELECT" | ||
2011 | " expiration" | ||
2012 | " FROM anastasis_truth_payment" | ||
2013 | " WHERE truth_uuid=$1" | ||
2014 | " AND expiration>$2;", | ||
2015 | 2), | ||
2016 | GNUNET_PQ_make_prepare ("challenge_payment_select", | ||
2017 | "SELECT" | ||
2018 | " creation_date" | ||
2019 | ",amount_val" | ||
2020 | ",amount_frac" | ||
2021 | ",paid" | ||
2022 | " FROM anastasis_challenge_payment" | ||
2023 | " WHERE payment_identifier=$1" | ||
2024 | " AND truth_uuid=$2" | ||
2025 | " AND refunded=FALSE" | ||
2026 | " AND counter>0;", | ||
2027 | 1), | ||
2028 | GNUNET_PQ_make_prepare ("challenge_pending_payment_select", | ||
2029 | "SELECT" | ||
2030 | " creation_date" | ||
2031 | ",payment_identifier" | ||
2032 | ",amount_val" | ||
2033 | ",amount_frac" | ||
2034 | " FROM anastasis_challenge_payment" | ||
2035 | " WHERE" | ||
2036 | " paid=FALSE" | ||
2037 | " AND" | ||
2038 | " refunded=FALSE" | ||
2039 | " AND" | ||
2040 | " truth_uuid=$1" | ||
2041 | " AND" | ||
2042 | " creation_date > $2;", | ||
2043 | 1), | ||
2044 | GNUNET_PQ_make_prepare ("recdoc_payments_select", | ||
2045 | "SELECT" | ||
2046 | " user_id" | ||
2047 | ",payment_identifier" | ||
2048 | ",amount_val" | ||
2049 | ",amount_frac" | ||
2050 | " FROM anastasis_recdoc_payment" | ||
2051 | " WHERE paid=FALSE;", | ||
2052 | 0), | ||
2053 | GNUNET_PQ_make_prepare ("gc_accounts", | ||
2054 | "DELETE FROM anastasis_user " | ||
2055 | "WHERE" | ||
2056 | " expiration_date < $1;", | ||
2057 | 1), | ||
2058 | GNUNET_PQ_make_prepare ("gc_recdoc_pending_payments", | ||
2059 | "DELETE FROM anastasis_recdoc_payment " | ||
2060 | "WHERE" | ||
2061 | " paid=FALSE" | ||
2062 | " AND" | ||
2063 | " creation_date < $1;", | ||
2064 | 1), | ||
2065 | GNUNET_PQ_make_prepare ("gc_challenge_pending_payments", | ||
2066 | "DELETE FROM anastasis_challenge_payment " | ||
2067 | "WHERE" | ||
2068 | " (paid=FALSE" | ||
2069 | " OR" | ||
2070 | " refunded=TRUE)" | ||
2071 | " AND" | ||
2072 | " creation_date < $1;", | ||
2073 | 1), | ||
2074 | GNUNET_PQ_make_prepare ("truth_insert", | ||
2075 | "INSERT INTO anastasis_truth " | ||
2076 | "(truth_uuid" | ||
2077 | ",key_share_data" | ||
2078 | ",method_name" | ||
2079 | ",encrypted_truth" | ||
2080 | ",truth_mime" | ||
2081 | ",expiration" | ||
2082 | ") VALUES " | ||
2083 | "($1, $2, $3, $4, $5, $6);", | ||
2084 | 6), | ||
2085 | GNUNET_PQ_make_prepare ("recovery_document_insert", | ||
2086 | "INSERT INTO anastasis_recoverydocument " | ||
2087 | "(user_id" | ||
2088 | ",version" | ||
2089 | ",account_sig" | ||
2090 | ",recovery_data_hash" | ||
2091 | ",recovery_data" | ||
2092 | ") VALUES " | ||
2093 | "($1, $2, $3, $4, $5);", | ||
2094 | 5), | ||
2095 | GNUNET_PQ_make_prepare ("truth_select", | ||
2096 | "SELECT " | ||
2097 | " method_name" | ||
2098 | ",encrypted_truth" | ||
2099 | ",truth_mime" | ||
2100 | " FROM anastasis_truth" | ||
2101 | " WHERE truth_uuid =$1;", | ||
2102 | 1), | ||
2103 | GNUNET_PQ_make_prepare ("latest_recoverydocument_select", | ||
2104 | "SELECT " | ||
2105 | " version" | ||
2106 | ",account_sig" | ||
2107 | ",recovery_data_hash" | ||
2108 | ",recovery_data" | ||
2109 | " FROM anastasis_recoverydocument" | ||
2110 | " WHERE user_id =$1 " | ||
2111 | " ORDER BY version DESC" | ||
2112 | " LIMIT 1;", | ||
2113 | 1), | ||
2114 | GNUNET_PQ_make_prepare ("latest_recovery_version_select", | ||
2115 | "SELECT" | ||
2116 | " version" | ||
2117 | ",recovery_data_hash" | ||
2118 | ",expiration_date" | ||
2119 | " FROM anastasis_recoverydocument" | ||
2120 | " JOIN anastasis_user USING (user_id)" | ||
2121 | " WHERE user_id=$1" | ||
2122 | " ORDER BY version DESC" | ||
2123 | " LIMIT 1;", | ||
2124 | 1), | ||
2125 | GNUNET_PQ_make_prepare ("recoverydocument_select", | ||
2126 | "SELECT " | ||
2127 | " account_sig" | ||
2128 | ",recovery_data_hash" | ||
2129 | ",recovery_data" | ||
2130 | " FROM anastasis_recoverydocument" | ||
2131 | " WHERE user_id=$1" | ||
2132 | " AND version=$2;", | ||
2133 | 2), | ||
2134 | GNUNET_PQ_make_prepare ("postcounter_select", | ||
2135 | "SELECT" | ||
2136 | " post_counter" | ||
2137 | " FROM anastasis_recdoc_payment" | ||
2138 | " WHERE user_id=$1" | ||
2139 | " AND payment_identifier=$2;", | ||
2140 | 2), | ||
2141 | GNUNET_PQ_make_prepare ("postcounter_update", | ||
2142 | "UPDATE " | ||
2143 | "anastasis_recdoc_payment " | ||
2144 | "SET " | ||
2145 | "post_counter=$1 " | ||
2146 | "WHERE user_id =$2 " | ||
2147 | "AND payment_identifier=$3;", | ||
2148 | 3), | ||
2149 | GNUNET_PQ_make_prepare ("key_share_select", | ||
2150 | "SELECT " | ||
2151 | "key_share_data " | ||
2152 | "FROM " | ||
2153 | "anastasis_truth " | ||
2154 | "WHERE truth_uuid =$1;", | ||
2155 | 1), | ||
2156 | GNUNET_PQ_make_prepare ("challengecode_insert", | ||
2157 | "INSERT INTO anastasis_challengecode " | ||
2158 | "(truth_uuid" | ||
2159 | ",code" | ||
2160 | ",creation_date" | ||
2161 | ",expiration_date" | ||
2162 | ",retry_counter" | ||
2163 | ") VALUES " | ||
2164 | "($1, $2, $3, $4, $5);", | ||
2165 | 5), | ||
2166 | GNUNET_PQ_make_prepare ("challengecode_select", | ||
2167 | "SELECT " | ||
2168 | " code" | ||
2169 | " FROM anastasis_challengecode" | ||
2170 | " WHERE truth_uuid=$1" | ||
2171 | " AND expiration_date > $2" | ||
2172 | " AND retry_counter > 0;", | ||
2173 | 2), | ||
2174 | GNUNET_PQ_make_prepare ("challengecode_select_meta", | ||
2175 | "SELECT " | ||
2176 | " code" | ||
2177 | ",retry_counter" | ||
2178 | ",retransmission_date" | ||
2179 | " FROM anastasis_challengecode" | ||
2180 | " WHERE truth_uuid=$1" | ||
2181 | " AND expiration_date > $2" | ||
2182 | " AND creation_date > $3" | ||
2183 | " ORDER BY creation_date DESC" | ||
2184 | " LIMIT 1;", | ||
2185 | 2), | ||
2186 | GNUNET_PQ_make_prepare ("challengecode_update_retry", | ||
2187 | "UPDATE anastasis_challengecode" | ||
2188 | " SET retry_counter=retry_counter - 1" | ||
2189 | " WHERE truth_uuid=$1" | ||
2190 | " AND code=$2" | ||
2191 | " AND retry_counter > 0;", | ||
2192 | 1), | ||
2193 | GNUNET_PQ_make_prepare ("challengepayment_dec_counter", | ||
2194 | "UPDATE anastasis_challenge_payment" | ||
2195 | " SET counter=counter - 1" | ||
2196 | " WHERE truth_uuid=$1" | ||
2197 | " AND payment_identifier=$2" | ||
2198 | " AND counter > 0;", | ||
2199 | 2), | ||
2200 | GNUNET_PQ_make_prepare ("challengecode_mark_sent", | ||
2201 | "UPDATE anastasis_challengecode" | ||
2202 | " SET retransmission_date=$3" | ||
2203 | " WHERE truth_uuid=$1" | ||
2204 | " AND code=$2" | ||
2205 | " AND creation_date IN" | ||
2206 | " (SELECT creation_date" | ||
2207 | " FROM anastasis_challengecode" | ||
2208 | " WHERE truth_uuid=$1" | ||
2209 | " AND code=$2" | ||
2210 | " ORDER BY creation_date DESC" | ||
2211 | " LIMIT 1);", | ||
2212 | 3), | ||
2213 | GNUNET_PQ_make_prepare ("gc_challengecodes", | ||
2214 | "DELETE FROM anastasis_challengecode " | ||
2215 | "WHERE " | ||
2216 | "expiration_date < $1;", | ||
2217 | 1), | ||
2218 | GNUNET_PQ_PREPARED_STATEMENT_END | ||
2219 | }; | ||
2220 | |||
2221 | pg = GNUNET_new (struct PostgresClosure); | ||
2222 | pg->cfg = cfg; | ||
2223 | pg->conn = GNUNET_PQ_connect_with_cfg (cfg, | ||
2224 | "stasis-postgres", | ||
2225 | "stasis-", | ||
2226 | NULL, | ||
2227 | ps); | ||
2228 | if (NULL == pg->conn) | ||
2229 | { | ||
2230 | GNUNET_free (pg); | ||
2231 | return NULL; | ||
2232 | } | ||
2233 | if (GNUNET_OK != | ||
2234 | GNUNET_CONFIGURATION_get_value_string (cfg, | ||
2235 | "taler", | ||
2236 | "CURRENCY", | ||
2237 | &pg->currency)) | ||
2238 | { | ||
2239 | GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, | ||
2240 | "taler", | ||
2241 | "CURRENCY"); | ||
2242 | GNUNET_PQ_disconnect (pg->conn); | ||
2243 | GNUNET_free (pg); | ||
2244 | return NULL; | ||
2245 | } | ||
2246 | plugin = GNUNET_new (struct ANASTASIS_DatabasePlugin); | ||
2247 | plugin->cls = pg; | ||
2248 | plugin->drop_tables = &postgres_drop_tables; | ||
2249 | plugin->gc = &postgres_gc; | ||
2250 | plugin->preflight = &postgres_preflight; | ||
2251 | plugin->rollback = &rollback; | ||
2252 | plugin->commit = &commit_transaction; | ||
2253 | plugin->store_recovery_document = &postgres_store_recovery_document; | ||
2254 | plugin->record_recdoc_payment = &postgres_record_recdoc_payment; | ||
2255 | plugin->store_truth = &postgres_store_truth; | ||
2256 | plugin->get_escrow_challenge = &postgres_get_escrow_challenge; | ||
2257 | plugin->get_key_share = &postgres_get_key_share; | ||
2258 | plugin->get_latest_recovery_document = &postgres_get_latest_recovery_document; | ||
2259 | plugin->get_recovery_document = &postgres_get_recovery_document; | ||
2260 | plugin->lookup_account = &postgres_lookup_account; | ||
2261 | plugin->check_payment_identifier = &postgres_check_payment_identifier; | ||
2262 | plugin->increment_lifetime = &postgres_increment_lifetime; | ||
2263 | plugin->update_lifetime = &postgres_update_lifetime; | ||
2264 | plugin->start = &begin_transaction; | ||
2265 | plugin->check_connection = &check_connection; | ||
2266 | plugin->verify_challenge_code = &postgres_verify_challenge_code; | ||
2267 | plugin->create_challenge_code = &postgres_create_challenge_code; | ||
2268 | plugin->mark_challenge_sent = &postgres_mark_challenge_sent; | ||
2269 | plugin->challenge_gc = &postgres_challenge_gc; | ||
2270 | plugin->record_truth_upload_payment = &postgres_record_truth_upload_payment; | ||
2271 | plugin->check_truth_upload_paid = &postgres_check_truth_upload_paid; | ||
2272 | plugin->record_challenge_payment = &postgres_record_challenge_payment; | ||
2273 | plugin->record_challenge_refund = &postgres_record_challenge_refund; | ||
2274 | plugin->check_challenge_payment = &postgres_check_challenge_payment; | ||
2275 | plugin->lookup_challenge_payment = &postgres_lookup_challenge_payment; | ||
2276 | plugin->update_challenge_payment = &postgres_update_challenge_payment; | ||
2277 | return plugin; | ||
2278 | } | ||
2279 | |||
2280 | |||
2281 | /** | ||
2282 | * Shutdown Postgres database subsystem. | ||
2283 | * | ||
2284 | * @param cls a `struct ANASTASIS_DB_STATUS_Plugin` | ||
2285 | * @return NULL (always) | ||
2286 | */ | ||
2287 | void * | ||
2288 | libanastasis_plugin_db_postgres_done (void *cls) | ||
2289 | { | ||
2290 | struct ANASTASIS_DatabasePlugin *plugin = cls; | ||
2291 | struct PostgresClosure *pg = plugin->cls; | ||
2292 | |||
2293 | GNUNET_PQ_disconnect (pg->conn); | ||
2294 | GNUNET_free (pg->currency); | ||
2295 | GNUNET_free (pg); | ||
2296 | GNUNET_free (plugin); | ||
2297 | return NULL; | ||
2298 | } | ||
2299 | |||
2300 | |||
2301 | /* end of plugin_anastasisdb_postgres.c */ | ||