Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014--2021 Taler Systems SA
4 :
5 : TALER 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 : 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 General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file plugin_merchantdb_postgres.c
18 : * @brief database helper functions for postgres used by the merchant
19 : * @author Sree Harsha Totakura <sreeharsha@totakura.in>
20 : * @author Christian Grothoff
21 : * @author Marcello Stanisci
22 : */
23 : #include "platform.h"
24 : #include <gnunet/gnunet_util_lib.h>
25 : #include <gnunet/gnunet_pq_lib.h>
26 : #include <taler/taler_util.h>
27 : #include <taler/taler_pq_lib.h>
28 : #include <taler/taler_json_lib.h>
29 : #include <taler/taler_mhd_lib.h>
30 : #include "taler_merchantdb_plugin.h"
31 :
32 : /**
33 : * How often do we re-try if we run into a DB serialization error?
34 : */
35 : #define MAX_RETRIES 3
36 :
37 :
38 : /**
39 : * Wrapper macro to add the currency from the plugin's state
40 : * when fetching amounts from the database.
41 : *
42 : * @param field name of the database field to fetch amount from
43 : * @param[out] amountp pointer to amount to set
44 : */
45 : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
46 : TALER_PQ_result_spec_amount ( \
47 : field,pg->currency,amountp)
48 :
49 : /**
50 : * Wrapper macro to add the currency from the plugin's state
51 : * when fetching amounts from the database. NBO variant.
52 : *
53 : * @param field name of the database field to fetch amount from
54 : * @param[out] amountp pointer to amount to set
55 : */
56 : #define TALER_PQ_RESULT_SPEC_AMOUNT_NBO(field, amountp) \
57 : TALER_PQ_result_spec_amount_nbo ( \
58 : field,pg->currency,amountp)
59 :
60 :
61 : /**
62 : * Wrapper macro to add the currency from the plugin's state
63 : * when fetching amounts from the database.
64 : *
65 : * @param field name of the database field to fetch amount from
66 : * @param[out] amountp pointer to amount to set
67 : */
68 : #define TALER_PQ_RESULT_SPEC_AMOUNT(field,amountp) \
69 : TALER_PQ_result_spec_amount ( \
70 : field,pg->currency,amountp)
71 :
72 :
73 : /**
74 : * Type of the "cls" argument given to each of the functions in
75 : * our API.
76 : */
77 : struct PostgresClosure
78 : {
79 :
80 : /**
81 : * Postgres connection handle.
82 : */
83 : struct GNUNET_PQ_Context *conn;
84 :
85 : /**
86 : * Which currency do we deal in?
87 : */
88 : char *currency;
89 :
90 : /**
91 : * Directory with SQL statements to run to create tables.
92 : */
93 : char *sql_dir;
94 :
95 : /**
96 : * Underlying configuration.
97 : */
98 : const struct GNUNET_CONFIGURATION_Handle *cfg;
99 :
100 : /**
101 : * Name of the currently active transaction, NULL if none is active.
102 : */
103 : const char *transaction_name;
104 :
105 : };
106 :
107 :
108 : /**
109 : * Drop all Taler tables. This should only be used by testcases.
110 : *
111 : * @param cls the `struct PostgresClosure` with the plugin-specific state
112 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
113 : */
114 : static enum GNUNET_GenericReturnValue
115 7 : postgres_drop_tables (void *cls)
116 : {
117 7 : struct PostgresClosure *pc = cls;
118 : struct GNUNET_PQ_Context *conn;
119 :
120 7 : conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
121 : "merchantdb-postgres",
122 : "drop",
123 : NULL,
124 : NULL);
125 7 : if (NULL == conn)
126 7 : return GNUNET_SYSERR;
127 0 : GNUNET_PQ_disconnect (conn);
128 0 : return GNUNET_OK;
129 : }
130 :
131 :
132 : /**
133 : * Initialize tables.
134 : *
135 : * @param cls the `struct PostgresClosure` with the plugin-specific state
136 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
137 : */
138 : static enum GNUNET_GenericReturnValue
139 9 : postgres_create_tables (void *cls)
140 : {
141 9 : struct PostgresClosure *pc = cls;
142 : struct GNUNET_PQ_Context *conn;
143 :
144 9 : conn = GNUNET_PQ_connect_with_cfg (pc->cfg,
145 : "merchantdb-postgres",
146 : "merchant-",
147 : NULL,
148 : NULL);
149 9 : if (NULL == conn)
150 0 : return GNUNET_SYSERR;
151 9 : GNUNET_PQ_disconnect (conn);
152 9 : return GNUNET_OK;
153 : }
154 :
155 :
156 : /**
157 : * Register callback to be invoked on events of type @a es.
158 : *
159 : * @param cls database context to use
160 : * @param es specification of the event to listen for
161 : * @param timeout how long to wait for the event
162 : * @param cb function to call when the event happens, possibly
163 : * multiple times (until cancel is invoked)
164 : * @param cb_cls closure for @a cb
165 : * @return handle useful to cancel the listener
166 : */
167 : static struct GNUNET_DB_EventHandler *
168 3 : postgres_event_listen (void *cls,
169 : const struct GNUNET_DB_EventHeaderP *es,
170 : struct GNUNET_TIME_Relative timeout,
171 : GNUNET_DB_EventCallback cb,
172 : void *cb_cls)
173 : {
174 3 : struct PostgresClosure *pg = cls;
175 :
176 3 : return GNUNET_PQ_event_listen (pg->conn,
177 : es,
178 : timeout,
179 : cb,
180 : cb_cls);
181 : }
182 :
183 :
184 : /**
185 : * Stop notifications.
186 : *
187 : * @param eh handle to unregister.
188 : */
189 : static void
190 3 : postgres_event_listen_cancel (struct GNUNET_DB_EventHandler *eh)
191 : {
192 3 : GNUNET_PQ_event_listen_cancel (eh);
193 3 : }
194 :
195 :
196 : /**
197 : * Notify all that listen on @a es of an event.
198 : *
199 : * @param cls database context to use
200 : * @param es specification of the event to generate
201 : * @param extra additional event data provided
202 : * @param extra_size number of bytes in @a extra
203 : */
204 : static void
205 8 : postgres_event_notify (void *cls,
206 : const struct GNUNET_DB_EventHeaderP *es,
207 : const void *extra,
208 : size_t extra_size)
209 : {
210 8 : struct PostgresClosure *pg = cls;
211 :
212 8 : return GNUNET_PQ_event_notify (pg->conn,
213 : es,
214 : extra,
215 : extra_size);
216 : }
217 :
218 :
219 : /**
220 : * Do a pre-flight check that we are not in an uncommitted transaction.
221 : * If we are, die.
222 : * Does not return anything, as we will continue regardless of the outcome.
223 : *
224 : * @param cls the `struct PostgresClosure` with the plugin-specific state
225 : */
226 : static void
227 24 : postgres_preflight (void *cls)
228 : {
229 24 : struct PostgresClosure *pg = cls;
230 :
231 24 : if (NULL == pg->transaction_name)
232 24 : return; /* all good */
233 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
234 : "BUG: Preflight check detected running transaction `%s'!\n",
235 : pg->transaction_name);
236 0 : GNUNET_assert (0);
237 : }
238 :
239 :
240 : /**
241 : * Check that the database connection is still up
242 : * and automatically reconnects unless we are
243 : * already inside of a transaction.
244 : *
245 : * @param pg connection to check
246 : */
247 : static void
248 329 : check_connection (struct PostgresClosure *pg)
249 : {
250 329 : if (NULL != pg->transaction_name)
251 16 : return;
252 313 : GNUNET_PQ_reconnect_if_down (pg->conn);
253 : }
254 :
255 :
256 : /**
257 : * Start a transaction.
258 : *
259 : * @param cls the `struct PostgresClosure` with the plugin-specific state
260 : * @param name unique name identifying the transaction (for debugging),
261 : * must point to a constant
262 : * @return #GNUNET_OK on success
263 : */
264 : static enum GNUNET_GenericReturnValue
265 17 : postgres_start (void *cls,
266 : const char *name)
267 : {
268 17 : struct PostgresClosure *pg = cls;
269 17 : struct GNUNET_PQ_ExecuteStatement es[] = {
270 17 : GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL SERIALIZABLE"),
271 : GNUNET_PQ_EXECUTE_STATEMENT_END
272 : };
273 :
274 17 : check_connection (pg);
275 17 : postgres_preflight (pg);
276 17 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
277 : "Starting merchant DB transaction `%s'\n",
278 : name);
279 17 : if (GNUNET_OK !=
280 17 : GNUNET_PQ_exec_statements (pg->conn,
281 : es))
282 : {
283 0 : TALER_LOG_ERROR ("Failed to start transaction\n");
284 0 : GNUNET_break (0);
285 0 : return GNUNET_SYSERR;
286 : }
287 17 : pg->transaction_name = name;
288 17 : return GNUNET_OK;
289 : }
290 :
291 :
292 : /**
293 : * Start a transaction in 'read committed' mode.
294 : *
295 : * @param cls the `struct PostgresClosure` with the plugin-specific state
296 : * @param name unique name identifying the transaction (for debugging),
297 : * must point to a constant
298 : * @return #GNUNET_OK on success
299 : */
300 : static enum GNUNET_GenericReturnValue
301 1 : postgres_start_read_committed (void *cls,
302 : const char *name)
303 : {
304 1 : struct PostgresClosure *pg = cls;
305 1 : struct GNUNET_PQ_ExecuteStatement es[] = {
306 1 : GNUNET_PQ_make_execute ("START TRANSACTION ISOLATION LEVEL READ COMMITTED"),
307 : GNUNET_PQ_EXECUTE_STATEMENT_END
308 : };
309 :
310 1 : check_connection (pg);
311 1 : postgres_preflight (pg);
312 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313 : "Starting merchant DB transaction %s (READ COMMITTED)\n",
314 : name);
315 1 : if (GNUNET_OK !=
316 1 : GNUNET_PQ_exec_statements (pg->conn,
317 : es))
318 : {
319 0 : TALER_LOG_ERROR ("Failed to start transaction\n");
320 0 : GNUNET_break (0);
321 0 : return GNUNET_SYSERR;
322 : }
323 1 : pg->transaction_name = name;
324 1 : return GNUNET_OK;
325 : }
326 :
327 :
328 : /**
329 : * Roll back the current transaction of a database connection.
330 : *
331 : * @param cls the `struct PostgresClosure` with the plugin-specific state
332 : */
333 : static void
334 1 : postgres_rollback (void *cls)
335 : {
336 1 : struct PostgresClosure *pg = cls;
337 1 : struct GNUNET_PQ_ExecuteStatement es[] = {
338 1 : GNUNET_PQ_make_execute ("ROLLBACK"),
339 : GNUNET_PQ_EXECUTE_STATEMENT_END
340 : };
341 :
342 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
343 : "Rolling back merchant DB transaction `%s'\n",
344 : pg->transaction_name);
345 1 : GNUNET_break (GNUNET_OK ==
346 : GNUNET_PQ_exec_statements (pg->conn,
347 : es));
348 1 : pg->transaction_name = NULL;
349 1 : }
350 :
351 :
352 : /**
353 : * Commit the current transaction of a database connection.
354 : *
355 : * @param cls the `struct PostgresClosure` with the plugin-specific state
356 : * @return transaction status code
357 : */
358 : static enum GNUNET_DB_QueryStatus
359 17 : postgres_commit (void *cls)
360 : {
361 17 : struct PostgresClosure *pg = cls;
362 17 : struct GNUNET_PQ_QueryParam params[] = {
363 : GNUNET_PQ_query_param_end
364 : };
365 :
366 17 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
367 : "Committing merchant DB transaction %s\n",
368 : pg->transaction_name);
369 17 : pg->transaction_name = NULL;
370 17 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
371 : "end_transaction",
372 : params);
373 : }
374 :
375 :
376 : /**
377 : * Context for lookup_instances().
378 : */
379 : struct LookupInstancesContext
380 : {
381 : /**
382 : * Function to call with the results.
383 : */
384 : TALER_MERCHANTDB_InstanceCallback cb;
385 :
386 : /**
387 : * Closure for @e cb.
388 : */
389 : void *cb_cls;
390 :
391 : /**
392 : * Database context.
393 : */
394 : struct PostgresClosure *pg;
395 :
396 : /**
397 : * Instance settings, valid only during find_instances_cb().
398 : */
399 : struct TALER_MERCHANTDB_InstanceSettings is;
400 :
401 : /**
402 : * Instance authentication settings, valid only during find_instances_cb().
403 : */
404 : struct TALER_MERCHANTDB_InstanceAuthSettings ias;
405 :
406 : /**
407 : * Instance serial number, valid only during find_instances_cb().
408 : */
409 : uint64_t instance_serial;
410 :
411 : /**
412 : * Public key of the current instance, valid only during find_instances_cb().
413 : */
414 : struct TALER_MerchantPublicKeyP merchant_pub;
415 :
416 : /**
417 : * Set to the return value on errors.
418 : */
419 : enum GNUNET_DB_QueryStatus qs;
420 :
421 : /**
422 : * true if we only are interested in instances for which we have the private key.
423 : */
424 : bool active_only;
425 : };
426 :
427 :
428 : /**
429 : * We are processing an instances lookup and have the @a accounts.
430 : * Find the private key if possible, and invoke the callback.
431 : *
432 : * @param lic context we are handling
433 : * @param num_accounts length of @a accounts array
434 : * @param accounts information about accounts of the instance in @a lic
435 : */
436 : static void
437 19 : call_with_accounts (struct LookupInstancesContext *lic,
438 : unsigned int num_accounts,
439 : const struct TALER_MERCHANTDB_AccountDetails accounts[])
440 : {
441 19 : struct PostgresClosure *pg = lic->pg;
442 : enum GNUNET_DB_QueryStatus qs;
443 19 : struct GNUNET_PQ_QueryParam params[] = {
444 19 : GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
445 : GNUNET_PQ_query_param_end
446 : };
447 : struct TALER_MerchantPrivateKeyP merchant_priv;
448 19 : struct GNUNET_PQ_ResultSpec rs[] = {
449 19 : GNUNET_PQ_result_spec_auto_from_type ("merchant_priv",
450 : &merchant_priv),
451 : GNUNET_PQ_result_spec_end
452 : };
453 :
454 19 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
455 : "lookup_instance_private_key",
456 : params,
457 : rs);
458 19 : if (qs < 0)
459 : {
460 0 : GNUNET_break (0);
461 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
462 2 : return;
463 : }
464 19 : if ( (0 == qs) &&
465 5 : (lic->active_only) )
466 2 : return; /* skip, not interesting */
467 17 : lic->cb (lic->cb_cls,
468 17 : &lic->merchant_pub,
469 : (0 == qs) ? NULL : &merchant_priv,
470 17 : &lic->is,
471 17 : &lic->ias,
472 : num_accounts,
473 : accounts);
474 : }
475 :
476 :
477 : /**
478 : * Function to be called with the results of a SELECT statement
479 : * that has returned @a num_results results about accounts.
480 : *
481 : * @param cls of type `struct FindInstancesContext *`
482 : * @param result the postgres result
483 : * @param num_results the number of results in @a result
484 : */
485 : static void
486 19 : lookup_accounts_cb (void *cls,
487 : PGresult *result,
488 : unsigned int num_results)
489 19 : {
490 19 : struct LookupInstancesContext *lic = cls;
491 19 : char *paytos[num_results];
492 19 : struct TALER_MERCHANTDB_AccountDetails accounts[num_results];
493 :
494 34 : for (unsigned int i = 0; i < num_results; i++)
495 : {
496 : uint8_t active;
497 15 : struct GNUNET_PQ_ResultSpec rs[] = {
498 15 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
499 : &accounts[i].h_wire),
500 15 : GNUNET_PQ_result_spec_auto_from_type ("salt",
501 : &accounts[i].salt),
502 15 : GNUNET_PQ_result_spec_string ("payto_uri",
503 : &paytos[i]),
504 15 : GNUNET_PQ_result_spec_auto_from_type ("active",
505 : &active),
506 : GNUNET_PQ_result_spec_end
507 : };
508 :
509 15 : if (GNUNET_OK !=
510 15 : GNUNET_PQ_extract_result (result,
511 : rs,
512 : i))
513 : {
514 0 : GNUNET_break (0);
515 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
516 0 : for (unsigned int j = 0; j < i; j++)
517 0 : GNUNET_free (paytos[j]);
518 0 : return;
519 : }
520 15 : accounts[i].active = (0 != active);
521 15 : accounts[i].payto_uri = paytos[i];
522 : }
523 19 : call_with_accounts (lic,
524 : num_results,
525 : accounts);
526 34 : for (unsigned int i = 0; i < num_results; i++)
527 15 : GNUNET_free (paytos[i]);
528 : }
529 :
530 :
531 : /**
532 : * Function to be called with the results of a SELECT statement
533 : * that has returned @a num_results results about instances.
534 : *
535 : * @param cls of type `struct FindInstancesContext *`
536 : * @param result the postgres result
537 : * @param num_results the number of results in @a result
538 : */
539 : static void
540 20 : lookup_instances_cb (void *cls,
541 : PGresult *result,
542 : unsigned int num_results)
543 : {
544 20 : struct LookupInstancesContext *lic = cls;
545 20 : struct PostgresClosure *pg = lic->pg;
546 :
547 39 : for (unsigned int i = 0; i < num_results; i++)
548 : {
549 : bool no_auth;
550 : bool no_salt;
551 19 : struct GNUNET_PQ_ResultSpec rs[] = {
552 19 : GNUNET_PQ_result_spec_uint64 ("merchant_serial",
553 : &lic->instance_serial),
554 19 : GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
555 : &lic->merchant_pub),
556 19 : GNUNET_PQ_result_spec_allow_null (
557 19 : GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
558 : &lic->ias.auth_hash),
559 : &no_auth),
560 19 : GNUNET_PQ_result_spec_allow_null (
561 19 : GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
562 : &lic->ias.auth_salt),
563 : &no_salt),
564 19 : GNUNET_PQ_result_spec_string ("merchant_id",
565 : &lic->is.id),
566 19 : GNUNET_PQ_result_spec_string ("merchant_name",
567 : &lic->is.name),
568 19 : TALER_PQ_result_spec_json ("address",
569 : &lic->is.address),
570 19 : TALER_PQ_result_spec_json ("jurisdiction",
571 : &lic->is.jurisdiction),
572 19 : TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_deposit_fee",
573 : &lic->is.default_max_deposit_fee),
574 19 : TALER_PQ_RESULT_SPEC_AMOUNT ("default_max_wire_fee",
575 : &lic->is.default_max_wire_fee),
576 19 : GNUNET_PQ_result_spec_uint32 ("default_wire_fee_amortization",
577 : &lic->is.default_wire_fee_amortization),
578 19 : GNUNET_PQ_result_spec_relative_time ("default_wire_transfer_delay",
579 : &lic->is.default_wire_transfer_delay),
580 19 : GNUNET_PQ_result_spec_relative_time ("default_pay_delay",
581 : &lic->is.default_pay_delay),
582 19 : GNUNET_PQ_result_spec_allow_null (
583 : GNUNET_PQ_result_spec_string ("website",
584 : &lic->is.website),
585 : NULL),
586 19 : GNUNET_PQ_result_spec_allow_null (
587 : GNUNET_PQ_result_spec_string ("email",
588 : &lic->is.email),
589 : NULL),
590 19 : GNUNET_PQ_result_spec_allow_null (
591 : GNUNET_PQ_result_spec_string ("logo",
592 : &lic->is.logo),
593 : NULL),
594 : GNUNET_PQ_result_spec_end
595 : };
596 19 : struct GNUNET_PQ_QueryParam params[] = {
597 19 : GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
598 : GNUNET_PQ_query_param_end
599 : };
600 :
601 19 : memset (&lic->ias.auth_salt,
602 : 0,
603 : sizeof (lic->ias.auth_salt));
604 19 : memset (&lic->ias.auth_hash,
605 : 0,
606 : sizeof (lic->ias.auth_hash));
607 19 : if (GNUNET_OK !=
608 19 : GNUNET_PQ_extract_result (result,
609 : rs,
610 : i))
611 : {
612 0 : GNUNET_break (0);
613 0 : lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
614 0 : return;
615 : }
616 19 : lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn,
617 : "lookup_accounts",
618 : params,
619 : &lookup_accounts_cb,
620 : lic);
621 19 : if (0 > lic->qs)
622 : {
623 : /* lookup_accounts_cb() did not run, still notify about the
624 : account-less instance! */
625 0 : call_with_accounts (lic,
626 : 0,
627 : NULL);
628 : }
629 19 : GNUNET_PQ_cleanup_result (rs);
630 19 : if (0 > lic->qs)
631 0 : break;
632 : }
633 : }
634 :
635 :
636 : /**
637 : * Lookup all of the instances this backend has configured.
638 : *
639 : * @param cls closure
640 : * @param active_only only find 'active' instances
641 : * @param cb function to call on all instances found
642 : * @param cb_cls closure for @a cb
643 : */
644 : static enum GNUNET_DB_QueryStatus
645 12 : postgres_lookup_instances (void *cls,
646 : bool active_only,
647 : TALER_MERCHANTDB_InstanceCallback cb,
648 : void *cb_cls)
649 : {
650 12 : struct PostgresClosure *pg = cls;
651 12 : struct LookupInstancesContext lic = {
652 : .cb = cb,
653 : .cb_cls = cb_cls,
654 : .active_only = active_only,
655 : .pg = pg
656 : };
657 12 : struct GNUNET_PQ_QueryParam params[] = {
658 : GNUNET_PQ_query_param_end
659 : };
660 : enum GNUNET_DB_QueryStatus qs;
661 :
662 12 : check_connection (pg);
663 12 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
664 : "lookup_instances",
665 : params,
666 : &lookup_instances_cb,
667 : &lic);
668 12 : if (0 > lic.qs)
669 0 : return lic.qs;
670 12 : return qs;
671 : }
672 :
673 :
674 : /**
675 : * Lookup all one of the instances this backend has configured.
676 : *
677 : * @param cls closure
678 : * @param id instance ID to resolve
679 : * @param active_only only find 'active' instances
680 : * @param cb function to call on all instances found
681 : * @param cb_cls closure for @a cb
682 : */
683 : static enum GNUNET_DB_QueryStatus
684 8 : postgres_lookup_instance (void *cls,
685 : const char *id,
686 : bool active_only,
687 : TALER_MERCHANTDB_InstanceCallback cb,
688 : void *cb_cls)
689 : {
690 8 : struct PostgresClosure *pg = cls;
691 8 : struct LookupInstancesContext lic = {
692 : .cb = cb,
693 : .cb_cls = cb_cls,
694 : .active_only = active_only,
695 : .pg = pg
696 : };
697 8 : struct GNUNET_PQ_QueryParam params[] = {
698 8 : GNUNET_PQ_query_param_string (id),
699 : GNUNET_PQ_query_param_end
700 : };
701 : enum GNUNET_DB_QueryStatus qs;
702 :
703 8 : check_connection (pg);
704 8 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
705 : "lookup_instance",
706 : params,
707 : &lookup_instances_cb,
708 : &lic);
709 8 : if (0 > lic.qs)
710 0 : return lic.qs;
711 8 : return qs;
712 : }
713 :
714 :
715 : /**
716 : * Lookup authentication data of an instance.
717 : *
718 : * @param cls closure
719 : * @param instance_id instance to query
720 : * @param[out] ias where to store the auth data
721 : */
722 : static enum GNUNET_DB_QueryStatus
723 2 : postgres_lookup_instance_auth (
724 : void *cls,
725 : const char *instance_id,
726 : struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
727 : {
728 2 : struct PostgresClosure *pg = cls;
729 2 : struct GNUNET_PQ_QueryParam params[] = {
730 2 : GNUNET_PQ_query_param_string (instance_id),
731 : GNUNET_PQ_query_param_end
732 : };
733 2 : struct GNUNET_PQ_ResultSpec rs[] = {
734 2 : GNUNET_PQ_result_spec_auto_from_type ("auth_hash",
735 : &ias->auth_hash),
736 2 : GNUNET_PQ_result_spec_auto_from_type ("auth_salt",
737 : &ias->auth_salt),
738 : GNUNET_PQ_result_spec_end
739 : };
740 :
741 2 : check_connection (pg);
742 2 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
743 : "lookup_instance_auth",
744 : params,
745 : rs);
746 : }
747 :
748 :
749 : /**
750 : * Insert information about an instance into our database.
751 : *
752 : * @param cls closure
753 : * @param merchant_pub public key of the instance
754 : * @param merchant_priv private key of the instance
755 : * @param is details about the instance
756 : * @param ias authentication settings for the instance
757 : * @return database result code
758 : */
759 : static enum GNUNET_DB_QueryStatus
760 17 : postgres_insert_instance (
761 : void *cls,
762 : const struct TALER_MerchantPublicKeyP *merchant_pub,
763 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
764 : const struct TALER_MERCHANTDB_InstanceSettings *is,
765 : const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
766 : {
767 17 : struct PostgresClosure *pg = cls;
768 68 : struct GNUNET_PQ_QueryParam params[] = {
769 17 : GNUNET_PQ_query_param_auto_from_type (merchant_pub),
770 17 : GNUNET_PQ_query_param_auto_from_type (&ias->auth_hash),
771 17 : GNUNET_PQ_query_param_auto_from_type (&ias->auth_salt),
772 17 : GNUNET_PQ_query_param_string (is->id),
773 17 : GNUNET_PQ_query_param_string (is->name),
774 17 : TALER_PQ_query_param_json (is->address),
775 17 : TALER_PQ_query_param_json (is->jurisdiction),
776 17 : TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
777 17 : TALER_PQ_query_param_amount (&is->default_max_wire_fee),
778 17 : GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
779 17 : GNUNET_PQ_query_param_relative_time (
780 : &is->default_wire_transfer_delay),
781 17 : GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
782 17 : (NULL == is->website)
783 17 : ? GNUNET_PQ_query_param_null ()
784 0 : : GNUNET_PQ_query_param_string (is->website),
785 17 : (NULL == is->email)
786 17 : ? GNUNET_PQ_query_param_null ()
787 0 : : GNUNET_PQ_query_param_string (is->email),
788 17 : (NULL == is->logo)
789 17 : ? GNUNET_PQ_query_param_null ()
790 0 : : GNUNET_PQ_query_param_string (is->logo),
791 : GNUNET_PQ_query_param_end
792 : };
793 17 : struct GNUNET_PQ_QueryParam params_priv[] = {
794 17 : GNUNET_PQ_query_param_auto_from_type (merchant_priv),
795 17 : GNUNET_PQ_query_param_string (is->id),
796 : GNUNET_PQ_query_param_end
797 : };
798 : enum GNUNET_DB_QueryStatus qs;
799 :
800 17 : check_connection (pg);
801 17 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
802 : "insert_instance",
803 : params);
804 17 : if (qs <= 0)
805 1 : return qs;
806 16 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
807 : "insert_keys",
808 : params_priv);
809 : }
810 :
811 :
812 : /**
813 : * Insert information about an instance's account into our database.
814 : *
815 : * @param cls closure
816 : * @param id identifier of the instance
817 : * @param account_details details about the account
818 : * @return database result code
819 : */
820 : static enum GNUNET_DB_QueryStatus
821 14 : postgres_insert_account (
822 : void *cls,
823 : const char *id,
824 : const struct TALER_MERCHANTDB_AccountDetails *account_details)
825 : {
826 14 : struct PostgresClosure *pg = cls;
827 14 : uint8_t active = account_details->active;
828 14 : struct GNUNET_PQ_QueryParam params[] = {
829 14 : GNUNET_PQ_query_param_string (id),
830 14 : GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
831 14 : GNUNET_PQ_query_param_auto_from_type (&account_details->salt),
832 14 : GNUNET_PQ_query_param_string (account_details->payto_uri),
833 14 : GNUNET_PQ_query_param_auto_from_type (&active),
834 : GNUNET_PQ_query_param_end
835 : };
836 :
837 14 : check_connection (pg);
838 14 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
839 : "insert_account",
840 : params);
841 : }
842 :
843 :
844 : /**
845 : * Closure for kyc_status_cb().
846 : */
847 : struct KycStatusContext
848 : {
849 : /**
850 : * Function to call with results.
851 : */
852 : TALER_MERCHANTDB_KycCallback kyc_cb;
853 :
854 : /**
855 : * Closure for @e kyc_cb.
856 : */
857 : void *kyc_cb_cls;
858 :
859 : /**
860 : * Filter, NULL to not filter.
861 : */
862 : const struct TALER_MerchantWireHashP *h_wire;
863 :
864 : /**
865 : * Filter, NULL to not filter.
866 : */
867 : const char *exchange_url;
868 :
869 : /**
870 : * Number of results found.
871 : */
872 : unsigned int count;
873 :
874 : /**
875 : * Set to true on failure(s).
876 : */
877 : bool failure;
878 : };
879 :
880 :
881 : /**
882 : * Function to be called with the results of a SELECT statement
883 : * that has returned @a num_results results about accounts.
884 : *
885 : * @param[in,out] cls of type `struct KycStatusContext *`
886 : * @param result the postgres result
887 : * @param num_results the number of results in @a result
888 : */
889 : static void
890 4 : kyc_status_cb (void *cls,
891 : PGresult *result,
892 : unsigned int num_results)
893 : {
894 4 : struct KycStatusContext *ksc = cls;
895 :
896 12 : for (unsigned int i = 0; i < num_results; i++)
897 : {
898 : struct TALER_MerchantWireHashP h_wire;
899 : uint64_t kyc_serial;
900 : char *exchange_url;
901 : char *payto_uri;
902 : struct GNUNET_TIME_Timestamp last_check;
903 : uint8_t kyc_ok;
904 8 : struct GNUNET_PQ_ResultSpec rs[] = {
905 8 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
906 : &h_wire),
907 8 : GNUNET_PQ_result_spec_uint64 ("exchange_kyc_serial",
908 : &kyc_serial),
909 8 : GNUNET_PQ_result_spec_string ("payto_uri",
910 : &payto_uri),
911 8 : GNUNET_PQ_result_spec_string ("exchange_url",
912 : &exchange_url),
913 8 : GNUNET_PQ_result_spec_timestamp ("kyc_timestamp",
914 : &last_check),
915 8 : GNUNET_PQ_result_spec_auto_from_type ("kyc_ok",
916 : &kyc_ok),
917 : GNUNET_PQ_result_spec_end
918 : };
919 :
920 8 : if (GNUNET_OK !=
921 8 : GNUNET_PQ_extract_result (result,
922 : rs,
923 : i))
924 : {
925 0 : GNUNET_break (0);
926 0 : ksc->failure = true;
927 0 : return;
928 : }
929 8 : if ( (NULL != ksc->exchange_url) &&
930 4 : (0 != strcmp (ksc->exchange_url,
931 : exchange_url)) )
932 : {
933 2 : GNUNET_PQ_cleanup_result (rs);
934 2 : continue;
935 : }
936 6 : if ( (NULL != ksc->h_wire) &&
937 1 : (0 != GNUNET_memcmp (ksc->h_wire,
938 : &h_wire)) )
939 : {
940 0 : GNUNET_PQ_cleanup_result (rs);
941 0 : continue;
942 : }
943 6 : ksc->count++;
944 6 : ksc->kyc_cb (ksc->kyc_cb_cls,
945 : &h_wire,
946 : kyc_serial,
947 : payto_uri,
948 : exchange_url,
949 : last_check,
950 : 0 != kyc_ok);
951 6 : GNUNET_PQ_cleanup_result (rs);
952 : }
953 : }
954 :
955 :
956 : /**
957 : * Check an instance's account's KYC status.
958 : *
959 : * @param cls closure
960 : * @param merchant_id merchant backend instance ID
961 : * @param h_wire hash of the wire account to check,
962 : * NULL to check all accounts of the merchant
963 : * @param exchange_url base URL of the exchange to check,
964 : * NULL to check all exchanges
965 : * @param kyc_cb KYC status callback to invoke
966 : * @param kyc_cb_cls closure for @a kyc_cb
967 : * @return database result code
968 : */
969 : static enum GNUNET_DB_QueryStatus
970 4 : postgres_account_kyc_get_status (void *cls,
971 : const char *merchant_id,
972 : const struct TALER_MerchantWireHashP *h_wire,
973 : const char *exchange_url,
974 : TALER_MERCHANTDB_KycCallback kyc_cb,
975 : void *kyc_cb_cls)
976 : {
977 4 : struct PostgresClosure *pg = cls;
978 4 : struct KycStatusContext ksc = {
979 : .kyc_cb = kyc_cb,
980 : .kyc_cb_cls = kyc_cb_cls,
981 : .exchange_url = exchange_url,
982 : .h_wire = h_wire
983 : };
984 4 : struct GNUNET_PQ_QueryParam params[] = {
985 4 : GNUNET_PQ_query_param_string (merchant_id),
986 : GNUNET_PQ_query_param_end
987 : };
988 : enum GNUNET_DB_QueryStatus qs;
989 :
990 4 : check_connection (pg);
991 4 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
992 : "lookup_kyc_status",
993 : params,
994 : &kyc_status_cb,
995 : &ksc);
996 4 : if (ksc.failure)
997 : {
998 0 : GNUNET_break (0);
999 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1000 : }
1001 4 : if (0 > qs)
1002 0 : return qs;
1003 4 : return ksc.count;
1004 : }
1005 :
1006 :
1007 : /**
1008 : * Update an instance's account's KYC status.
1009 : *
1010 : * @param cls closure
1011 : * @param merchant_id merchant backend instance ID
1012 : * @param h_wire hash of the wire account to check
1013 : * @param exchange_url base URL of the exchange to check
1014 : * @param exchange_kyc_serial serial number for our account at the exchange (0 if unknown)
1015 : * @param exchange_sig signature of the exchange, or NULL for none
1016 : * @param exchange_pub public key of the exchange, or NULL for none
1017 : * @param timestamp timestamp to store
1018 : * @param kyc_ok current KYC status (true for satisfied)
1019 : * @return database result code
1020 : */
1021 : static enum GNUNET_DB_QueryStatus
1022 3 : postgres_account_kyc_set_status (
1023 : void *cls,
1024 : const char *merchant_id,
1025 : const struct TALER_MerchantWireHashP *h_wire,
1026 : const char *exchange_url,
1027 : uint64_t exchange_kyc_serial,
1028 : const struct TALER_ExchangeSignatureP *exchange_sig,
1029 : const struct TALER_ExchangePublicKeyP *exchange_pub,
1030 : struct GNUNET_TIME_Timestamp timestamp,
1031 : bool kyc_ok)
1032 : {
1033 3 : struct PostgresClosure *pg = cls;
1034 3 : uint8_t ok = kyc_ok;
1035 6 : struct GNUNET_PQ_QueryParam params[] = {
1036 3 : GNUNET_PQ_query_param_string (merchant_id),
1037 3 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1038 3 : GNUNET_PQ_query_param_string (exchange_url),
1039 3 : GNUNET_PQ_query_param_uint64 (&exchange_kyc_serial),
1040 3 : GNUNET_PQ_query_param_timestamp (×tamp),
1041 3 : GNUNET_PQ_query_param_auto_from_type (&ok),
1042 : exchange_pub
1043 0 : ? GNUNET_PQ_query_param_auto_from_type (exchange_pub)
1044 3 : : GNUNET_PQ_query_param_null (),
1045 : exchange_sig
1046 0 : ? GNUNET_PQ_query_param_auto_from_type (exchange_sig)
1047 3 : : GNUNET_PQ_query_param_null (),
1048 : GNUNET_PQ_query_param_end
1049 : };
1050 :
1051 3 : check_connection (pg);
1052 3 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1053 : "upsert_account_kyc",
1054 : params);
1055 : }
1056 :
1057 :
1058 : /**
1059 : * Delete private key of an instance from our database.
1060 : *
1061 : * @param cls closure
1062 : * @param merchant_id identifier of the instance
1063 : * @return database result code
1064 : */
1065 : static enum GNUNET_DB_QueryStatus
1066 2 : postgres_delete_instance_private_key (
1067 : void *cls,
1068 : const char *merchant_id)
1069 : {
1070 2 : struct PostgresClosure *pg = cls;
1071 2 : struct GNUNET_PQ_QueryParam params[] = {
1072 2 : GNUNET_PQ_query_param_string (merchant_id),
1073 : GNUNET_PQ_query_param_end
1074 : };
1075 :
1076 2 : check_connection (pg);
1077 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1078 : "delete_key",
1079 : params);
1080 : }
1081 :
1082 :
1083 : /**
1084 : * Purge an instance and all associated information from our database.
1085 : * Highly likely to cause undesired data loss. Use with caution.
1086 : *
1087 : * @param cls closure
1088 : * @param merchant_id identifier of the instance
1089 : * @return database result code
1090 : */
1091 : static enum GNUNET_DB_QueryStatus
1092 2 : postgres_purge_instance (void *cls,
1093 : const char *merchant_id)
1094 : {
1095 2 : struct PostgresClosure *pg = cls;
1096 2 : struct GNUNET_PQ_QueryParam params[] = {
1097 2 : GNUNET_PQ_query_param_string (merchant_id),
1098 : GNUNET_PQ_query_param_end
1099 : };
1100 :
1101 2 : check_connection (pg);
1102 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1103 : "purge_instance",
1104 : params);
1105 : }
1106 :
1107 :
1108 : /**
1109 : * Update information about an instance into our database.
1110 : *
1111 : * @param cls closure
1112 : * @param is details about the instance
1113 : * @return database result code
1114 : */
1115 : static enum GNUNET_DB_QueryStatus
1116 2 : postgres_update_instance (void *cls,
1117 : const struct TALER_MERCHANTDB_InstanceSettings *is)
1118 : {
1119 2 : struct PostgresClosure *pg = cls;
1120 8 : struct GNUNET_PQ_QueryParam params[] = {
1121 2 : GNUNET_PQ_query_param_string (is->id),
1122 2 : GNUNET_PQ_query_param_string (is->name),
1123 2 : TALER_PQ_query_param_json (is->address),
1124 2 : TALER_PQ_query_param_json (is->jurisdiction),
1125 2 : TALER_PQ_query_param_amount (&is->default_max_deposit_fee),
1126 2 : TALER_PQ_query_param_amount (&is->default_max_wire_fee),
1127 2 : GNUNET_PQ_query_param_uint32 (&is->default_wire_fee_amortization),
1128 2 : GNUNET_PQ_query_param_relative_time (
1129 : &is->default_wire_transfer_delay),
1130 2 : GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
1131 2 : (NULL == is->website)
1132 2 : ? GNUNET_PQ_query_param_null ()
1133 0 : : GNUNET_PQ_query_param_string (is->website),
1134 2 : (NULL == is->email)
1135 2 : ? GNUNET_PQ_query_param_null ()
1136 0 : : GNUNET_PQ_query_param_string (is->email),
1137 2 : (NULL == is->logo)
1138 2 : ? GNUNET_PQ_query_param_null ()
1139 0 : : GNUNET_PQ_query_param_string (is->logo),
1140 : GNUNET_PQ_query_param_end
1141 : };
1142 :
1143 2 : check_connection (pg);
1144 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1145 : "update_instance",
1146 : params);
1147 : }
1148 :
1149 :
1150 : /**
1151 : * Update information about an instance's authentication settings
1152 : * into our database.
1153 : *
1154 : * @param cls closure
1155 : * @param merchant_id identity of the instance
1156 : * @param is authentication details about the instance
1157 : * @return database result code
1158 : */
1159 : static enum GNUNET_DB_QueryStatus
1160 2 : postgres_update_instance_auth (
1161 : void *cls,
1162 : const char *merchant_id,
1163 : const struct TALER_MERCHANTDB_InstanceAuthSettings *is)
1164 : {
1165 2 : struct PostgresClosure *pg = cls;
1166 2 : struct GNUNET_PQ_QueryParam params[] = {
1167 2 : GNUNET_PQ_query_param_string (merchant_id),
1168 2 : GNUNET_PQ_query_param_auto_from_type (&is->auth_hash),
1169 2 : GNUNET_PQ_query_param_auto_from_type (&is->auth_salt),
1170 : GNUNET_PQ_query_param_end
1171 : };
1172 :
1173 2 : check_connection (pg);
1174 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1175 : "update_instance_auth",
1176 : params);
1177 : }
1178 :
1179 :
1180 : /**
1181 : * Set an instance's account in our database to "inactive".
1182 : *
1183 : * @param cls closure
1184 : * @param merchant_id merchant backend instance ID
1185 : * @param h_wire hash of the wire account to set to inactive
1186 : * @return database result code
1187 : */
1188 : static enum GNUNET_DB_QueryStatus
1189 2 : postgres_inactivate_account (void *cls,
1190 : const char *merchant_id,
1191 : const struct TALER_MerchantWireHashP *h_wire)
1192 : {
1193 2 : struct PostgresClosure *pg = cls;
1194 2 : struct GNUNET_PQ_QueryParam params[] = {
1195 2 : GNUNET_PQ_query_param_string (merchant_id),
1196 2 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1197 : GNUNET_PQ_query_param_end
1198 : };
1199 :
1200 2 : check_connection (pg);
1201 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1202 : "inactivate_account",
1203 : params);
1204 : }
1205 :
1206 :
1207 : /**
1208 : * Set an instance's account in our database to "active".
1209 : *
1210 : * @param cls closure
1211 : * @param merchant_id merchant backend instance ID
1212 : * @param h_wire hash of the wire account to set to active
1213 : * @return database result code
1214 : */
1215 : static enum GNUNET_DB_QueryStatus
1216 0 : postgres_activate_account (void *cls,
1217 : const char *merchant_id,
1218 : const struct TALER_MerchantWireHashP *h_wire)
1219 : {
1220 0 : struct PostgresClosure *pg = cls;
1221 0 : struct GNUNET_PQ_QueryParam params[] = {
1222 0 : GNUNET_PQ_query_param_string (merchant_id),
1223 0 : GNUNET_PQ_query_param_auto_from_type (h_wire),
1224 : GNUNET_PQ_query_param_end
1225 : };
1226 :
1227 0 : check_connection (pg);
1228 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1229 : "activate_account",
1230 : params);
1231 : }
1232 :
1233 :
1234 : /**
1235 : * Context used for postgres_lookup_products().
1236 : */
1237 : struct LookupProductsContext
1238 : {
1239 : /**
1240 : * Function to call with the results.
1241 : */
1242 : TALER_MERCHANTDB_ProductsCallback cb;
1243 :
1244 : /**
1245 : * Closure for @a cb.
1246 : */
1247 : void *cb_cls;
1248 :
1249 : /**
1250 : * Did database result extraction fail?
1251 : */
1252 : bool extract_failed;
1253 : };
1254 :
1255 :
1256 : /**
1257 : * Function to be called with the results of a SELECT statement
1258 : * that has returned @a num_results results about products.
1259 : *
1260 : * @param[in,out] cls of type `struct LookupProductsContext *`
1261 : * @param result the postgres result
1262 : * @param num_results the number of results in @a result
1263 : */
1264 : static void
1265 3 : lookup_products_cb (void *cls,
1266 : PGresult *result,
1267 : unsigned int num_results)
1268 : {
1269 3 : struct LookupProductsContext *plc = cls;
1270 :
1271 6 : for (unsigned int i = 0; i < num_results; i++)
1272 : {
1273 : char *product_id;
1274 3 : struct GNUNET_PQ_ResultSpec rs[] = {
1275 3 : GNUNET_PQ_result_spec_string ("product_id",
1276 : &product_id),
1277 : GNUNET_PQ_result_spec_end
1278 : };
1279 :
1280 3 : if (GNUNET_OK !=
1281 3 : GNUNET_PQ_extract_result (result,
1282 : rs,
1283 : i))
1284 : {
1285 0 : GNUNET_break (0);
1286 0 : plc->extract_failed = true;
1287 0 : return;
1288 : }
1289 3 : plc->cb (plc->cb_cls,
1290 : product_id);
1291 3 : GNUNET_PQ_cleanup_result (rs);
1292 : }
1293 : }
1294 :
1295 :
1296 : /**
1297 : * Lookup all of the products the given instance has configured.
1298 : *
1299 : * @param cls closure
1300 : * @param instance_id instance to lookup products for
1301 : * @param cb function to call on all products found
1302 : * @param cb_cls closure for @a cb
1303 : * @return database result code
1304 : */
1305 : static enum GNUNET_DB_QueryStatus
1306 3 : postgres_lookup_products (void *cls,
1307 : const char *instance_id,
1308 : TALER_MERCHANTDB_ProductsCallback cb,
1309 : void *cb_cls)
1310 : {
1311 3 : struct PostgresClosure *pg = cls;
1312 3 : struct LookupProductsContext plc = {
1313 : .cb = cb,
1314 : .cb_cls = cb_cls,
1315 : /* Can be overwritten by the lookup_products_cb */
1316 : .extract_failed = false,
1317 : };
1318 3 : struct GNUNET_PQ_QueryParam params[] = {
1319 3 : GNUNET_PQ_query_param_string (instance_id),
1320 : GNUNET_PQ_query_param_end
1321 : };
1322 : enum GNUNET_DB_QueryStatus qs;
1323 :
1324 3 : check_connection (pg);
1325 3 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
1326 : "lookup_products",
1327 : params,
1328 : &lookup_products_cb,
1329 : &plc);
1330 : /* If there was an error inside lookup_products_cb, return a hard error. */
1331 3 : if (plc.extract_failed)
1332 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1333 3 : return qs;
1334 : }
1335 :
1336 :
1337 : /**
1338 : * Lookup details about a particular product.
1339 : *
1340 : * @param cls closure
1341 : * @param instance_id instance to lookup products for
1342 : * @param product_id product to lookup
1343 : * @param[out] pd set to the product details on success, can be NULL
1344 : * (in that case we only want to check if the product exists)
1345 : * @return database result code
1346 : */
1347 : static enum GNUNET_DB_QueryStatus
1348 3 : postgres_lookup_product (void *cls,
1349 : const char *instance_id,
1350 : const char *product_id,
1351 : struct TALER_MERCHANTDB_ProductDetails *pd)
1352 : {
1353 3 : struct PostgresClosure *pg = cls;
1354 3 : struct GNUNET_PQ_QueryParam params[] = {
1355 3 : GNUNET_PQ_query_param_string (instance_id),
1356 3 : GNUNET_PQ_query_param_string (product_id),
1357 : GNUNET_PQ_query_param_end
1358 : };
1359 :
1360 3 : if (NULL == pd)
1361 : {
1362 1 : struct GNUNET_PQ_ResultSpec rs_null[] = {
1363 : GNUNET_PQ_result_spec_end
1364 : };
1365 :
1366 1 : check_connection (pg);
1367 1 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1368 : "lookup_product",
1369 : params,
1370 : rs_null);
1371 : }
1372 : else
1373 : {
1374 2 : struct GNUNET_PQ_ResultSpec rs[] = {
1375 2 : GNUNET_PQ_result_spec_string ("description",
1376 : &pd->description),
1377 2 : TALER_PQ_result_spec_json ("description_i18n",
1378 : &pd->description_i18n),
1379 2 : GNUNET_PQ_result_spec_string ("unit",
1380 : &pd->unit),
1381 2 : TALER_PQ_RESULT_SPEC_AMOUNT ("price",
1382 : &pd->price),
1383 2 : TALER_PQ_result_spec_json ("taxes",
1384 : &pd->taxes),
1385 2 : GNUNET_PQ_result_spec_uint64 ("total_stock",
1386 : &pd->total_stock),
1387 2 : GNUNET_PQ_result_spec_uint64 ("total_sold",
1388 : &pd->total_sold),
1389 2 : GNUNET_PQ_result_spec_uint64 ("total_lost",
1390 : &pd->total_lost),
1391 2 : GNUNET_PQ_result_spec_string ("image",
1392 : &pd->image),
1393 2 : TALER_PQ_result_spec_json ("address",
1394 : &pd->address),
1395 2 : GNUNET_PQ_result_spec_timestamp ("next_restock",
1396 : &pd->next_restock),
1397 2 : GNUNET_PQ_result_spec_uint32 ("minimum_age",
1398 : &pd->minimum_age),
1399 : GNUNET_PQ_result_spec_end
1400 : };
1401 :
1402 2 : check_connection (pg);
1403 2 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1404 : "lookup_product",
1405 : params,
1406 : rs);
1407 : }
1408 : }
1409 :
1410 :
1411 : /**
1412 : * Delete information about a product. Note that the transaction must
1413 : * enforce that no stocks are currently locked.
1414 : *
1415 : * @param cls closure
1416 : * @param instance_id instance to delete product of
1417 : * @param product_id product to delete
1418 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
1419 : * if locks prevent deletion OR product unknown
1420 : */
1421 : static enum GNUNET_DB_QueryStatus
1422 3 : postgres_delete_product (void *cls,
1423 : const char *instance_id,
1424 : const char *product_id)
1425 : {
1426 3 : struct PostgresClosure *pg = cls;
1427 3 : struct GNUNET_PQ_QueryParam params[] = {
1428 3 : GNUNET_PQ_query_param_string (instance_id),
1429 3 : GNUNET_PQ_query_param_string (product_id),
1430 : GNUNET_PQ_query_param_end
1431 : };
1432 :
1433 3 : check_connection (pg);
1434 3 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1435 : "delete_product",
1436 : params);
1437 : }
1438 :
1439 :
1440 : /**
1441 : * Insert details about a particular product.
1442 : *
1443 : * @param cls closure
1444 : * @param instance_id instance to insert product for
1445 : * @param product_id product identifier of product to insert
1446 : * @param pd the product details to insert
1447 : * @return database result code
1448 : */
1449 : static enum GNUNET_DB_QueryStatus
1450 5 : postgres_insert_product (void *cls,
1451 : const char *instance_id,
1452 : const char *product_id,
1453 : const struct TALER_MERCHANTDB_ProductDetails *pd)
1454 : {
1455 5 : struct PostgresClosure *pg = cls;
1456 5 : struct GNUNET_PQ_QueryParam params[] = {
1457 5 : GNUNET_PQ_query_param_string (instance_id),
1458 5 : GNUNET_PQ_query_param_string (product_id),
1459 5 : GNUNET_PQ_query_param_string (pd->description),
1460 5 : TALER_PQ_query_param_json (pd->description_i18n),
1461 5 : GNUNET_PQ_query_param_string (pd->unit),
1462 5 : GNUNET_PQ_query_param_string (pd->image),
1463 5 : TALER_PQ_query_param_json (pd->taxes),
1464 5 : TALER_PQ_query_param_amount (&pd->price),
1465 5 : GNUNET_PQ_query_param_uint64 (&pd->total_stock),
1466 5 : TALER_PQ_query_param_json (pd->address),
1467 5 : GNUNET_PQ_query_param_timestamp (&pd->next_restock),
1468 5 : GNUNET_PQ_query_param_uint32 (&pd->minimum_age),
1469 : GNUNET_PQ_query_param_end
1470 : };
1471 :
1472 5 : check_connection (pg);
1473 5 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1474 : "insert_product",
1475 : params);
1476 : }
1477 :
1478 :
1479 : /**
1480 : * Update details about a particular product. Note that the
1481 : * transaction must enforce that the sold/stocked/lost counters
1482 : * are not reduced (i.e. by expanding the WHERE clause on the existing
1483 : * values).
1484 : *
1485 : * @param cls closure
1486 : * @param instance_id instance to lookup products for
1487 : * @param product_id product to lookup
1488 : * @param[out] pd set to the product details on success, can be NULL
1489 : * (in that case we only want to check if the product exists)
1490 : * total_sold in @a pd is ignored, total_lost must not
1491 : * exceed total_stock minus the existing total_sold;
1492 : * total_sold and total_stock must be larger or equal to
1493 : * the existing value;
1494 : * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
1495 : * non-decreasing constraints are not met *or* if the product
1496 : * does not yet exist.
1497 : */
1498 : static enum GNUNET_DB_QueryStatus
1499 4 : postgres_update_product (void *cls,
1500 : const char *instance_id,
1501 : const char *product_id,
1502 : const struct TALER_MERCHANTDB_ProductDetails *pd)
1503 : {
1504 4 : struct PostgresClosure *pg = cls;
1505 4 : struct GNUNET_PQ_QueryParam params[] = {
1506 4 : GNUNET_PQ_query_param_string (instance_id), /* $1 */
1507 4 : GNUNET_PQ_query_param_string (product_id),
1508 4 : GNUNET_PQ_query_param_string (pd->description),
1509 4 : TALER_PQ_query_param_json (pd->description_i18n),
1510 4 : GNUNET_PQ_query_param_string (pd->unit),
1511 4 : GNUNET_PQ_query_param_string (pd->image), /* $6 */
1512 4 : TALER_PQ_query_param_json (pd->taxes),
1513 4 : TALER_PQ_query_param_amount (&pd->price), /* $8+$9 */
1514 4 : GNUNET_PQ_query_param_uint64 (&pd->total_stock), /* $10 */
1515 4 : GNUNET_PQ_query_param_uint64 (&pd->total_lost),
1516 4 : TALER_PQ_query_param_json (pd->address),
1517 4 : GNUNET_PQ_query_param_timestamp (&pd->next_restock),
1518 4 : GNUNET_PQ_query_param_uint32 (&pd->minimum_age),
1519 : GNUNET_PQ_query_param_end
1520 : };
1521 :
1522 4 : if ( (pd->total_stock < pd->total_lost + pd->total_sold) ||
1523 4 : (pd->total_lost < pd->total_lost
1524 4 : + pd->total_sold) /* integer overflow */)
1525 : {
1526 0 : GNUNET_break (0);
1527 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1528 : }
1529 4 : check_connection (pg);
1530 4 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1531 : "update_product",
1532 : params);
1533 : }
1534 :
1535 :
1536 : /**
1537 : * Lock stocks of a particular product. Note that the transaction must
1538 : * enforce that the "stocked-sold-lost >= locked" constraint holds.
1539 : *
1540 : * @param cls closure
1541 : * @param instance_id instance to lookup products for
1542 : * @param product_id product to lookup
1543 : * @param uuid the UUID that holds the lock
1544 : * @param quantity how many units should be locked
1545 : * @param expiration_time when should the lock expire
1546 : * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the
1547 : * product is unknown OR if there insufficient stocks remaining
1548 : */
1549 : static enum GNUNET_DB_QueryStatus
1550 2 : postgres_lock_product (void *cls,
1551 : const char *instance_id,
1552 : const char *product_id,
1553 : const struct GNUNET_Uuid *uuid,
1554 : uint64_t quantity,
1555 : struct GNUNET_TIME_Timestamp expiration_time)
1556 : {
1557 2 : struct PostgresClosure *pg = cls;
1558 2 : struct GNUNET_PQ_QueryParam params[] = {
1559 2 : GNUNET_PQ_query_param_string (instance_id),
1560 2 : GNUNET_PQ_query_param_string (product_id),
1561 2 : GNUNET_PQ_query_param_auto_from_type (uuid),
1562 2 : GNUNET_PQ_query_param_uint64 (&quantity),
1563 2 : GNUNET_PQ_query_param_timestamp (&expiration_time),
1564 : GNUNET_PQ_query_param_end
1565 : };
1566 :
1567 2 : check_connection (pg);
1568 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1569 : "lock_product",
1570 : params);
1571 : }
1572 :
1573 :
1574 : /**
1575 : * Release all expired product locks, including
1576 : * those from expired offers -- across all
1577 : * instances.
1578 : *
1579 : * @param cls closure
1580 : */
1581 : static void
1582 0 : postgres_expire_locks (void *cls)
1583 : {
1584 0 : struct PostgresClosure *pg = cls;
1585 0 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1586 0 : struct GNUNET_PQ_QueryParam params[] = {
1587 0 : GNUNET_PQ_query_param_absolute_time (&now),
1588 : GNUNET_PQ_query_param_end
1589 : };
1590 : enum GNUNET_DB_QueryStatus qs1;
1591 : enum GNUNET_DB_QueryStatus qs2;
1592 : enum GNUNET_DB_QueryStatus qs3;
1593 :
1594 0 : check_connection (pg);
1595 0 : qs1 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1596 : "unlock_products",
1597 : params);
1598 0 : if (qs1 < 0)
1599 : {
1600 0 : GNUNET_break (0);
1601 0 : return;
1602 : }
1603 0 : qs2 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1604 : "unlock_orders",
1605 : params);
1606 0 : if (qs2 < 0)
1607 : {
1608 0 : GNUNET_break (0);
1609 0 : return;
1610 : }
1611 0 : qs3 = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1612 : "unlock_contracts",
1613 : params);
1614 0 : if (qs3 < 0)
1615 : {
1616 0 : GNUNET_break (0);
1617 0 : return;
1618 : }
1619 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1620 : "Released %d+%d+%d locks\n",
1621 : qs1,
1622 : qs2,
1623 : qs3);
1624 : }
1625 :
1626 :
1627 : /**
1628 : * Delete information about an order. Note that the transaction must
1629 : * enforce that the order is not awaiting payment anymore.
1630 : *
1631 : * @param cls closure
1632 : * @param instance_id instance to delete order of
1633 : * @param order_id order to delete
1634 : * @param force delete claimed but unpaid orders as well
1635 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
1636 : * if pending payment prevents deletion OR order unknown
1637 : */
1638 : static enum GNUNET_DB_QueryStatus
1639 4 : postgres_delete_order (void *cls,
1640 : const char *instance_id,
1641 : const char *order_id,
1642 : bool force)
1643 : {
1644 4 : struct PostgresClosure *pg = cls;
1645 4 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1646 4 : struct GNUNET_PQ_QueryParam params[] = {
1647 4 : GNUNET_PQ_query_param_string (instance_id),
1648 4 : GNUNET_PQ_query_param_string (order_id),
1649 4 : GNUNET_PQ_query_param_absolute_time (&now),
1650 4 : GNUNET_PQ_query_param_bool (force),
1651 : GNUNET_PQ_query_param_end
1652 : };
1653 4 : struct GNUNET_PQ_QueryParam params2[] = {
1654 4 : GNUNET_PQ_query_param_string (instance_id),
1655 4 : GNUNET_PQ_query_param_string (order_id),
1656 : GNUNET_PQ_query_param_end
1657 : };
1658 : enum GNUNET_DB_QueryStatus qs;
1659 :
1660 4 : check_connection (pg);
1661 4 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
1662 : "delete_order",
1663 : params);
1664 4 : if ( (qs <= 0) || (! force))
1665 4 : return qs;
1666 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1667 : "delete_contract",
1668 : params2);
1669 : }
1670 :
1671 :
1672 : /**
1673 : * Retrieve order given its @a order_id and the @a instance_id.
1674 : *
1675 : * @param cls closure
1676 : * @param instance_id instance to obtain order of
1677 : * @param order_id order id used to perform the lookup
1678 : * @param[out] claim_token the claim token generated for the order,
1679 : * NULL to only test if the order exists
1680 : * @param[out] h_post_data set to the hash of the POST data that created the order
1681 : * @param[out] contract_terms where to store the retrieved contract terms,
1682 : * NULL to only test if the order exists
1683 : * @return transaction status
1684 : */
1685 : static enum GNUNET_DB_QueryStatus
1686 2 : postgres_lookup_order (void *cls,
1687 : const char *instance_id,
1688 : const char *order_id,
1689 : struct TALER_ClaimTokenP *claim_token,
1690 : struct TALER_MerchantPostDataHashP *h_post_data,
1691 : json_t **contract_terms)
1692 : {
1693 2 : struct PostgresClosure *pg = cls;
1694 : json_t *j;
1695 : struct TALER_ClaimTokenP ct;
1696 : enum GNUNET_DB_QueryStatus qs;
1697 2 : struct GNUNET_PQ_QueryParam params[] = {
1698 2 : GNUNET_PQ_query_param_string (instance_id),
1699 2 : GNUNET_PQ_query_param_string (order_id),
1700 : GNUNET_PQ_query_param_end
1701 : };
1702 2 : struct GNUNET_PQ_ResultSpec rs[] = {
1703 2 : TALER_PQ_result_spec_json ("contract_terms",
1704 : &j),
1705 2 : GNUNET_PQ_result_spec_auto_from_type ("claim_token",
1706 : &ct),
1707 2 : GNUNET_PQ_result_spec_auto_from_type ("h_post_data",
1708 : h_post_data),
1709 : GNUNET_PQ_result_spec_end
1710 : };
1711 :
1712 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1713 : "Finding contract term, order_id: '%s', instance_id: '%s'.\n",
1714 : order_id,
1715 : instance_id);
1716 2 : check_connection (pg);
1717 2 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1718 : "lookup_order",
1719 : params,
1720 : rs);
1721 2 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
1722 : {
1723 1 : if (NULL != contract_terms)
1724 1 : *contract_terms = j;
1725 : else
1726 0 : json_decref (j);
1727 1 : if (NULL != claim_token)
1728 1 : *claim_token = ct;
1729 : }
1730 : else
1731 : {
1732 : /* just to be safe: NULL it */
1733 1 : if (NULL != contract_terms)
1734 0 : *contract_terms = NULL;
1735 1 : if (NULL != claim_token)
1736 0 : *claim_token = (struct TALER_ClaimTokenP) { 0 }
1737 : ;
1738 : }
1739 2 : return qs;
1740 : }
1741 :
1742 :
1743 : /**
1744 : * Retrieve order summary given its @a order_id and the @a instance_id.
1745 : *
1746 : * @param cls closure
1747 : * @param instance_id instance to obtain order of
1748 : * @param order_id order id used to perform the lookup
1749 : * @param[out] timestamp when was the order created
1750 : * @param[out] order_serial under which serial do we keep this order
1751 : * @return transaction status
1752 : */
1753 : static enum GNUNET_DB_QueryStatus
1754 0 : postgres_lookup_order_summary (void *cls,
1755 : const char *instance_id,
1756 : const char *order_id,
1757 : struct GNUNET_TIME_Timestamp *timestamp,
1758 : uint64_t *order_serial)
1759 : {
1760 0 : struct PostgresClosure *pg = cls;
1761 0 : struct GNUNET_PQ_QueryParam params[] = {
1762 0 : GNUNET_PQ_query_param_string (instance_id),
1763 0 : GNUNET_PQ_query_param_string (order_id),
1764 : GNUNET_PQ_query_param_end
1765 : };
1766 0 : struct GNUNET_PQ_ResultSpec rs[] = {
1767 0 : GNUNET_PQ_result_spec_uint64 ("order_serial",
1768 : order_serial),
1769 0 : GNUNET_PQ_result_spec_timestamp ("creation_time",
1770 : timestamp),
1771 : GNUNET_PQ_result_spec_end
1772 : };
1773 :
1774 0 : check_connection (pg);
1775 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
1776 : "lookup_order_summary",
1777 : params,
1778 : rs);
1779 : }
1780 :
1781 :
1782 : /**
1783 : * Context used for postgres_lookup_orders().
1784 : */
1785 : struct LookupOrdersContext
1786 : {
1787 : /**
1788 : * Function to call with the results.
1789 : */
1790 : TALER_MERCHANTDB_OrdersCallback cb;
1791 :
1792 : /**
1793 : * Closure for @a cb.
1794 : */
1795 : void *cb_cls;
1796 :
1797 : /**
1798 : * Did database result extraction fail?
1799 : */
1800 : bool extract_failed;
1801 : };
1802 :
1803 :
1804 : /**
1805 : * Function to be called with the results of a SELECT statement
1806 : * that has returned @a num_results results about orders.
1807 : *
1808 : * @param[in,out] cls of type `struct LookupOrdersContext *`
1809 : * @param result the postgres result
1810 : * @param num_results the number of results in @a result
1811 : */
1812 : static void
1813 128 : lookup_orders_cb (void *cls,
1814 : PGresult *result,
1815 : unsigned int num_results)
1816 : {
1817 128 : struct LookupOrdersContext *plc = cls;
1818 :
1819 3244 : for (unsigned int i = 0; i < num_results; i++)
1820 : {
1821 : char *order_id;
1822 : uint64_t order_serial;
1823 : struct GNUNET_TIME_Timestamp ts;
1824 3116 : struct GNUNET_PQ_ResultSpec rs[] = {
1825 3116 : GNUNET_PQ_result_spec_string ("order_id",
1826 : &order_id),
1827 3116 : GNUNET_PQ_result_spec_uint64 ("order_serial",
1828 : &order_serial),
1829 3116 : GNUNET_PQ_result_spec_timestamp ("creation_time",
1830 : &ts),
1831 : GNUNET_PQ_result_spec_end
1832 : };
1833 :
1834 3116 : if (GNUNET_OK !=
1835 3116 : GNUNET_PQ_extract_result (result,
1836 : rs,
1837 : i))
1838 : {
1839 0 : GNUNET_break (0);
1840 0 : plc->extract_failed = true;
1841 0 : return;
1842 : }
1843 3116 : plc->cb (plc->cb_cls,
1844 : order_id,
1845 : order_serial,
1846 : ts);
1847 3116 : GNUNET_PQ_cleanup_result (rs);
1848 : }
1849 : }
1850 :
1851 :
1852 : /**
1853 : * Retrieve orders given the @a instance_id.
1854 : *
1855 : * @param cls closure
1856 : * @param instance_id instance to obtain order of
1857 : * @param of filter to apply when looking up orders
1858 : * @param cb callback to pass all the orders that are found
1859 : * @param cb_cls closure for @a cb
1860 : * @return transaction status
1861 : */
1862 : static enum GNUNET_DB_QueryStatus
1863 128 : postgres_lookup_orders (void *cls,
1864 : const char *instance_id,
1865 : const struct TALER_MERCHANTDB_OrderFilter *of,
1866 : TALER_MERCHANTDB_OrdersCallback cb,
1867 : void *cb_cls)
1868 : {
1869 128 : struct PostgresClosure *pg = cls;
1870 128 : struct LookupOrdersContext plc = {
1871 : .cb = cb,
1872 : .cb_cls = cb_cls
1873 : };
1874 128 : uint64_t limit = (of->delta > 0) ? of->delta : -of->delta;
1875 : uint8_t paid;
1876 : uint8_t refunded;
1877 : uint8_t wired;
1878 128 : struct GNUNET_PQ_QueryParam params[] = {
1879 128 : GNUNET_PQ_query_param_string (instance_id),
1880 128 : GNUNET_PQ_query_param_uint64 (&limit),
1881 128 : GNUNET_PQ_query_param_uint64 (&of->start_row),
1882 128 : GNUNET_PQ_query_param_timestamp (&of->date),
1883 128 : GNUNET_PQ_query_param_auto_from_type (&paid),
1884 128 : GNUNET_PQ_query_param_auto_from_type (&refunded),
1885 128 : GNUNET_PQ_query_param_auto_from_type (&wired),
1886 : GNUNET_PQ_query_param_end
1887 : };
1888 : enum GNUNET_DB_QueryStatus qs;
1889 : char stmt[128];
1890 :
1891 128 : paid = (TALER_EXCHANGE_YNA_YES == of->paid);
1892 128 : refunded = (TALER_EXCHANGE_YNA_YES == of->refunded);
1893 128 : wired = (TALER_EXCHANGE_YNA_YES == of->wired);
1894 : /* painfully many cases..., note that "_xxx" being present in 'stmt' merely
1895 : means that we filter by that variable, the value we filter for is
1896 : computed above */
1897 512 : GNUNET_snprintf (stmt,
1898 : sizeof (stmt),
1899 : "lookup_orders_%s%s%s%s",
1900 128 : (of->delta > 0) ? "inc" : "dec",
1901 128 : (TALER_EXCHANGE_YNA_ALL == of->paid) ? "" : "_paid",
1902 128 : (TALER_EXCHANGE_YNA_ALL == of->refunded) ? "" :
1903 : "_refunded",
1904 128 : (TALER_EXCHANGE_YNA_ALL == of->wired) ? "" : "_wired");
1905 128 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
1906 : stmt,
1907 : params,
1908 : &lookup_orders_cb,
1909 : &plc);
1910 128 : if (plc.extract_failed)
1911 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1912 128 : return qs;
1913 : }
1914 :
1915 :
1916 : /**
1917 : * Insert order into the DB.
1918 : *
1919 : * @param cls closure
1920 : * @param instance_id identifies the instance responsible for the order
1921 : * @param order_id alphanumeric string that uniquely identifies the proposal
1922 : * @param h_post_data hash of the POST data for idempotency checks
1923 : * @param pay_deadline how long does the customer have to pay for the order
1924 : * @param claim_token token to use for access control
1925 : * @param contract_terms proposal data to store
1926 : * @return transaction status
1927 : */
1928 : static enum GNUNET_DB_QueryStatus
1929 73 : postgres_insert_order (void *cls,
1930 : const char *instance_id,
1931 : const char *order_id,
1932 : const struct TALER_MerchantPostDataHashP *h_post_data,
1933 : struct GNUNET_TIME_Timestamp pay_deadline,
1934 : const struct TALER_ClaimTokenP *claim_token,
1935 : const json_t *contract_terms)
1936 : {
1937 73 : struct PostgresClosure *pg = cls;
1938 : struct GNUNET_TIME_Timestamp now;
1939 73 : struct GNUNET_PQ_QueryParam params[] = {
1940 73 : GNUNET_PQ_query_param_string (instance_id),
1941 73 : GNUNET_PQ_query_param_string (order_id),
1942 73 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
1943 73 : GNUNET_PQ_query_param_auto_from_type (claim_token),
1944 73 : GNUNET_PQ_query_param_auto_from_type (h_post_data),
1945 73 : GNUNET_PQ_query_param_timestamp (&now),
1946 73 : TALER_PQ_query_param_json (contract_terms),
1947 : GNUNET_PQ_query_param_end
1948 : };
1949 :
1950 73 : now = GNUNET_TIME_timestamp_get ();
1951 73 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1952 : "inserting order: order_id: %s, instance_id: %s.\n",
1953 : order_id,
1954 : instance_id);
1955 73 : check_connection (pg);
1956 73 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1957 : "insert_order",
1958 : params);
1959 : }
1960 :
1961 :
1962 : /**
1963 : * Release an inventory lock by UUID. Releases ALL stocks locked under
1964 : * the given UUID.
1965 : *
1966 : * @param cls closure
1967 : * @param uuid the UUID to release locks for
1968 : * @return transaction status,
1969 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are no locks under @a uuid
1970 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
1971 : */
1972 : static enum GNUNET_DB_QueryStatus
1973 2 : postgres_unlock_inventory (void *cls,
1974 : const struct GNUNET_Uuid *uuid)
1975 : {
1976 2 : struct PostgresClosure *pg = cls;
1977 2 : struct GNUNET_PQ_QueryParam params[] = {
1978 2 : GNUNET_PQ_query_param_auto_from_type (uuid),
1979 : GNUNET_PQ_query_param_end
1980 : };
1981 :
1982 2 : check_connection (pg);
1983 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
1984 : "unlock_inventory",
1985 : params);
1986 : }
1987 :
1988 :
1989 : /**
1990 : * Lock inventory stock to a particular order.
1991 : *
1992 : * @param cls closure
1993 : * @param instance_id identifies the instance responsible for the order
1994 : * @param order_id alphanumeric string that uniquely identifies the order
1995 : * @param product_id uniquely identifies the product to be locked
1996 : * @param quantity how many units should be locked to the @a order_id
1997 : * @return transaction status,
1998 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS means there are insufficient stocks
1999 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT indicates success
2000 : */
2001 : static enum GNUNET_DB_QueryStatus
2002 1 : postgres_insert_order_lock (void *cls,
2003 : const char *instance_id,
2004 : const char *order_id,
2005 : const char *product_id,
2006 : uint64_t quantity)
2007 : {
2008 1 : struct PostgresClosure *pg = cls;
2009 1 : struct GNUNET_PQ_QueryParam params[] = {
2010 1 : GNUNET_PQ_query_param_string (instance_id),
2011 1 : GNUNET_PQ_query_param_string (order_id),
2012 1 : GNUNET_PQ_query_param_string (product_id),
2013 1 : GNUNET_PQ_query_param_uint64 (&quantity),
2014 : GNUNET_PQ_query_param_end
2015 : };
2016 :
2017 1 : check_connection (pg);
2018 1 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2019 : "insert_order_lock",
2020 : params);
2021 : }
2022 :
2023 :
2024 : /**
2025 : * Retrieve contract terms given its @a order_id
2026 : *
2027 : * @param cls closure
2028 : * @param instance_id instance's identifier
2029 : * @param order_id order_id used to lookup.
2030 : * @param[out] contract_terms where to store the result, NULL to only check for existence
2031 : * @param[out] order_serial set to the order's serial number
2032 : * @param[out] paid set to true if the order is fully paid
2033 : * @param[out] claim_token set to the claim token, NULL to only check for existence
2034 : * @return transaction status
2035 : */
2036 : static enum GNUNET_DB_QueryStatus
2037 3 : postgres_lookup_contract_terms (void *cls,
2038 : const char *instance_id,
2039 : const char *order_id,
2040 : json_t **contract_terms,
2041 : uint64_t *order_serial,
2042 : bool *paid,
2043 : struct TALER_ClaimTokenP *claim_token)
2044 : {
2045 3 : struct PostgresClosure *pg = cls;
2046 : enum GNUNET_DB_QueryStatus qs;
2047 : struct TALER_ClaimTokenP ct;
2048 3 : struct GNUNET_PQ_QueryParam params[] = {
2049 3 : GNUNET_PQ_query_param_string (instance_id),
2050 3 : GNUNET_PQ_query_param_string (order_id),
2051 : GNUNET_PQ_query_param_end
2052 : };
2053 3 : struct GNUNET_PQ_ResultSpec rs[] = {
2054 : /* contract_terms must be first! */
2055 3 : TALER_PQ_result_spec_json ("contract_terms",
2056 : contract_terms),
2057 3 : GNUNET_PQ_result_spec_uint64 ("order_serial",
2058 : order_serial),
2059 3 : GNUNET_PQ_result_spec_bool ("paid",
2060 : paid),
2061 3 : GNUNET_PQ_result_spec_auto_from_type ("claim_token",
2062 : &ct),
2063 : GNUNET_PQ_result_spec_end
2064 : };
2065 :
2066 3 : check_connection (pg);
2067 3 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2068 : "lookup_contract_terms",
2069 : params,
2070 : (NULL != contract_terms)
2071 : ? rs
2072 : : &rs[1]);
2073 3 : if (NULL != claim_token)
2074 0 : *claim_token = ct;
2075 3 : return qs;
2076 : }
2077 :
2078 :
2079 : /**
2080 : * Store contract terms given its @a order_id. Note that some attributes are
2081 : * expected to be calculated inside of the function, like the hash of the
2082 : * contract terms (to be hashed), the creation_time and pay_deadline (to be
2083 : * obtained from the merchant_orders table). The "session_id" should be
2084 : * initially set to the empty string. The "fulfillment_url" and "refund_deadline"
2085 : * must be extracted from @a contract_terms.
2086 : *
2087 : * @param cls closure
2088 : * @param instance_id instance's identifier
2089 : * @param order_id order_id used to store
2090 : * @param contract_terms contract terms to store
2091 : * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
2092 : * is malformed
2093 : */
2094 : static enum GNUNET_DB_QueryStatus
2095 40 : postgres_insert_contract_terms (void *cls,
2096 : const char *instance_id,
2097 : const char *order_id,
2098 : json_t *contract_terms)
2099 : {
2100 40 : struct PostgresClosure *pg = cls;
2101 : struct GNUNET_TIME_Timestamp pay_deadline;
2102 : struct GNUNET_TIME_Timestamp refund_deadline;
2103 : const char *fulfillment_url;
2104 : struct TALER_PrivateContractHashP h_contract_terms;
2105 :
2106 40 : if (GNUNET_OK !=
2107 40 : TALER_JSON_contract_hash (contract_terms,
2108 : &h_contract_terms))
2109 : {
2110 0 : GNUNET_break (0);
2111 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2112 : }
2113 :
2114 : {
2115 : struct GNUNET_JSON_Specification spec[] = {
2116 40 : GNUNET_JSON_spec_timestamp ("pay_deadline",
2117 : &pay_deadline),
2118 40 : GNUNET_JSON_spec_timestamp ("refund_deadline",
2119 : &refund_deadline),
2120 40 : GNUNET_JSON_spec_end ()
2121 : };
2122 : enum GNUNET_GenericReturnValue res;
2123 :
2124 40 : res = TALER_MHD_parse_json_data (NULL,
2125 : contract_terms,
2126 : spec);
2127 40 : if (GNUNET_OK != res)
2128 : {
2129 0 : GNUNET_break (0);
2130 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2131 : }
2132 : }
2133 :
2134 : fulfillment_url =
2135 40 : json_string_value (json_object_get (contract_terms,
2136 : "fulfillment_url"));
2137 40 : check_connection (pg);
2138 : {
2139 40 : struct GNUNET_PQ_QueryParam params[] = {
2140 40 : GNUNET_PQ_query_param_string (instance_id),
2141 40 : GNUNET_PQ_query_param_string (order_id),
2142 40 : TALER_PQ_query_param_json (contract_terms),
2143 40 : GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
2144 40 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
2145 40 : GNUNET_PQ_query_param_timestamp (&refund_deadline),
2146 : (NULL == fulfillment_url)
2147 0 : ? GNUNET_PQ_query_param_null ()
2148 40 : : GNUNET_PQ_query_param_string (fulfillment_url),
2149 : GNUNET_PQ_query_param_end
2150 : };
2151 :
2152 40 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2153 : "insert_contract_terms",
2154 : params);
2155 : }
2156 : }
2157 :
2158 :
2159 : /**
2160 : * Update the contract terms stored for @a order_id. Note that some attributes are
2161 : * expected to be calculated inside of the function, like the hash of the
2162 : * contract terms (to be hashed), the creation_time and pay_deadline (to be
2163 : * obtained from the merchant_orders table). The "session_id" should be
2164 : * initially set to the empty string. The "fulfillment_url" and "refund_deadline"
2165 : * must be extracted from @a contract_terms.
2166 : *
2167 : * @param cls closure
2168 : * @param instance_id instance's identifier
2169 : * @param order_id order_id used to store
2170 : * @param contract_terms contract to store
2171 : * @return transaction status, #GNUNET_DB_STATUS_HARD_ERROR if @a contract_terms
2172 : * is malformed
2173 : */
2174 : static enum GNUNET_DB_QueryStatus
2175 1 : postgres_update_contract_terms (void *cls,
2176 : const char *instance_id,
2177 : const char *order_id,
2178 : json_t *contract_terms)
2179 : {
2180 1 : struct PostgresClosure *pg = cls;
2181 : struct GNUNET_TIME_Timestamp pay_deadline;
2182 : struct GNUNET_TIME_Timestamp refund_deadline;
2183 1 : const char *fulfillment_url = NULL;
2184 : struct TALER_PrivateContractHashP h_contract_terms;
2185 :
2186 1 : if (GNUNET_OK !=
2187 1 : TALER_JSON_contract_hash (contract_terms,
2188 : &h_contract_terms))
2189 : {
2190 0 : GNUNET_break (0);
2191 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2192 : }
2193 :
2194 : {
2195 : struct GNUNET_JSON_Specification spec[] = {
2196 1 : GNUNET_JSON_spec_timestamp ("pay_deadline",
2197 : &pay_deadline),
2198 1 : GNUNET_JSON_spec_timestamp ("refund_deadline",
2199 : &refund_deadline),
2200 1 : GNUNET_JSON_spec_mark_optional (
2201 : GNUNET_JSON_spec_string ("fulfillment_url",
2202 : &fulfillment_url),
2203 : NULL),
2204 1 : GNUNET_JSON_spec_end ()
2205 : };
2206 : enum GNUNET_GenericReturnValue res;
2207 :
2208 1 : res = TALER_MHD_parse_json_data (NULL,
2209 : contract_terms,
2210 : spec);
2211 1 : if (GNUNET_OK != res)
2212 : {
2213 0 : GNUNET_break (0);
2214 0 : return GNUNET_DB_STATUS_HARD_ERROR;
2215 : }
2216 : }
2217 :
2218 1 : check_connection (pg);
2219 : {
2220 2 : struct GNUNET_PQ_QueryParam params[] = {
2221 1 : GNUNET_PQ_query_param_string (instance_id),
2222 1 : GNUNET_PQ_query_param_string (order_id),
2223 1 : TALER_PQ_query_param_json (contract_terms),
2224 1 : GNUNET_PQ_query_param_auto_from_type (&h_contract_terms),
2225 1 : GNUNET_PQ_query_param_timestamp (&pay_deadline),
2226 1 : GNUNET_PQ_query_param_timestamp (&refund_deadline),
2227 1 : (NULL == fulfillment_url)
2228 0 : ? GNUNET_PQ_query_param_null ()
2229 1 : : GNUNET_PQ_query_param_string (fulfillment_url),
2230 : GNUNET_PQ_query_param_end
2231 : };
2232 :
2233 1 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2234 : "update_contract_terms",
2235 : params);
2236 : }
2237 : }
2238 :
2239 :
2240 : /**
2241 : * Delete information about a contract. Note that the transaction must
2242 : * enforce that the contract is not awaiting payment anymore AND was not
2243 : * paid, or is past the legal expiration.
2244 : *
2245 : * @param cls closure
2246 : * @param instance_id instance to delete order of
2247 : * @param order_id order to delete
2248 : * @param legal_expiration how long do we need to keep (paid) contracts on
2249 : * file for legal reasons (i.e. taxation)
2250 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
2251 : * if locks prevent deletion OR order unknown
2252 : */
2253 : static enum GNUNET_DB_QueryStatus
2254 3 : postgres_delete_contract_terms (void *cls,
2255 : const char *instance_id,
2256 : const char *order_id,
2257 : struct GNUNET_TIME_Relative legal_expiration)
2258 : {
2259 3 : struct PostgresClosure *pg = cls;
2260 3 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
2261 3 : struct GNUNET_PQ_QueryParam params[] = {
2262 3 : GNUNET_PQ_query_param_string (instance_id),
2263 3 : GNUNET_PQ_query_param_string (order_id),
2264 3 : GNUNET_PQ_query_param_relative_time (&legal_expiration),
2265 3 : GNUNET_PQ_query_param_absolute_time (&now),
2266 : GNUNET_PQ_query_param_end
2267 : };
2268 :
2269 3 : check_connection (pg);
2270 3 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2271 : "delete_contract_terms",
2272 : params);
2273 : }
2274 :
2275 :
2276 : /**
2277 : * Closure for #lookup_deposits_cb().
2278 : */
2279 : struct LookupDepositsContext
2280 : {
2281 : /**
2282 : * Function to call with results.
2283 : */
2284 : TALER_MERCHANTDB_DepositsCallback cb;
2285 :
2286 : /**
2287 : * Closure for @e cls.
2288 : */
2289 : void *cb_cls;
2290 :
2291 : /**
2292 : * Plugin context.
2293 : */
2294 : struct PostgresClosure *pg;
2295 :
2296 : /**
2297 : * Transaction status (set).
2298 : */
2299 : enum GNUNET_DB_QueryStatus qs;
2300 : };
2301 :
2302 :
2303 : /**
2304 : * Function to be called with the results of a SELECT statement
2305 : * that has returned @a num_results results.
2306 : *
2307 : * @param[in,out] cls of type `struct LookupDepositsContext *`
2308 : * @param result the postgres result
2309 : * @param num_results the number of results in @a result
2310 : */
2311 : static void
2312 4 : lookup_deposits_cb (void *cls,
2313 : PGresult *result,
2314 : unsigned int num_results)
2315 : {
2316 4 : struct LookupDepositsContext *ldc = cls;
2317 4 : struct PostgresClosure *pg = ldc->pg;
2318 :
2319 8 : for (unsigned int i = 0; i<num_results; i++)
2320 : {
2321 : struct TALER_CoinSpendPublicKeyP coin_pub;
2322 : struct TALER_Amount amount_with_fee;
2323 : struct TALER_Amount deposit_fee;
2324 : struct TALER_Amount refund_fee;
2325 : struct TALER_Amount wire_fee;
2326 : char *exchange_url;
2327 4 : struct GNUNET_PQ_ResultSpec rs[] = {
2328 4 : GNUNET_PQ_result_spec_string ("exchange_url",
2329 : &exchange_url),
2330 4 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2331 : &coin_pub),
2332 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
2333 : &amount_with_fee),
2334 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
2335 : &deposit_fee),
2336 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
2337 : &refund_fee),
2338 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
2339 : &wire_fee),
2340 : GNUNET_PQ_result_spec_end
2341 : };
2342 :
2343 4 : if (GNUNET_OK !=
2344 4 : GNUNET_PQ_extract_result (result,
2345 : rs,
2346 : i))
2347 : {
2348 0 : GNUNET_break (0);
2349 0 : ldc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2350 0 : return;
2351 : }
2352 4 : ldc->cb (ldc->cb_cls,
2353 : exchange_url,
2354 : &coin_pub,
2355 : &amount_with_fee,
2356 : &deposit_fee,
2357 : &refund_fee,
2358 : &wire_fee);
2359 4 : GNUNET_PQ_cleanup_result (rs);
2360 : }
2361 4 : ldc->qs = num_results;
2362 : }
2363 :
2364 :
2365 : /**
2366 : * Lookup information about coins that were successfully deposited for a
2367 : * given contract.
2368 : *
2369 : * @param cls closure
2370 : * @param instance_id instance to lookup deposits for
2371 : * @param h_contract_terms proposal data's hashcode
2372 : * @param cb function to call with payment data
2373 : * @param cb_cls closure for @a cb
2374 : * @return transaction status
2375 : */
2376 : static enum GNUNET_DB_QueryStatus
2377 4 : postgres_lookup_deposits (
2378 : void *cls,
2379 : const char *instance_id,
2380 : const struct TALER_PrivateContractHashP *h_contract_terms,
2381 : TALER_MERCHANTDB_DepositsCallback cb,
2382 : void *cb_cls)
2383 : {
2384 4 : struct PostgresClosure *pg = cls;
2385 4 : struct GNUNET_PQ_QueryParam params[] = {
2386 4 : GNUNET_PQ_query_param_string (instance_id),
2387 4 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2388 : GNUNET_PQ_query_param_end
2389 : };
2390 4 : struct LookupDepositsContext ldc = {
2391 : .cb = cb,
2392 : .cb_cls = cb_cls,
2393 : .pg = pg
2394 : };
2395 : enum GNUNET_DB_QueryStatus qs;
2396 :
2397 : /* no preflight check here, run in its own transaction by the caller! */
2398 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2399 : "Finding deposits for h_contract_terms '%s'\n",
2400 : GNUNET_h2s (&h_contract_terms->hash));
2401 4 : check_connection (pg);
2402 4 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
2403 : "lookup_deposits",
2404 : params,
2405 : &lookup_deposits_cb,
2406 : &ldc);
2407 4 : if (qs <= 0)
2408 1 : return qs;
2409 3 : return ldc.qs;
2410 : }
2411 :
2412 :
2413 : /**
2414 : * Insert an exchange signing key into our database.
2415 : *
2416 : * @param cls closure
2417 : * @param master_pub exchange master public key used for @a master_sig
2418 : * @param exchange_pub exchange signing key to insert
2419 : * @param start_date when does the signing key become valid
2420 : * @param expire_date when does the signing key stop being used
2421 : * @param end_date when does the signing key become void as proof
2422 : * @param master_sig signature of @a master_pub over the @a exchange_pub and the dates
2423 : */
2424 : static enum GNUNET_DB_QueryStatus
2425 5 : postgres_insert_exchange_signkey (
2426 : void *cls,
2427 : const struct TALER_MasterPublicKeyP *master_pub,
2428 : const struct TALER_ExchangePublicKeyP *exchange_pub,
2429 : struct GNUNET_TIME_Timestamp start_date,
2430 : struct GNUNET_TIME_Timestamp expire_date,
2431 : struct GNUNET_TIME_Timestamp end_date,
2432 : const struct TALER_MasterSignatureP *master_sig)
2433 : {
2434 5 : struct PostgresClosure *pg = cls;
2435 5 : struct GNUNET_PQ_QueryParam params[] = {
2436 5 : GNUNET_PQ_query_param_auto_from_type (master_pub),
2437 5 : GNUNET_PQ_query_param_auto_from_type (exchange_pub),
2438 5 : GNUNET_PQ_query_param_timestamp (&start_date),
2439 5 : GNUNET_PQ_query_param_timestamp (&expire_date),
2440 5 : GNUNET_PQ_query_param_timestamp (&end_date),
2441 5 : GNUNET_PQ_query_param_auto_from_type (master_sig),
2442 : GNUNET_PQ_query_param_end
2443 : };
2444 :
2445 5 : check_connection (pg);
2446 5 : postgres_preflight (pg);
2447 5 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2448 : "insert_exchange_signkey",
2449 : params);
2450 :
2451 : }
2452 :
2453 :
2454 : /**
2455 : * Insert payment confirmation from the exchange into the database.
2456 : *
2457 : * @param cls closure
2458 : * @param instance_id instance to lookup deposits for
2459 : * @param deposit_timestamp time when the exchange generated the deposit confirmation
2460 : * @param h_contract_terms proposal data's hashcode
2461 : * @param coin_pub public key of the coin
2462 : * @param exchange_url URL of the exchange that issued @a coin_pub
2463 : * @param amount_with_fee amount the exchange will deposit for this coin
2464 : * @param deposit_fee fee the exchange will charge for this coin
2465 : * @param wire_fee wire fee the exchange charges
2466 : * @param refund_fee fee the exchange charges to refund this coin
2467 : * @param h_wire hash of the wire details of the target account of the merchant
2468 : * @param exchange_sig signature from exchange that coin was accepted
2469 : * @param exchange_pub signgin key that was used for @a exchange_sig
2470 : * @return transaction status
2471 : */
2472 : static enum GNUNET_DB_QueryStatus
2473 24 : postgres_insert_deposit (
2474 : void *cls,
2475 : const char *instance_id,
2476 : struct GNUNET_TIME_Timestamp deposit_timestamp,
2477 : const struct TALER_PrivateContractHashP *h_contract_terms,
2478 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2479 : const char *exchange_url,
2480 : const struct TALER_Amount *amount_with_fee,
2481 : const struct TALER_Amount *deposit_fee,
2482 : const struct TALER_Amount *refund_fee,
2483 : const struct TALER_Amount *wire_fee,
2484 : const struct TALER_MerchantWireHashP *h_wire,
2485 : const struct TALER_ExchangeSignatureP *exchange_sig,
2486 : const struct TALER_ExchangePublicKeyP *exchange_pub)
2487 : {
2488 24 : struct PostgresClosure *pg = cls;
2489 24 : struct GNUNET_PQ_QueryParam params[] = {
2490 24 : GNUNET_PQ_query_param_string (instance_id),
2491 24 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2492 24 : GNUNET_PQ_query_param_timestamp (&deposit_timestamp), /* $3 */
2493 24 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
2494 24 : GNUNET_PQ_query_param_string (exchange_url),
2495 24 : TALER_PQ_query_param_amount (amount_with_fee), /* $6/$7 */
2496 24 : TALER_PQ_query_param_amount (deposit_fee), /* $8, $9 */
2497 24 : TALER_PQ_query_param_amount (refund_fee), /* $10, $11 */
2498 24 : TALER_PQ_query_param_amount (wire_fee), /* $12, $13 */
2499 24 : GNUNET_PQ_query_param_auto_from_type (h_wire), /* $14 */
2500 24 : GNUNET_PQ_query_param_auto_from_type (exchange_sig), /* $15 */
2501 24 : GNUNET_PQ_query_param_auto_from_type (exchange_pub), /* $16 */
2502 : GNUNET_PQ_query_param_end
2503 : };
2504 :
2505 : /* no preflight check here, run in transaction by caller! */
2506 24 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2507 : "Storing deposit for instance `%s' h_contract_terms `%s', coin_pub: `%s', amount_with_fee: %s\n",
2508 : instance_id,
2509 : GNUNET_h2s (&h_contract_terms->hash),
2510 : TALER_B2S (coin_pub),
2511 : TALER_amount2s (amount_with_fee));
2512 24 : check_connection (pg);
2513 24 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2514 : "insert_deposit",
2515 : params);
2516 :
2517 : }
2518 :
2519 :
2520 : /**
2521 : * Closure for #lookup_refunds_cb().
2522 : */
2523 : struct LookupRefundsContext
2524 : {
2525 : /**
2526 : * Function to call for each refund.
2527 : */
2528 : TALER_MERCHANTDB_RefundCallback rc;
2529 :
2530 : /**
2531 : * Closure for @e rc.
2532 : */
2533 : void *rc_cls;
2534 :
2535 : /**
2536 : * Plugin context.
2537 : */
2538 : struct PostgresClosure *pg;
2539 :
2540 : /**
2541 : * Transaction result.
2542 : */
2543 : enum GNUNET_DB_QueryStatus qs;
2544 : };
2545 :
2546 :
2547 : /**
2548 : * Function to be called with the results of a SELECT statement
2549 : * that has returned @a num_results results.
2550 : *
2551 : * @param cls of type `struct LookupRefundsContext *`
2552 : * @param result the postgres result
2553 : * @param num_results the number of results in @a result
2554 : */
2555 : static void
2556 1 : lookup_refunds_cb (void *cls,
2557 : PGresult *result,
2558 : unsigned int num_results)
2559 : {
2560 1 : struct LookupRefundsContext *lrc = cls;
2561 1 : struct PostgresClosure *pg = lrc->pg;
2562 :
2563 2 : for (unsigned int i = 0; i<num_results; i++)
2564 : {
2565 : struct TALER_CoinSpendPublicKeyP coin_pub;
2566 : struct TALER_Amount refund_amount;
2567 1 : struct GNUNET_PQ_ResultSpec rs[] = {
2568 1 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2569 : &coin_pub),
2570 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
2571 : &refund_amount),
2572 : GNUNET_PQ_result_spec_end
2573 : };
2574 :
2575 1 : if (GNUNET_OK !=
2576 1 : GNUNET_PQ_extract_result (result,
2577 : rs,
2578 : i))
2579 : {
2580 0 : GNUNET_break (0);
2581 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2582 0 : return;
2583 : }
2584 1 : lrc->rc (lrc->rc_cls,
2585 : &coin_pub,
2586 : &refund_amount);
2587 1 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
2588 : }
2589 1 : lrc->qs = num_results;
2590 : }
2591 :
2592 :
2593 : /**
2594 : * Obtain refunds associated with a contract.
2595 : *
2596 : * @param cls closure, typically a connection to the db
2597 : * @param instance_id instance to lookup refunds for
2598 : * @param h_contract_terms hash code of the contract
2599 : * @param rc function to call for each coin on which there is a refund
2600 : * @param rc_cls closure for @a rc
2601 : * @return transaction status
2602 : */
2603 : static enum GNUNET_DB_QueryStatus
2604 1 : postgres_lookup_refunds (
2605 : void *cls,
2606 : const char *instance_id,
2607 : const struct TALER_PrivateContractHashP *h_contract_terms,
2608 : TALER_MERCHANTDB_RefundCallback rc,
2609 : void *rc_cls)
2610 : {
2611 1 : struct PostgresClosure *pg = cls;
2612 1 : struct GNUNET_PQ_QueryParam params[] = {
2613 1 : GNUNET_PQ_query_param_string (instance_id),
2614 1 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2615 : GNUNET_PQ_query_param_end
2616 : };
2617 1 : struct LookupRefundsContext lrc = {
2618 : .rc = rc,
2619 : .rc_cls = rc_cls,
2620 : .pg = pg
2621 : };
2622 : enum GNUNET_DB_QueryStatus qs;
2623 :
2624 : /* no preflight check here, run in transaction by caller! */
2625 1 : TALER_LOG_DEBUG ("Looking for refund of h_contract_terms %s at `%s'\n",
2626 : GNUNET_h2s (&h_contract_terms->hash),
2627 : instance_id);
2628 1 : check_connection (pg);
2629 1 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
2630 : "lookup_refunds",
2631 : params,
2632 : &lookup_refunds_cb,
2633 : &lrc);
2634 1 : if (0 >= qs)
2635 0 : return qs;
2636 1 : return lrc.qs;
2637 : }
2638 :
2639 :
2640 : /**
2641 : * Mark contract as paid and store the current @a session_id
2642 : * for which the contract was paid. Deletes the underlying order
2643 : * and marks the locked stocks of the order as sold.
2644 : *
2645 : * @param cls closure
2646 : * @param instance_id instance to mark contract as paid for
2647 : * @param h_contract_terms hash of the contract that is now paid
2648 : * @param session_id the session that paid the contract
2649 : * @return transaction status
2650 : */
2651 : static enum GNUNET_DB_QueryStatus
2652 20 : postgres_mark_contract_paid (
2653 : void *cls,
2654 : const char *instance_id,
2655 : const struct TALER_PrivateContractHashP *h_contract_terms,
2656 : const char *session_id)
2657 : {
2658 20 : struct PostgresClosure *pg = cls;
2659 20 : struct GNUNET_PQ_QueryParam params[] = {
2660 20 : GNUNET_PQ_query_param_string (instance_id),
2661 20 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2662 20 : GNUNET_PQ_query_param_string (session_id),
2663 : GNUNET_PQ_query_param_end
2664 : };
2665 20 : struct GNUNET_PQ_QueryParam uparams[] = {
2666 20 : GNUNET_PQ_query_param_string (instance_id),
2667 20 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2668 : GNUNET_PQ_query_param_end
2669 : };
2670 : enum GNUNET_DB_QueryStatus qs;
2671 :
2672 : /* Session ID must always be given by the caller. */
2673 20 : GNUNET_assert (NULL != session_id);
2674 :
2675 : /* no preflight check here, run in transaction by caller! */
2676 20 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2677 : "Marking h_contract_terms '%s' of %s as paid for session `%s'\n",
2678 : GNUNET_h2s (&h_contract_terms->hash),
2679 : instance_id,
2680 : session_id);
2681 20 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
2682 : "mark_contract_paid",
2683 : params);
2684 20 : if (qs <= 0)
2685 1 : return qs;
2686 19 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
2687 : "mark_inventory_sold",
2688 : uparams);
2689 19 : if (qs < 0)
2690 0 : return qs; /* 0: no inventory management, that's OK! */
2691 : /* ON DELETE CASCADE deletes from merchant_order_locks */
2692 19 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2693 : "delete_completed_order",
2694 : uparams);
2695 : }
2696 :
2697 :
2698 : /**
2699 : * Function called during aborts to refund a coin. Marks the
2700 : * respective coin as refunded.
2701 : *
2702 : * @param cls closure
2703 : * @param instance_id instance to refund payment for
2704 : * @param h_contract_terms hash of the contract to refund coin for
2705 : * @param refund_timestamp timestamp of the refund
2706 : * @param coin_pub public key of the coin to refund (fully)
2707 : * @param reason text justifying the refund
2708 : * @return transaction status
2709 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a coin_pub is unknown to us;
2710 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
2711 : * regardless of whether it actually increased the refund
2712 : */
2713 : static enum GNUNET_DB_QueryStatus
2714 18 : postgres_refund_coin (void *cls,
2715 : const char *instance_id,
2716 : const struct TALER_PrivateContractHashP *h_contract_terms,
2717 : struct GNUNET_TIME_Timestamp refund_timestamp,
2718 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2719 : const char *reason)
2720 : {
2721 18 : struct PostgresClosure *pg = cls;
2722 18 : struct GNUNET_PQ_QueryParam params[] = {
2723 18 : GNUNET_PQ_query_param_string (instance_id),
2724 18 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
2725 18 : GNUNET_PQ_query_param_timestamp (&refund_timestamp),
2726 18 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
2727 18 : GNUNET_PQ_query_param_string (reason),
2728 : GNUNET_PQ_query_param_end
2729 : };
2730 :
2731 18 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
2732 : "refund_coin",
2733 : params);
2734 : }
2735 :
2736 :
2737 : /**
2738 : * Retrieve contract terms given its @a order_id
2739 : *
2740 : * @param cls closure
2741 : * @param instance_id instance's identifier
2742 : * @param order_id order to lookup contract for
2743 : * @param[out] h_contract_terms set to the hash of the contract.
2744 : * @param[out] paid set to the payment status of the contract
2745 : * @return transaction status
2746 : */
2747 : static enum GNUNET_DB_QueryStatus
2748 4 : postgres_lookup_order_status (void *cls,
2749 : const char *instance_id,
2750 : const char *order_id,
2751 : struct TALER_PrivateContractHashP *
2752 : h_contract_terms,
2753 : bool *paid)
2754 : {
2755 4 : struct PostgresClosure *pg = cls;
2756 : uint8_t paid8;
2757 : enum GNUNET_DB_QueryStatus qs;
2758 4 : struct GNUNET_PQ_QueryParam params[] = {
2759 4 : GNUNET_PQ_query_param_string (instance_id),
2760 4 : GNUNET_PQ_query_param_string (order_id),
2761 : GNUNET_PQ_query_param_end
2762 : };
2763 4 : struct GNUNET_PQ_ResultSpec rs[] = {
2764 4 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
2765 : h_contract_terms),
2766 4 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2767 : &paid8),
2768 : GNUNET_PQ_result_spec_end
2769 : };
2770 :
2771 4 : check_connection (pg);
2772 4 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2773 : "lookup_order_status",
2774 : params,
2775 : rs);
2776 4 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2777 2 : *paid = (0 != paid8);
2778 : else
2779 2 : *paid = false; /* just to be safe(r) */
2780 4 : return qs;
2781 : }
2782 :
2783 :
2784 : /**
2785 : * Retrieve contract terms given its @a order_serial
2786 : *
2787 : * @param cls closure
2788 : * @param instance_id instance's identifier
2789 : * @param order_serial serial ID of the order to look up
2790 : * @param[out] order_id set to ID of the order
2791 : * @param[out] h_contract_terms set to the hash of the contract.
2792 : * @param[out] paid set to the payment status of the contract
2793 : * @return transaction status
2794 : */
2795 : static enum GNUNET_DB_QueryStatus
2796 0 : postgres_lookup_order_status_by_serial (void *cls,
2797 : const char *instance_id,
2798 : uint64_t order_serial,
2799 : char **order_id,
2800 : struct TALER_PrivateContractHashP *
2801 : h_contract_terms,
2802 : bool *paid)
2803 : {
2804 0 : struct PostgresClosure *pg = cls;
2805 : uint8_t paid8;
2806 : enum GNUNET_DB_QueryStatus qs;
2807 0 : struct GNUNET_PQ_QueryParam params[] = {
2808 0 : GNUNET_PQ_query_param_string (instance_id),
2809 0 : GNUNET_PQ_query_param_uint64 (&order_serial),
2810 : GNUNET_PQ_query_param_end
2811 : };
2812 0 : struct GNUNET_PQ_ResultSpec rs[] = {
2813 0 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
2814 : h_contract_terms),
2815 0 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2816 : &paid8),
2817 0 : GNUNET_PQ_result_spec_string ("order_id",
2818 : order_id),
2819 : GNUNET_PQ_result_spec_end
2820 : };
2821 :
2822 0 : check_connection (pg);
2823 0 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2824 : "lookup_order_status_by_serial",
2825 : params,
2826 : rs);
2827 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2828 0 : *paid = (0 != paid8);
2829 : else
2830 0 : *paid = false; /* just to be safe(r) */
2831 0 : return qs;
2832 : }
2833 :
2834 :
2835 : /**
2836 : * Retrieve payment and wire status for a given @a order_serial and session ID.
2837 : *
2838 : * @param cls closure
2839 : * @param order_serial identifies the order
2840 : * @param session_id session for which to check the payment status, NULL for any
2841 : * @param[out] paid set to the payment status of the contract
2842 : * @param[out] wired set to the wire transfer status of the exchange payment
2843 : * @return transaction status
2844 : */
2845 : static enum GNUNET_DB_QueryStatus
2846 7 : postgres_lookup_payment_status (void *cls,
2847 : uint64_t order_serial,
2848 : const char *session_id,
2849 : bool *paid,
2850 : bool *wired)
2851 : {
2852 7 : struct PostgresClosure *pg = cls;
2853 : uint8_t paid8;
2854 : uint8_t wired8;
2855 : enum GNUNET_DB_QueryStatus qs;
2856 7 : struct GNUNET_PQ_ResultSpec rs[] = {
2857 7 : GNUNET_PQ_result_spec_auto_from_type ("paid",
2858 : &paid8),
2859 7 : GNUNET_PQ_result_spec_auto_from_type ("wired",
2860 : &wired8),
2861 : GNUNET_PQ_result_spec_end
2862 : };
2863 7 : check_connection (pg);
2864 7 : if (NULL == session_id)
2865 : {
2866 5 : struct GNUNET_PQ_QueryParam params[] = {
2867 5 : GNUNET_PQ_query_param_uint64 (&order_serial),
2868 : GNUNET_PQ_query_param_end
2869 : };
2870 :
2871 5 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2872 : "lookup_payment_status",
2873 : params,
2874 : rs);
2875 : }
2876 : else
2877 : {
2878 2 : struct GNUNET_PQ_QueryParam params[] = {
2879 2 : GNUNET_PQ_query_param_uint64 (&order_serial),
2880 2 : GNUNET_PQ_query_param_string (session_id),
2881 : GNUNET_PQ_query_param_end
2882 : };
2883 :
2884 2 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
2885 : "lookup_payment_status_session_id",
2886 : params,
2887 : rs);
2888 : }
2889 7 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
2890 : {
2891 5 : *paid = (0 != paid8);
2892 5 : *wired = (0 != wired8);
2893 : }
2894 : else
2895 : {
2896 2 : *paid = false; /* just to be safe(r) */
2897 2 : *wired = false; /* just to be safe(r) */
2898 : }
2899 7 : return qs;
2900 : }
2901 :
2902 :
2903 : /**
2904 : * Closure for lookup_deposits_by_order_cb().
2905 : */
2906 : struct LookupDepositsByOrderContext
2907 : {
2908 :
2909 : /**
2910 : * Plugin context.
2911 : */
2912 : struct PostgresClosure *pg;
2913 :
2914 : /**
2915 : * Function to call with all results.
2916 : */
2917 : TALER_MERCHANTDB_DepositedCoinsCallback cb;
2918 :
2919 : /**
2920 : * Closure for @e cb.
2921 : */
2922 : void *cb_cls;
2923 :
2924 : /**
2925 : * Set to the query result.
2926 : */
2927 : enum GNUNET_DB_QueryStatus qs;
2928 : };
2929 :
2930 :
2931 : /**
2932 : * Function to be called with the results of a SELECT statement
2933 : * that has returned @a num_results results.
2934 : *
2935 : * @param cls of type `struct LookupDepositsByOrderContext *`
2936 : * @param result the postgres result
2937 : * @param num_results the number of results in @a result
2938 : */
2939 : static void
2940 4 : lookup_deposits_by_order_cb (void *cls,
2941 : PGresult *result,
2942 : unsigned int num_results)
2943 : {
2944 4 : struct LookupDepositsByOrderContext *ldoc = cls;
2945 4 : struct PostgresClosure *pg = ldoc->pg;
2946 :
2947 9 : for (unsigned int i = 0; i<num_results; i++)
2948 : {
2949 : uint64_t deposit_serial;
2950 : char *exchange_url;
2951 : struct TALER_MerchantWireHashP h_wire;
2952 : struct TALER_CoinSpendPublicKeyP coin_pub;
2953 : struct TALER_Amount amount_with_fee;
2954 : struct TALER_Amount deposit_fee;
2955 5 : struct GNUNET_PQ_ResultSpec rs[] = {
2956 5 : GNUNET_PQ_result_spec_uint64 ("deposit_serial",
2957 : &deposit_serial),
2958 5 : GNUNET_PQ_result_spec_string ("exchange_url",
2959 : &exchange_url),
2960 5 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
2961 : &h_wire),
2962 5 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
2963 : &amount_with_fee),
2964 5 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
2965 : &deposit_fee),
2966 5 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
2967 : &coin_pub),
2968 : GNUNET_PQ_result_spec_end
2969 : };
2970 :
2971 5 : if (GNUNET_OK !=
2972 5 : GNUNET_PQ_extract_result (result,
2973 : rs,
2974 : i))
2975 : {
2976 0 : GNUNET_break (0);
2977 0 : ldoc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2978 0 : return;
2979 : }
2980 5 : ldoc->cb (ldoc->cb_cls,
2981 : deposit_serial,
2982 : exchange_url,
2983 : &h_wire,
2984 : &amount_with_fee,
2985 : &deposit_fee,
2986 : &coin_pub);
2987 5 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
2988 : }
2989 4 : ldoc->qs = num_results;
2990 : }
2991 :
2992 :
2993 : /**
2994 : * Retrieve details about coins that were deposited for an order.
2995 : *
2996 : * @param cls closure
2997 : * @param order_serial identifies the order
2998 : * @param cb function to call for each deposited coin
2999 : * @param cb_cls closure for @a cb
3000 : * @return transaction status
3001 : */
3002 : static enum GNUNET_DB_QueryStatus
3003 4 : postgres_lookup_deposits_by_order (void *cls,
3004 : uint64_t order_serial,
3005 : TALER_MERCHANTDB_DepositedCoinsCallback cb,
3006 : void *cb_cls)
3007 : {
3008 4 : struct PostgresClosure *pg = cls;
3009 4 : struct LookupDepositsByOrderContext ldoc = {
3010 : .pg = pg,
3011 : .cb = cb,
3012 : .cb_cls = cb_cls
3013 : };
3014 4 : struct GNUNET_PQ_QueryParam params[] = {
3015 4 : GNUNET_PQ_query_param_uint64 (&order_serial),
3016 : GNUNET_PQ_query_param_end
3017 : };
3018 : enum GNUNET_DB_QueryStatus qs;
3019 :
3020 4 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3021 : "lookup_deposits_by_order",
3022 : params,
3023 : &lookup_deposits_by_order_cb,
3024 : &ldoc);
3025 4 : if (qs < 0)
3026 0 : return qs;
3027 4 : return ldoc.qs;
3028 : }
3029 :
3030 :
3031 : /**
3032 : * Closure for lookup_deposits_by_order_cb().
3033 : */
3034 : struct LookupTransferDetailsByOrderContext
3035 : {
3036 :
3037 : /**
3038 : * Plugin context.
3039 : */
3040 : struct PostgresClosure *pg;
3041 :
3042 : /**
3043 : * Function to call with all results.
3044 : */
3045 : TALER_MERCHANTDB_OrderTransferDetailsCallback cb;
3046 :
3047 : /**
3048 : * Closure for @e cb.
3049 : */
3050 : void *cb_cls;
3051 :
3052 : /**
3053 : * Set to the query result.
3054 : */
3055 : enum GNUNET_DB_QueryStatus qs;
3056 : };
3057 :
3058 :
3059 : /**
3060 : * Function to be called with the results of a SELECT statement
3061 : * that has returned @a num_results results.
3062 : *
3063 : * @param cls of type `struct LookupTransferDetailsByOrderContext *`
3064 : * @param result the postgres result
3065 : * @param num_results the number of results in @a result
3066 : */
3067 : static void
3068 1 : lookup_transfer_details_by_order_cb (void *cls,
3069 : PGresult *result,
3070 : unsigned int num_results)
3071 : {
3072 1 : struct LookupTransferDetailsByOrderContext *ltdo = cls;
3073 1 : struct PostgresClosure *pg = ltdo->pg;
3074 :
3075 2 : for (unsigned int i = 0; i<num_results; i++)
3076 : {
3077 : struct TALER_WireTransferIdentifierRawP wtid;
3078 : char *exchange_url;
3079 : uint64_t deposit_serial;
3080 : struct GNUNET_TIME_Timestamp execution_time;
3081 : struct TALER_Amount deposit_value;
3082 : struct TALER_Amount deposit_fee;
3083 : uint8_t transfer_confirmed;
3084 1 : struct GNUNET_PQ_ResultSpec rs[] = {
3085 1 : GNUNET_PQ_result_spec_uint64 ("deposit_serial",
3086 : &deposit_serial),
3087 1 : GNUNET_PQ_result_spec_timestamp ("deposit_timestamp",
3088 : &execution_time),
3089 1 : GNUNET_PQ_result_spec_string ("exchange_url",
3090 : &exchange_url),
3091 1 : GNUNET_PQ_result_spec_auto_from_type ("wtid",
3092 : &wtid),
3093 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
3094 : &deposit_value),
3095 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
3096 : &deposit_fee),
3097 1 : GNUNET_PQ_result_spec_auto_from_type ("transfer_confirmed",
3098 : &transfer_confirmed),
3099 : GNUNET_PQ_result_spec_end
3100 : };
3101 :
3102 1 : if (GNUNET_OK !=
3103 1 : GNUNET_PQ_extract_result (result,
3104 : rs,
3105 : i))
3106 : {
3107 0 : GNUNET_break (0);
3108 0 : ltdo->qs = GNUNET_DB_STATUS_HARD_ERROR;
3109 0 : return;
3110 : }
3111 1 : ltdo->cb (ltdo->cb_cls,
3112 : &wtid,
3113 : exchange_url,
3114 : execution_time,
3115 : &deposit_value,
3116 : &deposit_fee,
3117 : (0 != transfer_confirmed));
3118 1 : GNUNET_PQ_cleanup_result (rs); /* technically useless here */
3119 : }
3120 1 : ltdo->qs = num_results;
3121 : }
3122 :
3123 :
3124 : /**
3125 : * Retrieve wire transfer details for all deposits associated with
3126 : * a given @a order_serial.
3127 : *
3128 : * @param cls closure
3129 : * @param order_serial identifies the order
3130 : * @param cb function called with the wire transfer details
3131 : * @param cb_cls closure for @a cb
3132 : * @return transaction status
3133 : */
3134 : static enum GNUNET_DB_QueryStatus
3135 1 : postgres_lookup_transfer_details_by_order (
3136 : void *cls,
3137 : uint64_t order_serial,
3138 : TALER_MERCHANTDB_OrderTransferDetailsCallback cb,
3139 : void *cb_cls)
3140 : {
3141 1 : struct PostgresClosure *pg = cls;
3142 1 : struct LookupTransferDetailsByOrderContext ltdo = {
3143 : .pg = pg,
3144 : .cb = cb,
3145 : .cb_cls = cb_cls
3146 : };
3147 1 : struct GNUNET_PQ_QueryParam params[] = {
3148 1 : GNUNET_PQ_query_param_uint64 (&order_serial),
3149 : GNUNET_PQ_query_param_end
3150 : };
3151 : enum GNUNET_DB_QueryStatus qs;
3152 :
3153 1 : qs = GNUNET_PQ_eval_prepared_multi_select (
3154 : pg->conn,
3155 : "lookup_transfer_details_by_order",
3156 : params,
3157 : &lookup_transfer_details_by_order_cb,
3158 : <do);
3159 1 : if (qs < 0)
3160 0 : return qs;
3161 1 : return ltdo.qs;
3162 : }
3163 :
3164 :
3165 : /**
3166 : * Insert wire transfer details for a deposit.
3167 : *
3168 : * @param cls closure
3169 : * @param deposit_serial serial number of the deposit
3170 : * @param dd deposit transfer data from the exchange to store
3171 : * @return transaction status
3172 : */
3173 : static enum GNUNET_DB_QueryStatus
3174 2 : postgres_insert_deposit_to_transfer (
3175 : void *cls,
3176 : uint64_t deposit_serial,
3177 : const struct TALER_EXCHANGE_DepositData *dd)
3178 : {
3179 2 : struct PostgresClosure *pg = cls;
3180 2 : struct GNUNET_PQ_QueryParam params[] = {
3181 2 : GNUNET_PQ_query_param_uint64 (&deposit_serial),
3182 2 : TALER_PQ_query_param_amount (&dd->coin_contribution),
3183 2 : GNUNET_PQ_query_param_timestamp (&dd->execution_time),
3184 2 : GNUNET_PQ_query_param_auto_from_type (&dd->exchange_sig),
3185 2 : GNUNET_PQ_query_param_auto_from_type (&dd->exchange_pub),
3186 2 : GNUNET_PQ_query_param_auto_from_type (&dd->wtid),
3187 : GNUNET_PQ_query_param_end
3188 : };
3189 :
3190 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3191 : "insert_deposit_to_transfer",
3192 : params);
3193 : }
3194 :
3195 :
3196 : /**
3197 : * Set 'wired' status for an order to 'true'.
3198 : *
3199 : * @param cls closure
3200 : * @param order_serial serial number of the order
3201 : * @return transaction status
3202 : */
3203 : static enum GNUNET_DB_QueryStatus
3204 18 : postgres_mark_order_wired (void *cls,
3205 : uint64_t order_serial)
3206 : {
3207 18 : struct PostgresClosure *pg = cls;
3208 18 : struct GNUNET_PQ_QueryParam params[] = {
3209 18 : GNUNET_PQ_query_param_uint64 (&order_serial),
3210 : GNUNET_PQ_query_param_end
3211 : };
3212 :
3213 18 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3214 : "mark_order_wired",
3215 : params);
3216 : }
3217 :
3218 :
3219 : /**
3220 : * Closure for #process_refund_cb().
3221 : */
3222 : struct FindRefundContext
3223 : {
3224 :
3225 : /**
3226 : * Plugin context.
3227 : */
3228 : struct PostgresClosure *pg;
3229 :
3230 : /**
3231 : * Updated to reflect total amount refunded so far.
3232 : */
3233 : struct TALER_Amount refunded_amount;
3234 :
3235 : /**
3236 : * Set to the largest refund transaction ID encountered.
3237 : */
3238 : uint64_t max_rtransaction_id;
3239 :
3240 : /**
3241 : * Set to true on hard errors.
3242 : */
3243 : bool err;
3244 : };
3245 :
3246 :
3247 : /**
3248 : * Function to be called with the results of a SELECT statement
3249 : * that has returned @a num_results results.
3250 : *
3251 : * @param cls closure, our `struct FindRefundContext`
3252 : * @param result the postgres result
3253 : * @param num_results the number of results in @a result
3254 : */
3255 : static void
3256 6 : process_refund_cb (void *cls,
3257 : PGresult *result,
3258 : unsigned int num_results)
3259 : {
3260 6 : struct FindRefundContext *ictx = cls;
3261 6 : struct PostgresClosure *pg = ictx->pg;
3262 :
3263 10 : for (unsigned int i = 0; i<num_results; i++)
3264 : {
3265 : /* Sum up existing refunds */
3266 : struct TALER_Amount acc;
3267 : uint64_t rtransaction_id;
3268 4 : struct GNUNET_PQ_ResultSpec rs[] = {
3269 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
3270 : &acc),
3271 4 : GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
3272 : &rtransaction_id),
3273 : GNUNET_PQ_result_spec_end
3274 : };
3275 :
3276 4 : if (GNUNET_OK !=
3277 4 : GNUNET_PQ_extract_result (result,
3278 : rs,
3279 : i))
3280 : {
3281 0 : GNUNET_break (0);
3282 0 : ictx->err = true;
3283 0 : return;
3284 : }
3285 4 : if (0 >
3286 4 : TALER_amount_add (&ictx->refunded_amount,
3287 4 : &ictx->refunded_amount,
3288 : &acc))
3289 : {
3290 0 : GNUNET_break (0);
3291 0 : ictx->err = true;
3292 0 : return;
3293 : }
3294 4 : ictx->max_rtransaction_id = GNUNET_MAX (ictx->max_rtransaction_id,
3295 : rtransaction_id);
3296 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3297 : "Found refund of %s\n",
3298 : TALER_amount2s (&acc));
3299 : }
3300 : }
3301 :
3302 :
3303 : /**
3304 : * Closure for #process_deposits_for_refund_cb().
3305 : */
3306 : struct InsertRefundContext
3307 : {
3308 : /**
3309 : * Used to provide a connection to the db
3310 : */
3311 : struct PostgresClosure *pg;
3312 :
3313 : /**
3314 : * Amount to which increase the refund for this contract
3315 : */
3316 : const struct TALER_Amount *refund;
3317 :
3318 : /**
3319 : * Human-readable reason behind this refund
3320 : */
3321 : const char *reason;
3322 :
3323 : /**
3324 : * Transaction status code.
3325 : */
3326 : enum TALER_MERCHANTDB_RefundStatus rs;
3327 : };
3328 :
3329 :
3330 : /**
3331 : * Data extracted per coin.
3332 : */
3333 : struct RefundCoinData
3334 : {
3335 :
3336 : /**
3337 : * Public key of a coin.
3338 : */
3339 : struct TALER_CoinSpendPublicKeyP coin_pub;
3340 :
3341 : /**
3342 : * Amount deposited for this coin.
3343 : */
3344 : struct TALER_Amount deposited_with_fee;
3345 :
3346 : /**
3347 : * Amount refunded already for this coin.
3348 : */
3349 : struct TALER_Amount refund_amount;
3350 :
3351 : /**
3352 : * Order serial (actually not really per-coin).
3353 : */
3354 : uint64_t order_serial;
3355 :
3356 : /**
3357 : * Maximum rtransaction_id for this coin so far.
3358 : */
3359 : uint64_t max_rtransaction_id;
3360 :
3361 : };
3362 :
3363 :
3364 : /**
3365 : * Function to be called with the results of a SELECT statement
3366 : * that has returned @a num_results results.
3367 : *
3368 : * @param cls closure, our `struct InsertRefundContext`
3369 : * @param result the postgres result
3370 : * @param num_results the number of results in @a result
3371 : */
3372 : static void
3373 4 : process_deposits_for_refund_cb (void *cls,
3374 : PGresult *result,
3375 : unsigned int num_results)
3376 4 : {
3377 4 : struct InsertRefundContext *ctx = cls;
3378 4 : struct PostgresClosure *pg = ctx->pg;
3379 : struct TALER_Amount current_refund;
3380 4 : struct RefundCoinData rcd[GNUNET_NZL (num_results)];
3381 : struct GNUNET_TIME_Timestamp now;
3382 :
3383 4 : now = GNUNET_TIME_timestamp_get ();
3384 4 : GNUNET_assert (GNUNET_OK ==
3385 : TALER_amount_set_zero (ctx->refund->currency,
3386 : ¤t_refund));
3387 4 : memset (rcd, 0, sizeof (rcd));
3388 : /* Pass 1: Collect amount of existing refunds into current_refund.
3389 : * Also store existing refunded amount for each deposit in deposit_refund. */
3390 10 : for (unsigned int i = 0; i<num_results; i++)
3391 : {
3392 6 : struct GNUNET_PQ_ResultSpec rs[] = {
3393 6 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
3394 : &rcd[i].coin_pub),
3395 6 : GNUNET_PQ_result_spec_uint64 ("order_serial",
3396 : &rcd[i].order_serial),
3397 6 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
3398 : &rcd[i].deposited_with_fee),
3399 : GNUNET_PQ_result_spec_end
3400 : };
3401 6 : struct FindRefundContext ictx = {
3402 : .pg = pg
3403 : };
3404 :
3405 6 : if (GNUNET_OK !=
3406 6 : GNUNET_PQ_extract_result (result,
3407 : rs,
3408 : i))
3409 : {
3410 0 : GNUNET_break (0);
3411 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3412 0 : return;
3413 : }
3414 :
3415 : {
3416 : enum GNUNET_DB_QueryStatus ires;
3417 6 : struct GNUNET_PQ_QueryParam params[] = {
3418 6 : GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
3419 6 : GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
3420 : GNUNET_PQ_query_param_end
3421 : };
3422 :
3423 6 : GNUNET_assert (GNUNET_OK ==
3424 : TALER_amount_set_zero (ctx->refund->currency,
3425 : &ictx.refunded_amount));
3426 6 : ires = GNUNET_PQ_eval_prepared_multi_select (ctx->pg->conn,
3427 : "find_refunds_by_coin",
3428 : params,
3429 : &process_refund_cb,
3430 : &ictx);
3431 6 : if ( (ictx.err) ||
3432 : (GNUNET_DB_STATUS_HARD_ERROR == ires) )
3433 : {
3434 0 : GNUNET_break (0);
3435 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3436 0 : return;
3437 : }
3438 6 : if (GNUNET_DB_STATUS_SOFT_ERROR == ires)
3439 : {
3440 0 : ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
3441 0 : return;
3442 : }
3443 : }
3444 6 : if (0 >
3445 6 : TALER_amount_add (¤t_refund,
3446 : ¤t_refund,
3447 : &ictx.refunded_amount))
3448 : {
3449 0 : GNUNET_break (0);
3450 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3451 0 : return;
3452 : }
3453 6 : rcd[i].refund_amount = ictx.refunded_amount;
3454 6 : rcd[i].max_rtransaction_id = ictx.max_rtransaction_id;
3455 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3456 : "Existing refund for coin %s is %s\n",
3457 : TALER_B2S (&rcd[i].coin_pub),
3458 : TALER_amount2s (&ictx.refunded_amount));
3459 : }
3460 :
3461 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3462 : "Total existing refund is %s\n",
3463 : TALER_amount2s (¤t_refund));
3464 :
3465 : /* stop immediately if we are 'done' === amount already
3466 : * refunded. */
3467 4 : if (0 >= TALER_amount_cmp (ctx->refund,
3468 : ¤t_refund))
3469 : {
3470 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3471 : "Existing refund of %s at or above requested refund. Finished early.\n",
3472 : TALER_amount2s (¤t_refund));
3473 1 : ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
3474 1 : return;
3475 : }
3476 :
3477 : /* Phase 2: Try to increase current refund until it matches desired refund */
3478 5 : for (unsigned int i = 0; i<num_results; i++)
3479 : {
3480 : const struct TALER_Amount *increment;
3481 : struct TALER_Amount left;
3482 : struct TALER_Amount remaining_refund;
3483 :
3484 : /* How much of the coin is left after the existing refunds? */
3485 4 : if (0 >
3486 4 : TALER_amount_subtract (&left,
3487 4 : &rcd[i].deposited_with_fee,
3488 4 : &rcd[i].refund_amount))
3489 : {
3490 0 : GNUNET_break (0);
3491 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3492 2 : return;
3493 : }
3494 :
3495 4 : if ( (0 == left.value) &&
3496 1 : (0 == left.fraction) )
3497 : {
3498 : /* coin was fully refunded, move to next coin */
3499 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3500 : "Coin %s fully refunded, moving to next coin\n",
3501 : TALER_B2S (&rcd[i].coin_pub));
3502 1 : continue;
3503 : }
3504 :
3505 3 : rcd[i].max_rtransaction_id++;
3506 : /* How much of the refund is still to be paid back? */
3507 3 : if (0 >
3508 3 : TALER_amount_subtract (&remaining_refund,
3509 : ctx->refund,
3510 : ¤t_refund))
3511 : {
3512 0 : GNUNET_break (0);
3513 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3514 0 : return;
3515 : }
3516 :
3517 : /* By how much will we increase the refund for this coin? */
3518 3 : if (0 >= TALER_amount_cmp (&remaining_refund,
3519 : &left))
3520 : {
3521 : /* remaining_refund <= left */
3522 2 : increment = &remaining_refund;
3523 : }
3524 : else
3525 : {
3526 1 : increment = &left;
3527 : }
3528 :
3529 3 : if (0 >
3530 3 : TALER_amount_add (¤t_refund,
3531 : ¤t_refund,
3532 : increment))
3533 : {
3534 0 : GNUNET_break (0);
3535 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3536 0 : return;
3537 : }
3538 :
3539 : /* actually run the refund */
3540 3 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3541 : "Coin %s deposit amount is %s\n",
3542 : TALER_B2S (&rcd[i].coin_pub),
3543 : TALER_amount2s (&rcd[i].deposited_with_fee));
3544 3 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3545 : "Coin %s refund will be incremented by %s\n",
3546 : TALER_B2S (&rcd[i].coin_pub),
3547 : TALER_amount2s (increment));
3548 : {
3549 : enum GNUNET_DB_QueryStatus qs;
3550 3 : struct GNUNET_PQ_QueryParam params[] = {
3551 3 : GNUNET_PQ_query_param_uint64 (&rcd[i].order_serial),
3552 3 : GNUNET_PQ_query_param_uint64 (&rcd[i].max_rtransaction_id), /* already inc'ed */
3553 3 : GNUNET_PQ_query_param_timestamp (&now),
3554 3 : GNUNET_PQ_query_param_auto_from_type (&rcd[i].coin_pub),
3555 3 : GNUNET_PQ_query_param_string (ctx->reason),
3556 3 : TALER_PQ_query_param_amount (increment),
3557 : GNUNET_PQ_query_param_end
3558 : };
3559 :
3560 3 : check_connection (pg);
3561 3 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
3562 : "insert_refund",
3563 : params);
3564 3 : switch (qs)
3565 : {
3566 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3567 0 : GNUNET_break (0);
3568 0 : ctx->rs = TALER_MERCHANTDB_RS_HARD_ERROR;
3569 0 : return;
3570 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3571 0 : ctx->rs = TALER_MERCHANTDB_RS_SOFT_ERROR;
3572 0 : return;
3573 3 : default:
3574 3 : ctx->rs = (enum TALER_MERCHANTDB_RefundStatus) qs;
3575 3 : break;
3576 : }
3577 : }
3578 :
3579 : /* stop immediately if we are done */
3580 3 : if (0 == TALER_amount_cmp (ctx->refund,
3581 : ¤t_refund))
3582 : {
3583 2 : ctx->rs = TALER_MERCHANTDB_RS_SUCCESS;
3584 2 : return;
3585 : }
3586 : }
3587 :
3588 : /**
3589 : * We end up here if not all of the refund has been covered.
3590 : * Although this should be checked as the business should never
3591 : * issue a refund bigger than the contract's actual price, we cannot
3592 : * rely upon the frontend being correct.
3593 : *///
3594 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3595 : "The refund of %s is bigger than the order's value\n",
3596 : TALER_amount2s (ctx->refund));
3597 1 : ctx->rs = TALER_MERCHANTDB_RS_TOO_HIGH;
3598 : }
3599 :
3600 :
3601 : /**
3602 : * Function called when some backoffice staff decides to award or
3603 : * increase the refund on an existing contract. This function
3604 : * MUST be called from within a transaction scope setup by the
3605 : * caller as it executes multiple SQL statements.
3606 : *
3607 : * @param cls closure
3608 : * @param instance_id instance identifier
3609 : * @param order_id the order to increase the refund for
3610 : * @param refund maximum refund to return to the customer for this contract
3611 : * @param reason 0-terminated UTF-8 string giving the reason why the customer
3612 : * got a refund (free form, business-specific)
3613 : * @return transaction status
3614 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a refund is ABOVE the amount we
3615 : * were originally paid and thus the transaction failed;
3616 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if the request is valid,
3617 : * regardless of whether it actually increased the refund beyond
3618 : * what was already refunded (idempotency!)
3619 : */
3620 : static enum TALER_MERCHANTDB_RefundStatus
3621 4 : postgres_increase_refund (void *cls,
3622 : const char *instance_id,
3623 : const char *order_id,
3624 : const struct TALER_Amount *refund,
3625 : const char *reason)
3626 : {
3627 4 : struct PostgresClosure *pg = cls;
3628 : enum GNUNET_DB_QueryStatus qs;
3629 4 : struct GNUNET_PQ_QueryParam params[] = {
3630 4 : GNUNET_PQ_query_param_string (instance_id),
3631 4 : GNUNET_PQ_query_param_string (order_id),
3632 : GNUNET_PQ_query_param_end
3633 : };
3634 4 : struct InsertRefundContext ctx = {
3635 : .pg = pg,
3636 : .refund = refund,
3637 : .reason = reason
3638 : };
3639 :
3640 4 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3641 : "Asked to refund %s on order %s\n",
3642 : TALER_amount2s (refund),
3643 : order_id);
3644 4 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3645 : "find_deposits_for_refund",
3646 : params,
3647 : &process_deposits_for_refund_cb,
3648 : &ctx);
3649 4 : switch (qs)
3650 : {
3651 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
3652 : /* never paid, means we clearly cannot refund anything */
3653 0 : return TALER_MERCHANTDB_RS_NO_SUCH_ORDER;
3654 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
3655 0 : return TALER_MERCHANTDB_RS_SOFT_ERROR;
3656 0 : case GNUNET_DB_STATUS_HARD_ERROR:
3657 0 : return TALER_MERCHANTDB_RS_HARD_ERROR;
3658 4 : default:
3659 : /* Got one or more deposits */
3660 4 : return ctx.rs;
3661 : }
3662 : }
3663 :
3664 :
3665 : /**
3666 : * Closure for #lookup_refunds_detailed_cb().
3667 : */
3668 : struct LookupRefundsDetailedContext
3669 : {
3670 : /**
3671 : * Function to call for each refund.
3672 : */
3673 : TALER_MERCHANTDB_RefundDetailCallback rc;
3674 :
3675 : /**
3676 : * Closure for @e rc.
3677 : */
3678 : void *rc_cls;
3679 :
3680 : /**
3681 : * Plugin context.
3682 : */
3683 : struct PostgresClosure *pg;
3684 :
3685 : /**
3686 : * Transaction result.
3687 : */
3688 : enum GNUNET_DB_QueryStatus qs;
3689 : };
3690 :
3691 :
3692 : /**
3693 : * Function to be called with the results of a SELECT statement
3694 : * that has returned @a num_results results.
3695 : *
3696 : * @param cls of type `struct GetRefundsContext *`
3697 : * @param result the postgres result
3698 : * @param num_results the number of results in @a result
3699 : */
3700 : static void
3701 3 : lookup_refunds_detailed_cb (void *cls,
3702 : PGresult *result,
3703 : unsigned int num_results)
3704 : {
3705 3 : struct LookupRefundsDetailedContext *lrdc = cls;
3706 3 : struct PostgresClosure *pg = lrdc->pg;
3707 :
3708 7 : for (unsigned int i = 0; i<num_results; i++)
3709 : {
3710 : uint64_t refund_serial;
3711 : struct GNUNET_TIME_Timestamp timestamp;
3712 : struct TALER_CoinSpendPublicKeyP coin_pub;
3713 : uint64_t rtransaction_id;
3714 : struct TALER_Amount refund_amount;
3715 : char *reason;
3716 : char *exchange_url;
3717 : uint8_t pending8;
3718 4 : struct GNUNET_PQ_ResultSpec rs[] = {
3719 4 : GNUNET_PQ_result_spec_uint64 ("refund_serial",
3720 : &refund_serial),
3721 4 : GNUNET_PQ_result_spec_timestamp ("refund_timestamp",
3722 : ×tamp),
3723 4 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
3724 : &coin_pub),
3725 4 : GNUNET_PQ_result_spec_string ("exchange_url",
3726 : &exchange_url),
3727 4 : GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
3728 : &rtransaction_id),
3729 4 : GNUNET_PQ_result_spec_string ("reason",
3730 : &reason),
3731 4 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_amount",
3732 : &refund_amount),
3733 4 : GNUNET_PQ_result_spec_auto_from_type ("pending",
3734 : &pending8),
3735 : GNUNET_PQ_result_spec_end
3736 : };
3737 :
3738 4 : if (GNUNET_OK !=
3739 4 : GNUNET_PQ_extract_result (result,
3740 : rs,
3741 : i))
3742 : {
3743 0 : GNUNET_break (0);
3744 0 : lrdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
3745 0 : return;
3746 : }
3747 4 : lrdc->rc (lrdc->rc_cls,
3748 : refund_serial,
3749 : timestamp,
3750 : &coin_pub,
3751 : exchange_url,
3752 : rtransaction_id,
3753 : reason,
3754 : &refund_amount,
3755 : 0 != pending8);
3756 4 : GNUNET_PQ_cleanup_result (rs);
3757 : }
3758 3 : lrdc->qs = num_results;
3759 : }
3760 :
3761 :
3762 : /**
3763 : * Obtain detailed refund data associated with a contract.
3764 : *
3765 : * @param cls closure, typically a connection to the db
3766 : * @param instance_id instance to lookup refunds for
3767 : * @param h_contract_terms hash code of the contract
3768 : * @param rc function to call for each coin on which there is a refund
3769 : * @param rc_cls closure for @a rc
3770 : * @return transaction status
3771 : */
3772 : static enum GNUNET_DB_QueryStatus
3773 3 : postgres_lookup_refunds_detailed (
3774 : void *cls,
3775 : const char *instance_id,
3776 : const struct TALER_PrivateContractHashP *h_contract_terms,
3777 : TALER_MERCHANTDB_RefundDetailCallback rc,
3778 : void *rc_cls)
3779 : {
3780 3 : struct PostgresClosure *pg = cls;
3781 3 : struct GNUNET_PQ_QueryParam params[] = {
3782 3 : GNUNET_PQ_query_param_string (instance_id),
3783 3 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
3784 : GNUNET_PQ_query_param_end
3785 : };
3786 3 : struct LookupRefundsDetailedContext lrdc = {
3787 : .rc = rc,
3788 : .rc_cls = rc_cls,
3789 : .pg = pg
3790 : };
3791 : enum GNUNET_DB_QueryStatus qs;
3792 :
3793 : /* no preflight check here, run in transaction by caller! */
3794 3 : TALER_LOG_DEBUG ("Looking for refund %s + %s\n",
3795 : GNUNET_h2s (&h_contract_terms->hash),
3796 : instance_id);
3797 3 : check_connection (pg);
3798 3 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
3799 : "lookup_refunds_detailed",
3800 : params,
3801 : &lookup_refunds_detailed_cb,
3802 : &lrdc);
3803 3 : if (0 >= qs)
3804 0 : return qs;
3805 3 : return lrdc.qs;
3806 : }
3807 :
3808 :
3809 : /**
3810 : * Insert refund proof data from the exchange into the database.
3811 : *
3812 : * @param cls closure
3813 : * @param refund_serial serial number of the refund
3814 : * @param exchange_sig signature from exchange that coin was refunded
3815 : * @param exchange_pub signing key that was used for @a exchange_sig
3816 : * @return transaction status
3817 : */
3818 : static enum GNUNET_DB_QueryStatus
3819 2 : postgres_insert_refund_proof (
3820 : void *cls,
3821 : uint64_t refund_serial,
3822 : const struct TALER_ExchangeSignatureP *exchange_sig,
3823 : const struct TALER_ExchangePublicKeyP *exchange_pub)
3824 : {
3825 2 : struct PostgresClosure *pg = cls;
3826 2 : struct GNUNET_PQ_QueryParam params[] = {
3827 2 : GNUNET_PQ_query_param_uint64 (&refund_serial),
3828 2 : GNUNET_PQ_query_param_auto_from_type (exchange_sig),
3829 2 : GNUNET_PQ_query_param_auto_from_type (exchange_pub),
3830 : GNUNET_PQ_query_param_end
3831 : };
3832 :
3833 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3834 : "insert_refund_proof",
3835 : params);
3836 : }
3837 :
3838 :
3839 : /**
3840 : * Lookup refund proof data.
3841 : *
3842 : * @param cls closure
3843 : * @param refund_serial serial number of the refund
3844 : * @param[out] exchange_sig set to signature from exchange
3845 : * @param[out] exchange_pub signing key that was used for @a exchange_sig
3846 : * @return transaction status
3847 : */
3848 : static enum GNUNET_DB_QueryStatus
3849 1 : postgres_lookup_refund_proof (void *cls,
3850 : uint64_t refund_serial,
3851 : struct TALER_ExchangeSignatureP *exchange_sig,
3852 : struct TALER_ExchangePublicKeyP *exchange_pub)
3853 : {
3854 1 : struct PostgresClosure *pg = cls;
3855 1 : struct GNUNET_PQ_QueryParam params[] = {
3856 1 : GNUNET_PQ_query_param_uint64 (&refund_serial),
3857 : GNUNET_PQ_query_param_end
3858 : };
3859 1 : struct GNUNET_PQ_ResultSpec rs[] = {
3860 1 : GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
3861 : exchange_sig),
3862 1 : GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
3863 : exchange_pub),
3864 : GNUNET_PQ_result_spec_end
3865 : };
3866 :
3867 1 : check_connection (pg);
3868 1 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
3869 : "lookup_refund_proof",
3870 : params,
3871 : rs);
3872 : }
3873 :
3874 :
3875 : /**
3876 : * Retrieve the order ID that was used to pay for a resource within a session.
3877 : *
3878 : * @param cls closure
3879 : * @param instance_id identifying the instance
3880 : * @param fulfillment_url URL that canonically identifies the resource
3881 : * being paid for
3882 : * @param session_id session id
3883 : * @param[out] order_id where to store the order ID that was used when
3884 : * paying for the resource URL
3885 : * @return transaction status
3886 : */
3887 : enum GNUNET_DB_QueryStatus
3888 2 : postgres_lookup_order_by_fulfillment (void *cls,
3889 : const char *instance_id,
3890 : const char *fulfillment_url,
3891 : const char *session_id,
3892 : char **order_id)
3893 : {
3894 2 : struct PostgresClosure *pg = cls;
3895 2 : struct GNUNET_PQ_QueryParam params[] = {
3896 2 : GNUNET_PQ_query_param_string (instance_id),
3897 2 : GNUNET_PQ_query_param_string (fulfillment_url),
3898 2 : GNUNET_PQ_query_param_string (session_id),
3899 : GNUNET_PQ_query_param_end
3900 : };
3901 2 : struct GNUNET_PQ_ResultSpec rs[] = {
3902 2 : GNUNET_PQ_result_spec_string ("order_id",
3903 : order_id),
3904 : GNUNET_PQ_result_spec_end
3905 : };
3906 :
3907 2 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
3908 : "lookup_order_by_fulfillment",
3909 : params,
3910 : rs);
3911 : }
3912 :
3913 :
3914 : /**
3915 : * Insert information about a wire transfer the merchant has received.
3916 : *
3917 : * @param cls closure
3918 : * @param instance_id the instance that received the transfer
3919 : * @param exchange_url which exchange made the transfer
3920 : * @param wtid identifier of the wire transfer
3921 : * @param credit_amount how much did we receive
3922 : * @param payto_uri what is the merchant's bank account that received the transfer
3923 : * @param confirmed whether the transfer was confirmed by the merchant or
3924 : * was merely claimed by the exchange at this point
3925 : * @return transaction status
3926 : */
3927 : static enum GNUNET_DB_QueryStatus
3928 2 : postgres_insert_transfer (
3929 : void *cls,
3930 : const char *instance_id,
3931 : const char *exchange_url,
3932 : const struct TALER_WireTransferIdentifierRawP *wtid,
3933 : const struct TALER_Amount *credit_amount,
3934 : const char *payto_uri,
3935 : bool confirmed)
3936 : {
3937 2 : struct PostgresClosure *pg = cls;
3938 2 : uint8_t confirmed8 = confirmed;
3939 2 : struct GNUNET_PQ_QueryParam params[] = {
3940 2 : GNUNET_PQ_query_param_string (exchange_url),
3941 2 : GNUNET_PQ_query_param_auto_from_type (wtid),
3942 2 : TALER_PQ_query_param_amount (credit_amount),
3943 2 : GNUNET_PQ_query_param_string (payto_uri),
3944 2 : GNUNET_PQ_query_param_auto_from_type (&confirmed8),
3945 2 : GNUNET_PQ_query_param_string (instance_id),
3946 : GNUNET_PQ_query_param_end
3947 : };
3948 :
3949 2 : check_connection (pg);
3950 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3951 : "insert_transfer",
3952 : params);
3953 : }
3954 :
3955 :
3956 : /**
3957 : * Delete information about a transfer. Note that transfers
3958 : * confirmed by the exchange cannot be deleted anymore.
3959 : *
3960 : * @param cls closure
3961 : * @param instance_id instance to delete transfer of
3962 : * @param transfer_serial_id transfer to delete
3963 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
3964 : * if deletion is prohibited OR transfer is unknown
3965 : */
3966 : static enum GNUNET_DB_QueryStatus
3967 0 : postgres_delete_transfer (void *cls,
3968 : const char *instance_id,
3969 : uint64_t transfer_serial_id)
3970 : {
3971 0 : struct PostgresClosure *pg = cls;
3972 0 : struct GNUNET_PQ_QueryParam params[] = {
3973 0 : GNUNET_PQ_query_param_string (instance_id),
3974 0 : GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
3975 : GNUNET_PQ_query_param_end
3976 : };
3977 :
3978 0 : check_connection (pg);
3979 0 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
3980 : "delete_transfer",
3981 : params);
3982 : }
3983 :
3984 :
3985 : /**
3986 : * Check if information about a transfer exists with the
3987 : * backend. Returns no data, only the query status.
3988 : *
3989 : * @param cls closure
3990 : * @param instance_id instance to delete transfer of
3991 : * @param transfer_serial_id transfer to delete
3992 : * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
3993 : * if the transfer record exists
3994 : */
3995 : static enum GNUNET_DB_QueryStatus
3996 0 : postgres_check_transfer_exists (void *cls,
3997 : const char *instance_id,
3998 : uint64_t transfer_serial_id)
3999 : {
4000 0 : struct PostgresClosure *pg = cls;
4001 0 : struct GNUNET_PQ_QueryParam params[] = {
4002 0 : GNUNET_PQ_query_param_string (instance_id),
4003 0 : GNUNET_PQ_query_param_uint64 (&transfer_serial_id),
4004 : GNUNET_PQ_query_param_end
4005 : };
4006 0 : struct GNUNET_PQ_ResultSpec rs[] = {
4007 : GNUNET_PQ_result_spec_end
4008 : };
4009 :
4010 0 : check_connection (pg);
4011 0 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4012 : "check_transfer_exists",
4013 : params,
4014 : rs);
4015 : }
4016 :
4017 :
4018 : /**
4019 : * Lookup account serial by payto URI.
4020 : *
4021 : * @param cls closure
4022 : * @param instance_id instance to lookup the account from
4023 : * @param payto_uri what is the merchant's bank account to lookup
4024 : * @param[out] account_serial serial number of the account
4025 : * @return transaction status
4026 : */
4027 : static enum GNUNET_DB_QueryStatus
4028 2 : postgres_lookup_account (void *cls,
4029 : const char *instance_id,
4030 : const char *payto_uri,
4031 : uint64_t *account_serial)
4032 : {
4033 2 : struct PostgresClosure *pg = cls;
4034 2 : struct GNUNET_PQ_QueryParam params[] = {
4035 2 : GNUNET_PQ_query_param_string (instance_id),
4036 2 : GNUNET_PQ_query_param_string (payto_uri),
4037 : GNUNET_PQ_query_param_end
4038 : };
4039 2 : struct GNUNET_PQ_ResultSpec rs[] = {
4040 2 : GNUNET_PQ_result_spec_uint64 ("account_serial",
4041 : account_serial),
4042 : GNUNET_PQ_result_spec_end
4043 : };
4044 :
4045 2 : check_connection (pg);
4046 2 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4047 : "lookup_account",
4048 : params,
4049 : rs);
4050 : }
4051 :
4052 :
4053 : /**
4054 : * Insert information about a wire transfer the merchant has received.
4055 : *
4056 : * @param cls closure
4057 : * @param instance_id instance to provide transfer details for
4058 : * @param exchange_url which exchange made the transfer
4059 : * @param payto_uri what is the merchant's bank account that received the transfer
4060 : * @param wtid identifier of the wire transfer
4061 : * @param td transfer details to store
4062 : * @return transaction status,
4063 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the @a wtid and @a exchange_uri are not known for this @a instance_id
4064 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
4065 : */
4066 : static enum GNUNET_DB_QueryStatus
4067 1 : postgres_insert_transfer_details (
4068 : void *cls,
4069 : const char *instance_id,
4070 : const char *exchange_url,
4071 : const char *payto_uri,
4072 : const struct TALER_WireTransferIdentifierRawP *wtid,
4073 : const struct TALER_EXCHANGE_TransferData *td)
4074 : {
4075 1 : struct PostgresClosure *pg = cls;
4076 : enum GNUNET_DB_QueryStatus qs;
4077 : uint64_t credit_serial;
4078 : unsigned int retries;
4079 :
4080 1 : retries = 0;
4081 1 : check_connection (pg);
4082 1 : RETRY:
4083 1 : if (MAX_RETRIES < ++retries)
4084 0 : return GNUNET_DB_STATUS_SOFT_ERROR;
4085 1 : if (GNUNET_OK !=
4086 1 : postgres_start_read_committed (pg,
4087 : "insert transfer details"))
4088 : {
4089 0 : GNUNET_break (0);
4090 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4091 : }
4092 :
4093 : /* lookup credit serial */
4094 : {
4095 1 : struct GNUNET_PQ_QueryParam params[] = {
4096 1 : GNUNET_PQ_query_param_string (exchange_url),
4097 1 : GNUNET_PQ_query_param_string (payto_uri),
4098 1 : GNUNET_PQ_query_param_string (instance_id),
4099 1 : GNUNET_PQ_query_param_auto_from_type (wtid),
4100 : GNUNET_PQ_query_param_end
4101 : };
4102 1 : struct GNUNET_PQ_ResultSpec rs[] = {
4103 1 : GNUNET_PQ_result_spec_uint64 ("credit_serial",
4104 : &credit_serial),
4105 : GNUNET_PQ_result_spec_end
4106 : };
4107 :
4108 1 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4109 : "lookup_credit_serial",
4110 : params,
4111 : rs);
4112 1 : if (0 > qs)
4113 : {
4114 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4115 0 : postgres_rollback (pg);
4116 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4117 0 : goto RETRY;
4118 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4119 : "'lookup_credit_serial' for account %s and amount %s failed with status %d\n",
4120 : payto_uri,
4121 : TALER_amount2s (&td->total_amount),
4122 : qs);
4123 0 : return qs;
4124 : }
4125 1 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
4126 : {
4127 0 : postgres_rollback (pg);
4128 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4129 : "'lookup_credit_serial' for account %s failed with transfer unknown\n",
4130 : payto_uri);
4131 0 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
4132 : }
4133 : }
4134 :
4135 : /* update merchant_transfer_signatures table */
4136 : {
4137 1 : struct GNUNET_PQ_QueryParam params[] = {
4138 1 : GNUNET_PQ_query_param_uint64 (&credit_serial),
4139 1 : TALER_PQ_query_param_amount (&td->total_amount),
4140 1 : TALER_PQ_query_param_amount (&td->wire_fee),
4141 1 : GNUNET_PQ_query_param_timestamp (&td->execution_time),
4142 1 : GNUNET_PQ_query_param_auto_from_type (&td->exchange_sig),
4143 1 : GNUNET_PQ_query_param_auto_from_type (&td->exchange_pub),
4144 : GNUNET_PQ_query_param_end
4145 : };
4146 :
4147 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4148 : "insert_transfer_signature",
4149 : params);
4150 1 : if (0 > qs)
4151 : {
4152 : /* FIXME: distinguish hard error */
4153 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4154 0 : postgres_rollback (pg);
4155 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4156 0 : goto RETRY;
4157 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4158 : "'insert_transfer_signature' failed with status %d\n",
4159 : qs);
4160 0 : return qs;
4161 : }
4162 1 : if (0 == qs)
4163 : {
4164 0 : postgres_rollback (pg);
4165 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4166 : "'insert_transfer_signature' failed with status %d\n",
4167 : qs);
4168 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4169 : }
4170 : }
4171 :
4172 : /* Update transfer-coin association table */
4173 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4174 : "Updating transfer-coin association table\n");
4175 2 : for (unsigned int i = 0; i<td->details_length; i++)
4176 : {
4177 1 : const struct TALER_TrackTransferDetails *d = &td->details[i];
4178 1 : uint64_t i64 = (uint64_t) i;
4179 1 : struct GNUNET_PQ_QueryParam params[] = {
4180 1 : GNUNET_PQ_query_param_uint64 (&credit_serial),
4181 1 : GNUNET_PQ_query_param_uint64 (&i64),
4182 1 : TALER_PQ_query_param_amount (&d->coin_value),
4183 1 : TALER_PQ_query_param_amount (&d->coin_fee), /* deposit fee */
4184 1 : GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
4185 1 : GNUNET_PQ_query_param_auto_from_type (&d->h_contract_terms),
4186 1 : GNUNET_PQ_query_param_string (instance_id),
4187 : GNUNET_PQ_query_param_end
4188 : };
4189 :
4190 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4191 : "insert_transfer_to_coin_mapping",
4192 : params);
4193 1 : if (0 > qs)
4194 : {
4195 : /* FIXME: distinguish hard error */
4196 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4197 0 : postgres_rollback (pg);
4198 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4199 0 : goto RETRY;
4200 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4201 : "'insert_transfer_to_coin_mapping' failed with status %d\n",
4202 : qs);
4203 0 : return qs;
4204 : }
4205 1 : if (0 == qs)
4206 : {
4207 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4208 : "'insert_transfer_to_coin_mapping' failed at %u: deposit unknown\n",
4209 : i);
4210 : }
4211 : }
4212 : /* Update merchant_contract_terms 'wired' status: for all coins
4213 : that were wired, set the respective order's "wired" status to
4214 : true, *if* all other deposited coins associated with that order
4215 : have also been wired (this time or earlier) */
4216 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4217 : "Updating contract terms 'wired' status\n");
4218 2 : for (unsigned int i = 0; i<td->details_length; i++)
4219 : {
4220 1 : const struct TALER_TrackTransferDetails *d = &td->details[i];
4221 1 : struct GNUNET_PQ_QueryParam params[] = {
4222 1 : GNUNET_PQ_query_param_auto_from_type (&d->coin_pub),
4223 : GNUNET_PQ_query_param_end
4224 : };
4225 :
4226 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
4227 : "update_wired_by_coin_pub",
4228 : params);
4229 1 : if (0 > qs)
4230 : {
4231 : /* FIXME: distinguish hard error */
4232 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4233 0 : postgres_rollback (pg);
4234 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4235 0 : goto RETRY;
4236 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4237 : "'update_wired_by_coin_pub' failed with status %d\n",
4238 : qs);
4239 0 : return qs;
4240 : }
4241 : }
4242 1 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4243 : "Committing transaction...\n");
4244 1 : qs = postgres_commit (pg);
4245 1 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
4246 1 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
4247 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
4248 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
4249 0 : goto RETRY;
4250 0 : return qs;
4251 : }
4252 :
4253 :
4254 : /**
4255 : * Obtain information about wire fees charged by an exchange,
4256 : * including signature (so we have proof).
4257 : *
4258 : * @param cls closure
4259 : * @param master_pub public key of the exchange
4260 : * @param wire_method the wire method
4261 : * @param contract_date date of the contract to use for the lookup
4262 : * @param[out] fees wire fees charged
4263 : * @param[out] start_date start of fee being used
4264 : * @param[out] end_date end of fee being used
4265 : * @param[out] master_sig signature of exchange over fee structure
4266 : * @return transaction status code
4267 : */
4268 : static enum GNUNET_DB_QueryStatus
4269 3 : postgres_lookup_wire_fee (void *cls,
4270 : const struct TALER_MasterPublicKeyP *master_pub,
4271 : const char *wire_method,
4272 : struct GNUNET_TIME_Timestamp contract_date,
4273 : struct TALER_WireFeeSet *fees,
4274 : struct GNUNET_TIME_Timestamp *start_date,
4275 : struct GNUNET_TIME_Timestamp *end_date,
4276 : struct TALER_MasterSignatureP *master_sig)
4277 : {
4278 3 : struct PostgresClosure *pg = cls;
4279 : struct GNUNET_HashCode h_wire_method;
4280 3 : struct GNUNET_PQ_QueryParam params[] = {
4281 3 : GNUNET_PQ_query_param_auto_from_type (master_pub),
4282 3 : GNUNET_PQ_query_param_auto_from_type (&h_wire_method),
4283 3 : GNUNET_PQ_query_param_timestamp (&contract_date),
4284 : GNUNET_PQ_query_param_end
4285 : };
4286 3 : struct GNUNET_PQ_ResultSpec rs[] = {
4287 3 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4288 : &fees->wire),
4289 3 : TALER_PQ_RESULT_SPEC_AMOUNT ("closing_fee",
4290 : &fees->closing),
4291 3 : TALER_PQ_RESULT_SPEC_AMOUNT ("wad_fee",
4292 : &fees->wad),
4293 3 : GNUNET_PQ_result_spec_timestamp ("start_date",
4294 : start_date),
4295 3 : GNUNET_PQ_result_spec_timestamp ("end_date",
4296 : end_date),
4297 3 : GNUNET_PQ_result_spec_auto_from_type ("master_sig",
4298 : master_sig),
4299 : GNUNET_PQ_result_spec_end
4300 : };
4301 :
4302 3 : check_connection (pg);
4303 3 : GNUNET_CRYPTO_hash (wire_method,
4304 3 : strlen (wire_method) + 1,
4305 : &h_wire_method);
4306 3 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4307 : "lookup_wire_fee",
4308 : params,
4309 : rs);
4310 : }
4311 :
4312 :
4313 : /**
4314 : * Closure for #lookup_deposits_by_contract_and_coin_cb().
4315 : */
4316 : struct LookupDepositsByCnCContext
4317 : {
4318 : /**
4319 : * Function to call for each deposit.
4320 : */
4321 : TALER_MERCHANTDB_CoinDepositCallback cb;
4322 :
4323 : /**
4324 : * Closure for @e cb.
4325 : */
4326 : void *cb_cls;
4327 :
4328 : /**
4329 : * Plugin context.
4330 : */
4331 : struct PostgresClosure *pg;
4332 :
4333 : /**
4334 : * Transaction result.
4335 : */
4336 : enum GNUNET_DB_QueryStatus qs;
4337 : };
4338 :
4339 :
4340 : /**
4341 : * Function to be called with the results of a SELECT statement
4342 : * that has returned @a num_results results.
4343 : *
4344 : * @param cls of type `struct LookupDepositsByCnCContext *`
4345 : * @param result the postgres result
4346 : * @param num_results the number of results in @a result
4347 : */
4348 : static void
4349 1 : lookup_deposits_by_contract_and_coin_cb (void *cls,
4350 : PGresult *result,
4351 : unsigned int num_results)
4352 : {
4353 1 : struct LookupDepositsByCnCContext *ldcc = cls;
4354 1 : struct PostgresClosure *pg = ldcc->pg;
4355 :
4356 2 : for (unsigned int i = 0; i<num_results; i++)
4357 : {
4358 : char *exchange_url;
4359 : struct TALER_Amount amount_with_fee;
4360 : struct TALER_Amount deposit_fee;
4361 : struct TALER_Amount refund_fee;
4362 : struct TALER_Amount wire_fee;
4363 : struct TALER_MerchantWireHashP h_wire;
4364 : struct GNUNET_TIME_Timestamp deposit_timestamp;
4365 : struct GNUNET_TIME_Timestamp refund_deadline;
4366 : struct TALER_ExchangeSignatureP exchange_sig;
4367 : struct TALER_ExchangePublicKeyP exchange_pub;
4368 1 : struct GNUNET_PQ_ResultSpec rs[] = {
4369 1 : GNUNET_PQ_result_spec_string ("exchange_url",
4370 : &exchange_url),
4371 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
4372 : &amount_with_fee),
4373 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("deposit_fee",
4374 : &deposit_fee),
4375 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("refund_fee",
4376 : &refund_fee),
4377 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4378 : &wire_fee),
4379 1 : GNUNET_PQ_result_spec_auto_from_type ("h_wire",
4380 : &h_wire),
4381 1 : GNUNET_PQ_result_spec_timestamp ("deposit_timestamp",
4382 : &deposit_timestamp),
4383 1 : GNUNET_PQ_result_spec_timestamp ("refund_deadline",
4384 : &refund_deadline),
4385 1 : GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
4386 : &exchange_sig),
4387 1 : GNUNET_PQ_result_spec_auto_from_type ("exchange_pub",
4388 : &exchange_pub),
4389 : GNUNET_PQ_result_spec_end
4390 : };
4391 :
4392 1 : if (GNUNET_OK !=
4393 1 : GNUNET_PQ_extract_result (result,
4394 : rs,
4395 : i))
4396 : {
4397 0 : GNUNET_break (0);
4398 0 : ldcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4399 0 : return;
4400 : }
4401 1 : ldcc->cb (ldcc->cb_cls,
4402 : exchange_url,
4403 : &amount_with_fee,
4404 : &deposit_fee,
4405 : &refund_fee,
4406 : &wire_fee,
4407 : &h_wire,
4408 : deposit_timestamp,
4409 : refund_deadline,
4410 : &exchange_sig,
4411 : &exchange_pub);
4412 1 : GNUNET_PQ_cleanup_result (rs);
4413 : }
4414 1 : ldcc->qs = num_results;
4415 : }
4416 :
4417 :
4418 : /**
4419 : * Lookup information about coin payments by @a h_contract_terms and
4420 : * @a coin_pub.
4421 : *
4422 : * @param cls closure
4423 : * @param instance_id instance to lookup payments for
4424 : * @param h_contract_terms proposal data's hashcode
4425 : * @param coin_pub public key to use for the search
4426 : * @param cb function to call with payment data
4427 : * @param cb_cls closure for @a cb
4428 : * @return transaction status
4429 : */
4430 : static enum GNUNET_DB_QueryStatus
4431 1 : postgres_lookup_deposits_by_contract_and_coin (
4432 : void *cls,
4433 : const char *instance_id,
4434 : const struct TALER_PrivateContractHashP *h_contract_terms,
4435 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
4436 : TALER_MERCHANTDB_CoinDepositCallback cb,
4437 : void *cb_cls)
4438 : {
4439 1 : struct PostgresClosure *pg = cls;
4440 1 : struct GNUNET_PQ_QueryParam params[] = {
4441 1 : GNUNET_PQ_query_param_string (instance_id),
4442 1 : GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
4443 1 : GNUNET_PQ_query_param_auto_from_type (coin_pub),
4444 : GNUNET_PQ_query_param_end
4445 : };
4446 1 : struct LookupDepositsByCnCContext ldcc = {
4447 : .cb = cb,
4448 : .cb_cls = cb_cls,
4449 : .pg = pg
4450 : };
4451 : enum GNUNET_DB_QueryStatus qs;
4452 :
4453 1 : check_connection (pg);
4454 1 : qs = GNUNET_PQ_eval_prepared_multi_select (
4455 : pg->conn,
4456 : "lookup_deposits_by_contract_and_coin",
4457 : params,
4458 : &lookup_deposits_by_contract_and_coin_cb,
4459 : &ldcc);
4460 1 : if (0 >= qs)
4461 0 : return qs;
4462 1 : return ldcc.qs;
4463 : }
4464 :
4465 :
4466 : /**
4467 : * Lookup transfer status.
4468 : *
4469 : * @param cls closure
4470 : * @param instance_id at which instance should we resolve the transfer
4471 : * @param exchange_url the exchange that made the transfer
4472 : * @param wtid wire transfer subject
4473 : * @param[out] total_amount amount that was debited from our
4474 : * aggregate balance at the exchange (in total, sum of
4475 : * the wire transfer amount and the @a wire_fee)
4476 : * @param[out] wire_fee the wire fee the exchange charged (only set if @a have_exchange_sig is true)
4477 : * @param[out] exchange_amount the amount the exchange claims was transferred (only set if @a have_exchange_sig is true)
4478 : * @param[out] execution_time when the transfer was executed by the exchange (only set if @a have_exchange_sig is true)
4479 : * @param[out] have_exchange_sig do we have a response from the exchange about this transfer
4480 : * @param[out] verified did we confirm the transfer was OK
4481 : * @return transaction status
4482 : */
4483 : static enum GNUNET_DB_QueryStatus
4484 2 : postgres_lookup_transfer (
4485 : void *cls,
4486 : const char *instance_id,
4487 : const char *exchange_url,
4488 : const struct TALER_WireTransferIdentifierRawP *wtid,
4489 : struct TALER_Amount *total_amount,
4490 : struct TALER_Amount *wire_fee,
4491 : struct TALER_Amount *exchange_amount,
4492 : struct GNUNET_TIME_Timestamp *execution_time,
4493 : bool *have_exchange_sig,
4494 : bool *verified)
4495 : {
4496 2 : struct PostgresClosure *pg = cls;
4497 2 : struct GNUNET_PQ_QueryParam params[] = {
4498 2 : GNUNET_PQ_query_param_string (exchange_url),
4499 2 : GNUNET_PQ_query_param_auto_from_type (wtid),
4500 2 : GNUNET_PQ_query_param_string (instance_id),
4501 : GNUNET_PQ_query_param_end
4502 : };
4503 : uint8_t verified8;
4504 : /** Amount we got actually credited, _excludes_ the wire fee */
4505 : bool no_sig;
4506 : struct TALER_Amount credit_amount;
4507 2 : struct GNUNET_PQ_ResultSpec rs[] = {
4508 2 : TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
4509 : &credit_amount),
4510 2 : GNUNET_PQ_result_spec_allow_null (
4511 2 : TALER_PQ_RESULT_SPEC_AMOUNT ("wire_fee",
4512 : wire_fee),
4513 : &no_sig),
4514 2 : GNUNET_PQ_result_spec_allow_null (
4515 2 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_amount",
4516 : exchange_amount),
4517 : NULL),
4518 2 : GNUNET_PQ_result_spec_allow_null (
4519 : GNUNET_PQ_result_spec_timestamp ("execution_time",
4520 : execution_time),
4521 : NULL),
4522 2 : GNUNET_PQ_result_spec_auto_from_type ("verified",
4523 : &verified8),
4524 : GNUNET_PQ_result_spec_end
4525 : };
4526 : enum GNUNET_DB_QueryStatus qs;
4527 :
4528 2 : check_connection (pg);
4529 2 : *execution_time = GNUNET_TIME_UNIT_ZERO_TS;
4530 2 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
4531 : "lookup_transfer",
4532 : params,
4533 : rs);
4534 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
4535 : "Lookup transfer returned %d\n",
4536 : qs);
4537 2 : if (qs > 0)
4538 : {
4539 2 : *have_exchange_sig = ! no_sig;
4540 2 : *verified = (0 != verified8);
4541 4 : if ( (! no_sig) &&
4542 : (0 >
4543 2 : TALER_amount_add (total_amount,
4544 : &credit_amount,
4545 : wire_fee)) )
4546 : {
4547 0 : GNUNET_break (0);
4548 0 : return GNUNET_DB_STATUS_HARD_ERROR;
4549 : }
4550 : }
4551 : else
4552 : {
4553 0 : *verified = false;
4554 0 : *have_exchange_sig = false;
4555 : }
4556 2 : return qs;
4557 : }
4558 :
4559 :
4560 : /**
4561 : * Set transfer status to verified.
4562 : *
4563 : * @param cls closure
4564 : * @param exchange_url the exchange that made the transfer
4565 : * @param wtid wire transfer subject
4566 : * @return transaction status
4567 : */
4568 : static enum GNUNET_DB_QueryStatus
4569 1 : postgres_set_transfer_status_to_verified (
4570 : void *cls,
4571 : const char *exchange_url,
4572 : const struct TALER_WireTransferIdentifierRawP *wtid)
4573 : {
4574 1 : struct PostgresClosure *pg = cls;
4575 1 : struct GNUNET_PQ_QueryParam params[] = {
4576 1 : GNUNET_PQ_query_param_auto_from_type (wtid),
4577 1 : GNUNET_PQ_query_param_string (exchange_url),
4578 : GNUNET_PQ_query_param_end
4579 : };
4580 :
4581 1 : check_connection (pg);
4582 1 : return GNUNET_PQ_eval_prepared_non_select (
4583 : pg->conn,
4584 : "set_transfer_status_to_verified",
4585 : params);
4586 : }
4587 :
4588 :
4589 : /**
4590 : * Closure for #lookup_transfer_summary_cb().
4591 : */
4592 : struct LookupTransferSummaryContext
4593 : {
4594 : /**
4595 : * Function to call for each order that was aggregated.
4596 : */
4597 : TALER_MERCHANTDB_TransferSummaryCallback cb;
4598 :
4599 : /**
4600 : * Closure for @e cb.
4601 : */
4602 : void *cb_cls;
4603 :
4604 : /**
4605 : * Plugin context.
4606 : */
4607 : struct PostgresClosure *pg;
4608 :
4609 : /**
4610 : * Transaction result.
4611 : */
4612 : enum GNUNET_DB_QueryStatus qs;
4613 : };
4614 :
4615 :
4616 : /**
4617 : * Function to be called with the results of a SELECT statement
4618 : * that has returned @a num_results results.
4619 : *
4620 : * @param cls of type `struct LookupTransferSummaryContext *`
4621 : * @param result the postgres result
4622 : * @param num_results the number of results in @a result
4623 : */
4624 : static void
4625 1 : lookup_transfer_summary_cb (void *cls,
4626 : PGresult *result,
4627 : unsigned int num_results)
4628 : {
4629 1 : struct LookupTransferSummaryContext *ltdc = cls;
4630 1 : struct PostgresClosure *pg = ltdc->pg;
4631 :
4632 2 : for (unsigned int i = 0; i<num_results; i++)
4633 : {
4634 : char *order_id;
4635 : struct TALER_Amount deposit_value;
4636 : struct TALER_Amount deposit_fee;
4637 1 : struct GNUNET_PQ_ResultSpec rs[] = {
4638 1 : GNUNET_PQ_result_spec_string ("order_id",
4639 : &order_id),
4640 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
4641 : &deposit_value),
4642 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
4643 : &deposit_fee),
4644 : GNUNET_PQ_result_spec_end
4645 : };
4646 :
4647 1 : if (GNUNET_OK !=
4648 1 : GNUNET_PQ_extract_result (result,
4649 : rs,
4650 : i))
4651 : {
4652 0 : GNUNET_break (0);
4653 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4654 0 : return;
4655 : }
4656 1 : ltdc->cb (ltdc->cb_cls,
4657 : order_id,
4658 : &deposit_value,
4659 : &deposit_fee);
4660 1 : GNUNET_PQ_cleanup_result (rs);
4661 : }
4662 1 : ltdc->qs = num_results;
4663 : }
4664 :
4665 :
4666 : /**
4667 : * Lookup transfer summary.
4668 : *
4669 : * @param cls closure
4670 : * @param exchange_url the exchange that made the transfer
4671 : * @param wtid wire transfer subject
4672 : * @param cb function to call with detailed transfer data
4673 : * @param cb_cls closure for @a cb
4674 : * @return transaction status
4675 : */
4676 : static enum GNUNET_DB_QueryStatus
4677 1 : postgres_lookup_transfer_summary (
4678 : void *cls,
4679 : const char *exchange_url,
4680 : const struct TALER_WireTransferIdentifierRawP *wtid,
4681 : TALER_MERCHANTDB_TransferSummaryCallback cb,
4682 : void *cb_cls)
4683 : {
4684 1 : struct PostgresClosure *pg = cls;
4685 1 : struct GNUNET_PQ_QueryParam params[] = {
4686 1 : GNUNET_PQ_query_param_string (exchange_url),
4687 1 : GNUNET_PQ_query_param_auto_from_type (wtid),
4688 : GNUNET_PQ_query_param_end
4689 : };
4690 1 : struct LookupTransferSummaryContext ltdc = {
4691 : .cb = cb,
4692 : .cb_cls = cb_cls,
4693 : .pg = pg
4694 : };
4695 : enum GNUNET_DB_QueryStatus qs;
4696 :
4697 1 : check_connection (pg);
4698 1 : qs = GNUNET_PQ_eval_prepared_multi_select (
4699 : pg->conn,
4700 : "lookup_transfer_summary",
4701 : params,
4702 : &lookup_transfer_summary_cb,
4703 : <dc);
4704 1 : if (0 >= qs)
4705 0 : return qs;
4706 1 : return ltdc.qs;
4707 : }
4708 :
4709 :
4710 : /**
4711 : * Closure for #lookup_transfer_details_cb().
4712 : */
4713 : struct LookupTransferDetailsContext
4714 : {
4715 : /**
4716 : * Function to call for each order that was aggregated.
4717 : */
4718 : TALER_MERCHANTDB_TransferDetailsCallback cb;
4719 :
4720 : /**
4721 : * Closure for @e cb.
4722 : */
4723 : void *cb_cls;
4724 :
4725 : /**
4726 : * Plugin context.
4727 : */
4728 : struct PostgresClosure *pg;
4729 :
4730 : /**
4731 : * Transaction result.
4732 : */
4733 : enum GNUNET_DB_QueryStatus qs;
4734 : };
4735 :
4736 :
4737 : /**
4738 : * Function to be called with the results of a SELECT statement
4739 : * that has returned @a num_results results.
4740 : *
4741 : * @param cls of type `struct LookupTransferDetailsContext *`
4742 : * @param result the postgres result
4743 : * @param num_results the number of results in @a result
4744 : */
4745 : static void
4746 1 : lookup_transfer_details_cb (void *cls,
4747 : PGresult *result,
4748 : unsigned int num_results)
4749 : {
4750 1 : struct LookupTransferDetailsContext *ltdc = cls;
4751 1 : struct PostgresClosure *pg = ltdc->pg;
4752 :
4753 2 : for (unsigned int i = 0; i<num_results; i++)
4754 : {
4755 : uint64_t current_offset;
4756 : struct TALER_TrackTransferDetails ttd;
4757 1 : struct GNUNET_PQ_ResultSpec rs[] = {
4758 1 : GNUNET_PQ_result_spec_uint64 ("offset_in_exchange_list",
4759 : ¤t_offset),
4760 1 : GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
4761 : &ttd.h_contract_terms),
4762 1 : GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
4763 : &ttd.coin_pub),
4764 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_value",
4765 : &ttd.coin_value),
4766 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_deposit_fee",
4767 : &ttd.coin_fee),
4768 : GNUNET_PQ_result_spec_end
4769 : };
4770 :
4771 1 : if (GNUNET_OK !=
4772 1 : GNUNET_PQ_extract_result (result,
4773 : rs,
4774 : i))
4775 : {
4776 0 : GNUNET_break (0);
4777 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4778 0 : return;
4779 : }
4780 1 : ltdc->cb (ltdc->cb_cls,
4781 : (unsigned int) current_offset,
4782 : &ttd);
4783 1 : GNUNET_PQ_cleanup_result (rs);
4784 : }
4785 1 : ltdc->qs = num_results;
4786 : }
4787 :
4788 :
4789 : /**
4790 : * Lookup transfer details.
4791 : *
4792 : * @param cls closure
4793 : * @param exchange_url the exchange that made the transfer
4794 : * @param wtid wire transfer subject
4795 : * @param cb function to call with detailed transfer data
4796 : * @param cb_cls closure for @a cb
4797 : * @return transaction status
4798 : */
4799 : static enum GNUNET_DB_QueryStatus
4800 1 : postgres_lookup_transfer_details (
4801 : void *cls,
4802 : const char *exchange_url,
4803 : const struct TALER_WireTransferIdentifierRawP *wtid,
4804 : TALER_MERCHANTDB_TransferDetailsCallback cb,
4805 : void *cb_cls)
4806 : {
4807 1 : struct PostgresClosure *pg = cls;
4808 1 : struct GNUNET_PQ_QueryParam params[] = {
4809 1 : GNUNET_PQ_query_param_string (exchange_url),
4810 1 : GNUNET_PQ_query_param_auto_from_type (wtid),
4811 : GNUNET_PQ_query_param_end
4812 : };
4813 1 : struct LookupTransferDetailsContext ltdc = {
4814 : .cb = cb,
4815 : .cb_cls = cb_cls,
4816 : .pg = pg
4817 : };
4818 : enum GNUNET_DB_QueryStatus qs;
4819 :
4820 1 : check_connection (pg);
4821 1 : qs = GNUNET_PQ_eval_prepared_multi_select (
4822 : pg->conn,
4823 : "lookup_transfer_details",
4824 : params,
4825 : &lookup_transfer_details_cb,
4826 : <dc);
4827 1 : if (0 >= qs)
4828 0 : return qs;
4829 1 : return ltdc.qs;
4830 : }
4831 :
4832 :
4833 : /**
4834 : * Closure for #lookup_transfers_cb().
4835 : */
4836 : struct LookupTransfersContext
4837 : {
4838 : /**
4839 : * Function to call on results.
4840 : */
4841 : TALER_MERCHANTDB_TransferCallback cb;
4842 :
4843 : /**
4844 : * Closure for @e cb.
4845 : */
4846 : void *cb_cls;
4847 :
4848 : /**
4849 : * Postgres context.
4850 : */
4851 : struct PostgresClosure *pg;
4852 :
4853 : /**
4854 : * Transaction status (set).
4855 : */
4856 : enum GNUNET_DB_QueryStatus qs;
4857 :
4858 : /**
4859 : * Filter to apply by verification status.
4860 : */
4861 : enum TALER_EXCHANGE_YesNoAll verified;
4862 : };
4863 :
4864 :
4865 : /**
4866 : * Function to be called with the results of a SELECT statement
4867 : * that has returned @a num_results results.
4868 : *
4869 : * @param cls of type `struct LookupTransfersContext *`
4870 : * @param result the postgres result
4871 : * @param num_results the number of results in @a result
4872 : */
4873 : static void
4874 1 : lookup_transfers_cb (void *cls,
4875 : PGresult *result,
4876 : unsigned int num_results)
4877 : {
4878 1 : struct LookupTransfersContext *ltc = cls;
4879 1 : struct PostgresClosure *pg = ltc->pg;
4880 :
4881 2 : for (unsigned int i = 0; i<num_results; i++)
4882 : {
4883 : struct TALER_Amount credit_amount;
4884 : struct TALER_WireTransferIdentifierRawP wtid;
4885 : char *payto_uri;
4886 : char *exchange_url;
4887 : uint64_t transfer_serial_id;
4888 : struct GNUNET_TIME_Timestamp execution_time;
4889 : enum TALER_EXCHANGE_YesNoAll verified;
4890 : uint8_t verified8;
4891 : uint8_t confirmed8;
4892 1 : struct GNUNET_PQ_ResultSpec rs[] = {
4893 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("credit_amount",
4894 : &credit_amount),
4895 1 : GNUNET_PQ_result_spec_auto_from_type ("wtid",
4896 : &wtid),
4897 1 : GNUNET_PQ_result_spec_string ("payto_uri",
4898 : &payto_uri),
4899 1 : GNUNET_PQ_result_spec_string ("exchange_url",
4900 : &exchange_url),
4901 1 : GNUNET_PQ_result_spec_uint64 ("credit_serial",
4902 : &transfer_serial_id),
4903 1 : GNUNET_PQ_result_spec_timestamp ("execution_time",
4904 : &execution_time),
4905 1 : GNUNET_PQ_result_spec_auto_from_type ("verified",
4906 : &verified8),
4907 1 : GNUNET_PQ_result_spec_auto_from_type ("confirmed",
4908 : &confirmed8),
4909 : GNUNET_PQ_result_spec_end
4910 : };
4911 :
4912 1 : if (GNUNET_OK !=
4913 1 : GNUNET_PQ_extract_result (result,
4914 : rs,
4915 : i))
4916 : {
4917 0 : GNUNET_break (0);
4918 0 : ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
4919 0 : return;
4920 : }
4921 1 : if (0 == verified8)
4922 0 : verified = TALER_EXCHANGE_YNA_NO;
4923 : else
4924 1 : verified = TALER_EXCHANGE_YNA_YES;
4925 1 : if ( (ltc->verified == TALER_EXCHANGE_YNA_ALL) ||
4926 0 : (ltc->verified == verified) )
4927 : {
4928 1 : ltc->cb (ltc->cb_cls,
4929 : &credit_amount,
4930 : &wtid,
4931 : payto_uri,
4932 : exchange_url,
4933 : transfer_serial_id,
4934 : execution_time,
4935 : TALER_EXCHANGE_YNA_YES == verified,
4936 : 0 != confirmed8);
4937 : }
4938 1 : GNUNET_PQ_cleanup_result (rs);
4939 : }
4940 1 : ltc->qs = num_results;
4941 : }
4942 :
4943 :
4944 : /**
4945 : * Lookup transfers. Note that filtering by @a verified status is done
4946 : * outside of SQL, as we already have 8 prepared statements and adding
4947 : * a filter on verified would further double the number of statements for
4948 : * a likely rather ineffective filter. So we apply that filter in
4949 : * #lookup_transfers_cb().
4950 : *
4951 : * @param cls closure
4952 : * @param instance_id instance to lookup payments for
4953 : * @param payto_uri account that we are interested in transfers to
4954 : * @param before timestamp for the earliest transfer we care about
4955 : * @param after timestamp for the last transfer we care about
4956 : * @param limit number of entries to return, negative for descending in execution time,
4957 : * positive for ascending in execution time
4958 : * @param offset transfer_serial number of the transfer we want to offset from
4959 : * @param verified filter transfers by verification status
4960 : * @param cb function to call with detailed transfer data
4961 : * @param cb_cls closure for @a cb
4962 : * @return transaction status
4963 : */
4964 : static enum GNUNET_DB_QueryStatus
4965 1 : postgres_lookup_transfers (void *cls,
4966 : const char *instance_id,
4967 : const char *payto_uri,
4968 : struct GNUNET_TIME_Timestamp before,
4969 : struct GNUNET_TIME_Timestamp after,
4970 : int64_t limit,
4971 : uint64_t offset,
4972 : enum TALER_EXCHANGE_YesNoAll verified,
4973 : TALER_MERCHANTDB_TransferCallback cb,
4974 : void *cb_cls)
4975 : {
4976 1 : struct PostgresClosure *pg = cls;
4977 1 : uint64_t plimit = (uint64_t) ((limit < 0) ? -limit : limit);
4978 1 : struct LookupTransfersContext ltc = {
4979 : .cb = cb,
4980 : .cb_cls = cb_cls,
4981 : .pg = pg,
4982 : .verified = verified
4983 : };
4984 : enum GNUNET_DB_QueryStatus qs;
4985 : bool by_time;
4986 :
4987 2 : by_time = ( (! GNUNET_TIME_absolute_is_never (before.abs_time)) ||
4988 1 : (! GNUNET_TIME_absolute_is_zero (after.abs_time)) );
4989 1 : check_connection (pg);
4990 1 : if (by_time)
4991 : {
4992 0 : if (NULL != payto_uri)
4993 : {
4994 0 : struct GNUNET_PQ_QueryParam params[] = {
4995 0 : GNUNET_PQ_query_param_string (instance_id),
4996 0 : GNUNET_PQ_query_param_timestamp (&before),
4997 0 : GNUNET_PQ_query_param_timestamp (&after),
4998 0 : GNUNET_PQ_query_param_uint64 (&offset),
4999 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5000 0 : GNUNET_PQ_query_param_string (payto_uri),
5001 : GNUNET_PQ_query_param_end
5002 : };
5003 :
5004 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5005 : pg->conn,
5006 : (limit > 0)
5007 : ? "lookup_transfers_time_payto_asc"
5008 : : "lookup_transfers_time_payto_desc",
5009 : params,
5010 : &lookup_transfers_cb,
5011 : <c);
5012 : }
5013 : else
5014 : {
5015 0 : struct GNUNET_PQ_QueryParam params[] = {
5016 0 : GNUNET_PQ_query_param_string (instance_id),
5017 0 : GNUNET_PQ_query_param_timestamp (&before),
5018 0 : GNUNET_PQ_query_param_timestamp (&after),
5019 0 : GNUNET_PQ_query_param_uint64 (&offset),
5020 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5021 : GNUNET_PQ_query_param_end
5022 : };
5023 :
5024 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5025 : pg->conn,
5026 : (limit > 0)
5027 : ? "lookup_transfers_time_asc"
5028 : : "lookup_transfers_time_desc",
5029 : params,
5030 : &lookup_transfers_cb,
5031 : <c);
5032 : }
5033 : }
5034 : else
5035 : {
5036 1 : if (NULL != payto_uri)
5037 : {
5038 1 : struct GNUNET_PQ_QueryParam params[] = {
5039 1 : GNUNET_PQ_query_param_string (instance_id),
5040 1 : GNUNET_PQ_query_param_uint64 (&offset),
5041 1 : GNUNET_PQ_query_param_uint64 (&plimit),
5042 1 : GNUNET_PQ_query_param_string (payto_uri),
5043 : GNUNET_PQ_query_param_end
5044 : };
5045 :
5046 1 : qs = GNUNET_PQ_eval_prepared_multi_select (
5047 : pg->conn,
5048 : (limit > 0)
5049 : ? "lookup_transfers_payto_asc"
5050 : : "lookup_transfers_payto_desc",
5051 : params,
5052 : &lookup_transfers_cb,
5053 : <c);
5054 : }
5055 : else
5056 : {
5057 0 : struct GNUNET_PQ_QueryParam params[] = {
5058 0 : GNUNET_PQ_query_param_string (instance_id),
5059 0 : GNUNET_PQ_query_param_uint64 (&offset),
5060 0 : GNUNET_PQ_query_param_uint64 (&plimit),
5061 : GNUNET_PQ_query_param_end
5062 : };
5063 :
5064 0 : qs = GNUNET_PQ_eval_prepared_multi_select (
5065 : pg->conn,
5066 : (limit > 0)
5067 : ? "lookup_transfers_asc"
5068 : : "lookup_transfers_desc",
5069 : params,
5070 : &lookup_transfers_cb,
5071 : <c);
5072 : }
5073 : }
5074 1 : if (0 >= qs)
5075 0 : return qs;
5076 1 : return ltc.qs;
5077 : }
5078 :
5079 :
5080 : /**
5081 : * Store information about wire fees charged by an exchange,
5082 : * including signature (so we have proof).
5083 : *
5084 : * @param cls closure
5085 : * @param master_pub public key of the exchange
5086 : * @param h_wire_method hash of wire method
5087 : * @param fees the fee charged
5088 : * @param start_date start of fee being used
5089 : * @param end_date end of fee being used
5090 : * @param master_sig signature of exchange over fee structure
5091 : * @return transaction status code
5092 : */
5093 : static enum GNUNET_DB_QueryStatus
5094 3 : postgres_store_wire_fee_by_exchange (
5095 : void *cls,
5096 : const struct TALER_MasterPublicKeyP *master_pub,
5097 : const struct GNUNET_HashCode *h_wire_method,
5098 : const struct TALER_WireFeeSet *fees,
5099 : struct GNUNET_TIME_Timestamp start_date,
5100 : struct GNUNET_TIME_Timestamp end_date,
5101 : const struct TALER_MasterSignatureP *master_sig)
5102 : {
5103 3 : struct PostgresClosure *pg = cls;
5104 3 : struct GNUNET_PQ_QueryParam params[] = {
5105 3 : GNUNET_PQ_query_param_auto_from_type (master_pub),
5106 3 : GNUNET_PQ_query_param_auto_from_type (h_wire_method),
5107 3 : TALER_PQ_query_param_amount (&fees->wire),
5108 3 : TALER_PQ_query_param_amount (&fees->closing),
5109 3 : TALER_PQ_query_param_amount (&fees->wad),
5110 3 : GNUNET_PQ_query_param_timestamp (&start_date),
5111 3 : GNUNET_PQ_query_param_timestamp (&end_date),
5112 3 : GNUNET_PQ_query_param_auto_from_type (master_sig),
5113 : GNUNET_PQ_query_param_end
5114 : };
5115 :
5116 : /* no preflight check here, run in its own transaction by the caller */
5117 3 : check_connection (pg);
5118 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
5119 : "Storing wire fee for %s starting at %s of %s\n",
5120 : TALER_B2S (master_pub),
5121 : GNUNET_TIME_timestamp2s (start_date),
5122 : TALER_amount2s (&fees->wire));
5123 3 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5124 : "insert_wire_fee",
5125 : params);
5126 : }
5127 :
5128 :
5129 : /**
5130 : * Add @a credit to a reserve to be used for tipping. Note that
5131 : * this function does not actually perform any wire transfers to
5132 : * credit the reserve, it merely tells the merchant backend that
5133 : * a reserve now exists. This has to happen before tips can be
5134 : * authorized.
5135 : *
5136 : * @param cls closure, typically a connection to the db
5137 : * @param instance_id which instance is the reserve tied to
5138 : * @param reserve_priv which reserve is topped up or created
5139 : * @param reserve_pub which reserve is topped up or created
5140 : * @param exchange_url what URL is the exchange reachable at where the reserve is located
5141 : * @param payto_uri URI to use to fund the reserve
5142 : * @param initial_balance how much money will be added to the reserve
5143 : * @param expiration when does the reserve expire?
5144 : * @return transaction status, usually
5145 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5146 : */
5147 : static enum TALER_ErrorCode
5148 2 : postgres_insert_reserve (void *cls,
5149 : const char *instance_id,
5150 : const struct TALER_ReservePrivateKeyP *reserve_priv,
5151 : const struct TALER_ReservePublicKeyP *reserve_pub,
5152 : const char *exchange_url,
5153 : const char *payto_uri,
5154 : const struct TALER_Amount *initial_balance,
5155 : struct GNUNET_TIME_Timestamp expiration)
5156 : {
5157 2 : struct PostgresClosure *pg = cls;
5158 : unsigned int retries;
5159 : enum GNUNET_DB_QueryStatus qs;
5160 :
5161 2 : retries = 0;
5162 2 : check_connection (pg);
5163 2 : RETRY:
5164 2 : if (MAX_RETRIES < ++retries)
5165 0 : return TALER_EC_GENERIC_DB_SOFT_FAILURE;
5166 2 : if (GNUNET_OK !=
5167 2 : postgres_start (pg,
5168 : "insert reserve"))
5169 : {
5170 0 : GNUNET_break (0);
5171 0 : return TALER_EC_GENERIC_DB_START_FAILED;
5172 : }
5173 :
5174 : /* Setup reserve */
5175 : {
5176 : struct GNUNET_TIME_Timestamp now;
5177 2 : struct GNUNET_PQ_QueryParam params[] = {
5178 2 : GNUNET_PQ_query_param_string (instance_id),
5179 2 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5180 2 : GNUNET_PQ_query_param_timestamp (&now),
5181 2 : GNUNET_PQ_query_param_timestamp (&expiration),
5182 2 : TALER_PQ_query_param_amount (initial_balance),
5183 : GNUNET_PQ_query_param_end
5184 : };
5185 :
5186 2 : now = GNUNET_TIME_timestamp_get ();
5187 2 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
5188 : "insert_reserve",
5189 : params);
5190 2 : if (0 > qs)
5191 : {
5192 0 : postgres_rollback (pg);
5193 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5194 0 : goto RETRY;
5195 0 : return qs;
5196 : }
5197 : }
5198 : /* Store private key */
5199 : {
5200 2 : struct GNUNET_PQ_QueryParam params[] = {
5201 2 : GNUNET_PQ_query_param_string (instance_id),
5202 2 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5203 2 : GNUNET_PQ_query_param_auto_from_type (reserve_priv),
5204 2 : GNUNET_PQ_query_param_string (exchange_url),
5205 2 : GNUNET_PQ_query_param_string (payto_uri),
5206 : GNUNET_PQ_query_param_end
5207 : };
5208 :
5209 2 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
5210 : "insert_reserve_key",
5211 : params);
5212 2 : if (0 > qs)
5213 : {
5214 0 : postgres_rollback (pg);
5215 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5216 0 : goto RETRY;
5217 0 : return qs;
5218 : }
5219 : }
5220 2 : qs = postgres_commit (pg);
5221 2 : if (0 <= qs)
5222 2 : return TALER_EC_NONE; /* success */
5223 0 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
5224 0 : goto RETRY;
5225 0 : return qs;
5226 : }
5227 :
5228 :
5229 : /**
5230 : * Confirms @a credit as the amount the exchange claims to have received and
5231 : * thus really 'activates' the reserve. This has to happen before tips can
5232 : * be authorized.
5233 : *
5234 : * @param cls closure, typically a connection to the db
5235 : * @param instance_id which instance is the reserve tied to
5236 : * @param reserve_pub which reserve is topped up or created
5237 : * @param initial_exchange_balance how much money was be added to the reserve
5238 : * according to the exchange
5239 : * @return transaction status, usually
5240 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5241 : */
5242 : static enum GNUNET_DB_QueryStatus
5243 2 : postgres_activate_reserve (void *cls,
5244 : const char *instance_id,
5245 : const struct TALER_ReservePublicKeyP *reserve_pub,
5246 : const struct TALER_Amount *initial_exchange_balance)
5247 : {
5248 2 : struct PostgresClosure *pg = cls;
5249 2 : struct GNUNET_PQ_QueryParam params[] = {
5250 2 : GNUNET_PQ_query_param_string (instance_id),
5251 2 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5252 2 : TALER_PQ_query_param_amount (initial_exchange_balance),
5253 : GNUNET_PQ_query_param_end
5254 : };
5255 :
5256 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5257 : "activate_reserve",
5258 : params);
5259 : }
5260 :
5261 :
5262 : /**
5263 : * Closure for #lookup_reserves_cb.
5264 : */
5265 : struct LookupReservesContext
5266 : {
5267 : /**
5268 : * Postgres context.
5269 : */
5270 : struct PostgresClosure *pg;
5271 :
5272 : /**
5273 : * Function to call with the results
5274 : */
5275 : TALER_MERCHANTDB_ReservesCallback cb;
5276 :
5277 : /**
5278 : * Closure for @e cb
5279 : */
5280 : void *cb_cls;
5281 :
5282 : /**
5283 : * Filter by active reserves.
5284 : */
5285 : enum TALER_EXCHANGE_YesNoAll active;
5286 :
5287 : /**
5288 : * Filter by failures (mismatch in exchange claimed and
5289 : * merchant claimed initial amounts).
5290 : */
5291 : enum TALER_EXCHANGE_YesNoAll failures;
5292 :
5293 : /**
5294 : * Set in case of errors.
5295 : */
5296 : enum GNUNET_DB_QueryStatus qs;
5297 :
5298 : };
5299 :
5300 :
5301 : /**
5302 : * Function to be called with the results of a SELECT statement
5303 : * that has returned @a num_results results about accounts.
5304 : *
5305 : * @param[in,out] cls of type `struct LookupReservesContext *`
5306 : * @param result the postgres result
5307 : * @param num_results the number of results in @a result
5308 : */
5309 : static void
5310 1 : lookup_reserves_cb (void *cls,
5311 : PGresult *result,
5312 : unsigned int num_results)
5313 : {
5314 1 : struct LookupReservesContext *lrc = cls;
5315 1 : struct PostgresClosure *pg = lrc->pg;
5316 :
5317 2 : for (unsigned int i = 0; i < num_results; i++)
5318 : {
5319 : struct TALER_ReservePublicKeyP reserve_pub;
5320 : struct GNUNET_TIME_Timestamp creation_time;
5321 : struct GNUNET_TIME_Timestamp expiration_time;
5322 : struct TALER_Amount merchant_initial_balance;
5323 : struct TALER_Amount exchange_initial_balance;
5324 : struct TALER_Amount pickup_amount;
5325 : struct TALER_Amount committed_amount;
5326 : uint8_t active;
5327 1 : struct GNUNET_PQ_ResultSpec rs[] = {
5328 1 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5329 : &reserve_pub),
5330 1 : GNUNET_PQ_result_spec_timestamp ("creation_time",
5331 : &creation_time),
5332 1 : GNUNET_PQ_result_spec_timestamp ("expiration",
5333 : &expiration_time),
5334 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5335 : &merchant_initial_balance),
5336 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5337 : &exchange_initial_balance),
5338 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5339 : &committed_amount),
5340 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
5341 : &pickup_amount),
5342 1 : GNUNET_PQ_result_spec_auto_from_type ("active",
5343 : &active),
5344 : GNUNET_PQ_result_spec_end
5345 : };
5346 :
5347 1 : if (GNUNET_OK !=
5348 1 : GNUNET_PQ_extract_result (result,
5349 : rs,
5350 : i))
5351 : {
5352 0 : GNUNET_break (0);
5353 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5354 0 : return;
5355 : }
5356 1 : switch (lrc->active)
5357 : {
5358 0 : case TALER_EXCHANGE_YNA_YES:
5359 0 : if (0 == active)
5360 0 : continue;
5361 0 : break;
5362 0 : case TALER_EXCHANGE_YNA_NO:
5363 0 : if (0 != active)
5364 0 : continue;
5365 0 : break;
5366 1 : case TALER_EXCHANGE_YNA_ALL:
5367 1 : break;
5368 : }
5369 1 : switch (lrc->failures)
5370 : {
5371 0 : case TALER_EXCHANGE_YNA_YES:
5372 0 : if (0 ==
5373 0 : TALER_amount_cmp (&merchant_initial_balance,
5374 : &exchange_initial_balance))
5375 0 : continue;
5376 0 : break;
5377 0 : case TALER_EXCHANGE_YNA_NO:
5378 0 : if (0 !=
5379 0 : TALER_amount_cmp (&merchant_initial_balance,
5380 : &exchange_initial_balance))
5381 0 : continue;
5382 0 : break;
5383 1 : case TALER_EXCHANGE_YNA_ALL:
5384 1 : break;
5385 : }
5386 1 : lrc->cb (lrc->cb_cls,
5387 : &reserve_pub,
5388 : creation_time,
5389 : expiration_time,
5390 : &merchant_initial_balance,
5391 : &exchange_initial_balance,
5392 : &pickup_amount,
5393 : &committed_amount,
5394 : (0 != active));
5395 : }
5396 : }
5397 :
5398 :
5399 : /**
5400 : * Lookup reserves.
5401 : *
5402 : * @param cls closure
5403 : * @param instance_id instance to lookup payments for
5404 : * @param created_after filter by reserves created after this date
5405 : * @param active filter by active reserves
5406 : * @param failures filter by reserves with a disagreement on the initial balance
5407 : * @param cb function to call with reserve summary data
5408 : * @param cb_cls closure for @a cb
5409 : * @return transaction status
5410 : */
5411 : static enum GNUNET_DB_QueryStatus
5412 1 : postgres_lookup_reserves (void *cls,
5413 : const char *instance_id,
5414 : struct GNUNET_TIME_Timestamp created_after,
5415 : enum TALER_EXCHANGE_YesNoAll active,
5416 : enum TALER_EXCHANGE_YesNoAll failures,
5417 : TALER_MERCHANTDB_ReservesCallback cb,
5418 : void *cb_cls)
5419 : {
5420 1 : struct PostgresClosure *pg = cls;
5421 1 : struct LookupReservesContext lrc = {
5422 : .pg = pg,
5423 : .active = active,
5424 : .failures = failures,
5425 : .cb = cb,
5426 : .cb_cls = cb_cls
5427 : };
5428 1 : struct GNUNET_PQ_QueryParam params[] = {
5429 1 : GNUNET_PQ_query_param_string (instance_id),
5430 1 : GNUNET_PQ_query_param_timestamp (&created_after),
5431 : GNUNET_PQ_query_param_end
5432 : };
5433 : enum GNUNET_DB_QueryStatus qs;
5434 :
5435 1 : check_connection (pg);
5436 1 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5437 : "lookup_reserves",
5438 : params,
5439 : &lookup_reserves_cb,
5440 : &lrc);
5441 1 : if (lrc.qs < 0)
5442 0 : return lrc.qs;
5443 1 : return qs;
5444 : }
5445 :
5446 :
5447 : /**
5448 : * Closure for #lookup_pending_reserves_cb.
5449 : */
5450 : struct LookupPendingReservesContext
5451 : {
5452 : /**
5453 : * Postgres context.
5454 : */
5455 : struct PostgresClosure *pg;
5456 :
5457 : /**
5458 : * Function to call with the results
5459 : */
5460 : TALER_MERCHANTDB_PendingReservesCallback cb;
5461 :
5462 : /**
5463 : * Closure for @e cb
5464 : */
5465 : void *cb_cls;
5466 :
5467 : /**
5468 : * Set in case of errors.
5469 : */
5470 : enum GNUNET_DB_QueryStatus qs;
5471 :
5472 : };
5473 :
5474 :
5475 : /**
5476 : * Function to be called with the results of a SELECT statement
5477 : * that has returned @a num_results results about accounts.
5478 : *
5479 : * @param[in,out] cls of type `struct LookupReservesContext *`
5480 : * @param result the postgres result
5481 : * @param num_results the number of results in @a result
5482 : */
5483 : static void
5484 5 : lookup_pending_reserves_cb (void *cls,
5485 : PGresult *result,
5486 : unsigned int num_results)
5487 : {
5488 5 : struct LookupPendingReservesContext *lrc = cls;
5489 5 : struct PostgresClosure *pg = lrc->pg;
5490 :
5491 6 : for (unsigned int i = 0; i < num_results; i++)
5492 : {
5493 : struct TALER_ReservePublicKeyP reserve_pub;
5494 : struct TALER_Amount merchant_initial_balance;
5495 : char *exchange_url;
5496 : char *instance_id;
5497 1 : struct GNUNET_PQ_ResultSpec rs[] = {
5498 1 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5499 : &reserve_pub),
5500 1 : GNUNET_PQ_result_spec_string ("merchant_id",
5501 : &instance_id),
5502 1 : GNUNET_PQ_result_spec_string ("exchange_url",
5503 : &exchange_url),
5504 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5505 : &merchant_initial_balance),
5506 : GNUNET_PQ_result_spec_end
5507 : };
5508 :
5509 1 : if (GNUNET_OK !=
5510 1 : GNUNET_PQ_extract_result (result,
5511 : rs,
5512 : i))
5513 : {
5514 0 : GNUNET_break (0);
5515 0 : lrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5516 0 : return;
5517 : }
5518 1 : lrc->cb (lrc->cb_cls,
5519 : instance_id,
5520 : exchange_url,
5521 : &reserve_pub,
5522 : &merchant_initial_balance);
5523 1 : GNUNET_PQ_cleanup_result (rs);
5524 : }
5525 : }
5526 :
5527 :
5528 : /**
5529 : * Lookup reserves pending activation across all instances.
5530 : *
5531 : * @param cls closure
5532 : * @param cb function to call with reserve summary data
5533 : * @param cb_cls closure for @a cb
5534 : * @return transaction status
5535 : */
5536 : static enum GNUNET_DB_QueryStatus
5537 5 : postgres_lookup_pending_reserves (void *cls,
5538 : TALER_MERCHANTDB_PendingReservesCallback cb,
5539 : void *cb_cls)
5540 : {
5541 5 : struct PostgresClosure *pg = cls;
5542 5 : struct LookupPendingReservesContext lrc = {
5543 : .pg = pg,
5544 : .cb = cb,
5545 : .cb_cls = cb_cls
5546 : };
5547 5 : struct GNUNET_PQ_QueryParam params[] = {
5548 : GNUNET_PQ_query_param_end
5549 : };
5550 : enum GNUNET_DB_QueryStatus qs;
5551 :
5552 5 : check_connection (pg);
5553 5 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5554 : "lookup_pending_reserves",
5555 : params,
5556 : &lookup_pending_reserves_cb,
5557 : &lrc);
5558 5 : if (lrc.qs < 0)
5559 0 : return lrc.qs;
5560 5 : return qs;
5561 : }
5562 :
5563 :
5564 : /**
5565 : * Closure for #lookup_reserve_tips_cb().
5566 : */
5567 : struct LookupTipsContext
5568 : {
5569 : /**
5570 : * Postgres context.
5571 : */
5572 : struct PostgresClosure *pg;
5573 :
5574 : /**
5575 : * Array with information about tips generated from this reserve.
5576 : */
5577 : struct TALER_MERCHANTDB_TipDetails *tips;
5578 :
5579 : /**
5580 : * Length of the @e tips array.
5581 : */
5582 : unsigned int tips_length;
5583 :
5584 : /**
5585 : * Set in case of errors.
5586 : */
5587 : enum GNUNET_DB_QueryStatus qs;
5588 : };
5589 :
5590 :
5591 : /**
5592 : * Function to be called with the results of a SELECT statement
5593 : * that has returned @a num_results results about accounts.
5594 : *
5595 : * @param[in,out] cls of type `struct LookupTipsContext *`
5596 : * @param result the postgres result
5597 : * @param num_results the number of results in @a result
5598 : */
5599 : static void
5600 0 : lookup_reserve_tips_cb (void *cls,
5601 : PGresult *result,
5602 : unsigned int num_results)
5603 : {
5604 0 : struct LookupTipsContext *ltc = cls;
5605 0 : struct PostgresClosure *pg = ltc->pg;
5606 :
5607 0 : GNUNET_array_grow (ltc->tips,
5608 : ltc->tips_length,
5609 : num_results);
5610 0 : for (unsigned int i = 0; i < num_results; i++)
5611 : {
5612 0 : struct TALER_MERCHANTDB_TipDetails *td = <c->tips[i];
5613 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5614 0 : GNUNET_PQ_result_spec_string ("justification",
5615 : &td->reason),
5616 0 : GNUNET_PQ_result_spec_auto_from_type ("tip_id",
5617 : &td->tip_id),
5618 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
5619 : &td->total_amount),
5620 : GNUNET_PQ_result_spec_end
5621 : };
5622 :
5623 0 : if (GNUNET_OK !=
5624 0 : GNUNET_PQ_extract_result (result,
5625 : rs,
5626 : i))
5627 : {
5628 0 : GNUNET_break (0);
5629 0 : ltc->qs = GNUNET_DB_STATUS_HARD_ERROR;
5630 0 : return;
5631 : }
5632 : }
5633 : }
5634 :
5635 :
5636 : /**
5637 : * Lookup reserve details.
5638 : *
5639 : * @param cls closure
5640 : * @param instance_id instance to lookup payments for
5641 : * @param reserve_pub public key of the reserve to inspect
5642 : * @param fetch_tips if true, also return information about tips
5643 : * @param cb function to call with reserve summary data
5644 : * @param cb_cls closure for @a cb
5645 : * @return transaction status
5646 : */
5647 : static enum GNUNET_DB_QueryStatus
5648 1 : postgres_lookup_reserve (void *cls,
5649 : const char *instance_id,
5650 : const struct TALER_ReservePublicKeyP *reserve_pub,
5651 : bool fetch_tips,
5652 : TALER_MERCHANTDB_ReserveDetailsCallback cb,
5653 : void *cb_cls)
5654 : {
5655 1 : struct PostgresClosure *pg = cls;
5656 1 : struct LookupTipsContext ltc = {
5657 : .pg = pg,
5658 : .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
5659 : };
5660 1 : struct GNUNET_PQ_QueryParam params[] = {
5661 1 : GNUNET_PQ_query_param_string (instance_id),
5662 1 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5663 : GNUNET_PQ_query_param_end
5664 : };
5665 : struct GNUNET_TIME_Timestamp creation_time;
5666 : struct GNUNET_TIME_Timestamp expiration_time;
5667 : struct TALER_Amount merchant_initial_balance;
5668 : struct TALER_Amount exchange_initial_balance;
5669 : struct TALER_Amount pickup_amount;
5670 : struct TALER_Amount committed_amount;
5671 : uint8_t active;
5672 1 : char *exchange_url = NULL;
5673 1 : char *payto_uri = NULL;
5674 1 : struct GNUNET_PQ_ResultSpec rs[] = {
5675 1 : GNUNET_PQ_result_spec_timestamp ("creation_time",
5676 : &creation_time),
5677 1 : GNUNET_PQ_result_spec_timestamp ("expiration",
5678 : &expiration_time),
5679 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("merchant_initial_balance",
5680 : &merchant_initial_balance),
5681 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5682 : &exchange_initial_balance),
5683 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
5684 : &pickup_amount),
5685 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5686 : &committed_amount),
5687 1 : GNUNET_PQ_result_spec_auto_from_type ("active",
5688 : &active),
5689 1 : GNUNET_PQ_result_spec_allow_null (
5690 : GNUNET_PQ_result_spec_string ("exchange_url",
5691 : &exchange_url),
5692 : NULL),
5693 1 : GNUNET_PQ_result_spec_allow_null (
5694 : GNUNET_PQ_result_spec_string ("payto_uri",
5695 : &payto_uri),
5696 : NULL),
5697 : GNUNET_PQ_result_spec_end
5698 : };
5699 : enum GNUNET_DB_QueryStatus qs;
5700 :
5701 1 : check_connection (pg);
5702 1 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
5703 : "lookup_reserve",
5704 : params,
5705 : rs);
5706 1 : if (qs < 0)
5707 0 : return qs;
5708 1 : if (! fetch_tips)
5709 : {
5710 1 : cb (cb_cls,
5711 : creation_time,
5712 : expiration_time,
5713 : &merchant_initial_balance,
5714 : &exchange_initial_balance,
5715 : &pickup_amount,
5716 : &committed_amount,
5717 : (0 != active),
5718 : exchange_url,
5719 : payto_uri,
5720 : 0,
5721 : NULL);
5722 1 : GNUNET_PQ_cleanup_result (rs);
5723 1 : return qs;
5724 : }
5725 :
5726 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
5727 : "lookup_reserve_tips",
5728 : params,
5729 : &lookup_reserve_tips_cb,
5730 : <c);
5731 0 : if (qs < 0)
5732 0 : return qs;
5733 0 : if (ltc.qs >= 0)
5734 : {
5735 0 : cb (cb_cls,
5736 : creation_time,
5737 : expiration_time,
5738 : &merchant_initial_balance,
5739 : &exchange_initial_balance,
5740 : &pickup_amount,
5741 : &committed_amount,
5742 : 0 != active,
5743 : exchange_url,
5744 : payto_uri,
5745 : ltc.tips_length,
5746 0 : ltc.tips);
5747 : }
5748 0 : for (unsigned int i = 0; i<ltc.tips_length; i++)
5749 0 : GNUNET_free (ltc.tips[i].reason);
5750 0 : GNUNET_array_grow (ltc.tips,
5751 : ltc.tips_length,
5752 : 0);
5753 0 : GNUNET_PQ_cleanup_result (rs);
5754 0 : return ltc.qs;
5755 : }
5756 :
5757 :
5758 : /**
5759 : * Delete a reserve's private key.
5760 : *
5761 : * @param cls closure, typically a connection to the db
5762 : * @param instance_id which instance is the reserve tied to
5763 : * @param reserve_pub which reserve is to be deleted
5764 : * @return transaction status, usually
5765 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5766 : */
5767 : static enum GNUNET_DB_QueryStatus
5768 2 : postgres_delete_reserve (void *cls,
5769 : const char *instance_id,
5770 : const struct TALER_ReservePublicKeyP *reserve_pub)
5771 : {
5772 2 : struct PostgresClosure *pg = cls;
5773 2 : struct GNUNET_PQ_QueryParam params[] = {
5774 2 : GNUNET_PQ_query_param_string (instance_id),
5775 2 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5776 : GNUNET_PQ_query_param_end
5777 : };
5778 :
5779 2 : check_connection (pg);
5780 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5781 : "delete_reserve",
5782 : params);
5783 : }
5784 :
5785 :
5786 : /**
5787 : * Purge all of the information about a reserve, including tips.
5788 : *
5789 : * @param cls closure, typically a connection to the db
5790 : * @param instance_id which instance is the reserve tied to
5791 : * @param reserve_pub which reserve is to be purged
5792 : * @return transaction status, usually
5793 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
5794 : */
5795 : static enum GNUNET_DB_QueryStatus
5796 2 : postgres_purge_reserve (void *cls,
5797 : const char *instance_id,
5798 : const struct TALER_ReservePublicKeyP *reserve_pub)
5799 : {
5800 2 : struct PostgresClosure *pg = cls;
5801 2 : struct GNUNET_PQ_QueryParam params[] = {
5802 2 : GNUNET_PQ_query_param_string (instance_id),
5803 2 : GNUNET_PQ_query_param_auto_from_type (reserve_pub),
5804 : GNUNET_PQ_query_param_end
5805 : };
5806 :
5807 2 : check_connection (pg);
5808 2 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
5809 : "purge_reserve",
5810 : params);
5811 : }
5812 :
5813 :
5814 : /**
5815 : * Closure for #lookup_reserve_for_tip_cb().
5816 : */
5817 : struct LookupReserveForTipContext
5818 : {
5819 : /**
5820 : * Postgres context.
5821 : */
5822 : struct PostgresClosure *pg;
5823 :
5824 : /**
5825 : * Public key of the reserve we found.
5826 : */
5827 : struct TALER_ReservePublicKeyP reserve_pub;
5828 :
5829 : /**
5830 : * How much money must be left in the reserve.
5831 : */
5832 : struct TALER_Amount required_amount;
5833 :
5834 : /**
5835 : * Set to the expiration time of the reserve we found.
5836 : * #GNUNET_TIME_UNIT_FOREVER_ABS if we found none.
5837 : */
5838 : struct GNUNET_TIME_Timestamp expiration;
5839 :
5840 : /**
5841 : * Error status.
5842 : */
5843 : enum TALER_ErrorCode ec;
5844 :
5845 : /**
5846 : * Did we find a good reserve?
5847 : */
5848 : bool ok;
5849 : };
5850 :
5851 :
5852 : /**
5853 : * How long must a reserve be at least still valid before we use
5854 : * it for a tip?
5855 : */
5856 : #define MIN_EXPIRATION GNUNET_TIME_UNIT_HOURS
5857 :
5858 :
5859 : /**
5860 : * Function to be called with the results of a SELECT statement
5861 : * that has returned @a num_results results about accounts.
5862 : *
5863 : * @param[in,out] cls of type `struct LookupReserveForTipContext *`
5864 : * @param result the postgres result
5865 : * @param num_results the number of results in @a result
5866 : */
5867 : static void
5868 0 : lookup_reserve_for_tip_cb (void *cls,
5869 : PGresult *result,
5870 : unsigned int num_results)
5871 : {
5872 0 : struct LookupReserveForTipContext *lac = cls;
5873 0 : struct PostgresClosure *pg = lac->pg;
5874 :
5875 0 : for (unsigned int i = 0; i < num_results; i++)
5876 : {
5877 : struct TALER_ReservePublicKeyP reserve_pub;
5878 : struct TALER_Amount committed_amount;
5879 : struct TALER_Amount remaining;
5880 : struct TALER_Amount initial_balance;
5881 : struct GNUNET_TIME_Timestamp expiration;
5882 0 : struct GNUNET_PQ_ResultSpec rs[] = {
5883 0 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
5884 : &reserve_pub),
5885 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
5886 : &initial_balance),
5887 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
5888 : &committed_amount),
5889 0 : GNUNET_PQ_result_spec_timestamp ("expiration",
5890 : &expiration),
5891 : GNUNET_PQ_result_spec_end
5892 : };
5893 :
5894 0 : if (GNUNET_OK !=
5895 0 : GNUNET_PQ_extract_result (result,
5896 : rs,
5897 : i))
5898 : {
5899 0 : GNUNET_break (0);
5900 0 : lac->ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
5901 0 : return;
5902 : }
5903 0 : if (0 >
5904 0 : TALER_amount_subtract (&remaining,
5905 : &initial_balance,
5906 : &committed_amount))
5907 : {
5908 0 : GNUNET_break (0);
5909 0 : continue;
5910 : }
5911 0 : if (0 >
5912 0 : TALER_amount_cmp (&remaining,
5913 0 : &lac->required_amount))
5914 : {
5915 : /* insufficient balance */
5916 0 : if (lac->ok)
5917 0 : continue; /* got another reserve */
5918 0 : lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
5919 0 : continue;
5920 : }
5921 0 : if ( (! GNUNET_TIME_absolute_is_never (lac->expiration.abs_time)) &&
5922 0 : GNUNET_TIME_timestamp_cmp (expiration, >, lac->expiration) &&
5923 0 : GNUNET_TIME_relative_cmp (
5924 : GNUNET_TIME_absolute_get_remaining (lac->expiration.abs_time),
5925 : >,
5926 : MIN_EXPIRATION) )
5927 : {
5928 : /* reserve expired */
5929 0 : if (lac->ok)
5930 0 : continue; /* got another reserve */
5931 0 : lac->ec = TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED;
5932 0 : continue;
5933 : }
5934 0 : lac->ok = true;
5935 0 : lac->ec = TALER_EC_NONE;
5936 0 : lac->expiration = expiration;
5937 0 : lac->reserve_pub = reserve_pub;
5938 : }
5939 : }
5940 :
5941 :
5942 : /**
5943 : * Authorize a tip over @a amount from reserve @a reserve_pub. Remember
5944 : * the authorization under @a tip_id for later, together with the
5945 : * @a justification.
5946 : *
5947 : * @param cls closure, typically a connection to the db
5948 : * @param instance_id which instance should generate the tip
5949 : * @param reserve_pub which reserve is debited, NULL to pick one in the DB
5950 : * @param amount how high is the tip (with fees)
5951 : * @param justification why was the tip approved
5952 : * @param next_url where to send the URL post tip pickup
5953 : * @param[out] tip_id set to the unique ID for the tip
5954 : * @param[out] expiration set to when the tip expires
5955 : * @return transaction status,
5956 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_EXPIRED if the reserve is known but has expired
5957 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND if the reserve is not known
5958 : * #TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS if the reserve has insufficient funds left
5959 : * #TALER_EC_GENERIC_DB_START_FAILED on hard DB errors
5960 : * #TALER_EC_GENERIC_DB_FETCH_FAILED on hard DB errors
5961 : * #TALER_EC_GENERIC_DB_STORE_FAILED on hard DB errors
5962 : * #TALER_EC_GENERIC_DB_INVARIANT_FAILURE on hard DB errors
5963 : * #TALER_EC_GENERIC_DB_SOFT_FAILURE soft DB errors (client should retry)
5964 : * #TALER_EC_NONE upon success
5965 : */
5966 : static enum TALER_ErrorCode
5967 7 : postgres_authorize_tip (void *cls,
5968 : const char *instance_id,
5969 : const struct TALER_ReservePublicKeyP *reserve_pub,
5970 : const struct TALER_Amount *amount,
5971 : const char *justification,
5972 : const char *next_url,
5973 : struct TALER_TipIdentifierP *tip_id,
5974 : struct GNUNET_TIME_Timestamp *expiration)
5975 : {
5976 7 : struct PostgresClosure *pg = cls;
5977 7 : unsigned int retries = 0;
5978 : enum GNUNET_DB_QueryStatus qs;
5979 : struct TALER_Amount tips_committed;
5980 : struct TALER_Amount exchange_initial_balance;
5981 : const struct TALER_ReservePublicKeyP *reserve_pubp;
5982 7 : struct LookupReserveForTipContext lac = {
5983 : .pg = pg,
5984 : .required_amount = *amount,
5985 : .expiration = GNUNET_TIME_UNIT_FOREVER_TS
5986 : };
5987 :
5988 7 : check_connection (pg);
5989 7 : RETRY:
5990 7 : reserve_pubp = reserve_pub;
5991 7 : if (MAX_RETRIES < ++retries)
5992 : {
5993 0 : GNUNET_break (0);
5994 : return
5995 0 : TALER_EC_GENERIC_DB_SOFT_FAILURE;
5996 : }
5997 7 : if (GNUNET_OK !=
5998 7 : postgres_start (pg,
5999 : "authorize tip"))
6000 : {
6001 0 : GNUNET_break (0);
6002 0 : return TALER_EC_GENERIC_DB_START_FAILED;
6003 : }
6004 7 : if (NULL == reserve_pubp)
6005 : {
6006 0 : struct GNUNET_PQ_QueryParam params[] = {
6007 0 : GNUNET_PQ_query_param_string (instance_id),
6008 : GNUNET_PQ_query_param_end
6009 : };
6010 :
6011 0 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6012 : "lookup_reserve_for_tip",
6013 : params,
6014 : &lookup_reserve_for_tip_cb,
6015 : &lac);
6016 0 : switch (qs)
6017 : {
6018 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
6019 0 : postgres_rollback (pg);
6020 0 : goto RETRY;
6021 0 : case GNUNET_DB_STATUS_HARD_ERROR:
6022 0 : GNUNET_break (0);
6023 0 : postgres_rollback (pg);
6024 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
6025 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
6026 0 : postgres_rollback (pg);
6027 0 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
6028 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
6029 : default:
6030 0 : break;
6031 : }
6032 0 : if (TALER_EC_NONE != lac.ec)
6033 : {
6034 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6035 : "Enabling tip reserved failed with status %d\n",
6036 : lac.ec);
6037 0 : postgres_rollback (pg);
6038 0 : return lac.ec;
6039 : }
6040 0 : GNUNET_assert (lac.ok);
6041 0 : reserve_pubp = &lac.reserve_pub;
6042 : }
6043 :
6044 : {
6045 7 : struct GNUNET_PQ_QueryParam params[] = {
6046 7 : GNUNET_PQ_query_param_string (instance_id),
6047 7 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6048 : GNUNET_PQ_query_param_end
6049 : };
6050 7 : struct GNUNET_PQ_ResultSpec rs[] = {
6051 7 : GNUNET_PQ_result_spec_timestamp ("expiration",
6052 : expiration),
6053 7 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_committed",
6054 : &tips_committed),
6055 7 : TALER_PQ_RESULT_SPEC_AMOUNT ("exchange_initial_balance",
6056 : &exchange_initial_balance),
6057 : GNUNET_PQ_result_spec_end
6058 : };
6059 :
6060 7 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6061 : "lookup_reserve_status",
6062 : params,
6063 : rs);
6064 7 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6065 : {
6066 0 : postgres_rollback (pg);
6067 0 : goto RETRY;
6068 : }
6069 7 : if (qs < 0)
6070 : {
6071 0 : GNUNET_break (0);
6072 0 : postgres_rollback (pg);
6073 0 : return TALER_EC_GENERIC_DB_FETCH_FAILED;
6074 : }
6075 7 : if (0 == qs)
6076 : {
6077 0 : postgres_rollback (pg);
6078 0 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_RESERVE_NOT_FOUND;
6079 : }
6080 : }
6081 : {
6082 : struct TALER_Amount remaining;
6083 :
6084 7 : if (0 >
6085 7 : TALER_amount_subtract (&remaining,
6086 : &exchange_initial_balance,
6087 : &tips_committed))
6088 : {
6089 0 : GNUNET_break (0);
6090 0 : postgres_rollback (pg);
6091 1 : return TALER_EC_GENERIC_DB_INVARIANT_FAILURE;
6092 : }
6093 7 : if (0 >
6094 7 : TALER_amount_cmp (&remaining,
6095 : amount))
6096 : {
6097 1 : postgres_rollback (pg);
6098 1 : return TALER_EC_MERCHANT_PRIVATE_POST_TIP_AUTHORIZE_INSUFFICIENT_FUNDS;
6099 : }
6100 : }
6101 6 : GNUNET_assert (0 <=
6102 : TALER_amount_add (&tips_committed,
6103 : &tips_committed,
6104 : amount));
6105 : {
6106 6 : struct GNUNET_PQ_QueryParam params[] = {
6107 6 : GNUNET_PQ_query_param_string (instance_id),
6108 6 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6109 6 : TALER_PQ_query_param_amount (&tips_committed),
6110 : GNUNET_PQ_query_param_end
6111 : };
6112 :
6113 6 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6114 : "update_reserve_tips_committed",
6115 : params);
6116 6 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6117 : {
6118 0 : postgres_rollback (pg);
6119 0 : goto RETRY;
6120 : }
6121 6 : if (qs < 0)
6122 : {
6123 0 : GNUNET_break (0);
6124 0 : postgres_rollback (pg);
6125 0 : return TALER_EC_GENERIC_DB_STORE_FAILED;
6126 : }
6127 : }
6128 6 : GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
6129 : tip_id,
6130 : sizeof (*tip_id));
6131 : {
6132 6 : struct GNUNET_PQ_QueryParam params[] = {
6133 6 : GNUNET_PQ_query_param_string (instance_id),
6134 6 : GNUNET_PQ_query_param_auto_from_type (reserve_pubp),
6135 6 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6136 6 : GNUNET_PQ_query_param_string (justification),
6137 6 : GNUNET_PQ_query_param_string (next_url),
6138 6 : GNUNET_PQ_query_param_timestamp (expiration),
6139 6 : TALER_PQ_query_param_amount (amount),
6140 : GNUNET_PQ_query_param_end
6141 : };
6142 :
6143 6 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6144 : "insert_tip",
6145 : params);
6146 6 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6147 : {
6148 0 : postgres_rollback (pg);
6149 0 : goto RETRY;
6150 : }
6151 6 : if (qs < 0)
6152 : {
6153 0 : GNUNET_break (0);
6154 0 : postgres_rollback (pg);
6155 0 : return TALER_EC_GENERIC_DB_STORE_FAILED;
6156 : }
6157 : }
6158 6 : qs = postgres_commit (pg);
6159 6 : if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
6160 0 : goto RETRY;
6161 6 : if (qs < 0)
6162 : {
6163 0 : GNUNET_break (0);
6164 0 : postgres_rollback (pg);
6165 0 : return TALER_EC_GENERIC_DB_COMMIT_FAILED;
6166 : }
6167 6 : return TALER_EC_NONE;
6168 : }
6169 :
6170 :
6171 : /**
6172 : * Closure for #lookup_signatures_cb().
6173 : */
6174 : struct LookupSignaturesContext
6175 : {
6176 : /**
6177 : * Length of the @e sigs array
6178 : */
6179 : unsigned int sigs_length;
6180 :
6181 : /**
6182 : * Where to store the signatures.
6183 : */
6184 : struct TALER_BlindedDenominationSignature *sigs;
6185 : };
6186 :
6187 :
6188 : /**
6189 : * Function to be called with the results of a SELECT statement
6190 : * that has returned @a num_results results about accounts.
6191 : *
6192 : * @param[in,out] cls of type `struct LookupSignaturesContext *`
6193 : * @param result the postgres result
6194 : * @param num_results the number of results in @a result
6195 : */
6196 : static void
6197 1 : lookup_signatures_cb (void *cls,
6198 : PGresult *result,
6199 : unsigned int num_results)
6200 : {
6201 1 : struct LookupSignaturesContext *lsc = cls;
6202 :
6203 1 : for (unsigned int i = 0; i < num_results; i++)
6204 : {
6205 : uint32_t offset;
6206 : struct TALER_BlindedDenominationSignature bsig;
6207 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6208 0 : GNUNET_PQ_result_spec_uint32 ("coin_offset",
6209 : &offset),
6210 0 : TALER_PQ_result_spec_blinded_denom_sig ("blind_sig",
6211 : &bsig),
6212 : GNUNET_PQ_result_spec_end
6213 : };
6214 :
6215 0 : if (GNUNET_OK !=
6216 0 : GNUNET_PQ_extract_result (result,
6217 : rs,
6218 : i))
6219 : {
6220 0 : GNUNET_break (0);
6221 0 : return;
6222 : }
6223 0 : if (offset >= lsc->sigs_length)
6224 : {
6225 0 : GNUNET_break_op (0);
6226 0 : GNUNET_PQ_cleanup_result (rs);
6227 0 : continue;
6228 : }
6229 : /* Must be NULL due to UNIQUE constraint on offset and
6230 : requirement that client launched us with 'sigs'
6231 : pre-initialized to NULL. */
6232 0 : lsc->sigs[offset] = bsig;
6233 : }
6234 : }
6235 :
6236 :
6237 : /**
6238 : * Lookup pickup details for pickup @a pickup_id.
6239 : *
6240 : * @param cls closure, typically a connection to the db
6241 : * @param instance_id which instance should we lookup tip details for
6242 : * @param tip_id which tip should we lookup details on
6243 : * @param pickup_id which pickup should we lookup details on
6244 : * @param[out] exchange_url which exchange is the tip withdrawn from
6245 : * @param[out] reserve_priv private key the tip is withdrawn from (set if still available!)
6246 : * @param sigs_length length of the @a sigs array
6247 : * @param[out] sigs set to the (blind) signatures we have for this @a pickup_id,
6248 : * those that are unavailable are left at NULL
6249 : * @return transaction status
6250 : */
6251 : static enum GNUNET_DB_QueryStatus
6252 1 : postgres_lookup_pickup (void *cls,
6253 : const char *instance_id,
6254 : const struct TALER_TipIdentifierP *tip_id,
6255 : const struct TALER_PickupIdentifierP *pickup_id,
6256 : char **exchange_url,
6257 : struct TALER_ReservePrivateKeyP *reserve_priv,
6258 : unsigned int sigs_length,
6259 : struct TALER_BlindedDenominationSignature sigs[])
6260 : {
6261 1 : struct PostgresClosure *pg = cls;
6262 : uint64_t pickup_serial;
6263 :
6264 : {
6265 1 : struct GNUNET_PQ_QueryParam params[] = {
6266 1 : GNUNET_PQ_query_param_string (instance_id),
6267 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6268 1 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6269 : GNUNET_PQ_query_param_end
6270 : };
6271 1 : struct GNUNET_PQ_ResultSpec rs[] = {
6272 1 : GNUNET_PQ_result_spec_string ("exchange_url",
6273 : exchange_url),
6274 1 : GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
6275 : reserve_priv),
6276 1 : GNUNET_PQ_result_spec_uint64 ("pickup_serial",
6277 : &pickup_serial),
6278 : GNUNET_PQ_result_spec_end
6279 : };
6280 : enum GNUNET_DB_QueryStatus qs;
6281 :
6282 1 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6283 : "lookup_pickup",
6284 : params,
6285 : rs);
6286 1 : if (qs <= 0)
6287 0 : return qs;
6288 : }
6289 : {
6290 1 : struct GNUNET_PQ_QueryParam params[] = {
6291 1 : GNUNET_PQ_query_param_uint64 (&pickup_serial),
6292 : GNUNET_PQ_query_param_end
6293 : };
6294 1 : struct LookupSignaturesContext lsc = {
6295 : .sigs_length = sigs_length,
6296 : .sigs = sigs
6297 : };
6298 :
6299 1 : return GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6300 : "lookup_pickup_signatures",
6301 : params,
6302 : &lookup_signatures_cb,
6303 : &lsc);
6304 : }
6305 : }
6306 :
6307 :
6308 : /**
6309 : * Lookup tip details for tip @a tip_id.
6310 : *
6311 : * @param cls closure, typically a connection to the db
6312 : * @param instance_id which instance should we lookup tip details for
6313 : * @param tip_id which tip should we lookup details on
6314 : * @param[out] total_authorized amount how high is the tip (with fees)
6315 : * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
6316 : * @param[out] expiration set to when the tip expires
6317 : * @param[out] exchange_url set to the exchange URL where the reserve is
6318 : * @param[out] reserve_priv set to private key of reserve to be debited
6319 : * @return transaction status
6320 : */
6321 : static enum GNUNET_DB_QueryStatus
6322 1 : postgres_lookup_tip (void *cls,
6323 : const char *instance_id,
6324 : const struct TALER_TipIdentifierP *tip_id,
6325 : struct TALER_Amount *total_authorized,
6326 : struct TALER_Amount *total_picked_up,
6327 : struct GNUNET_TIME_Timestamp *expiration,
6328 : char **exchange_url,
6329 : struct TALER_ReservePrivateKeyP *reserve_priv)
6330 : {
6331 1 : struct PostgresClosure *pg = cls;
6332 1 : struct GNUNET_PQ_QueryParam params[] = {
6333 1 : GNUNET_PQ_query_param_string (instance_id),
6334 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6335 : GNUNET_PQ_query_param_end
6336 : };
6337 1 : struct GNUNET_PQ_ResultSpec rs[] = {
6338 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6339 : total_authorized),
6340 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
6341 : total_picked_up),
6342 1 : GNUNET_PQ_result_spec_timestamp ("expiration",
6343 : expiration),
6344 1 : GNUNET_PQ_result_spec_string ("exchange_url",
6345 : exchange_url),
6346 1 : GNUNET_PQ_result_spec_auto_from_type ("reserve_priv",
6347 : reserve_priv),
6348 : GNUNET_PQ_result_spec_end
6349 : };
6350 :
6351 1 : return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6352 : "lookup_tip",
6353 : params,
6354 : rs);
6355 : }
6356 :
6357 :
6358 : /**
6359 : * Context used for postgres_lookup_tips().
6360 : */
6361 : struct LookupMerchantTipsContext
6362 : {
6363 : /**
6364 : * Postgres context.
6365 : */
6366 : struct PostgresClosure *pg;
6367 :
6368 : /**
6369 : * Function to call with the results.
6370 : */
6371 : TALER_MERCHANTDB_TipsCallback cb;
6372 :
6373 : /**
6374 : * Closure for @a cb.
6375 : */
6376 : void *cb_cls;
6377 :
6378 : /**
6379 : * Internal result.
6380 : */
6381 : enum GNUNET_DB_QueryStatus qs;
6382 : };
6383 :
6384 :
6385 : /**
6386 : * Function to be called with the results of a SELECT statement
6387 : * that has returned @a num_results results about tips.
6388 : *
6389 : * @param[in,out] cls of type `struct LookupTipsContext *`
6390 : * @param result the postgres result
6391 : * @param num_results the number of results in @a result
6392 : */
6393 : static void
6394 7 : lookup_tips_cb (void *cls,
6395 : PGresult *result,
6396 : unsigned int num_results)
6397 : {
6398 7 : struct LookupMerchantTipsContext *plc = cls;
6399 7 : struct PostgresClosure *pg = plc->pg;
6400 :
6401 32 : for (unsigned int i = 0; i < num_results; i++)
6402 : {
6403 : uint64_t row_id;
6404 : struct TALER_TipIdentifierP tip_id;
6405 : struct TALER_Amount tip_amount;
6406 25 : struct GNUNET_PQ_ResultSpec rs[] = {
6407 25 : GNUNET_PQ_result_spec_uint64 ("tip_serial",
6408 : &row_id),
6409 25 : GNUNET_PQ_result_spec_auto_from_type ("tip_id",
6410 : &tip_id),
6411 25 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6412 : &tip_amount),
6413 : GNUNET_PQ_result_spec_end
6414 : };
6415 :
6416 25 : if (GNUNET_OK !=
6417 25 : GNUNET_PQ_extract_result (result,
6418 : rs,
6419 : i))
6420 : {
6421 0 : GNUNET_break (0);
6422 0 : plc->qs = GNUNET_DB_STATUS_HARD_ERROR;
6423 0 : return;
6424 : }
6425 25 : plc->cb (plc->cb_cls,
6426 : row_id,
6427 : tip_id,
6428 : tip_amount);
6429 25 : GNUNET_PQ_cleanup_result (rs);
6430 : }
6431 : }
6432 :
6433 :
6434 : /**
6435 : * Lookup tips
6436 : *
6437 : * @param cls closure, typically a connection to the db
6438 : * @param instance_id which instance should we lookup tips for
6439 : * @param expired should we include expired tips?
6440 : * @param limit maximum number of results to return, positive for
6441 : * ascending row id, negative for descending
6442 : * @param offset row id to start returning results from
6443 : * @param cb function to call with tip data
6444 : * @param cb_cls closure for @a cb
6445 : * @return transaction status
6446 : */
6447 : static enum GNUNET_DB_QueryStatus
6448 7 : postgres_lookup_tips (void *cls,
6449 : const char *instance_id,
6450 : enum TALER_EXCHANGE_YesNoAll expired,
6451 : int64_t limit,
6452 : uint64_t offset,
6453 : TALER_MERCHANTDB_TipsCallback cb,
6454 : void *cb_cls)
6455 : {
6456 7 : struct PostgresClosure *pg = cls;
6457 7 : struct LookupMerchantTipsContext plc = {
6458 : .pg = pg,
6459 : .cb = cb,
6460 : .cb_cls = cb_cls
6461 : };
6462 7 : uint64_t ulimit = (limit > 0) ? limit : -limit;
6463 : uint8_t bexpired;
6464 7 : struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
6465 7 : struct GNUNET_PQ_QueryParam params[] = {
6466 7 : GNUNET_PQ_query_param_string (instance_id),
6467 7 : GNUNET_PQ_query_param_uint64 (&ulimit),
6468 7 : GNUNET_PQ_query_param_uint64 (&offset),
6469 7 : GNUNET_PQ_query_param_absolute_time (&now),
6470 7 : GNUNET_PQ_query_param_auto_from_type (&bexpired),
6471 : GNUNET_PQ_query_param_end
6472 : };
6473 : enum GNUNET_DB_QueryStatus qs;
6474 : char stmt[128];
6475 :
6476 7 : bexpired = (TALER_EXCHANGE_YNA_YES == expired);
6477 7 : GNUNET_snprintf (stmt,
6478 : sizeof (stmt),
6479 : "lookup_tips_%s%s",
6480 : (limit > 0) ? "inc" : "dec",
6481 : (TALER_EXCHANGE_YNA_ALL == expired) ? "" : "_expired");
6482 7 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6483 : stmt,
6484 : params,
6485 : &lookup_tips_cb,
6486 : &plc);
6487 7 : if (0 != plc.qs)
6488 0 : return plc.qs;
6489 7 : return qs;
6490 : }
6491 :
6492 :
6493 : /**
6494 : * Closure for #lookup_pickup_details_cb().
6495 : */
6496 : struct LookupTipDetailsContext
6497 : {
6498 : /**
6499 : * Length of the @e sigs array
6500 : */
6501 : unsigned int *pickups_length;
6502 :
6503 : /**
6504 : * Where to store the signatures.
6505 : */
6506 : struct TALER_MERCHANTDB_PickupDetails **pickups;
6507 :
6508 : /**
6509 : * Database handle.
6510 : */
6511 : struct PostgresClosure *pg;
6512 :
6513 : /**
6514 : * Transaction status.
6515 : */
6516 : enum GNUNET_DB_QueryStatus qs;
6517 :
6518 : };
6519 :
6520 :
6521 : /**
6522 : * Function to be called with the results of a SELECT statement
6523 : * that has returned @a num_results results about pickups.
6524 : *
6525 : * @param[in,out] cls of type `struct LookupTipDetailsContext *`
6526 : * @param result the postgres result
6527 : * @param num_results the number of results in @a result
6528 : */
6529 : static void
6530 1 : lookup_pickup_details_cb (void *cls,
6531 : PGresult *result,
6532 : unsigned int num_results)
6533 : {
6534 1 : struct LookupTipDetailsContext *ltdc = cls;
6535 1 : struct PostgresClosure *pg = ltdc->pg;
6536 :
6537 1 : *ltdc->pickups_length = num_results;
6538 1 : *ltdc->pickups = GNUNET_new_array (num_results,
6539 : struct TALER_MERCHANTDB_PickupDetails);
6540 1 : for (unsigned int i = 0; i < num_results; i++)
6541 : {
6542 0 : struct TALER_MERCHANTDB_PickupDetails *pd = &((*ltdc->pickups)[i]);
6543 0 : uint64_t num_planchets = 0;
6544 0 : struct GNUNET_PQ_ResultSpec rs[] = {
6545 0 : GNUNET_PQ_result_spec_auto_from_type ("pickup_id",
6546 : &pd->pickup_id),
6547 0 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6548 : &pd->requested_amount),
6549 0 : GNUNET_PQ_result_spec_uint64 ("num_planchets",
6550 : &num_planchets),
6551 : GNUNET_PQ_result_spec_end
6552 : };
6553 :
6554 0 : if (GNUNET_OK !=
6555 0 : GNUNET_PQ_extract_result (result,
6556 : rs,
6557 : i))
6558 : {
6559 0 : GNUNET_break (0);
6560 0 : ltdc->qs = GNUNET_DB_STATUS_HARD_ERROR;
6561 0 : GNUNET_array_grow (*ltdc->pickups,
6562 : *ltdc->pickups_length,
6563 : 0);
6564 0 : return;
6565 : }
6566 :
6567 0 : pd->num_planchets = num_planchets;
6568 : }
6569 : }
6570 :
6571 :
6572 : /**
6573 : * Lookup tip details for tip @a tip_id.
6574 : *
6575 : * @param cls closure, typically a connection to the db
6576 : * @param instance_id which instance should we lookup tip details for
6577 : * @param tip_id which tip should we lookup details on
6578 : * @param fpu should we fetch details about individual pickups
6579 : * @param[out] total_authorized amount how high is the tip (with fees)
6580 : * @param[out] total_picked_up how much of the tip was so far picked up (with fees)
6581 : * @param[out] justification why was the tip approved
6582 : * @param[out] expiration set to when the tip expires
6583 : * @param[out] reserve_pub set to which reserve is debited
6584 : * @param[out] pickups_length set to the length of @e pickups
6585 : * @param[out] pickups if @a fpu is true, set to details about the pickup operations
6586 : * @return transaction status,
6587 : */
6588 : static enum GNUNET_DB_QueryStatus
6589 1 : postgres_lookup_tip_details (void *cls,
6590 : const char *instance_id,
6591 : const struct TALER_TipIdentifierP *tip_id,
6592 : bool fpu,
6593 : struct TALER_Amount *total_authorized,
6594 : struct TALER_Amount *total_picked_up,
6595 : char **justification,
6596 : struct GNUNET_TIME_Timestamp *expiration,
6597 : struct TALER_ReservePublicKeyP *reserve_pub,
6598 : unsigned int *pickups_length,
6599 : struct TALER_MERCHANTDB_PickupDetails **pickups)
6600 : {
6601 1 : struct PostgresClosure *pg = cls;
6602 : uint64_t tip_serial;
6603 : enum GNUNET_DB_QueryStatus qs;
6604 : {
6605 1 : struct GNUNET_PQ_QueryParam params[] = {
6606 1 : GNUNET_PQ_query_param_string (instance_id),
6607 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6608 : GNUNET_PQ_query_param_end
6609 : };
6610 1 : struct GNUNET_PQ_ResultSpec rs[] = {
6611 1 : GNUNET_PQ_result_spec_uint64 ("tip_serial",
6612 : &tip_serial),
6613 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("amount",
6614 : total_authorized),
6615 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("picked_up",
6616 : total_picked_up),
6617 1 : GNUNET_PQ_result_spec_string ("justification",
6618 : justification),
6619 1 : GNUNET_PQ_result_spec_timestamp ("expiration",
6620 : expiration),
6621 1 : GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
6622 : reserve_pub),
6623 : GNUNET_PQ_result_spec_end
6624 : };
6625 :
6626 1 : check_connection (pg);
6627 1 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6628 : "lookup_tip_details",
6629 : params,
6630 : rs);
6631 1 : if (qs <= 0)
6632 0 : return qs;
6633 1 : if (! fpu)
6634 : {
6635 0 : *pickups_length = 0;
6636 0 : *pickups = NULL;
6637 0 : return qs;
6638 : }
6639 : }
6640 : {
6641 1 : struct GNUNET_PQ_QueryParam params[] = {
6642 1 : GNUNET_PQ_query_param_uint64 (&tip_serial),
6643 : GNUNET_PQ_query_param_end
6644 : };
6645 :
6646 1 : struct LookupTipDetailsContext ltdc = {
6647 : .pickups_length = pickups_length,
6648 : .pickups = pickups,
6649 : .pg = pg,
6650 : .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
6651 : };
6652 :
6653 1 : qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
6654 : "lookup_pickup_details",
6655 : params,
6656 : &lookup_pickup_details_cb,
6657 : <dc);
6658 1 : if (qs < 0)
6659 0 : return qs;
6660 1 : return ltdc.qs;
6661 : }
6662 : }
6663 :
6664 :
6665 : /**
6666 : * Insert details about a tip pickup operation. The @a total_picked_up
6667 : * UPDATES the total amount under the @a tip_id, while the @a
6668 : * total_requested is the amount to be associated with this @a pickup_id.
6669 : * While there is usually only one pickup event that picks up the entire
6670 : * amount, our schema allows for wallets to pick up the amount incrementally
6671 : * over multiple pick up operations.
6672 : *
6673 : * @param cls closure, typically a connection to the db
6674 : * @param instance_id which instance gave the tip
6675 : * @param tip_id the unique ID for the tip
6676 : * @param total_picked_up how much was picked up overall at this
6677 : * point (includes @a total_requested)
6678 : * @param pickup_id unique ID for the operation
6679 : * @param total_requested how much is being picked up in this operation
6680 : * @return transaction status, usually
6681 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
6682 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
6683 : */
6684 : static enum GNUNET_DB_QueryStatus
6685 1 : postgres_insert_pickup (void *cls,
6686 : const char *instance_id,
6687 : const struct TALER_TipIdentifierP *tip_id,
6688 : const struct TALER_Amount *total_picked_up,
6689 : const struct TALER_PickupIdentifierP *pickup_id,
6690 : const struct TALER_Amount *total_requested)
6691 : {
6692 1 : struct PostgresClosure *pg = cls;
6693 : enum GNUNET_DB_QueryStatus qs;
6694 :
6695 : {
6696 1 : struct GNUNET_PQ_QueryParam params[] = {
6697 1 : GNUNET_PQ_query_param_string (instance_id),
6698 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6699 1 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6700 1 : TALER_PQ_query_param_amount (total_requested),
6701 : GNUNET_PQ_query_param_end
6702 : };
6703 :
6704 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6705 : "insert_pickup",
6706 : params);
6707 1 : if (0 > qs)
6708 0 : return qs;
6709 : }
6710 :
6711 : {
6712 1 : struct GNUNET_PQ_QueryParam params[] = {
6713 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6714 1 : TALER_PQ_query_param_amount (total_picked_up),
6715 : GNUNET_PQ_query_param_end
6716 : };
6717 :
6718 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6719 : "update_picked_up_tip",
6720 : params);
6721 1 : if (0 > qs)
6722 0 : return qs;
6723 : }
6724 : {
6725 : uint64_t reserve_serial;
6726 : struct TALER_Amount reserve_picked_up;
6727 : {
6728 1 : struct GNUNET_PQ_QueryParam params[] = {
6729 1 : GNUNET_PQ_query_param_string (instance_id),
6730 1 : GNUNET_PQ_query_param_auto_from_type (tip_id),
6731 : GNUNET_PQ_query_param_end
6732 : };
6733 1 : struct GNUNET_PQ_ResultSpec rs[] = {
6734 1 : GNUNET_PQ_result_spec_uint64 ("reserve_serial",
6735 : &reserve_serial),
6736 1 : TALER_PQ_RESULT_SPEC_AMOUNT ("tips_picked_up",
6737 : &reserve_picked_up),
6738 : GNUNET_PQ_result_spec_end
6739 :
6740 : };
6741 :
6742 1 : qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
6743 : "lookup_picked_up_reserve",
6744 : params,
6745 : rs);
6746 1 : if (0 > qs)
6747 0 : return qs;
6748 : }
6749 1 : if (0 >=
6750 1 : TALER_amount_add (&reserve_picked_up,
6751 : &reserve_picked_up,
6752 : total_requested))
6753 : {
6754 0 : GNUNET_break (0);
6755 0 : return GNUNET_DB_STATUS_HARD_ERROR;
6756 : }
6757 :
6758 : {
6759 1 : struct GNUNET_PQ_QueryParam params[] = {
6760 1 : GNUNET_PQ_query_param_uint64 (&reserve_serial),
6761 1 : TALER_PQ_query_param_amount (&reserve_picked_up),
6762 : GNUNET_PQ_query_param_end
6763 : };
6764 :
6765 1 : qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
6766 : "update_picked_up_reserve",
6767 : params);
6768 1 : if (0 > qs)
6769 0 : return qs;
6770 : }
6771 : }
6772 1 : return qs;
6773 : }
6774 :
6775 :
6776 : /**
6777 : * Insert blind signature obtained from the exchange during a
6778 : * tip pickup operation.
6779 : *
6780 : * @param cls closure, typically a connection to the db
6781 : * @param pickup_id unique ID for the operation
6782 : * @param offset offset of the blind signature for the pickup
6783 : * @param blind_sig the blind signature
6784 : * @return transaction status, usually
6785 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success
6786 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known
6787 : */
6788 : static enum GNUNET_DB_QueryStatus
6789 1 : postgres_insert_pickup_blind_signature (
6790 : void *cls,
6791 : const struct TALER_PickupIdentifierP *pickup_id,
6792 : uint32_t offset,
6793 : const struct TALER_BlindedDenominationSignature *blind_sig)
6794 : {
6795 1 : struct PostgresClosure *pg = cls;
6796 1 : struct GNUNET_PQ_QueryParam params[] = {
6797 1 : GNUNET_PQ_query_param_auto_from_type (pickup_id),
6798 1 : GNUNET_PQ_query_param_uint32 (&offset),
6799 1 : TALER_PQ_query_param_blinded_denom_sig (blind_sig),
6800 : GNUNET_PQ_query_param_end
6801 : };
6802 :
6803 1 : check_connection (pg);
6804 1 : return GNUNET_PQ_eval_prepared_non_select (pg->conn,
6805 : "insert_pickup_blind_signature",
6806 : params);
6807 : }
6808 :
6809 :
6810 : /**
6811 : * Establish connection to the database.
6812 : *
6813 : * @param cls plugin context
6814 : * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
6815 : */
6816 : static int
6817 4 : postgres_connect (void *cls)
6818 : {
6819 4 : struct PostgresClosure *pg = cls;
6820 4 : struct GNUNET_PQ_PreparedStatement ps[] = {
6821 4 : GNUNET_PQ_make_prepare ("end_transaction",
6822 : "COMMIT",
6823 : 0),
6824 : /* for call_with_accounts(), part of postgres_lookup_instances() */
6825 4 : GNUNET_PQ_make_prepare ("lookup_instance_private_key",
6826 : "SELECT"
6827 : " merchant_priv"
6828 : " FROM merchant_keys"
6829 : " WHERE merchant_serial=$1",
6830 : 1),
6831 : /* for find_instances_cb(), part of postgres_lookup_instances() */
6832 4 : GNUNET_PQ_make_prepare ("lookup_accounts",
6833 : "SELECT"
6834 : " h_wire"
6835 : ",salt"
6836 : ",payto_uri"
6837 : ",active"
6838 : " FROM merchant_accounts"
6839 : " WHERE merchant_serial=$1",
6840 : 1),
6841 : /* for postgres_lookup_instances() */
6842 4 : GNUNET_PQ_make_prepare ("lookup_instance_auth",
6843 : "SELECT"
6844 : " auth_hash"
6845 : ",auth_salt"
6846 : " FROM merchant_instances"
6847 : " WHERE merchant_id=$1",
6848 : 1),
6849 : /* for postgres_lookup_instances() */
6850 4 : GNUNET_PQ_make_prepare ("lookup_instances",
6851 : "SELECT"
6852 : " merchant_serial"
6853 : ",merchant_pub"
6854 : ",auth_hash"
6855 : ",auth_salt"
6856 : ",merchant_id"
6857 : ",merchant_name"
6858 : ",address"
6859 : ",jurisdiction"
6860 : ",default_max_deposit_fee_val"
6861 : ",default_max_deposit_fee_frac"
6862 : ",default_max_wire_fee_val"
6863 : ",default_max_wire_fee_frac"
6864 : ",default_wire_fee_amortization"
6865 : ",default_wire_transfer_delay"
6866 : ",default_pay_delay"
6867 : ",website"
6868 : ",email"
6869 : ",logo"
6870 : " FROM merchant_instances",
6871 : 0),
6872 : /* for postgres_lookup_instance() */
6873 4 : GNUNET_PQ_make_prepare ("lookup_instance",
6874 : "SELECT"
6875 : " merchant_serial"
6876 : ",merchant_pub"
6877 : ",auth_hash"
6878 : ",auth_salt"
6879 : ",merchant_id"
6880 : ",merchant_name"
6881 : ",address"
6882 : ",jurisdiction"
6883 : ",default_max_deposit_fee_val"
6884 : ",default_max_deposit_fee_frac"
6885 : ",default_max_wire_fee_val"
6886 : ",default_max_wire_fee_frac"
6887 : ",default_wire_fee_amortization"
6888 : ",default_wire_transfer_delay"
6889 : ",default_pay_delay"
6890 : ",website"
6891 : ",email"
6892 : ",logo"
6893 : " FROM merchant_instances"
6894 : " WHERE merchant_id=$1",
6895 : 1),
6896 : /* for postgres_insert_instance() */
6897 4 : GNUNET_PQ_make_prepare ("insert_instance",
6898 : "INSERT INTO merchant_instances"
6899 : "(merchant_pub"
6900 : ",auth_hash"
6901 : ",auth_salt"
6902 : ",merchant_id"
6903 : ",merchant_name"
6904 : ",address"
6905 : ",jurisdiction"
6906 : ",default_max_deposit_fee_val"
6907 : ",default_max_deposit_fee_frac"
6908 : ",default_max_wire_fee_val"
6909 : ",default_max_wire_fee_frac"
6910 : ",default_wire_fee_amortization"
6911 : ",default_wire_transfer_delay"
6912 : ",default_pay_delay"
6913 : ",website"
6914 : ",email"
6915 : ",logo)"
6916 : "VALUES"
6917 : "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)",
6918 : 14),
6919 : /* for postgres_insert_instance() */
6920 4 : GNUNET_PQ_make_prepare ("insert_keys",
6921 : "INSERT INTO merchant_keys"
6922 : "(merchant_priv"
6923 : ",merchant_serial)"
6924 : " SELECT $1, merchant_serial"
6925 : " FROM merchant_instances"
6926 : " WHERE merchant_id=$2",
6927 : 2),
6928 : /* for postgres_account_kyc_set_status */
6929 4 : GNUNET_PQ_make_prepare ("upsert_account_kyc",
6930 : "INSERT INTO merchant_kyc"
6931 : "(kyc_timestamp"
6932 : ",kyc_ok"
6933 : ",exchange_kyc_serial"
6934 : ",account_serial"
6935 : ",exchange_url"
6936 : ",exchange_pub"
6937 : ",exchange_sig)"
6938 : " SELECT $5, $6, $4, account_serial, $3, $7, $8"
6939 : " FROM merchant_instances"
6940 : " JOIN merchant_accounts USING (merchant_serial)"
6941 : " WHERE merchant_id=$1"
6942 : " AND h_wire=$2"
6943 : " ON CONFLICT(account_serial,exchange_url) DO "
6944 : "UPDATE"
6945 : " SET exchange_kyc_serial=$4"
6946 : " ,kyc_timestamp=$5"
6947 : " ,kyc_ok=$6"
6948 : " ,exchange_pub=$7"
6949 : " ,exchange_sig=$8",
6950 : 8),
6951 : /* for postgres_account_kyc_get_status */
6952 4 : GNUNET_PQ_make_prepare ("lookup_kyc_status",
6953 : "SELECT"
6954 : " h_wire"
6955 : ",exchange_kyc_serial"
6956 : ",payto_uri"
6957 : ",exchange_url"
6958 : ",kyc_timestamp"
6959 : ",kyc_ok"
6960 : " FROM merchant_instances"
6961 : " JOIN merchant_accounts"
6962 : " USING (merchant_serial)"
6963 : " JOIN merchant_kyc"
6964 : " USING (account_serial)"
6965 : " WHERE merchant_instances.merchant_id=$1",
6966 : 1),
6967 : /* for postgres_insert_account() */
6968 4 : GNUNET_PQ_make_prepare ("insert_account",
6969 : "INSERT INTO merchant_accounts"
6970 : "(merchant_serial"
6971 : ",h_wire"
6972 : ",salt"
6973 : ",payto_uri"
6974 : ",active)"
6975 : " SELECT merchant_serial, $2, $3, $4, $5"
6976 : " FROM merchant_instances"
6977 : " WHERE merchant_id=$1",
6978 : 5),
6979 : /* for postgres_delete_instance_private_key() */
6980 4 : GNUNET_PQ_make_prepare ("delete_key",
6981 : "DELETE FROM merchant_keys"
6982 : " USING merchant_instances"
6983 : " WHERE merchant_keys.merchant_serial"
6984 : " = merchant_instances.merchant_serial"
6985 : " AND merchant_instances.merchant_id = $1",
6986 : 1),
6987 : /* for postgres_purge_instance() */
6988 4 : GNUNET_PQ_make_prepare ("purge_instance",
6989 : "DELETE FROM merchant_instances"
6990 : " WHERE merchant_instances.merchant_id = $1",
6991 : 1),
6992 : /* for postgres_update_instance() */
6993 4 : GNUNET_PQ_make_prepare ("update_instance",
6994 : "UPDATE merchant_instances SET"
6995 : " merchant_name=$2"
6996 : ",address=$3"
6997 : ",jurisdiction=$4"
6998 : ",default_max_deposit_fee_val=$5"
6999 : ",default_max_deposit_fee_frac=$6"
7000 : ",default_max_wire_fee_val=$7"
7001 : ",default_max_wire_fee_frac=$8"
7002 : ",default_wire_fee_amortization=$9"
7003 : ",default_wire_transfer_delay=$10"
7004 : ",default_pay_delay=$11"
7005 : ",website=$12"
7006 : ",email=$13"
7007 : ",logo=$14"
7008 : " WHERE merchant_id = $1",
7009 : 11),
7010 : /* for postgres_update_instance_auth() */
7011 4 : GNUNET_PQ_make_prepare ("update_instance_auth",
7012 : "UPDATE merchant_instances SET"
7013 : " auth_hash=$2"
7014 : ",auth_salt=$3"
7015 : " WHERE merchant_id=$1",
7016 : 3),
7017 : /* for postgres_inactivate_account(); the merchant
7018 : instance is implied from the random salt that
7019 : is part of the h_wire calculation */
7020 4 : GNUNET_PQ_make_prepare ("inactivate_account",
7021 : "UPDATE merchant_accounts SET"
7022 : " active=FALSE"
7023 : " WHERE h_wire=$2 AND"
7024 : " merchant_serial="
7025 : " (SELECT merchant_serial"
7026 : " FROM merchant_instances"
7027 : " WHERE merchant_id=$1)",
7028 : 2),
7029 : /* for postgres_activate_account() */
7030 4 : GNUNET_PQ_make_prepare ("activate_account",
7031 : "UPDATE merchant_accounts SET"
7032 : " active=TRUE"
7033 : " WHERE h_wire=$2 AND"
7034 : " merchant_serial="
7035 : " (SELECT merchant_serial"
7036 : " FROM merchant_instances"
7037 : " WHERE merchant_id=$1)",
7038 : 2),
7039 : /* for postgres_lookup_products() */
7040 4 : GNUNET_PQ_make_prepare ("lookup_products",
7041 : "SELECT"
7042 : " product_id"
7043 : " FROM merchant_inventory"
7044 : " JOIN merchant_instances"
7045 : " USING (merchant_serial)"
7046 : " WHERE merchant_instances.merchant_id=$1",
7047 : 1),
7048 : /* for postgres_lookup_product() */
7049 4 : GNUNET_PQ_make_prepare ("lookup_product",
7050 : "SELECT"
7051 : " description"
7052 : ",description_i18n"
7053 : ",unit"
7054 : ",price_val"
7055 : ",price_frac"
7056 : ",taxes"
7057 : ",total_stock"
7058 : ",total_sold"
7059 : ",total_lost"
7060 : ",image"
7061 : ",merchant_inventory.address"
7062 : ",next_restock"
7063 : ",minimum_age"
7064 : " FROM merchant_inventory"
7065 : " JOIN merchant_instances"
7066 : " USING (merchant_serial)"
7067 : " WHERE merchant_instances.merchant_id=$1"
7068 : " AND merchant_inventory.product_id=$2",
7069 : 2),
7070 : /* for postgres_delete_product() */
7071 4 : GNUNET_PQ_make_prepare ("delete_product",
7072 : "DELETE"
7073 : " FROM merchant_inventory"
7074 : " WHERE merchant_inventory.merchant_serial="
7075 : " (SELECT merchant_serial "
7076 : " FROM merchant_instances"
7077 : " WHERE merchant_id=$1)"
7078 : " AND merchant_inventory.product_id=$2"
7079 : " AND product_serial NOT IN "
7080 : " (SELECT product_serial FROM merchant_order_locks)"
7081 : " AND product_serial NOT IN "
7082 : " (SELECT product_serial FROM merchant_inventory_locks)",
7083 : 2),
7084 : /* for postgres_insert_product() */
7085 4 : GNUNET_PQ_make_prepare ("insert_product",
7086 : "INSERT INTO merchant_inventory"
7087 : "(merchant_serial"
7088 : ",product_id"
7089 : ",description"
7090 : ",description_i18n"
7091 : ",unit"
7092 : ",image"
7093 : ",taxes"
7094 : ",price_val"
7095 : ",price_frac"
7096 : ",total_stock"
7097 : ",address"
7098 : ",next_restock"
7099 : ",minimum_age"
7100 : ")"
7101 : " SELECT merchant_serial,"
7102 : " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13"
7103 : " FROM merchant_instances"
7104 : " WHERE merchant_id=$1",
7105 : 13),
7106 : /* for postgres_update_product() */
7107 4 : GNUNET_PQ_make_prepare ("update_product",
7108 : "UPDATE merchant_inventory SET"
7109 : " description=$3"
7110 : ",description_i18n=$4"
7111 : ",unit=$5"
7112 : ",image=$6"
7113 : ",taxes=$7"
7114 : ",price_val=$8"
7115 : ",price_frac=$9"
7116 : ",total_stock=$10"
7117 : ",total_lost=$11"
7118 : ",address=$12"
7119 : ",next_restock=$13"
7120 : ",minimum_age=$14"
7121 : " WHERE merchant_serial="
7122 : " (SELECT merchant_serial"
7123 : " FROM merchant_instances"
7124 : " WHERE merchant_id=$1)"
7125 : " AND product_id=$2"
7126 : " AND total_stock <= $10"
7127 : " AND total_lost <= $11",
7128 : 14),
7129 :
7130 : /* for postgres_lock_product() */
7131 4 : GNUNET_PQ_make_prepare ("lock_product",
7132 : "WITH ps AS"
7133 : " (SELECT product_serial"
7134 : " FROM merchant_inventory"
7135 : " WHERE product_id=$2"
7136 : " AND merchant_serial="
7137 : " (SELECT merchant_serial"
7138 : " FROM merchant_instances"
7139 : " WHERE merchant_id=$1))"
7140 : "INSERT INTO merchant_inventory_locks"
7141 : "(product_serial"
7142 : ",lock_uuid"
7143 : ",total_locked"
7144 : ",expiration)"
7145 : " SELECT product_serial, $3, $4, $5"
7146 : " FROM merchant_inventory"
7147 : " JOIN ps USING (product_serial)"
7148 : " WHERE "
7149 : " total_stock - total_sold - total_lost - $4 >= "
7150 : " (SELECT COALESCE(SUM(total_locked), 0)"
7151 : " FROM merchant_inventory_locks"
7152 : " WHERE product_serial=ps.product_serial) + "
7153 : " (SELECT COALESCE(SUM(total_locked), 0)"
7154 : " FROM merchant_order_locks"
7155 : " WHERE product_serial=ps.product_serial)",
7156 : 5),
7157 :
7158 : /* for postgres_expire_locks() */
7159 4 : GNUNET_PQ_make_prepare ("unlock_products",
7160 : "DELETE FROM merchant_inventory_locks"
7161 : " WHERE expiration < $1",
7162 : 1),
7163 : /* for postgres_expire_locks() */
7164 4 : GNUNET_PQ_make_prepare ("unlock_orders",
7165 : "DELETE FROM merchant_orders"
7166 : " WHERE pay_deadline < $1",
7167 : 1),
7168 : /* for postgres_expire_locks() */
7169 4 : GNUNET_PQ_make_prepare ("unlock_contracts",
7170 : "DELETE FROM merchant_contract_terms"
7171 : " WHERE NOT paid"
7172 : " AND pay_deadline < $1",
7173 : 1),
7174 :
7175 : /* for postgres_delete_order() */
7176 4 : GNUNET_PQ_make_prepare ("delete_order",
7177 : "WITH ms AS"
7178 : "(SELECT merchant_serial "
7179 : " FROM merchant_instances"
7180 : " WHERE merchant_id=$1)"
7181 : ", mc AS"
7182 : "(SELECT paid"
7183 : " FROM merchant_contract_terms"
7184 : " JOIN ms USING (merchant_serial)"
7185 : " WHERE order_id=$2) "
7186 : "DELETE"
7187 : " FROM merchant_orders mo"
7188 : " WHERE order_id=$2"
7189 : " AND merchant_serial=(SELECT merchant_serial FROM ms)"
7190 : " AND ( (pay_deadline < $3)"
7191 : " OR (NOT EXISTS (SELECT paid FROM mc))"
7192 : " OR ($4 AND (FALSE=(SELECT paid FROM mc))) );",
7193 : 4),
7194 4 : GNUNET_PQ_make_prepare ("delete_contract",
7195 : "DELETE"
7196 : " FROM merchant_contract_terms"
7197 : " WHERE order_id=$2 AND"
7198 : " merchant_serial="
7199 : " (SELECT merchant_serial "
7200 : " FROM merchant_instances"
7201 : " WHERE merchant_id=$1)"
7202 : " AND NOT paid;",
7203 : 2),
7204 : /* for postgres_lookup_order() */
7205 4 : GNUNET_PQ_make_prepare ("lookup_order",
7206 : "SELECT"
7207 : " contract_terms"
7208 : ",claim_token"
7209 : ",h_post_data"
7210 : " FROM merchant_orders"
7211 : " WHERE merchant_orders.merchant_serial="
7212 : " (SELECT merchant_serial "
7213 : " FROM merchant_instances"
7214 : " WHERE merchant_id=$1)"
7215 : " AND merchant_orders.order_id=$2",
7216 : 2),
7217 : /* for postgres_lookup_order_summary() */
7218 4 : GNUNET_PQ_make_prepare ("lookup_order_summary",
7219 : "(SELECT"
7220 : " creation_time"
7221 : ",order_serial"
7222 : " FROM merchant_contract_terms"
7223 : " WHERE merchant_contract_terms.merchant_serial="
7224 : " (SELECT merchant_serial "
7225 : " FROM merchant_instances"
7226 : " WHERE merchant_id=$1)"
7227 : " AND merchant_contract_terms.order_id=$2)"
7228 : "UNION"
7229 : "(SELECT"
7230 : " creation_time"
7231 : ",order_serial"
7232 : " FROM merchant_orders"
7233 : " WHERE merchant_orders.merchant_serial="
7234 : " (SELECT merchant_serial "
7235 : " FROM merchant_instances"
7236 : " WHERE merchant_id=$1)"
7237 : " AND merchant_orders.order_id=$2)",
7238 : 2),
7239 : /* for postgres_lookup_orders() */
7240 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc",
7241 : "(SELECT"
7242 : " order_id"
7243 : ",order_serial"
7244 : ",creation_time"
7245 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7246 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7247 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7248 : " FROM merchant_orders"
7249 : " WHERE merchant_orders.merchant_serial="
7250 : " (SELECT merchant_serial "
7251 : " FROM merchant_instances"
7252 : " WHERE merchant_id=$1)"
7253 : " AND"
7254 : " order_serial > $3"
7255 : " AND"
7256 : " creation_time > $4"
7257 : " ORDER BY order_serial ASC"
7258 : " LIMIT $2)"
7259 : "UNION " /* union ensures elements are distinct! */
7260 : "(SELECT"
7261 : " order_id"
7262 : ",order_serial"
7263 : ",creation_time"
7264 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7265 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7266 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7267 : " FROM merchant_contract_terms"
7268 : " WHERE merchant_contract_terms.merchant_serial="
7269 : " (SELECT merchant_serial "
7270 : " FROM merchant_instances"
7271 : " WHERE merchant_id=$1)"
7272 : " AND"
7273 : " order_serial > $3"
7274 : " AND"
7275 : " creation_time > $4"
7276 : " ORDER BY order_serial ASC"
7277 : " LIMIT $2)"
7278 : " ORDER BY order_serial ASC"
7279 : " LIMIT $2",
7280 : 7),
7281 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid",
7282 : "(SELECT"
7283 : " order_id"
7284 : ",order_serial"
7285 : ",creation_time"
7286 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7287 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7288 : " FROM merchant_orders"
7289 : " WHERE merchant_orders.merchant_serial="
7290 : " (SELECT merchant_serial "
7291 : " FROM merchant_instances"
7292 : " WHERE merchant_id=$1)"
7293 : " AND"
7294 : " order_serial > $3"
7295 : " AND"
7296 : " creation_time > $4"
7297 : " AND"
7298 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7299 : " AND"
7300 : " order_serial NOT IN"
7301 : " (SELECT order_serial"
7302 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7303 : " ORDER BY order_serial ASC"
7304 : " LIMIT $2)"
7305 : "UNION " /* union ensures elements are distinct! */
7306 : "(SELECT"
7307 : " order_id"
7308 : ",order_serial"
7309 : ",creation_time"
7310 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7311 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7312 : " FROM merchant_contract_terms"
7313 : " WHERE merchant_contract_terms.merchant_serial="
7314 : " (SELECT merchant_serial "
7315 : " FROM merchant_instances"
7316 : " WHERE merchant_id=$1)"
7317 : " AND"
7318 : " order_serial > $3"
7319 : " AND"
7320 : " creation_time > $4"
7321 : " AND"
7322 : " BOOL($5) = paid"
7323 : " ORDER BY order_serial ASC"
7324 : " LIMIT $2)"
7325 : " ORDER BY order_serial ASC"
7326 : " LIMIT $2",
7327 : 7),
7328 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded",
7329 : "(SELECT"
7330 : " order_id"
7331 : ",order_serial"
7332 : ",creation_time"
7333 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7334 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7335 : " FROM merchant_orders"
7336 : " WHERE merchant_orders.merchant_serial="
7337 : " (SELECT merchant_serial "
7338 : " FROM merchant_instances"
7339 : " WHERE merchant_id=$1)"
7340 : " AND"
7341 : " order_serial > $3"
7342 : " AND"
7343 : " creation_time > $4"
7344 : " AND"
7345 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7346 : " AND"
7347 : " order_serial NOT IN"
7348 : " (SELECT order_serial"
7349 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7350 : " ORDER BY order_serial ASC"
7351 : " LIMIT $2)"
7352 : "UNION " /* union ensures elements are distinct! */
7353 : "(SELECT"
7354 : " order_id"
7355 : ",order_serial"
7356 : ",creation_time"
7357 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7358 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7359 : " FROM merchant_contract_terms"
7360 : " WHERE merchant_contract_terms.merchant_serial="
7361 : " (SELECT merchant_serial "
7362 : " FROM merchant_instances"
7363 : " WHERE merchant_id=$1)"
7364 : " AND"
7365 : " order_serial > $3"
7366 : " AND"
7367 : " creation_time > $4"
7368 : " AND"
7369 : " CAST($6 as BOOL) = (order_serial IN"
7370 : " (SELECT order_serial "
7371 : " FROM merchant_refunds))"
7372 : " ORDER BY order_serial ASC"
7373 : " LIMIT $2)"
7374 : " ORDER BY order_serial ASC"
7375 : " LIMIT $2",
7376 : 7),
7377 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_wired",
7378 : "(SELECT"
7379 : " order_id"
7380 : ",order_serial"
7381 : ",creation_time"
7382 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7383 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7384 : " FROM merchant_orders"
7385 : " WHERE merchant_orders.merchant_serial="
7386 : " (SELECT merchant_serial "
7387 : " FROM merchant_instances"
7388 : " WHERE merchant_id=$1)"
7389 : " AND"
7390 : " order_serial > $3"
7391 : " AND"
7392 : " creation_time > $4"
7393 : " AND"
7394 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7395 : " AND"
7396 : " order_serial NOT IN"
7397 : " (SELECT order_serial"
7398 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7399 : " ORDER BY order_serial ASC"
7400 : " LIMIT $2)"
7401 : "UNION " /* union ensures elements are distinct! */
7402 : "(SELECT"
7403 : " order_id"
7404 : ",order_serial"
7405 : ",creation_time"
7406 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7407 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7408 : " FROM merchant_contract_terms"
7409 : " WHERE merchant_contract_terms.merchant_serial="
7410 : " (SELECT merchant_serial "
7411 : " FROM merchant_instances"
7412 : " WHERE merchant_id=$1)"
7413 : " AND"
7414 : " order_serial > $3"
7415 : " AND"
7416 : " creation_time > $4"
7417 : " AND"
7418 : " BOOL($7) = wired"
7419 : " ORDER BY order_serial ASC"
7420 : " LIMIT $2)"
7421 : " ORDER BY order_serial ASC"
7422 : " LIMIT $2",
7423 : 7),
7424 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded",
7425 : "(SELECT"
7426 : " order_id"
7427 : ",order_serial"
7428 : ",creation_time"
7429 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7430 : " FROM merchant_orders"
7431 : " WHERE merchant_orders.merchant_serial="
7432 : " (SELECT merchant_serial "
7433 : " FROM merchant_instances"
7434 : " WHERE merchant_id=$1)"
7435 : " AND"
7436 : " order_serial > $3"
7437 : " AND"
7438 : " creation_time > $4"
7439 : " AND"
7440 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7441 : " AND"
7442 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7443 : " AND"
7444 : " order_serial NOT IN"
7445 : " (SELECT order_serial"
7446 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7447 : " ORDER BY order_serial ASC"
7448 : " LIMIT $2)"
7449 : "UNION " /* union ensures elements are distinct! */
7450 : "(SELECT"
7451 : " order_id"
7452 : ",order_serial"
7453 : ",creation_time"
7454 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7455 : " FROM merchant_contract_terms"
7456 : " WHERE merchant_contract_terms.merchant_serial="
7457 : " (SELECT merchant_serial "
7458 : " FROM merchant_instances"
7459 : " WHERE merchant_id=$1)"
7460 : " AND"
7461 : " order_serial > $3"
7462 : " AND"
7463 : " creation_time > $4"
7464 : " AND"
7465 : " BOOL($5) = paid"
7466 : " AND"
7467 : " BOOL($6) = (order_serial IN"
7468 : " (SELECT order_serial "
7469 : " FROM merchant_refunds))"
7470 : " ORDER BY order_serial ASC"
7471 : " LIMIT $2)"
7472 : " ORDER BY order_serial ASC"
7473 : " LIMIT $2",
7474 : 7),
7475 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_wired",
7476 : "(SELECT"
7477 : " order_id"
7478 : ",order_serial"
7479 : ",creation_time"
7480 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7481 : " FROM merchant_orders"
7482 : " WHERE merchant_orders.merchant_serial="
7483 : " (SELECT merchant_serial "
7484 : " FROM merchant_instances"
7485 : " WHERE merchant_id=$1)"
7486 : " AND"
7487 : " order_serial > $3"
7488 : " AND"
7489 : " creation_time > $4"
7490 : " AND"
7491 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7492 : " AND"
7493 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7494 : " AND"
7495 : " order_serial NOT IN"
7496 : " (SELECT order_serial"
7497 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7498 : " ORDER BY order_serial ASC"
7499 : " LIMIT $2)"
7500 : "UNION " /* union ensures elements are distinct! */
7501 : "(SELECT"
7502 : " order_id"
7503 : ",order_serial"
7504 : ",creation_time"
7505 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7506 : " FROM merchant_contract_terms"
7507 : " WHERE merchant_contract_terms.merchant_serial="
7508 : " (SELECT merchant_serial "
7509 : " FROM merchant_instances"
7510 : " WHERE merchant_id=$1)"
7511 : " AND"
7512 : " order_serial > $3"
7513 : " AND"
7514 : " creation_time > $4"
7515 : " AND"
7516 : " BOOL($5) = paid"
7517 : " AND"
7518 : " BOOL($7) = wired"
7519 : " ORDER BY order_serial ASC"
7520 : " LIMIT $2)"
7521 : " ORDER BY order_serial ASC"
7522 : " LIMIT $2",
7523 : 7),
7524 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_refunded_wired",
7525 : "(SELECT"
7526 : " order_id"
7527 : ",order_serial"
7528 : ",creation_time"
7529 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7530 : " FROM merchant_orders"
7531 : " WHERE merchant_orders.merchant_serial="
7532 : " (SELECT merchant_serial "
7533 : " FROM merchant_instances"
7534 : " WHERE merchant_id=$1)"
7535 : " AND"
7536 : " order_serial > $3"
7537 : " AND"
7538 : " creation_time > $4"
7539 : " AND"
7540 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7541 : " AND"
7542 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7543 : " AND"
7544 : " order_serial NOT IN"
7545 : " (SELECT order_serial"
7546 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7547 : " ORDER BY order_serial ASC"
7548 : " LIMIT $2)"
7549 : "UNION " /* union ensures elements are distinct! */
7550 : "(SELECT"
7551 : " order_id"
7552 : ",order_serial"
7553 : ",creation_time"
7554 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7555 : " FROM merchant_contract_terms"
7556 : " WHERE merchant_contract_terms.merchant_serial="
7557 : " (SELECT merchant_serial "
7558 : " FROM merchant_instances"
7559 : " WHERE merchant_id=$1)"
7560 : " AND"
7561 : " order_serial > $3"
7562 : " AND"
7563 : " creation_time > $4"
7564 : " AND"
7565 : " BOOL($6) = (order_serial IN"
7566 : " (SELECT order_serial "
7567 : " FROM merchant_refunds))"
7568 : " AND"
7569 : " BOOL($7) = wired"
7570 : " ORDER BY order_serial ASC"
7571 : " LIMIT $2)"
7572 : " ORDER BY order_serial ASC"
7573 : " LIMIT $2",
7574 : 7),
7575 4 : GNUNET_PQ_make_prepare ("lookup_orders_inc_paid_refunded_wired",
7576 : "(SELECT"
7577 : " order_id"
7578 : ",order_serial"
7579 : ",creation_time"
7580 : " FROM merchant_orders"
7581 : " WHERE merchant_orders.merchant_serial="
7582 : " (SELECT merchant_serial "
7583 : " FROM merchant_instances"
7584 : " WHERE merchant_id=$1)"
7585 : " AND"
7586 : " order_serial > $3"
7587 : " AND"
7588 : " creation_time > $4"
7589 : " AND"
7590 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7591 : " AND"
7592 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7593 : " AND"
7594 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7595 : " AND"
7596 : " order_serial NOT IN"
7597 : " (SELECT order_serial"
7598 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7599 : " ORDER BY order_serial ASC"
7600 : " LIMIT $2)"
7601 : "UNION " /* union ensures elements are distinct! */
7602 : "(SELECT"
7603 : " order_id"
7604 : ",order_serial"
7605 : ",creation_time"
7606 : " FROM merchant_contract_terms"
7607 : " WHERE merchant_contract_terms.merchant_serial="
7608 : " (SELECT merchant_serial "
7609 : " FROM merchant_instances"
7610 : " WHERE merchant_id=$1)"
7611 : " AND"
7612 : " order_serial > $3"
7613 : " AND"
7614 : " creation_time > $4"
7615 : " AND"
7616 : " BOOL($5) = paid"
7617 : " AND"
7618 : " BOOL($6) = (order_serial IN"
7619 : " (SELECT order_serial "
7620 : " FROM merchant_refunds))"
7621 : " AND"
7622 : " BOOL($7) = wired"
7623 : " ORDER BY order_serial ASC"
7624 : " LIMIT $2)"
7625 : " ORDER BY order_serial ASC"
7626 : " LIMIT $2",
7627 : 7),
7628 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec",
7629 : "(SELECT"
7630 : " order_id"
7631 : ",order_serial"
7632 : ",creation_time"
7633 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7634 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7635 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7636 : " FROM merchant_orders"
7637 : " WHERE merchant_orders.merchant_serial="
7638 : " (SELECT merchant_serial "
7639 : " FROM merchant_instances"
7640 : " WHERE merchant_id=$1)"
7641 : " AND"
7642 : " order_serial < $3"
7643 : " AND"
7644 : " creation_time < $4"
7645 : " ORDER BY order_serial DESC"
7646 : " LIMIT $2)"
7647 : "UNION " /* union ensures elements are distinct! */
7648 : "(SELECT"
7649 : " order_id"
7650 : ",order_serial"
7651 : ",creation_time"
7652 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7653 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7654 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7655 : " FROM merchant_contract_terms"
7656 : " WHERE merchant_contract_terms.merchant_serial="
7657 : " (SELECT merchant_serial "
7658 : " FROM merchant_instances"
7659 : " WHERE merchant_id=$1)"
7660 : " AND"
7661 : " order_serial < $3"
7662 : " AND"
7663 : " creation_time < $4"
7664 : " ORDER BY order_serial DESC"
7665 : " LIMIT $2)"
7666 : " ORDER BY order_serial DESC"
7667 : " LIMIT $2",
7668 : 7),
7669 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid",
7670 : "(SELECT"
7671 : " order_id"
7672 : ",order_serial"
7673 : ",creation_time"
7674 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7675 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7676 : " FROM merchant_orders"
7677 : " WHERE merchant_orders.merchant_serial="
7678 : " (SELECT merchant_serial "
7679 : " FROM merchant_instances"
7680 : " WHERE merchant_id=$1)"
7681 : " AND"
7682 : " order_serial < $3"
7683 : " AND"
7684 : " creation_time < $4"
7685 : " AND"
7686 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7687 : " AND"
7688 : " order_serial NOT IN"
7689 : " (SELECT order_serial"
7690 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7691 : " ORDER BY order_serial DESC"
7692 : " LIMIT $2)"
7693 : "UNION " /* union ensures elements are distinct! */
7694 : "(SELECT"
7695 : " order_id"
7696 : ",order_serial"
7697 : ",creation_time"
7698 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7699 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7700 : " FROM merchant_contract_terms"
7701 : " WHERE merchant_contract_terms.merchant_serial="
7702 : " (SELECT merchant_serial "
7703 : " FROM merchant_instances"
7704 : " WHERE merchant_id=$1)"
7705 : " AND"
7706 : " order_serial < $3"
7707 : " AND"
7708 : " creation_time < $4"
7709 : " AND"
7710 : " BOOL($5) = paid"
7711 : " ORDER BY order_serial DESC"
7712 : " LIMIT $2)"
7713 : " ORDER BY order_serial DESC"
7714 : " LIMIT $2",
7715 : 7),
7716 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded",
7717 : "(SELECT"
7718 : " order_id"
7719 : ",order_serial"
7720 : ",creation_time"
7721 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7722 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7723 : " FROM merchant_orders"
7724 : " WHERE merchant_orders.merchant_serial="
7725 : " (SELECT merchant_serial "
7726 : " FROM merchant_instances"
7727 : " WHERE merchant_id=$1)"
7728 : " AND"
7729 : " order_serial < $3"
7730 : " AND"
7731 : " creation_time < $4"
7732 : " AND"
7733 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7734 : " AND"
7735 : " order_serial NOT IN"
7736 : " (SELECT order_serial"
7737 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7738 : " ORDER BY order_serial DESC"
7739 : " LIMIT $2)"
7740 : "UNION " /* union ensures elements are distinct! */
7741 : "(SELECT"
7742 : " order_id"
7743 : ",order_serial"
7744 : ",creation_time"
7745 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7746 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7747 : " FROM merchant_contract_terms"
7748 : " WHERE merchant_contract_terms.merchant_serial="
7749 : " (SELECT merchant_serial "
7750 : " FROM merchant_instances"
7751 : " WHERE merchant_id=$1)"
7752 : " AND"
7753 : " order_serial < $3"
7754 : " AND"
7755 : " creation_time < $4"
7756 : " AND"
7757 : " BOOL($6) = (order_serial IN"
7758 : " (SELECT order_serial "
7759 : " FROM merchant_refunds))"
7760 : " ORDER BY order_serial DESC"
7761 : " LIMIT $2)"
7762 : " ORDER BY order_serial DESC"
7763 : " LIMIT $2",
7764 : 7),
7765 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_wired",
7766 : "(SELECT"
7767 : " order_id"
7768 : ",order_serial"
7769 : ",creation_time"
7770 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7771 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7772 : " FROM merchant_orders"
7773 : " WHERE merchant_orders.merchant_serial="
7774 : " (SELECT merchant_serial "
7775 : " FROM merchant_instances"
7776 : " WHERE merchant_id=$1)"
7777 : " AND"
7778 : " order_serial < $3"
7779 : " AND"
7780 : " creation_time < $4"
7781 : " AND"
7782 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7783 : " AND"
7784 : " order_serial NOT IN"
7785 : " (SELECT order_serial"
7786 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7787 : " ORDER BY order_serial DESC"
7788 : " LIMIT $2)"
7789 : "UNION " /* union ensures elements are distinct! */
7790 : "(SELECT"
7791 : " order_id"
7792 : ",order_serial"
7793 : ",creation_time"
7794 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7795 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7796 : " FROM merchant_contract_terms"
7797 : " WHERE merchant_contract_terms.merchant_serial="
7798 : " (SELECT merchant_serial "
7799 : " FROM merchant_instances"
7800 : " WHERE merchant_id=$1)"
7801 : " AND"
7802 : " order_serial < $3"
7803 : " AND"
7804 : " creation_time < $4"
7805 : " AND"
7806 : " BOOL($7) = wired"
7807 : " ORDER BY order_serial DESC"
7808 : " LIMIT $2)"
7809 : " ORDER BY order_serial DESC"
7810 : " LIMIT $2",
7811 : 7),
7812 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded",
7813 : "(SELECT"
7814 : " order_id"
7815 : ",order_serial"
7816 : ",creation_time"
7817 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7818 : " FROM merchant_orders"
7819 : " WHERE merchant_orders.merchant_serial="
7820 : " (SELECT merchant_serial "
7821 : " FROM merchant_instances"
7822 : " WHERE merchant_id=$1)"
7823 : " AND"
7824 : " order_serial < $3"
7825 : " AND"
7826 : " creation_time < $4"
7827 : " AND"
7828 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7829 : " AND"
7830 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7831 : " AND"
7832 : " order_serial NOT IN"
7833 : " (SELECT order_serial"
7834 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7835 : " ORDER BY order_serial DESC"
7836 : " LIMIT $2)"
7837 : "UNION " /* union ensures elements are distinct! */
7838 : "(SELECT"
7839 : " order_id"
7840 : ",order_serial"
7841 : ",creation_time"
7842 : ",CAST($7 as BOOL)" /* otherwise $7 is unused and Postgres unhappy */
7843 : " FROM merchant_contract_terms"
7844 : " WHERE merchant_contract_terms.merchant_serial="
7845 : " (SELECT merchant_serial "
7846 : " FROM merchant_instances"
7847 : " WHERE merchant_id=$1)"
7848 : " AND"
7849 : " order_serial < $3"
7850 : " AND"
7851 : " creation_time < $4"
7852 : " AND"
7853 : " BOOL($5) = paid"
7854 : " AND"
7855 : " BOOL($6) = (order_serial IN"
7856 : " (SELECT order_serial "
7857 : " FROM merchant_refunds))"
7858 : " ORDER BY order_serial DESC"
7859 : " LIMIT $2)"
7860 : " ORDER BY order_serial DESC"
7861 : " LIMIT $2",
7862 : 7),
7863 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_wired",
7864 : "(SELECT"
7865 : " order_id"
7866 : ",order_serial"
7867 : ",creation_time"
7868 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7869 : " FROM merchant_orders"
7870 : " WHERE merchant_orders.merchant_serial="
7871 : " (SELECT merchant_serial "
7872 : " FROM merchant_instances"
7873 : " WHERE merchant_id=$1)"
7874 : " AND"
7875 : " order_serial < $3"
7876 : " AND"
7877 : " creation_time < $4"
7878 : " AND"
7879 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7880 : " AND"
7881 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7882 : " AND"
7883 : " order_serial NOT IN"
7884 : " (SELECT order_serial"
7885 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7886 : " ORDER BY order_serial DESC"
7887 : " LIMIT $2)"
7888 : "UNION " /* union ensures elements are distinct! */
7889 : "(SELECT"
7890 : " order_id"
7891 : ",order_serial"
7892 : ",creation_time"
7893 : ",CAST($6 as BOOL)" /* otherwise $6 is unused and Postgres unhappy */
7894 : " FROM merchant_contract_terms"
7895 : " WHERE merchant_contract_terms.merchant_serial="
7896 : " (SELECT merchant_serial "
7897 : " FROM merchant_instances"
7898 : " WHERE merchant_id=$1)"
7899 : " AND"
7900 : " order_serial < $3"
7901 : " AND"
7902 : " creation_time < $4"
7903 : " AND"
7904 : " BOOL($5) = paid"
7905 : " AND"
7906 : " BOOL($7) = wired"
7907 : " ORDER BY order_serial DESC"
7908 : " LIMIT $2)"
7909 : " ORDER BY order_serial DESC"
7910 : " LIMIT $2",
7911 : 7),
7912 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_refunded_wired",
7913 : "(SELECT"
7914 : " order_id"
7915 : ",order_serial"
7916 : ",creation_time"
7917 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7918 : " FROM merchant_orders"
7919 : " WHERE merchant_orders.merchant_serial="
7920 : " (SELECT merchant_serial "
7921 : " FROM merchant_instances"
7922 : " WHERE merchant_id=$1)"
7923 : " AND"
7924 : " order_serial < $3"
7925 : " AND"
7926 : " creation_time < $4"
7927 : " AND"
7928 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7929 : " AND"
7930 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7931 : " AND"
7932 : " order_serial NOT IN"
7933 : " (SELECT order_serial"
7934 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7935 : " ORDER BY order_serial DESC"
7936 : " LIMIT $2)"
7937 : "UNION " /* union ensures elements are distinct! */
7938 : "(SELECT"
7939 : " order_id"
7940 : ",order_serial"
7941 : ",creation_time"
7942 : ",CAST($5 as BOOL)" /* otherwise $5 is unused and Postgres unhappy */
7943 : " FROM merchant_contract_terms"
7944 : " WHERE merchant_contract_terms.merchant_serial="
7945 : " (SELECT merchant_serial "
7946 : " FROM merchant_instances"
7947 : " WHERE merchant_id=$1)"
7948 : " AND"
7949 : " order_serial < $3"
7950 : " AND"
7951 : " creation_time < $4"
7952 : " AND"
7953 : " BOOL($6) = (order_serial IN"
7954 : " (SELECT order_serial "
7955 : " FROM merchant_refunds))"
7956 : " AND"
7957 : " BOOL($7) = wired"
7958 : " ORDER BY order_serial DESC"
7959 : " LIMIT $2)"
7960 : " ORDER BY order_serial DESC"
7961 : " LIMIT $2",
7962 : 7),
7963 4 : GNUNET_PQ_make_prepare ("lookup_orders_dec_paid_refunded_wired",
7964 : "(SELECT"
7965 : " order_id"
7966 : ",order_serial"
7967 : ",creation_time"
7968 : " FROM merchant_orders"
7969 : " WHERE merchant_orders.merchant_serial="
7970 : " (SELECT merchant_serial "
7971 : " FROM merchant_instances"
7972 : " WHERE merchant_id=$1)"
7973 : " AND"
7974 : " order_serial < $3"
7975 : " AND"
7976 : " creation_time < $4"
7977 : " AND"
7978 : " NOT CAST($5 as BOOL)" /* unclaimed orders are never paid */
7979 : " AND"
7980 : " NOT CAST($6 as BOOL)"/* unclaimed orders are never refunded */
7981 : " AND"
7982 : " NOT CAST($7 as BOOL)" /* unclaimed orders are never wired */
7983 : " AND"
7984 : " order_serial NOT IN"
7985 : " (SELECT order_serial"
7986 : " FROM merchant_contract_terms)" /* only select unclaimed orders */
7987 : " ORDER BY order_serial DESC"
7988 : " LIMIT $2)"
7989 : "UNION " /* union ensures elements are distinct! */
7990 : "(SELECT"
7991 : " order_id"
7992 : ",order_serial"
7993 : ",creation_time"
7994 : " FROM merchant_contract_terms"
7995 : " WHERE merchant_contract_terms.merchant_serial="
7996 : " (SELECT merchant_serial "
7997 : " FROM merchant_instances"
7998 : " WHERE merchant_id=$1)"
7999 : " AND"
8000 : " order_serial < $3"
8001 : " AND"
8002 : " creation_time < $4"
8003 : " AND"
8004 : " BOOL($5) = paid"
8005 : " AND"
8006 : " BOOL($6) = (order_serial IN"
8007 : " (SELECT order_serial "
8008 : " FROM merchant_refunds))"
8009 : " AND"
8010 : " BOOL($7) = wired"
8011 : " ORDER BY order_serial DESC"
8012 : " LIMIT $2)"
8013 : " ORDER BY order_serial DESC"
8014 : " LIMIT $2",
8015 : 7),
8016 : /* for postgres_insert_order() */
8017 4 : GNUNET_PQ_make_prepare ("insert_order",
8018 : "INSERT INTO merchant_orders"
8019 : "(merchant_serial"
8020 : ",order_id"
8021 : ",pay_deadline"
8022 : ",claim_token"
8023 : ",h_post_data"
8024 : ",creation_time"
8025 : ",contract_terms)"
8026 : " SELECT merchant_serial,"
8027 : " $2, $3, $4, $5, $6, $7"
8028 : " FROM merchant_instances"
8029 : " WHERE merchant_id=$1",
8030 : 7),
8031 : /* for postgres_unlock_inventory() */
8032 4 : GNUNET_PQ_make_prepare ("unlock_inventory",
8033 : "DELETE"
8034 : " FROM merchant_inventory_locks"
8035 : " WHERE lock_uuid=$1",
8036 : 1),
8037 : /* for postgres_insert_order_lock() */
8038 4 : GNUNET_PQ_make_prepare ("insert_order_lock",
8039 : "WITH tmp AS"
8040 : " (SELECT "
8041 : " product_serial"
8042 : " ,merchant_serial"
8043 : " ,total_stock"
8044 : " ,total_sold"
8045 : " ,total_lost"
8046 : " FROM merchant_inventory"
8047 : " WHERE product_id=$3"
8048 : " AND merchant_serial="
8049 : " (SELECT merchant_serial"
8050 : " FROM merchant_instances"
8051 : " WHERE merchant_id=$1))"
8052 : " INSERT INTO merchant_order_locks"
8053 : " (product_serial"
8054 : " ,total_locked"
8055 : " ,order_serial)"
8056 : " SELECT tmp.product_serial, $4, order_serial"
8057 : " FROM merchant_orders"
8058 : " JOIN tmp USING(merchant_serial)"
8059 : " WHERE order_id=$2 AND"
8060 : " tmp.total_stock - tmp.total_sold - tmp.total_lost - $4 >= "
8061 : " (SELECT COALESCE(SUM(total_locked), 0)"
8062 : " FROM merchant_inventory_locks"
8063 : " WHERE product_serial=tmp.product_serial) + "
8064 : " (SELECT COALESCE(SUM(total_locked), 0)"
8065 : " FROM merchant_order_locks"
8066 : " WHERE product_serial=tmp.product_serial)",
8067 : 4),
8068 : /* for postgres_lookup_contract_terms() */
8069 4 : GNUNET_PQ_make_prepare ("lookup_contract_terms",
8070 : "SELECT"
8071 : " contract_terms"
8072 : ",order_serial"
8073 : ",claim_token"
8074 : ",paid"
8075 : " FROM merchant_contract_terms"
8076 : " WHERE order_id=$2"
8077 : " AND merchant_serial="
8078 : " (SELECT merchant_serial"
8079 : " FROM merchant_instances"
8080 : " WHERE merchant_id=$1)",
8081 : 2),
8082 : /* for postgres_insert_contract_terms() */
8083 4 : GNUNET_PQ_make_prepare ("insert_contract_terms",
8084 : "INSERT INTO merchant_contract_terms"
8085 : "(order_serial"
8086 : ",merchant_serial"
8087 : ",order_id"
8088 : ",contract_terms"
8089 : ",h_contract_terms"
8090 : ",creation_time"
8091 : ",pay_deadline"
8092 : ",refund_deadline"
8093 : ",fulfillment_url"
8094 : ",claim_token)"
8095 : "SELECT"
8096 : " mo.order_serial"
8097 : ",mo.merchant_serial"
8098 : ",mo.order_id"
8099 : ",$3" /* contract_terms */
8100 : ",$4" /* h_contract_terms */
8101 : ",mo.creation_time"
8102 : ",$5" /* pay_deadline */
8103 : ",$6" /* refund_deadline */
8104 : ",$7" /* fulfillment_url */
8105 : ",mo.claim_token "
8106 : "FROM merchant_orders mo"
8107 : " WHERE order_id=$2"
8108 : " AND merchant_serial="
8109 : " (SELECT merchant_serial"
8110 : " FROM merchant_instances"
8111 : " WHERE merchant_id=$1)",
8112 : 7),
8113 : /* for postgres_update_contract_terms() */
8114 4 : GNUNET_PQ_make_prepare ("update_contract_terms",
8115 : "UPDATE merchant_contract_terms SET"
8116 : " contract_terms=$3"
8117 : ",h_contract_terms=$4"
8118 : ",pay_deadline=$5"
8119 : ",refund_deadline=$6"
8120 : ",fulfillment_url=$7"
8121 : " WHERE order_id=$2"
8122 : " AND merchant_serial="
8123 : " (SELECT merchant_serial"
8124 : " FROM merchant_instances"
8125 : " WHERE merchant_id=$1)",
8126 : 7),
8127 : /* for postgres_delete_contract_terms() */
8128 4 : GNUNET_PQ_make_prepare ("delete_contract_terms",
8129 : "DELETE FROM merchant_contract_terms"
8130 : " WHERE order_id=$2"
8131 : " AND merchant_serial="
8132 : " (SELECT merchant_serial"
8133 : " FROM merchant_instances"
8134 : " WHERE merchant_id=$1)"
8135 : " AND ( ( (pay_deadline < $4) AND"
8136 : " (NOT paid) ) OR"
8137 : " (creation_time + $3 < $4) )",
8138 : 4),
8139 : /* for postgres_lookup_deposits() */
8140 4 : GNUNET_PQ_make_prepare ("lookup_deposits",
8141 : "SELECT"
8142 : " exchange_url"
8143 : ",coin_pub"
8144 : ",amount_with_fee_val"
8145 : ",amount_with_fee_frac"
8146 : ",deposit_fee_val"
8147 : ",deposit_fee_frac"
8148 : ",refund_fee_val"
8149 : ",refund_fee_frac"
8150 : ",wire_fee_val"
8151 : ",wire_fee_frac"
8152 : " FROM merchant_deposits"
8153 : " WHERE order_serial="
8154 : " (SELECT order_serial"
8155 : " FROM merchant_contract_terms"
8156 : " WHERE h_contract_terms=$2"
8157 : " AND merchant_serial="
8158 : " (SELECT merchant_serial"
8159 : " FROM merchant_instances"
8160 : " WHERE merchant_id=$1))",
8161 : 2),
8162 : /* for postgres_insert_exchange_signkey() */
8163 4 : GNUNET_PQ_make_prepare ("insert_exchange_signkey",
8164 : "INSERT INTO merchant_exchange_signing_keys"
8165 : "(master_pub"
8166 : ",exchange_pub"
8167 : ",start_date"
8168 : ",expire_date"
8169 : ",end_date"
8170 : ",master_sig)"
8171 : "VALUES"
8172 : "($1, $2, $3, $4, $5, $6)",
8173 : 6),
8174 : /* for postgres_insert_deposit() */
8175 4 : GNUNET_PQ_make_prepare ("insert_deposit",
8176 : "WITH md AS"
8177 : " (SELECT account_serial, merchant_serial"
8178 : " FROM merchant_accounts"
8179 : " WHERE h_wire=$14"
8180 : " AND merchant_serial="
8181 : " (SELECT merchant_serial"
8182 : " FROM merchant_instances"
8183 : " WHERE merchant_id=$1))"
8184 : ", ed AS"
8185 : " (SELECT signkey_serial"
8186 : " FROM merchant_exchange_signing_keys"
8187 : " WHERE exchange_pub=$16"
8188 : " ORDER BY start_date DESC"
8189 : " LIMIT 1)"
8190 : "INSERT INTO merchant_deposits"
8191 : "(order_serial"
8192 : ",deposit_timestamp"
8193 : ",coin_pub"
8194 : ",exchange_url"
8195 : ",amount_with_fee_val"
8196 : ",amount_with_fee_frac"
8197 : ",deposit_fee_val"
8198 : ",deposit_fee_frac"
8199 : ",refund_fee_val"
8200 : ",refund_fee_frac"
8201 : ",wire_fee_val"
8202 : ",wire_fee_frac"
8203 : ",exchange_sig"
8204 : ",signkey_serial"
8205 : ",account_serial)"
8206 : " SELECT "
8207 : " order_serial"
8208 : " ,$3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $15"
8209 : " ,ed.signkey_serial"
8210 : " ,md.account_serial"
8211 : " FROM merchant_contract_terms"
8212 : " JOIN md USING (merchant_serial)"
8213 : " FULL OUTER JOIN ed ON TRUE"
8214 : " WHERE h_contract_terms=$2",
8215 : 16),
8216 : /* for postgres_lookup_refunds() */
8217 4 : GNUNET_PQ_make_prepare ("lookup_refunds",
8218 : "SELECT"
8219 : " coin_pub"
8220 : ",refund_amount_val"
8221 : ",refund_amount_frac"
8222 : " FROM merchant_refunds"
8223 : " WHERE order_serial="
8224 : " (SELECT order_serial"
8225 : " FROM merchant_contract_terms"
8226 : " WHERE h_contract_terms=$2"
8227 : " AND merchant_serial="
8228 : " (SELECT merchant_serial"
8229 : " FROM merchant_instances"
8230 : " WHERE merchant_id=$1))",
8231 : 2),
8232 : /* for postgres_mark_contract_paid() */
8233 4 : GNUNET_PQ_make_prepare ("mark_contract_paid",
8234 : "UPDATE merchant_contract_terms SET"
8235 : " paid=TRUE"
8236 : ",session_id=$3"
8237 : " WHERE h_contract_terms=$2"
8238 : " AND merchant_serial="
8239 : " (SELECT merchant_serial"
8240 : " FROM merchant_instances"
8241 : " WHERE merchant_id=$1)",
8242 : 3),
8243 : /* for postgres_mark_contract_paid() */
8244 4 : GNUNET_PQ_make_prepare ("mark_inventory_sold",
8245 : "UPDATE merchant_inventory SET"
8246 : " total_sold=total_sold + order_locks.total_locked"
8247 : " FROM (SELECT total_locked,product_serial"
8248 : " FROM merchant_order_locks"
8249 : " WHERE order_serial="
8250 : " (SELECT order_serial"
8251 : " FROM merchant_contract_terms"
8252 : " WHERE h_contract_terms=$2"
8253 : " AND merchant_serial="
8254 : " (SELECT merchant_serial"
8255 : " FROM merchant_instances"
8256 : " WHERE merchant_id=$1))"
8257 : " ) AS order_locks"
8258 : " WHERE merchant_inventory.product_serial"
8259 : " =order_locks.product_serial",
8260 : 2),
8261 : /* for postgres_mark_contract_paid() */
8262 4 : GNUNET_PQ_make_prepare ("delete_completed_order",
8263 : "WITH md AS"
8264 : " (SELECT merchant_serial"
8265 : " FROM merchant_instances"
8266 : " WHERE merchant_id=$1) "
8267 : "DELETE"
8268 : " FROM merchant_orders"
8269 : " WHERE order_serial="
8270 : " (SELECT order_serial"
8271 : " FROM merchant_contract_terms"
8272 : " JOIN md USING (merchant_serial)"
8273 : " WHERE h_contract_terms=$2)",
8274 : 2),
8275 : /* for postgres_refund_coin() */
8276 4 : GNUNET_PQ_make_prepare ("refund_coin",
8277 : "INSERT INTO merchant_refunds"
8278 : "(order_serial"
8279 : ",rtransaction_id"
8280 : ",refund_timestamp"
8281 : ",coin_pub"
8282 : ",reason"
8283 : ",refund_amount_val"
8284 : ",refund_amount_frac"
8285 : ") "
8286 : "SELECT "
8287 : " order_serial"
8288 : ",0" /* rtransaction_id always 0 for /abort */
8289 : ",$3"
8290 : ",coin_pub"
8291 : ",$5"
8292 : ",amount_with_fee_val"
8293 : ",amount_with_fee_frac"
8294 : " FROM merchant_deposits"
8295 : " WHERE coin_pub=$4"
8296 : " AND order_serial="
8297 : " (SELECT order_serial"
8298 : " FROM merchant_contract_terms"
8299 : " WHERE h_contract_terms=$2"
8300 : " AND merchant_serial="
8301 : " (SELECT merchant_serial"
8302 : " FROM merchant_instances"
8303 : " WHERE merchant_id=$1))",
8304 : 5),
8305 :
8306 : /* for postgres_lookup_order_status() */
8307 4 : GNUNET_PQ_make_prepare ("lookup_order_status",
8308 : "SELECT"
8309 : " h_contract_terms"
8310 : ",paid"
8311 : " FROM merchant_contract_terms"
8312 : " WHERE merchant_serial="
8313 : " (SELECT merchant_serial "
8314 : " FROM merchant_instances"
8315 : " WHERE merchant_id=$1)"
8316 : " AND order_id=$2",
8317 : 2),
8318 :
8319 : /* for postgres_lookup_order_status_by_serial() */
8320 4 : GNUNET_PQ_make_prepare ("lookup_order_status_by_serial",
8321 : "SELECT"
8322 : " h_contract_terms"
8323 : ",order_id"
8324 : ",paid"
8325 : " FROM merchant_contract_terms"
8326 : " WHERE merchant_serial="
8327 : " (SELECT merchant_serial "
8328 : " FROM merchant_instances"
8329 : " WHERE merchant_id=$1)"
8330 : " AND order_serial=$2",
8331 : 2),
8332 :
8333 : /* for postgres_lookup_payment_status() */
8334 4 : GNUNET_PQ_make_prepare ("lookup_payment_status",
8335 : "SELECT"
8336 : " wired"
8337 : ",paid"
8338 : " FROM merchant_contract_terms"
8339 : " WHERE order_serial=$1",
8340 : 1),
8341 : /* for postgres_lookup_payment_status() */
8342 4 : GNUNET_PQ_make_prepare ("lookup_payment_status_session_id",
8343 : "SELECT"
8344 : " wired"
8345 : ",paid"
8346 : " FROM merchant_contract_terms"
8347 : " WHERE order_serial=$1"
8348 : " AND session_id=$2",
8349 : 2),
8350 : /* for postgres_lookup_deposits_by_order() */
8351 4 : GNUNET_PQ_make_prepare ("lookup_deposits_by_order",
8352 : "SELECT"
8353 : " deposit_serial"
8354 : ",exchange_url"
8355 : ",h_wire"
8356 : ",amount_with_fee_val"
8357 : ",amount_with_fee_frac"
8358 : ",deposit_fee_val"
8359 : ",deposit_fee_frac"
8360 : ",coin_pub"
8361 : " FROM merchant_deposits"
8362 : " JOIN merchant_accounts USING (account_serial)"
8363 : " WHERE order_serial=$1",
8364 : 1),
8365 : /* for postgres_lookup_transfer_details_by_order() */
8366 4 : GNUNET_PQ_make_prepare ("lookup_transfer_details_by_order",
8367 : "SELECT"
8368 : " md.deposit_serial"
8369 : ",md.exchange_url"
8370 : ",mt.wtid"
8371 : ",exchange_deposit_value_val"
8372 : ",exchange_deposit_value_frac"
8373 : ",exchange_deposit_fee_val"
8374 : ",exchange_deposit_fee_frac"
8375 : ",deposit_timestamp"
8376 : ",mt.confirmed AS transfer_confirmed"
8377 : " FROM merchant_transfer_to_coin"
8378 : " JOIN merchant_deposits AS md USING (deposit_serial)"
8379 : " JOIN merchant_transfers AS mt USING (credit_serial)"
8380 : " WHERE deposit_serial IN"
8381 : " (SELECT deposit_serial"
8382 : " FROM merchant_deposits"
8383 : " WHERE order_serial=$1)",
8384 : 1),
8385 : /* for postgres_insert_deposit_to_transfer() */
8386 4 : GNUNET_PQ_make_prepare ("insert_deposit_to_transfer",
8387 : "INSERT INTO merchant_deposit_to_transfer"
8388 : "(deposit_serial"
8389 : ",coin_contribution_value_val"
8390 : ",coin_contribution_value_frac"
8391 : ",credit_serial"
8392 : ",execution_time"
8393 : ",signkey_serial"
8394 : ",exchange_sig"
8395 : ") SELECT $1, $2, $3, credit_serial, $4, signkey_serial, $5"
8396 : " FROM merchant_transfers"
8397 : " CROSS JOIN merchant_exchange_signing_keys"
8398 : " WHERE exchange_pub=$6"
8399 : " AND wtid=$7",
8400 : 7),
8401 : /* for postgres_mark_order_wired() */
8402 4 : GNUNET_PQ_make_prepare ("mark_order_wired",
8403 : "UPDATE merchant_contract_terms SET"
8404 : " wired=true"
8405 : " WHERE order_serial=$1",
8406 : 1),
8407 : /* for process_refund_cb() used in postgres_increase_refund() */
8408 4 : GNUNET_PQ_make_prepare ("find_refunds_by_coin",
8409 : "SELECT"
8410 : " refund_amount_val"
8411 : ",refund_amount_frac"
8412 : ",rtransaction_id"
8413 : " FROM merchant_refunds"
8414 : " WHERE coin_pub=$1"
8415 : " AND order_serial=$2",
8416 : 2),
8417 : /* for process_deposits_for_refund_cb() used in postgres_increase_refund() */
8418 4 : GNUNET_PQ_make_prepare ("insert_refund",
8419 : "INSERT INTO merchant_refunds"
8420 : "(order_serial"
8421 : ",rtransaction_id"
8422 : ",refund_timestamp"
8423 : ",coin_pub"
8424 : ",reason"
8425 : ",refund_amount_val"
8426 : ",refund_amount_frac"
8427 : ") VALUES"
8428 : "($1, $2, $3, $4, $5, $6, $7)",
8429 : 7),
8430 : /* for postgres_increase_refund() */
8431 4 : GNUNET_PQ_make_prepare ("find_deposits_for_refund",
8432 : "SELECT"
8433 : " coin_pub"
8434 : ",order_serial"
8435 : ",amount_with_fee_val"
8436 : ",amount_with_fee_frac"
8437 : " FROM merchant_deposits"
8438 : " WHERE order_serial="
8439 : " (SELECT order_serial"
8440 : " FROM merchant_contract_terms"
8441 : " WHERE order_id=$2"
8442 : " AND paid=TRUE"
8443 : " AND merchant_serial="
8444 : " (SELECT merchant_serial"
8445 : " FROM merchant_instances"
8446 : " WHERE merchant_id=$1))",
8447 : 2),
8448 : /* for postgres_lookup_refunds_detailed() */
8449 4 : GNUNET_PQ_make_prepare ("lookup_refunds_detailed",
8450 : "SELECT"
8451 : " refund_serial"
8452 : ",refund_timestamp"
8453 : ",coin_pub"
8454 : ",merchant_deposits.exchange_url"
8455 : ",rtransaction_id"
8456 : ",reason"
8457 : ",refund_amount_val"
8458 : ",refund_amount_frac"
8459 : ",merchant_refund_proofs.exchange_sig IS NULL AS pending"
8460 : " FROM merchant_refunds"
8461 : " JOIN merchant_deposits USING (order_serial, coin_pub)"
8462 : " LEFT JOIN merchant_refund_proofs USING (refund_serial)"
8463 : " WHERE order_serial="
8464 : " (SELECT order_serial"
8465 : " FROM merchant_contract_terms"
8466 : " WHERE h_contract_terms=$2"
8467 : " AND merchant_serial="
8468 : " (SELECT merchant_serial"
8469 : " FROM merchant_instances"
8470 : " WHERE merchant_id=$1))",
8471 : 2),
8472 : /* for postgres_insert_refund_proof() */
8473 4 : GNUNET_PQ_make_prepare ("insert_refund_proof",
8474 : "INSERT INTO merchant_refund_proofs"
8475 : "(refund_serial"
8476 : ",exchange_sig"
8477 : ",signkey_serial)"
8478 : "SELECT $1, $2, signkey_serial"
8479 : " FROM merchant_exchange_signing_keys"
8480 : " WHERE exchange_pub=$3"
8481 : " ORDER BY start_date DESC"
8482 : " LIMIT 1",
8483 : 5),
8484 : /* for postgres_lookup_refund_proof() */
8485 4 : GNUNET_PQ_make_prepare ("lookup_refund_proof",
8486 : "SELECT"
8487 : " merchant_exchange_signing_keys.exchange_pub"
8488 : ",exchange_sig"
8489 : " FROM merchant_refund_proofs"
8490 : " JOIN merchant_exchange_signing_keys"
8491 : " USING (signkey_serial)"
8492 : " WHERE"
8493 : " refund_serial=$1",
8494 : 1),
8495 : /* for postgres_lookup_order_by_fulfillment() */
8496 4 : GNUNET_PQ_make_prepare ("lookup_order_by_fulfillment",
8497 : "SELECT"
8498 : " order_id"
8499 : " FROM merchant_contract_terms"
8500 : " WHERE fulfillment_url=$2"
8501 : " AND session_id=$3"
8502 : " AND merchant_serial="
8503 : " (SELECT merchant_serial"
8504 : " FROM merchant_instances"
8505 : " WHERE merchant_id=$1)",
8506 : 3),
8507 : /* for postgres_insert_transfer() */
8508 4 : GNUNET_PQ_make_prepare ("insert_transfer",
8509 : "INSERT INTO merchant_transfers"
8510 : "(exchange_url"
8511 : ",wtid"
8512 : ",credit_amount_val"
8513 : ",credit_amount_frac"
8514 : ",account_serial"
8515 : ",confirmed)"
8516 : "SELECT"
8517 : " $1, $2, $3, $4, account_serial, $6"
8518 : " FROM merchant_accounts"
8519 : " WHERE payto_uri=$5"
8520 : " AND merchant_serial="
8521 : " (SELECT merchant_serial"
8522 : " FROM merchant_instances"
8523 : " WHERE merchant_id=$7)",
8524 : 7),
8525 : /* for postgres_delete_transfer() */
8526 4 : GNUNET_PQ_make_prepare ("delete_transfer",
8527 : "DELETE FROM merchant_transfers"
8528 : " WHERE"
8529 : " credit_serial=$2"
8530 : " AND account_serial IN "
8531 : " (SELECT account_serial "
8532 : " FROM merchant_accounts"
8533 : " WHERE merchant_serial="
8534 : " (SELECT merchant_serial"
8535 : " FROM merchant_instances"
8536 : " WHERE merchant_id=$1))",
8537 : 2),
8538 : /* for postgres_check_transfer_exists() */
8539 4 : GNUNET_PQ_make_prepare ("check_transfer_exists",
8540 : "SELECT"
8541 : " 1"
8542 : " FROM merchant_transfers"
8543 : " JOIN merchant_accounts"
8544 : " USING (account_serial)"
8545 : " WHERE"
8546 : " credit_serial=$2"
8547 : " AND"
8548 : " merchant_serial="
8549 : " (SELECT merchant_serial"
8550 : " FROM merchant_instances"
8551 : " WHERE merchant_id=$1)",
8552 : 2),
8553 : /* for postgres_lookup_account() */
8554 4 : GNUNET_PQ_make_prepare ("lookup_account",
8555 : "SELECT"
8556 : " account_serial"
8557 : " FROM merchant_accounts"
8558 : " WHERE payto_uri=$2"
8559 : " AND merchant_serial="
8560 : " (SELECT merchant_serial"
8561 : " FROM merchant_instances"
8562 : " WHERE merchant_id=$1)",
8563 : 2),
8564 : /* for postgres_insert_transfer_details() */
8565 4 : GNUNET_PQ_make_prepare ("lookup_credit_serial",
8566 : "SELECT"
8567 : " credit_serial"
8568 : " FROM merchant_transfers"
8569 : " WHERE exchange_url=$1"
8570 : " AND wtid=$4"
8571 : " AND account_serial="
8572 : " (SELECT account_serial"
8573 : " FROM merchant_accounts"
8574 : " WHERE payto_uri=$2"
8575 : " AND exchange_url=$1"
8576 : " AND merchant_serial="
8577 : " (SELECT merchant_serial"
8578 : " FROM merchant_instances"
8579 : " WHERE merchant_id=$3))",
8580 : 4),
8581 : /* for postgres_insert_transfer_details() */
8582 4 : GNUNET_PQ_make_prepare ("insert_transfer_signature",
8583 : "INSERT INTO merchant_transfer_signatures"
8584 : "(credit_serial"
8585 : ",signkey_serial"
8586 : ",credit_amount_val"
8587 : ",credit_amount_frac"
8588 : ",wire_fee_val"
8589 : ",wire_fee_frac"
8590 : ",execution_time"
8591 : ",exchange_sig) "
8592 : "SELECT $1, signkey_serial, $2, $3, $4, $5, $6, $7"
8593 : " FROM merchant_exchange_signing_keys"
8594 : " WHERE exchange_pub=$8"
8595 : " ORDER BY start_date DESC"
8596 : " LIMIT 1",
8597 : 8),
8598 : /* for postgres_insert_transfer_details() */
8599 4 : GNUNET_PQ_make_prepare ("insert_transfer_to_coin_mapping",
8600 : "INSERT INTO merchant_transfer_to_coin"
8601 : "(deposit_serial"
8602 : ",credit_serial"
8603 : ",offset_in_exchange_list"
8604 : ",exchange_deposit_value_val"
8605 : ",exchange_deposit_value_frac"
8606 : ",exchange_deposit_fee_val"
8607 : ",exchange_deposit_fee_frac) "
8608 : "SELECT deposit_serial, $1, $2, $3, $4, $5, $6"
8609 : " FROM merchant_deposits"
8610 : " JOIN merchant_contract_terms USING (order_serial)"
8611 : " WHERE coin_pub=$7"
8612 : " AND h_contract_terms=$8"
8613 : " AND merchant_serial="
8614 : " (SELECT merchant_serial"
8615 : " FROM merchant_instances"
8616 : " WHERE merchant_id=$9)",
8617 : 9),
8618 : /* for postgres_insert_transfer_details() */
8619 4 : GNUNET_PQ_make_prepare ("update_wired_by_coin_pub",
8620 : "WITH os AS" /* select orders affected by the coin */
8621 : "(SELECT order_serial"
8622 : " FROM merchant_deposits"
8623 : " WHERE coin_pub=$1)"
8624 : "UPDATE merchant_contract_terms "
8625 : " SET wired=TRUE "
8626 : " WHERE order_serial IN "
8627 : " (SELECT order_serial FROM merchant_deposits" /* only orders for which NO un-wired coin exists*/
8628 : " WHERE NOT EXISTS "
8629 : " (SELECT order_serial FROM merchant_deposits" /* orders for which ANY un-wired coin exists */
8630 : " JOIN os USING (order_serial)" /* filter early */
8631 : " WHERE deposit_serial NOT IN"
8632 : " (SELECT deposit_serial " /* all coins associated with order that WERE wired */
8633 : " FROM merchant_deposits "
8634 : " JOIN os USING (order_serial)" /* filter early */
8635 : " JOIN merchant_deposit_to_transfer USING (deposit_serial)"
8636 : " JOIN merchant_transfers USING (credit_serial)"
8637 : " WHERE confirmed=TRUE)))",
8638 : 1),
8639 : /* for postgres_lookup_wire_fee() */
8640 4 : GNUNET_PQ_make_prepare ("lookup_wire_fee",
8641 : "SELECT"
8642 : " wire_fee_val"
8643 : ",wire_fee_frac"
8644 : ",closing_fee_val"
8645 : ",closing_fee_frac"
8646 : ",wad_fee_val"
8647 : ",wad_fee_frac"
8648 : ",start_date"
8649 : ",end_date"
8650 : ",master_sig"
8651 : " FROM merchant_exchange_wire_fees"
8652 : " WHERE master_pub=$1"
8653 : " AND h_wire_method=$2"
8654 : " AND start_date <= $3"
8655 : " AND end_date > $3",
8656 : 3),
8657 : /* for postgres_lookup_deposits_by_contract_and_coin() */
8658 4 : GNUNET_PQ_make_prepare ("lookup_deposits_by_contract_and_coin",
8659 : "SELECT"
8660 : " exchange_url"
8661 : ",amount_with_fee_val"
8662 : ",amount_with_fee_frac"
8663 : ",deposit_fee_val"
8664 : ",deposit_fee_frac"
8665 : ",refund_fee_val"
8666 : ",refund_fee_frac"
8667 : ",wire_fee_val"
8668 : ",wire_fee_frac"
8669 : ",h_wire"
8670 : ",deposit_timestamp"
8671 : ",refund_deadline"
8672 : ",exchange_sig"
8673 : ",exchange_pub"
8674 : " FROM merchant_contract_terms"
8675 : " JOIN merchant_deposits USING (order_serial)"
8676 : " JOIN merchant_exchange_signing_keys USING (signkey_serial)"
8677 : " JOIN merchant_accounts USING (account_serial)"
8678 : " WHERE h_contract_terms=$2"
8679 : " AND coin_pub=$3"
8680 : " AND merchant_contract_terms.merchant_serial="
8681 : " (SELECT merchant_serial"
8682 : " FROM merchant_instances"
8683 : " WHERE merchant_id=$1)",
8684 : 3),
8685 : /* for postgres_lookup_transfer() */
8686 4 : GNUNET_PQ_make_prepare ("lookup_transfer",
8687 : "SELECT"
8688 : " mt.credit_amount_val AS credit_amount_val"
8689 : ",mt.credit_amount_frac AS credit_amount_frac"
8690 : ",mts.credit_amount_val AS exchange_amount_val"
8691 : ",mts.credit_amount_frac AS exchange_amount_frac"
8692 : ",wire_fee_val"
8693 : ",wire_fee_frac"
8694 : ",execution_time"
8695 : ",verified"
8696 : " FROM merchant_transfers mt"
8697 : " JOIN merchant_accounts USING (account_serial)"
8698 : " JOIN merchant_instances USING (merchant_serial)"
8699 : " LEFT JOIN merchant_transfer_signatures mts USING (credit_serial)"
8700 : " WHERE wtid=$2"
8701 : " AND exchange_url=$1"
8702 : " AND merchant_id=$3;",
8703 : 3),
8704 : /* for postgres_set_transfer_status_to_verified() */
8705 4 : GNUNET_PQ_make_prepare ("set_transfer_status_to_verified",
8706 : "UPDATE merchant_transfers SET"
8707 : " verified=TRUE"
8708 : " WHERE wtid=$1"
8709 : " AND exchange_url=$2",
8710 : 2),
8711 : /* for postgres_lookup_transfer_summary() */
8712 4 : GNUNET_PQ_make_prepare ("lookup_transfer_summary",
8713 : "SELECT"
8714 : " order_id"
8715 : ",exchange_deposit_value_val"
8716 : ",exchange_deposit_value_frac"
8717 : ",exchange_deposit_fee_val"
8718 : ",exchange_deposit_fee_frac"
8719 : " FROM merchant_transfers"
8720 : " JOIN merchant_transfer_to_coin USING (credit_serial)"
8721 : " JOIN merchant_deposits USING (deposit_serial)"
8722 : " JOIN merchant_contract_terms USING (order_serial)"
8723 : " WHERE wtid=$2"
8724 : " AND merchant_transfers.exchange_url=$1",
8725 : 2),
8726 : /* for postgres_lookup_transfer_details() */
8727 4 : GNUNET_PQ_make_prepare ("lookup_transfer_details",
8728 : "SELECT"
8729 : " merchant_contract_terms.h_contract_terms"
8730 : ",merchant_transfer_to_coin.offset_in_exchange_list"
8731 : ",merchant_deposits.coin_pub"
8732 : ",exchange_deposit_value_val"
8733 : ",exchange_deposit_value_frac"
8734 : ",exchange_deposit_fee_val"
8735 : ",exchange_deposit_fee_frac"
8736 : " FROM merchant_transfer_to_coin"
8737 : " JOIN merchant_deposits USING (deposit_serial)"
8738 : " JOIN merchant_contract_terms USING (order_serial)"
8739 : " JOIN merchant_transfers USING (credit_serial)"
8740 : " WHERE merchant_transfers.wtid=$2"
8741 : " AND merchant_transfers.exchange_url=$1",
8742 : 2),
8743 : /* for postgres_lookup_transfers() */
8744 4 : GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_asc",
8745 : "SELECT"
8746 : " mt.credit_amount_val"
8747 : ",mt.credit_amount_frac"
8748 : ",wtid"
8749 : ",merchant_accounts.payto_uri"
8750 : ",exchange_url"
8751 : ",credit_serial"
8752 : ",merchant_transfer_signatures.execution_time"
8753 : ",verified"
8754 : ",confirmed"
8755 : " FROM merchant_transfers mt"
8756 : " JOIN merchant_accounts USING (account_serial)"
8757 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8758 : " WHERE execution_time < $2"
8759 : " AND execution_time >= $3"
8760 : " AND credit_serial > $4"
8761 : " AND payto_uri = $6"
8762 : " AND merchant_serial ="
8763 : " (SELECT merchant_serial"
8764 : " FROM merchant_instances"
8765 : " WHERE merchant_id=$1)"
8766 : " ORDER BY credit_serial ASC"
8767 : " LIMIT $5",
8768 : 6),
8769 : /* for postgres_lookup_transfers() */
8770 4 : GNUNET_PQ_make_prepare ("lookup_transfers_time_asc",
8771 : "SELECT"
8772 : " mt.credit_amount_val"
8773 : ",mt.credit_amount_frac"
8774 : ",wtid"
8775 : ",merchant_accounts.payto_uri"
8776 : ",exchange_url"
8777 : ",credit_serial"
8778 : ",merchant_transfer_signatures.execution_time"
8779 : ",verified"
8780 : ",confirmed"
8781 : " FROM merchant_transfers mt"
8782 : " JOIN merchant_accounts USING (account_serial)"
8783 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8784 : " WHERE execution_time < $2"
8785 : " AND execution_time >= $3"
8786 : " AND credit_serial > $4"
8787 : " AND merchant_serial ="
8788 : " (SELECT merchant_serial"
8789 : " FROM merchant_instances"
8790 : " WHERE merchant_id=$1)"
8791 : " ORDER BY credit_serial ASC"
8792 : " LIMIT $5",
8793 : 5),
8794 : /* for postgres_lookup_transfers() */
8795 4 : GNUNET_PQ_make_prepare ("lookup_transfers_payto_asc",
8796 : "SELECT"
8797 : " mt.credit_amount_val"
8798 : ",mt.credit_amount_frac"
8799 : ",wtid"
8800 : ",merchant_accounts.payto_uri"
8801 : ",exchange_url"
8802 : ",credit_serial"
8803 : ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
8804 : " THEN 9223372036854775807" /* largest BIGINT possible */
8805 : " ELSE merchant_transfer_signatures.execution_time"
8806 : " END AS execution_time"
8807 : ",verified"
8808 : ",confirmed"
8809 : " FROM merchant_transfers mt"
8810 : " JOIN merchant_accounts USING (account_serial)"
8811 : " LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
8812 : " WHERE credit_serial > $2"
8813 : " AND payto_uri = $4"
8814 : " AND merchant_serial ="
8815 : " (SELECT merchant_serial"
8816 : " FROM merchant_instances"
8817 : " WHERE merchant_id=$1)"
8818 : " ORDER BY credit_serial ASC"
8819 : " LIMIT $3",
8820 : 4),
8821 : /* for postgres_lookup_transfers() */
8822 4 : GNUNET_PQ_make_prepare ("lookup_transfers_asc",
8823 : "SELECT"
8824 : " mt.credit_amount_val"
8825 : ",mt.credit_amount_frac"
8826 : ",wtid"
8827 : ",merchant_accounts.payto_uri"
8828 : ",exchange_url"
8829 : ",credit_serial"
8830 : ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
8831 : " THEN 9223372036854775807" /* largest BIGINT possible */
8832 : " ELSE merchant_transfer_signatures.execution_time"
8833 : " END AS execution_time"
8834 : ",verified"
8835 : ",confirmed"
8836 : " FROM merchant_transfers mt"
8837 : " JOIN merchant_accounts USING (account_serial)"
8838 : " LEFT JOIN merchant_transfer_signatures USING (credit_serial)"
8839 : " WHERE credit_serial > $2"
8840 : " AND merchant_serial ="
8841 : " (SELECT merchant_serial"
8842 : " FROM merchant_instances"
8843 : " WHERE merchant_id=$1)"
8844 : " ORDER BY credit_serial ASC"
8845 : " LIMIT $3",
8846 : 3),
8847 : /* for postgres_lookup_transfers() */
8848 4 : GNUNET_PQ_make_prepare ("lookup_transfers_time_payto_desc",
8849 : "SELECT"
8850 : " mt.credit_amount_val"
8851 : ",mt.credit_amount_frac"
8852 : ",wtid"
8853 : ",merchant_accounts.payto_uri"
8854 : ",exchange_url"
8855 : ",credit_serial"
8856 : ",merchant_transfer_signatures.execution_time"
8857 : ",verified"
8858 : ",confirmed"
8859 : " FROM merchant_transfers mt"
8860 : " JOIN merchant_accounts USING (account_serial)"
8861 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8862 : " WHERE execution_time < $2"
8863 : " AND execution_time >= $3"
8864 : " AND credit_serial < $4"
8865 : " AND payto_uri = $6"
8866 : " AND merchant_serial ="
8867 : " (SELECT merchant_serial"
8868 : " FROM merchant_instances"
8869 : " WHERE merchant_id=$1)"
8870 : " ORDER BY credit_serial DESC"
8871 : " LIMIT $5",
8872 : 6),
8873 : /* for postgres_lookup_transfers() */
8874 4 : GNUNET_PQ_make_prepare ("lookup_transfers_time_desc",
8875 : "SELECT"
8876 : " mt.credit_amount_val"
8877 : ",mt.credit_amount_frac"
8878 : ",wtid"
8879 : ",merchant_accounts.payto_uri"
8880 : ",exchange_url"
8881 : ",credit_serial"
8882 : ",merchant_transfer_signatures.execution_time"
8883 : ",verified"
8884 : ",confirmed"
8885 : " FROM merchant_transfers mt"
8886 : " JOIN merchant_accounts USING (account_serial)"
8887 : " JOIN merchant_transfer_signatures USING (credit_serial)"
8888 : " WHERE execution_time < $2"
8889 : " AND execution_time >= $3"
8890 : " AND credit_serial < $4"
8891 : " AND merchant_serial ="
8892 : " (SELECT merchant_serial"
8893 : " FROM merchant_instances"
8894 : " WHERE merchant_id=$1)"
8895 : " ORDER BY credit_serial DESC"
8896 : " LIMIT $5",
8897 : 5),
8898 : /* for postgres_lookup_transfers() */
8899 4 : GNUNET_PQ_make_prepare ("lookup_transfers_payto_desc",
8900 : "SELECT"
8901 : " mt.credit_amount_val"
8902 : ",mt.credit_amount_frac"
8903 : ",wtid"
8904 : ",merchant_accounts.payto_uri"
8905 : ",exchange_url"
8906 : ",credit_serial"
8907 : ",CASE WHEN (merchant_transfer_signatures.execution_time) IS NULL"
8908 : " THEN 9223372036854775807" /* largest BIGINT possible */
8909 : " ELSE merchant_transfer_signatures.execution_time"
8910 : " END AS execution_time"
8911 : ",verified"
8912 : ",confirmed"
8913 : " FROM merchant_transfers mt"
8914 : " JOIN merchant_accounts USING (account_serial)"
|