pq_query_helper.c (23111B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2014-2023 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU 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 pq/pq_query_helper.c 18 * @brief helper functions for Taler-specific libpq (PostGres) interactions 19 * @author Johannes Casaburi 20 */ 21 #include <gnunet/gnunet_common.h> 22 #include <gnunet/gnunet_util_lib.h> 23 #include <gnunet/gnunet_pq_lib.h> 24 #include <taler/taler_pq_lib.h> 25 #include "donau_util.h" 26 #include "donau_pq_lib.h" 27 #include "pq_common.h" 28 29 30 /** 31 * Function called to convert input argument into SQL parameters. 32 * 33 * @param cls closure 34 * @param data pointer to input argument 35 * @param data_len number of bytes in @a data (if applicable) 36 * @param[out] param_values SQL data to set 37 * @param[out] param_lengths SQL length data to set 38 * @param[out] param_formats SQL format data to set 39 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays 40 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() 41 * @param scratch_length number of entries left in @a scratch 42 * @return -1 on error, number of offsets used in @a scratch otherwise 43 */ 44 static int 45 qconv_donation_unit_pub (void *cls, 46 const void *data, 47 size_t data_len, 48 void *param_values[], 49 int param_lengths[], 50 int param_formats[], 51 unsigned int param_length, 52 void *scratch[], 53 unsigned int scratch_length) 54 { 55 const struct DONAU_DonationUnitPublicKey *donation_unit_pub = data; 56 const struct GNUNET_CRYPTO_BlindSignPublicKey *bsp = donation_unit_pub-> 57 bsign_pub_key; 58 size_t tlen; 59 size_t len; 60 uint32_t be[1]; 61 char *buf; 62 void *tbuf; 63 64 (void) cls; 65 (void) data_len; 66 GNUNET_assert (1 == param_length); 67 GNUNET_assert (scratch_length > 0); 68 GNUNET_break (NULL == cls); 69 be[0] = htonl ((uint32_t) bsp->cipher); 70 switch (bsp->cipher) 71 { 72 case GNUNET_CRYPTO_BSA_RSA: 73 tlen = GNUNET_CRYPTO_rsa_public_key_encode ( 74 bsp->details.rsa_public_key, 75 &tbuf); 76 break; 77 case GNUNET_CRYPTO_BSA_CS: 78 tlen = sizeof (bsp->details.cs_public_key); 79 break; 80 default: 81 GNUNET_assert (0); 82 } 83 len = tlen + sizeof (be); 84 buf = GNUNET_malloc (len); 85 GNUNET_memcpy (buf, 86 be, 87 sizeof (be)); 88 switch (bsp->cipher) 89 { 90 case GNUNET_CRYPTO_BSA_RSA: 91 GNUNET_memcpy (&buf[sizeof (be)], 92 tbuf, 93 tlen); 94 GNUNET_free (tbuf); 95 break; 96 case GNUNET_CRYPTO_BSA_CS: 97 GNUNET_memcpy (&buf[sizeof (be)], 98 &bsp->details.cs_public_key, 99 tlen); 100 break; 101 default: 102 GNUNET_assert (0); 103 } 104 105 scratch[0] = buf; 106 param_values[0] = (void *) buf; 107 param_lengths[0] = len; 108 param_formats[0] = 1; 109 return 1; 110 } 111 112 113 struct GNUNET_PQ_QueryParam 114 DONAU_PQ_query_param_donation_unit_pub ( 115 const struct DONAU_DonationUnitPublicKey *donation_unit_pub) 116 { 117 struct GNUNET_PQ_QueryParam res = { 118 .conv = &qconv_donation_unit_pub, 119 .data = donation_unit_pub, 120 .num_params = 1 121 }; 122 123 return res; 124 } 125 126 127 /** 128 * Closure for the array result specifications. Contains type information 129 * for the generic parser extract_array_generic and out-pointers for the results. 130 */ 131 struct ArrayResultCls 132 { 133 /** 134 * Oid of the expected type, must match the oid in the header of the PQResult struct 135 */ 136 Oid oid; 137 138 /** 139 * Target type 140 */ 141 // enum TALER_PQ_ArrayType typ; 142 143 /** 144 * If not 0, defines the expected size of each entry 145 */ 146 size_t same_size; 147 148 /** 149 * Out-pointer to write the number of elements in the array 150 */ 151 size_t *num; 152 153 /** 154 * Out-pointer. If @a typ is TALER_PQ_array_of_byte and @a same_size is 0, 155 * allocate and put the array of @a num sizes here. NULL otherwise 156 */ 157 size_t **sizes; 158 159 /** 160 * DB_connection, needed for OID-lookup for composite types 161 */ 162 const struct GNUNET_PQ_Context *db; 163 164 /** 165 * Currency information for amount composites 166 */ 167 char currency[TALER_CURRENCY_LEN]; 168 }; 169 170 /** 171 * Closure for the array type handlers. 172 * 173 * May contain sizes information for the data, given (and handled) by the 174 * caller. 175 */ 176 struct qconv_array_cls 177 { 178 /** 179 * If not null, contains the array of sizes (the size of the array is the 180 * .size field in the ambient GNUNET_PQ_QueryParam struct). We do not free 181 * this memory. 182 * 183 * If not null, this value has precedence over @a sizes, which MUST be NULL */ 184 const size_t *sizes; 185 186 /** 187 * If @a size and @a c_sizes are NULL, this field defines the same size 188 * for each element in the array. 189 */ 190 size_t same_size; 191 192 /** 193 * If true, the array parameter to the data pointer to the qconv_array is a 194 * continuous byte array of data, either with @a same_size each or sizes 195 * provided bytes by @a sizes; 196 */ 197 bool continuous; 198 199 /** 200 * Type of the array elements 201 */ 202 enum DONAU_PQ_ArrayType typ; 203 204 /** 205 * Oid of the array elements 206 */ 207 Oid oid; 208 209 /** 210 * db context, needed for OID-lookup of basis-types 211 */ 212 struct GNUNET_PQ_Context *db; 213 }; 214 215 /** 216 * Function called to convert input argument into SQL parameters for arrays 217 * 218 * Note: the format for the encoding of arrays for libpq is not very well 219 * documented. We peeked into various sources (postgresql and libpqtypes) for 220 * guidance. 221 * 222 * @param cls Closure of type struct qconv_array_cls* 223 * @param data Pointer to first element in the array 224 * @param data_len Number of _elements_ in array @a data (if applicable) 225 * @param[out] param_values SQL data to set 226 * @param[out] param_lengths SQL length data to set 227 * @param[out] param_formats SQL format data to set 228 * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays 229 * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() 230 * @param scratch_length number of entries left in @a scratch 231 * @return -1 on error, number of offsets used in @a scratch otherwise 232 */ 233 static int 234 qconv_array ( 235 void *cls, 236 const void *data, 237 size_t data_len, 238 void *param_values[], 239 int param_lengths[], 240 int param_formats[], 241 unsigned int param_length, 242 void *scratch[], 243 unsigned int scratch_length) 244 { 245 struct qconv_array_cls *meta = cls; 246 size_t num = data_len; 247 size_t total_size; 248 const size_t *sizes; 249 bool same_sized; 250 void *elements = NULL; 251 bool noerror = true; 252 /* needed to capture the encoded rsa signatures */ 253 void **buffers = NULL; 254 size_t *buffer_lengths = NULL; 255 256 (void) (param_length); 257 (void) (scratch_length); 258 259 GNUNET_assert (NULL != meta); 260 GNUNET_assert (num < INT_MAX); 261 262 sizes = meta->sizes; 263 same_sized = (0 != meta->same_size); 264 265 #define RETURN_UNLESS(cond) \ 266 do { \ 267 if (! (cond)) \ 268 { \ 269 GNUNET_break ((cond)); \ 270 noerror = false; \ 271 goto DONE; \ 272 } \ 273 } while (0) 274 275 /* Calculate sizes and check bounds */ 276 { 277 /* num * length-field */ 278 size_t x = sizeof(uint32_t); 279 size_t y = x * num; 280 RETURN_UNLESS ((0 == num) || (y / num == x)); 281 282 /* size of header */ 283 total_size = x = sizeof(struct GNUNET_PQ_ArrayHeader_P); 284 total_size += y; 285 RETURN_UNLESS (total_size >= x); 286 287 /* sizes of elements */ 288 if (same_sized) 289 { 290 x = num * meta->same_size; 291 RETURN_UNLESS ((0 == num) || (x / num == meta->same_size)); 292 293 y = total_size; 294 total_size += x; 295 RETURN_UNLESS (total_size >= y); 296 } 297 else /* sizes are different per element */ 298 { 299 switch (meta->typ) 300 { 301 case DONAU_PQ_array_of_blinded_du_sig: 302 { 303 const struct DONAU_BlindedDonationUnitSignature *du_sigs = data; 304 size_t len; 305 306 buffers = GNUNET_new_array (num, void *); 307 buffer_lengths = GNUNET_new_array (num, size_t); 308 309 for (size_t i = 0; i<num; i++) 310 { 311 const struct GNUNET_CRYPTO_BlindedSignature *bs = 312 du_sigs[i].blinded_sig; 313 314 switch (bs->cipher) 315 { 316 case GNUNET_CRYPTO_BSA_RSA: 317 len = GNUNET_CRYPTO_rsa_signature_encode ( 318 bs->details.blinded_rsa_signature, 319 &buffers[i]); 320 RETURN_UNLESS (len != 0); 321 break; 322 case GNUNET_CRYPTO_BSA_CS: 323 len = sizeof (bs->details.blinded_cs_answer); 324 break; 325 default: 326 GNUNET_assert (0); 327 break; 328 } 329 330 /* for the cipher and marker */ 331 len += 2 * sizeof(uint32_t); 332 buffer_lengths[i] = len; 333 334 y = total_size; 335 total_size += len; 336 RETURN_UNLESS (total_size >= y); 337 } 338 sizes = buffer_lengths; 339 break; 340 } 341 case DONAU_PQ_array_of_unblinded_du_sig: 342 { 343 const struct DONAU_DonationUnitSignature *du_sigs = data; 344 size_t len; 345 346 buffers = GNUNET_new_array (num, 347 void *); 348 buffer_lengths = GNUNET_new_array (num, 349 size_t); 350 for (size_t i = 0; i<num; i++) 351 { 352 const struct GNUNET_CRYPTO_UnblindedSignature *ubs = 353 du_sigs[i].unblinded_sig; 354 355 switch (ubs->cipher) 356 { 357 case GNUNET_CRYPTO_BSA_RSA: 358 len = GNUNET_CRYPTO_rsa_signature_encode ( 359 ubs->details.rsa_signature, 360 &buffers[i]); 361 RETURN_UNLESS (len != 0); 362 break; 363 case GNUNET_CRYPTO_BSA_CS: 364 len = sizeof (ubs->details.cs_signature); 365 break; 366 default: 367 GNUNET_assert (0); 368 break; 369 } 370 371 /* for the cipher and marker */ 372 len += 2 * sizeof(uint32_t); 373 buffer_lengths[i] = len; 374 375 y = total_size; 376 total_size += len; 377 RETURN_UNLESS (total_size >= y); 378 } 379 sizes = buffer_lengths; 380 break; 381 } 382 default: 383 { 384 GNUNET_assert (0); 385 break; 386 } 387 } 388 } 389 390 RETURN_UNLESS (INT_MAX > total_size); 391 RETURN_UNLESS (0 != total_size); 392 393 elements = GNUNET_malloc (total_size); 394 } 395 396 /* Write data */ 397 { 398 char *out = elements; 399 struct GNUNET_PQ_ArrayHeader_P h = { 400 .ndim = htonl (1), /* We only support one-dimensional arrays */ 401 .has_null = htonl (0), /* We do not support NULL entries in arrays */ 402 .lbound = htonl (1), /* Default start index value */ 403 .dim = htonl (num), 404 .oid = htonl (meta->oid), 405 }; 406 407 /* Write header */ 408 GNUNET_memcpy (out, 409 &h, 410 sizeof(h)); 411 out += sizeof(h); 412 413 /* Write elements */ 414 for (size_t i = 0; i < num; i++) 415 { 416 size_t sz = same_sized ? meta->same_size : sizes[i]; 417 418 *(uint32_t *) out = htonl (sz); 419 out += sizeof(uint32_t); 420 switch (meta->typ) 421 { 422 case DONAU_PQ_array_of_blinded_du_sig: 423 { 424 const struct DONAU_BlindedDonationUnitSignature *denom_sigs = data; 425 const struct GNUNET_CRYPTO_BlindedSignature *bs = 426 denom_sigs[i].blinded_sig; 427 uint32_t be[2]; 428 429 be[0] = htonl ((uint32_t) bs->cipher); 430 be[1] = htonl (0x01); /* magic margker: blinded */ 431 GNUNET_memcpy (out, 432 &be, 433 sizeof(be)); 434 out += sizeof(be); 435 sz -= sizeof(be); 436 437 switch (bs->cipher) 438 { 439 case GNUNET_CRYPTO_BSA_RSA: 440 /* For RSA, 'same_sized' must have been false */ 441 GNUNET_assert (NULL != buffers); 442 GNUNET_memcpy (out, 443 buffers[i], 444 sz); 445 break; 446 case GNUNET_CRYPTO_BSA_CS: 447 GNUNET_memcpy (out, 448 &bs->details.blinded_cs_answer, 449 sz); 450 break; 451 default: 452 GNUNET_assert (0); 453 } 454 break; 455 } 456 case DONAU_PQ_array_of_unblinded_du_sig: 457 { 458 const struct DONAU_DonationUnitSignature *du_sigs = data; 459 const struct GNUNET_CRYPTO_UnblindedSignature *ubs = 460 du_sigs[i].unblinded_sig; 461 uint32_t be[2]; 462 463 be[0] = htonl ((uint32_t) ubs->cipher); 464 be[1] = htonl (0x00); /* magic margker: unblinded */ 465 GNUNET_memcpy (out, 466 &be, 467 sizeof(be)); 468 out += sizeof(be); 469 sz -= sizeof(be); 470 471 switch (ubs->cipher) 472 { 473 case GNUNET_CRYPTO_BSA_RSA: 474 /* For RSA, 'same_sized' must have been false */ 475 GNUNET_assert (NULL != buffers); 476 GNUNET_memcpy (out, 477 buffers[i], 478 sz); 479 break; 480 case GNUNET_CRYPTO_BSA_CS: 481 GNUNET_memcpy (out, 482 &ubs->details.cs_signature, 483 sz); 484 break; 485 default: 486 GNUNET_assert (0); 487 } 488 break; 489 } 490 default: 491 { 492 GNUNET_assert (0); 493 break; 494 } 495 } 496 out += sz; 497 } 498 } 499 param_values[0] = elements; 500 param_lengths[0] = total_size; 501 param_formats[0] = 1; 502 scratch[0] = elements; 503 504 DONE: 505 if (NULL != buffers) 506 { 507 for (size_t i = 0; i<num; i++) 508 GNUNET_free (buffers[i]); 509 GNUNET_free (buffers); 510 } 511 GNUNET_free (buffer_lengths); 512 if (noerror) 513 return 1; 514 return -1; 515 } 516 517 518 /** 519 * Callback to cleanup a qconv_array_cls to be used during 520 * GNUNET_PQ_cleanup_query_params_closures 521 */ 522 static void 523 qconv_array_cls_cleanup (void *cls) 524 { 525 GNUNET_free (cls); 526 } 527 528 529 /** 530 * Function to generate a typ specific query parameter and corresponding closure 531 * 532 * @param num Number of elements in @a elements 533 * @param continuous If true, @a elements is an continuous array of data 534 * @param elements Array of @a num elements, either continuous or pointers 535 * @param sizes Array of @a num sizes, one per element, may be NULL 536 * @param same_size If not 0, all elements in @a elements have this size 537 * @param typ Supported internal type of each element in @a elements 538 * @param oid Oid of the type to be used in Postgres 539 * @param[in,out] db our database handle for looking up OIDs 540 * @return Query parameter 541 */ 542 static struct GNUNET_PQ_QueryParam 543 query_param_array_generic ( 544 unsigned int num, 545 bool continuous, 546 const void *elements, 547 const size_t *sizes, 548 size_t same_size, 549 enum DONAU_PQ_ArrayType typ, 550 Oid oid, 551 struct GNUNET_PQ_Context *db) 552 { 553 struct qconv_array_cls *meta = GNUNET_new (struct qconv_array_cls); 554 555 meta->typ = typ; 556 meta->oid = oid; 557 meta->sizes = sizes; 558 meta->same_size = same_size; 559 meta->continuous = continuous; 560 meta->db = db; 561 562 { 563 struct GNUNET_PQ_QueryParam res = { 564 .conv = qconv_array, 565 .conv_cls = meta, 566 .conv_cls_cleanup = qconv_array_cls_cleanup, 567 .data = elements, 568 .size = num, 569 .num_params = 1, 570 }; 571 572 return res; 573 } 574 } 575 576 577 struct GNUNET_PQ_QueryParam 578 DONAU_PQ_query_param_array_blinded_donation_unit_sig ( 579 size_t num, 580 const struct DONAU_BlindedDonationUnitSignature *du_sigs, 581 struct GNUNET_PQ_Context *db) 582 { 583 Oid oid; 584 585 GNUNET_assert (GNUNET_OK == 586 GNUNET_PQ_get_oid_by_name (db, 587 "bytea", 588 &oid)); 589 return query_param_array_generic (num, 590 true, 591 du_sigs, 592 NULL, 593 0, 594 DONAU_PQ_array_of_blinded_du_sig, 595 oid, 596 NULL); 597 } 598 599 600 struct GNUNET_PQ_QueryParam 601 DONAU_PQ_query_param_array_donation_unit_sig ( 602 size_t num, 603 const struct DONAU_DonationUnitSignature *du_sigs, 604 struct GNUNET_PQ_Context *db) 605 { 606 Oid oid; 607 608 GNUNET_assert (GNUNET_OK == 609 GNUNET_PQ_get_oid_by_name (db, 610 "bytea", 611 &oid)); 612 return query_param_array_generic (num, 613 true, 614 du_sigs, 615 NULL, 616 0, 617 DONAU_PQ_array_of_unblinded_du_sig, 618 oid, 619 NULL); 620 } 621 622 623 /** 624 * Extract data from a Postgres database @a result as array of a specific type 625 * from row @a row. The type information and optionally additional 626 * out-parameters are given in @a cls which is of type array_result_cls. 627 * 628 * @param cls closure of type array_result_cls 629 * @param result where to extract data from 630 * @param row row to extract data from 631 * @param fname name (or prefix) of the fields to extract from 632 * @param[in,out] dst_size where to store size of result, may be NULL 633 * @param[out] dst where to store the result 634 * @return 635 * #GNUNET_YES if all results could be extracted 636 * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) 637 */ 638 static enum GNUNET_GenericReturnValue 639 extract_array_generic ( 640 void *cls, 641 PGresult *result, 642 int row, 643 const char *fname, 644 size_t *dst_size, 645 void *dst) 646 { 647 const struct ArrayResultCls *info = cls; 648 int data_sz; 649 char *data; 650 // void *out = NULL; 651 struct DONAU_BlindedDonationUnitSignature *du_sigs = NULL; 652 struct GNUNET_PQ_ArrayHeader_P header; 653 int col_num; 654 655 GNUNET_assert (NULL != dst); 656 *((void **) dst) = NULL; 657 658 #define FAIL_IF(cond) \ 659 do { \ 660 if ((cond)) \ 661 { \ 662 GNUNET_break (! (cond)); \ 663 goto FAIL; \ 664 } \ 665 } while (0) 666 667 col_num = PQfnumber (result, 668 fname); 669 FAIL_IF (0 > col_num); 670 data_sz = PQgetlength (result, 671 row, 672 col_num); 673 FAIL_IF (0 > data_sz); 674 FAIL_IF (sizeof(header) > (size_t) data_sz); 675 676 data = PQgetvalue (result, row, col_num); 677 FAIL_IF (NULL == data); 678 679 { 680 struct GNUNET_PQ_ArrayHeader_P *h = 681 (struct GNUNET_PQ_ArrayHeader_P *) data; 682 683 header.ndim = ntohl (h->ndim); 684 header.has_null = ntohl (h->has_null); 685 header.oid = ntohl (h->oid); 686 header.dim = ntohl (h->dim); 687 header.lbound = ntohl (h->lbound); 688 689 FAIL_IF (1 != header.ndim); 690 FAIL_IF (INT_MAX <= header.dim); 691 FAIL_IF (0 != header.has_null); 692 FAIL_IF (1 != header.lbound); 693 FAIL_IF (info->oid != header.oid); 694 } 695 696 if (NULL != info->num) 697 *info->num = header.dim; 698 699 { 700 char *in = data + sizeof(header); 701 702 if (0 == header.dim) 703 { 704 if (NULL != dst_size) 705 *dst_size = 0; 706 goto FAIL; 707 } 708 du_sigs = GNUNET_new_array (header.dim, 709 struct DONAU_BlindedDonationUnitSignature); 710 *((void **) dst) = du_sigs; 711 712 /* copy data */ 713 for (uint32_t i = 0; i < header.dim; i++) 714 { 715 struct DONAU_BlindedDonationUnitSignature *du_sig = &du_sigs[i]; 716 struct GNUNET_CRYPTO_BlindedSignature *bs; 717 uint32_t be[2]; 718 uint32_t val; 719 size_t sz; 720 721 GNUNET_memcpy (&val, 722 in, 723 sizeof(val)); 724 sz = ntohl (val); 725 FAIL_IF (sizeof(be) > sz); 726 in += sizeof(val); 727 GNUNET_memcpy (&be, 728 in, 729 sizeof(be)); 730 FAIL_IF (0x01 != ntohl (be[1])); /* magic marker: blinded */ 731 732 in += sizeof(be); 733 sz -= sizeof(be); 734 bs = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); 735 bs->cipher = ntohl (be[0]); 736 bs->rc = 1; 737 switch (bs->cipher) 738 { 739 case GNUNET_CRYPTO_BSA_RSA: 740 bs->details.blinded_rsa_signature 741 = GNUNET_CRYPTO_rsa_signature_decode (in, 742 sz); 743 if (NULL == bs->details.blinded_rsa_signature) 744 { 745 GNUNET_free (bs); 746 FAIL_IF (true); 747 } 748 break; 749 case GNUNET_CRYPTO_BSA_CS: 750 if (sizeof(bs->details.blinded_cs_answer) != sz) 751 { 752 GNUNET_free (bs); 753 FAIL_IF (true); 754 } 755 GNUNET_memcpy (&bs->details.blinded_cs_answer, 756 in, 757 sz); 758 break; 759 default: 760 GNUNET_free (bs); 761 FAIL_IF (true); 762 } 763 du_sig->blinded_sig = bs; 764 in += sz; 765 } 766 return GNUNET_OK; 767 } 768 FAIL: 769 if (NULL != du_sigs) 770 { 771 for (size_t i = 0; i < *info->num; i++) 772 if (NULL != du_sigs[i].blinded_sig) 773 GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig); 774 GNUNET_free (du_sigs); 775 *((void **) dst) = NULL; 776 } 777 return GNUNET_SYSERR; 778 #undef FAIL_IF 779 } 780 781 782 /** 783 * Cleanup of the data and closure of an array spec. 784 */ 785 static void 786 array_cleanup (void *cls, 787 void *rd) 788 { 789 struct ArrayResultCls *info = cls; 790 void **dst = rd; 791 struct DONAU_BlindedDonationUnitSignature *du_sigs = *dst; 792 793 if ( (0 == info->same_size) && 794 (NULL != info->sizes) ) 795 GNUNET_free (*(info->sizes)); 796 797 GNUNET_assert (NULL != info->num); 798 for (size_t i = 0; i < *info->num; i++) 799 if (NULL != du_sigs[i].blinded_sig) 800 GNUNET_CRYPTO_blinded_sig_decref (du_sigs[i].blinded_sig); 801 GNUNET_free (info); 802 GNUNET_free (du_sigs); 803 *dst = NULL; 804 } 805 806 807 struct GNUNET_PQ_ResultSpec 808 DONAU_PQ_result_spec_array_blinded_donation_unit_sig ( 809 struct GNUNET_PQ_Context *db, 810 const char *name, 811 size_t *num, 812 struct DONAU_BlindedDonationUnitSignature **du_sigs) 813 { 814 struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); 815 816 *num = 0; 817 *du_sigs = NULL; 818 info->num = num; 819 // info->typ = TALER_PQ_array_of_blinded_denom_sig; 820 GNUNET_assert (GNUNET_OK == 821 GNUNET_PQ_get_oid_by_name (db, 822 "bytea", 823 &info->oid)); 824 { 825 struct GNUNET_PQ_ResultSpec res = { 826 .conv = extract_array_generic, 827 .cleaner = array_cleanup, 828 .dst = (void *) du_sigs, 829 .fname = name, 830 .cls = info 831 }; 832 833 return res; 834 } 835 } 836 837 838 /* end of pq/pq_query_helper.c */