diff options
Diffstat (limited to 'src/pq')
-rw-r--r-- | src/pq/Makefile.am | 2 | ||||
-rw-r--r-- | src/pq/pq_common.c | 68 | ||||
-rw-r--r-- | src/pq/pq_common.h | 148 | ||||
-rw-r--r-- | src/pq/pq_query_helper.c | 971 | ||||
-rw-r--r-- | src/pq/pq_result_helper.c | 1057 | ||||
-rw-r--r-- | src/pq/test_pq.c | 215 |
6 files changed, 2011 insertions, 450 deletions
diff --git a/src/pq/Makefile.am b/src/pq/Makefile.am index b0717dfc7..4b192d762 100644 --- a/src/pq/Makefile.am +++ b/src/pq/Makefile.am @@ -10,11 +10,13 @@ lib_LTLIBRARIES = \ libtalerpq.la libtalerpq_la_SOURCES = \ + pq_common.h pq_common.c \ pq_query_helper.c \ pq_result_helper.c libtalerpq_la_LIBADD = \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetutil -ljansson \ + -lgnunetpq \ -lpq \ $(XLIB) libtalerpq_la_LDFLAGS = \ diff --git a/src/pq/pq_common.c b/src/pq/pq_common.c new file mode 100644 index 000000000..8b6f8f22c --- /dev/null +++ b/src/pq/pq_common.c @@ -0,0 +1,68 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file pq/pq_common.c + * @brief common defines for the pq functions + * @author Özgür Kesim + */ +#include "platform.h" +#include "pq_common.h" + +struct TALER_PQ_AmountP +TALER_PQ_make_taler_pq_amount_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f) +{ + struct TALER_PQ_AmountP rval = { + .cnt = htonl (2), + .oid_v = htonl (oid_v), + .oid_f = htonl (oid_f), + .sz_v = htonl (sizeof((amount)->value)), + .sz_f = htonl (sizeof((amount)->fraction)), + .v = GNUNET_htonll ((amount)->value), + .f = htonl ((amount)->fraction) + }; + + return rval; +} + + +size_t +TALER_PQ_make_taler_pq_amount_currency_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f, + uint32_t oid_c, + struct TALER_PQ_AmountCurrencyP *rval) +{ + size_t clen = strlen (amount->currency); + + GNUNET_assert (clen < TALER_CURRENCY_LEN); + rval->cnt = htonl (3); + rval->oid_v = htonl (oid_v); + rval->oid_f = htonl (oid_f); + rval->oid_c = htonl (oid_c); + rval->sz_v = htonl (sizeof(amount->value)); + rval->sz_f = htonl (sizeof(amount->fraction)); + rval->sz_c = htonl (clen); + rval->v = GNUNET_htonll (amount->value); + rval->f = htonl (amount->fraction); + memcpy (rval->c, + amount->currency, + clen); + return sizeof (*rval) - TALER_CURRENCY_LEN + clen; +} diff --git a/src/pq/pq_common.h b/src/pq/pq_common.h new file mode 100644 index 000000000..3248778a0 --- /dev/null +++ b/src/pq/pq_common.h @@ -0,0 +1,148 @@ +/* + This file is part of TALER + Copyright (C) 2023 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file pq/pq_common.h + * @brief common defines for the pq functions + * @author Özgür Kesim + */ +#ifndef TALER_PQ_COMMON_H_ +#define TALER_PQ_COMMON_H_ + +#include "taler_util.h" + +/** + * Internal types that are supported as TALER-exchange-specific array types. + * + * To support a new type, + * 1. add a new entry into this list, + * 2. for query-support, implement the size calculation and memory copying in + * qconv_array() accordingly, in pq_query_helper.c + * 3. provide a query-API for arrays of the type, by calling + * query_param_array_generic with the appropriate parameters, + * in pq_query_helper.c + * 4. for result-support, implement memory copying by adding another case + * to extract_array_generic, in pq_result_helper.c + * 5. provide a result-spec-API for arrays of the type, + * in pq_result_helper.c + * 6. expose the API's in taler_pq_lib.h + */ +enum TALER_PQ_ArrayType +{ + TALER_PQ_array_of_blinded_denom_sig, + TALER_PQ_array_of_blinded_coin_hash, + TALER_PQ_array_of_denom_hash, + TALER_PQ_array_of_hash_code, + + /** + * Amounts *without* currency. + */ + TALER_PQ_array_of_amount, + + /** + * Amounts *with* currency. + */ + TALER_PQ_array_of_amount_currency, + TALER_PQ_array_of_MAX, /* must be last */ +}; + + +/** + * Memory representation of an taler amount record for Postgres. + * + * All values need to be in network-byte-order. + */ +struct TALER_PQ_AmountP +{ + uint32_t cnt; /* # elements in the tuple (== 2) */ + uint32_t oid_v; /* oid of .v */ + uint32_t sz_v; /* size of .v */ + uint64_t v; /* value */ + uint32_t oid_f; /* oid of .f */ + uint32_t sz_f; /* size of .f */ + uint32_t f; /* fraction */ +} __attribute__((packed)); + + +/** + * Memory representation of an taler amount record for Postgres. + * + * All values need to be in network-byte-order. + */ +struct TALER_PQ_AmountNullP +{ + uint32_t cnt; /* # elements in the tuple (== 2) */ + uint32_t oid_v; /* oid of .v */ + uint32_t sz_v; /* size of .v */ + uint32_t oid_f; /* oid of .f */ + uint32_t sz_f; /* size of .f */ +} __attribute__((packed)); + + +/** + * Memory representation of an taler amount record with currency for Postgres. + * + * All values need to be in network-byte-order. + */ +struct TALER_PQ_AmountCurrencyP +{ + uint32_t cnt; /* # elements in the tuple (== 3) */ + uint32_t oid_v; /* oid of .v */ + uint32_t sz_v; /* size of .v */ + uint64_t v; /* value */ + uint32_t oid_f; /* oid of .f */ + uint32_t sz_f; /* size of .f */ + uint32_t f; /* fraction */ + uint32_t oid_c; /* oid of .c */ + uint32_t sz_c; /* size of .c */ + uint8_t c[TALER_CURRENCY_LEN]; /* currency */ +} __attribute__((packed)); + + +/** + * Create a `struct TALER_PQ_AmountP` for initialization + * + * @param amount amount of type `struct TALER_Amount *` + * @param oid_v OID of the INT8 type in postgres + * @param oid_f OID of the INT4 type in postgres + */ +struct TALER_PQ_AmountP +TALER_PQ_make_taler_pq_amount_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f); + + +/** + * Create a `struct TALER_PQ_AmountCurrencyP` for initialization + * + * @param amount amount of type `struct TALER_Amount *` + * @param oid_v OID of the INT8 type in postgres + * @param oid_f OID of the INT4 type in postgres + * @param oid_c OID of the TEXT type in postgres + * @param[out] rval set to encoded @a amount + * @return actual (useful) size of @a rval for Postgres + */ +size_t +TALER_PQ_make_taler_pq_amount_currency_ ( + const struct TALER_Amount *amount, + uint32_t oid_v, + uint32_t oid_f, + uint32_t oid_c, + struct TALER_PQ_AmountCurrencyP *rval); + + +#endif /* TALER_PQ_COMMON_H_ */ +/* end of pg/pq_common.h */ diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c index 1db608edd..b1dfd4cf1 100644 --- a/src/pq/pq_query_helper.c +++ b/src/pq/pq_query_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016, 2021, 2022 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -21,16 +21,18 @@ * @author Christian Grothoff */ #include "platform.h" +#include <gnunet/gnunet_common.h> #include <gnunet/gnunet_util_lib.h> #include <gnunet/gnunet_pq_lib.h> #include "taler_pq_lib.h" +#include "pq_common.h" /** - * Function called to convert input argument into SQL parameters. + * Function called to convert input amount into SQL parameter as tuple. * * @param cls closure - * @param data pointer to input argument, here a `struct TALER_AmountNBO` + * @param data pointer to input argument, here a `struct TALER_Amount` * @param data_len number of bytes in @a data (if applicable) * @param[out] param_values SQL data to set * @param[out] param_lengths SQL length data to set @@ -41,43 +43,76 @@ * @return -1 on error, number of offsets used in @a scratch otherwise */ static int -qconv_amount_nbo (void *cls, - const void *data, - size_t data_len, - void *param_values[], - int param_lengths[], - int param_formats[], - unsigned int param_length, - void *scratch[], - unsigned int scratch_length) +qconv_amount_currency_tuple (void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length) { - const struct TALER_AmountNBO *amount = data; - unsigned int off = 0; + struct GNUNET_PQ_Context *db = cls; + const struct TALER_Amount *amount = data; + size_t sz; - (void) cls; - (void) scratch; - (void) scratch_length; - GNUNET_assert (sizeof (struct TALER_AmountNBO) == data_len); - GNUNET_assert (2 == param_length); - param_values[off] = (void *) &amount->value; - param_lengths[off] = sizeof (amount->value); - param_formats[off] = 1; - off++; - param_values[off] = (void *) &amount->fraction; - param_lengths[off] = sizeof (amount->fraction); - param_formats[off] = 1; - return 0; + GNUNET_assert (NULL != db); + GNUNET_assert (NULL != amount); + GNUNET_assert (1 == param_length); + GNUNET_assert (1 <= scratch_length); + GNUNET_assert (sizeof (struct TALER_Amount) == data_len); + GNUNET_static_assert (sizeof(uint32_t) == sizeof(Oid)); + { + char *out; + Oid oid_v; + Oid oid_f; + Oid oid_c; + struct TALER_PQ_AmountCurrencyP d; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int4", + &oid_f)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "varchar", + &oid_c)); + sz = TALER_PQ_make_taler_pq_amount_currency_ (amount, + oid_v, + oid_f, + oid_c, + &d); + out = GNUNET_malloc (sz); + memcpy (out, + &d, + sz); + scratch[0] = out; + } + + param_values[0] = scratch[0]; + param_lengths[0] = sz; + param_formats[0] = 1; + + return 1; } struct GNUNET_PQ_QueryParam -TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) +TALER_PQ_query_param_amount_with_currency ( + const struct GNUNET_PQ_Context *db, + const struct TALER_Amount *amount) { struct GNUNET_PQ_QueryParam res = { - .conv = &qconv_amount_nbo, - .data = x, - .size = sizeof (*x), - .num_params = 2 + .conv_cls = (void *) db, + .conv = &qconv_amount_currency_tuple, + .data = amount, + .size = sizeof (*amount), + .num_params = 1, }; return res; @@ -85,7 +120,7 @@ TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) /** - * Function called to convert input argument into SQL parameters. + * Function called to convert input amount into SQL parameter as tuple. * * @param cls closure * @param data pointer to input argument, here a `struct TALER_Amount` @@ -99,49 +134,74 @@ TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) * @return -1 on error, number of offsets used in @a scratch otherwise */ static int -qconv_amount (void *cls, - const void *data, - size_t data_len, - void *param_values[], - int param_lengths[], - int param_formats[], - unsigned int param_length, - void *scratch[], - unsigned int scratch_length) +qconv_amount_tuple (void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length) { - const struct TALER_Amount *amount_hbo = data; - struct TALER_AmountNBO *amount; + struct GNUNET_PQ_Context *db = cls; + const struct TALER_Amount *amount = data; + size_t sz; + + GNUNET_assert (NULL != db); + GNUNET_assert (NULL != amount); + GNUNET_assert (1 == param_length); + GNUNET_assert (1 <= scratch_length); + GNUNET_assert (sizeof (struct TALER_Amount) == data_len); + GNUNET_static_assert (sizeof(uint32_t) == sizeof(Oid)); + { + char *out; + Oid oid_v; + Oid oid_f; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "int4", + &oid_f)); + + { + struct TALER_PQ_AmountP d + = TALER_PQ_make_taler_pq_amount_ (amount, + oid_v, + oid_f); + + sz = sizeof(d); + out = GNUNET_malloc (sz); + scratch[0] = out; + GNUNET_memcpy (out, + &d, + sizeof(d)); + } + } + + param_values[0] = scratch[0]; + param_lengths[0] = sz; + param_formats[0] = 1; - (void) cls; - (void) scratch; - (void) scratch_length; - GNUNET_assert (2 == param_length); - GNUNET_assert (sizeof (struct TALER_AmountNBO) == data_len); - amount = GNUNET_new (struct TALER_AmountNBO); - scratch[0] = amount; - TALER_amount_hton (amount, - amount_hbo); - qconv_amount_nbo (cls, - amount, - sizeof (struct TALER_AmountNBO), - param_values, - param_lengths, - param_formats, - param_length, - &scratch[1], - scratch_length - 1); return 1; } struct GNUNET_PQ_QueryParam -TALER_PQ_query_param_amount (const struct TALER_Amount *x) +TALER_PQ_query_param_amount ( + const struct GNUNET_PQ_Context *db, + const struct TALER_Amount *amount) { struct GNUNET_PQ_QueryParam res = { - .conv = &qconv_amount, - .data = x, - .size = sizeof (*x), - .num_params = 2 + .conv_cls = (void *) db, + .conv = &qconv_amount_tuple, + .data = amount, + .size = sizeof (*amount), + .num_params = 1, }; return res; @@ -174,6 +234,7 @@ qconv_denom_pub (void *cls, unsigned int scratch_length) { const struct TALER_DenominationPublicKey *denom_pub = data; + const struct GNUNET_CRYPTO_BlindSignPublicKey *bsp = denom_pub->bsign_pub_key; size_t tlen; size_t len; uint32_t be[2]; @@ -185,38 +246,38 @@ qconv_denom_pub (void *cls, GNUNET_assert (1 == param_length); GNUNET_assert (scratch_length > 0); GNUNET_break (NULL == cls); - be[0] = htonl ((uint32_t) denom_pub->cipher); + be[0] = htonl ((uint32_t) bsp->cipher); be[1] = htonl (denom_pub->age_mask.bits); - switch (denom_pub->cipher) + switch (bsp->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: tlen = GNUNET_CRYPTO_rsa_public_key_encode ( - denom_pub->details.rsa_public_key, + bsp->details.rsa_public_key, &tbuf); break; - case TALER_DENOMINATION_CS: - tlen = sizeof (denom_pub->details.cs_public_key); + case GNUNET_CRYPTO_BSA_CS: + tlen = sizeof (bsp->details.cs_public_key); break; default: GNUNET_assert (0); } len = tlen + sizeof (be); buf = GNUNET_malloc (len); - memcpy (buf, - be, - sizeof (be)); - switch (denom_pub->cipher) + GNUNET_memcpy (buf, + be, + sizeof (be)); + switch (bsp->cipher) { - case TALER_DENOMINATION_RSA: - memcpy (&buf[sizeof (be)], - tbuf, - tlen); + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_memcpy (&buf[sizeof (be)], + tbuf, + tlen); GNUNET_free (tbuf); break; - case TALER_DENOMINATION_CS: - memcpy (&buf[sizeof (be)], - &denom_pub->details.cs_public_key, - tlen); + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (&buf[sizeof (be)], + &bsp->details.cs_public_key, + tlen); break; default: GNUNET_assert (0); @@ -270,6 +331,7 @@ qconv_denom_sig (void *cls, unsigned int scratch_length) { const struct TALER_DenominationSignature *denom_sig = data; + const struct GNUNET_CRYPTO_UnblindedSignature *ubs = denom_sig->unblinded_sig; size_t tlen; size_t len; uint32_t be[2]; @@ -281,38 +343,38 @@ qconv_denom_sig (void *cls, GNUNET_assert (1 == param_length); GNUNET_assert (scratch_length > 0); GNUNET_break (NULL == cls); - be[0] = htonl ((uint32_t) denom_sig->cipher); + be[0] = htonl ((uint32_t) ubs->cipher); be[1] = htonl (0x00); /* magic marker: unblinded */ - switch (denom_sig->cipher) + switch (ubs->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: tlen = GNUNET_CRYPTO_rsa_signature_encode ( - denom_sig->details.rsa_signature, + ubs->details.rsa_signature, &tbuf); break; - case TALER_DENOMINATION_CS: - tlen = sizeof (denom_sig->details.cs_signature); + case GNUNET_CRYPTO_BSA_CS: + tlen = sizeof (ubs->details.cs_signature); break; default: GNUNET_assert (0); } len = tlen + sizeof (be); buf = GNUNET_malloc (len); - memcpy (buf, - &be, - sizeof (be)); - switch (denom_sig->cipher) + GNUNET_memcpy (buf, + &be, + sizeof (be)); + switch (ubs->cipher) { - case TALER_DENOMINATION_RSA: - memcpy (&buf[sizeof (be)], - tbuf, - tlen); + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_memcpy (&buf[sizeof (be)], + tbuf, + tlen); GNUNET_free (tbuf); break; - case TALER_DENOMINATION_CS: - memcpy (&buf[sizeof (be)], - &denom_sig->details.cs_signature, - tlen); + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (&buf[sizeof (be)], + &ubs->details.cs_signature, + tlen); break; default: GNUNET_assert (0); @@ -366,6 +428,7 @@ qconv_blinded_denom_sig (void *cls, unsigned int scratch_length) { const struct TALER_BlindedDenominationSignature *denom_sig = data; + const struct GNUNET_CRYPTO_BlindedSignature *bs = denom_sig->blinded_sig; size_t tlen; size_t len; uint32_t be[2]; @@ -377,38 +440,38 @@ qconv_blinded_denom_sig (void *cls, GNUNET_assert (1 == param_length); GNUNET_assert (scratch_length > 0); GNUNET_break (NULL == cls); - be[0] = htonl ((uint32_t) denom_sig->cipher); + be[0] = htonl ((uint32_t) bs->cipher); be[1] = htonl (0x01); /* magic marker: blinded */ - switch (denom_sig->cipher) + switch (bs->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: tlen = GNUNET_CRYPTO_rsa_signature_encode ( - denom_sig->details.blinded_rsa_signature, + bs->details.blinded_rsa_signature, &tbuf); break; - case TALER_DENOMINATION_CS: - tlen = sizeof (denom_sig->details.blinded_cs_answer); + case GNUNET_CRYPTO_BSA_CS: + tlen = sizeof (bs->details.blinded_cs_answer); break; default: GNUNET_assert (0); } len = tlen + sizeof (be); buf = GNUNET_malloc (len); - memcpy (buf, - &be, - sizeof (be)); - switch (denom_sig->cipher) + GNUNET_memcpy (buf, + &be, + sizeof (be)); + switch (bs->cipher) { - case TALER_DENOMINATION_RSA: - memcpy (&buf[sizeof (be)], - tbuf, - tlen); + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_memcpy (&buf[sizeof (be)], + tbuf, + tlen); GNUNET_free (tbuf); break; - case TALER_DENOMINATION_CS: - memcpy (&buf[sizeof (be)], - &denom_sig->details.blinded_cs_answer, - tlen); + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (&buf[sizeof (be)], + &bs->details.blinded_cs_answer, + tlen); break; default: GNUNET_assert (0); @@ -462,6 +525,7 @@ qconv_blinded_planchet (void *cls, unsigned int scratch_length) { const struct TALER_BlindedPlanchet *bp = data; + const struct GNUNET_CRYPTO_BlindedMessage *bm = bp->blinded_message; size_t tlen; size_t len; uint32_t be[2]; @@ -472,35 +536,35 @@ qconv_blinded_planchet (void *cls, GNUNET_assert (1 == param_length); GNUNET_assert (scratch_length > 0); GNUNET_break (NULL == cls); - be[0] = htonl ((uint32_t) bp->cipher); + be[0] = htonl ((uint32_t) bm->cipher); be[1] = htonl (0x0100); /* magic marker: blinded */ - switch (bp->cipher) + switch (bm->cipher) { - case TALER_DENOMINATION_RSA: - tlen = bp->details.rsa_blinded_planchet.blinded_msg_size; + case GNUNET_CRYPTO_BSA_RSA: + tlen = bm->details.rsa_blinded_message.blinded_msg_size; break; - case TALER_DENOMINATION_CS: - tlen = sizeof (bp->details.cs_blinded_planchet); + case GNUNET_CRYPTO_BSA_CS: + tlen = sizeof (bm->details.cs_blinded_message); break; default: GNUNET_assert (0); } len = tlen + sizeof (be); buf = GNUNET_malloc (len); - memcpy (buf, - &be, - sizeof (be)); - switch (bp->cipher) + GNUNET_memcpy (buf, + &be, + sizeof (be)); + switch (bm->cipher) { - case TALER_DENOMINATION_RSA: - memcpy (&buf[sizeof (be)], - bp->details.rsa_blinded_planchet.blinded_msg, - tlen); + case GNUNET_CRYPTO_BSA_RSA: + GNUNET_memcpy (&buf[sizeof (be)], + bm->details.rsa_blinded_message.blinded_msg, + tlen); break; - case TALER_DENOMINATION_CS: - memcpy (&buf[sizeof (be)], - &bp->details.cs_blinded_planchet, - tlen); + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (&buf[sizeof (be)], + &bm->details.cs_blinded_message, + tlen); break; default: GNUNET_assert (0); @@ -553,6 +617,8 @@ qconv_exchange_withdraw_values (void *cls, unsigned int scratch_length) { const struct TALER_ExchangeWithdrawValues *alg_values = data; + const struct GNUNET_CRYPTO_BlindingInputValues *bi = + alg_values->blinding_inputs; size_t tlen; size_t len; uint32_t be[2]; @@ -563,32 +629,32 @@ qconv_exchange_withdraw_values (void *cls, GNUNET_assert (1 == param_length); GNUNET_assert (scratch_length > 0); GNUNET_break (NULL == cls); - be[0] = htonl ((uint32_t) alg_values->cipher); + be[0] = htonl ((uint32_t) bi->cipher); be[1] = htonl (0x010000); /* magic marker: EWV */ - switch (alg_values->cipher) + switch (bi->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: tlen = 0; break; - case TALER_DENOMINATION_CS: - tlen = sizeof (struct TALER_DenominationCSPublicRPairP); + case GNUNET_CRYPTO_BSA_CS: + tlen = sizeof (struct GNUNET_CRYPTO_CSPublicRPairP); break; default: GNUNET_assert (0); } len = tlen + sizeof (be); buf = GNUNET_malloc (len); - memcpy (buf, - &be, - sizeof (be)); - switch (alg_values->cipher) + GNUNET_memcpy (buf, + &be, + sizeof (be)); + switch (bi->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_RSA: break; - case TALER_DENOMINATION_CS: - memcpy (&buf[sizeof (be)], - &alg_values->details.cs_values, - tlen); + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (&buf[sizeof (be)], + &bi->details.cs_values, + tlen); break; default: GNUNET_assert (0); @@ -671,4 +737,603 @@ TALER_PQ_query_param_json (const json_t *x) } +/** ------------------- Array support -----------------------------------**/ + +/** + * Closure for the array type handlers. + * + * May contain sizes information for the data, given (and handled) by the + * caller. + */ +struct qconv_array_cls +{ + /** + * If not null, contains the array of sizes (the size of the array is the + * .size field in the ambient GNUNET_PQ_QueryParam struct). We do not free + * this memory. + * + * If not null, this value has precedence over @a sizes, which MUST be NULL */ + const size_t *sizes; + + /** + * If @a size and @a c_sizes are NULL, this field defines the same size + * for each element in the array. + */ + size_t same_size; + + /** + * If true, the array parameter to the data pointer to the qconv_array is a + * continuous byte array of data, either with @a same_size each or sizes + * provided bytes by @a sizes; + */ + bool continuous; + + /** + * Type of the array elements + */ + enum TALER_PQ_ArrayType typ; + + /** + * Oid of the array elements + */ + Oid oid; + + /** + * db context, needed for OID-lookup of basis-types + */ + struct GNUNET_PQ_Context *db; +}; + +/** + * Callback to cleanup a qconv_array_cls to be used during + * GNUNET_PQ_cleanup_query_params_closures + */ +static void +qconv_array_cls_cleanup (void *cls) +{ + GNUNET_free (cls); +} + + +/** + * Function called to convert input argument into SQL parameters for arrays + * + * Note: the format for the encoding of arrays for libpq is not very well + * documented. We peeked into various sources (postgresql and libpqtypes) for + * guidance. + * + * @param cls Closure of type struct qconv_array_cls* + * @param data Pointer to first element in the array + * @param data_len Number of _elements_ in array @a data (if applicable) + * @param[out] param_values SQL data to set + * @param[out] param_lengths SQL length data to set + * @param[out] param_formats SQL format data to set + * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays + * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc() + * @param scratch_length number of entries left in @a scratch + * @return -1 on error, number of offsets used in @a scratch otherwise + */ +static int +qconv_array ( + void *cls, + const void *data, + size_t data_len, + void *param_values[], + int param_lengths[], + int param_formats[], + unsigned int param_length, + void *scratch[], + unsigned int scratch_length) +{ + struct qconv_array_cls *meta = cls; + size_t num = data_len; + size_t total_size; + const size_t *sizes; + bool same_sized; + void *elements = NULL; + bool noerror = true; + /* needed to capture the encoded rsa signatures */ + void **buffers = NULL; + size_t *buffer_lengths = NULL; + + (void) (param_length); + (void) (scratch_length); + + GNUNET_assert (NULL != meta); + GNUNET_assert (num < INT_MAX); + + sizes = meta->sizes; + same_sized = (0 != meta->same_size); + +#define RETURN_UNLESS(cond) \ + do { \ + if (! (cond)) \ + { \ + GNUNET_break ((cond)); \ + noerror = false; \ + goto DONE; \ + } \ + } while (0) + + /* Calculate sizes and check bounds */ + { + /* num * length-field */ + size_t x = sizeof(uint32_t); + size_t y = x * num; + RETURN_UNLESS ((0 == num) || (y / num == x)); + + /* size of header */ + total_size = x = sizeof(struct GNUNET_PQ_ArrayHeader_P); + total_size += y; + RETURN_UNLESS (total_size >= x); + + /* sizes of elements */ + if (same_sized) + { + x = num * meta->same_size; + RETURN_UNLESS ((0 == num) || (x / num == meta->same_size)); + + y = total_size; + total_size += x; + RETURN_UNLESS (total_size >= y); + } + else /* sizes are different per element */ + { + switch (meta->typ) + { + case TALER_PQ_array_of_amount_currency: + { + const struct TALER_Amount *amounts = data; + Oid oid_v; + Oid oid_f; + Oid oid_c; + + buffer_lengths = GNUNET_new_array (num, size_t); + /* hoist out of loop? */ + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int4", + &oid_f)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "varchar", + &oid_c)); + for (size_t i = 0; i<num; i++) + { + struct TALER_PQ_AmountCurrencyP am; + size_t len; + + len = TALER_PQ_make_taler_pq_amount_currency_ ( + &amounts[i], + oid_v, + oid_f, + oid_c, + &am); + buffer_lengths[i] = len; + y = total_size; + total_size += len; + RETURN_UNLESS (total_size >= y); + } + sizes = buffer_lengths; + break; + } + case TALER_PQ_array_of_blinded_denom_sig: + { + const struct TALER_BlindedDenominationSignature *denom_sigs = data; + size_t len; + + buffers = GNUNET_new_array (num, void *); + buffer_lengths = GNUNET_new_array (num, size_t); + + for (size_t i = 0; i<num; i++) + { + const struct GNUNET_CRYPTO_BlindedSignature *bs = + denom_sigs[i].blinded_sig; + + switch (bs->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + len = GNUNET_CRYPTO_rsa_signature_encode ( + bs->details.blinded_rsa_signature, + &buffers[i]); + RETURN_UNLESS (len != 0); + break; + case GNUNET_CRYPTO_BSA_CS: + len = sizeof (bs->details.blinded_cs_answer); + break; + default: + GNUNET_assert (0); + } + + /* for the cipher and marker */ + len += 2 * sizeof(uint32_t); + buffer_lengths[i] = len; + + y = total_size; + total_size += len; + RETURN_UNLESS (total_size >= y); + } + sizes = buffer_lengths; + break; + } + default: + GNUNET_assert (0); + } + } + + RETURN_UNLESS (INT_MAX > total_size); + RETURN_UNLESS (0 != total_size); + + elements = GNUNET_malloc (total_size); + } + + /* Write data */ + { + char *out = elements; + struct GNUNET_PQ_ArrayHeader_P h = { + .ndim = htonl (1), /* We only support one-dimensional arrays */ + .has_null = htonl (0), /* We do not support NULL entries in arrays */ + .lbound = htonl (1), /* Default start index value */ + .dim = htonl (num), + .oid = htonl (meta->oid), + }; + + /* Write header */ + GNUNET_memcpy (out, + &h, + sizeof(h)); + out += sizeof(h); + + /* Write elements */ + for (size_t i = 0; i < num; i++) + { + size_t sz = same_sized ? meta->same_size : sizes[i]; + + *(uint32_t *) out = htonl (sz); + out += sizeof(uint32_t); + switch (meta->typ) + { + case TALER_PQ_array_of_amount: + { + const struct TALER_Amount *amounts = data; + Oid oid_v; + Oid oid_f; + + /* hoist out of loop? */ + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int4", + &oid_f)); + { + struct TALER_PQ_AmountP am + = TALER_PQ_make_taler_pq_amount_ ( + &amounts[i], + oid_v, + oid_f); + + GNUNET_memcpy (out, + &am, + sizeof(am)); + } + break; + } + case TALER_PQ_array_of_amount_currency: + { + const struct TALER_Amount *amounts = data; + Oid oid_v; + Oid oid_f; + Oid oid_c; + + /* hoist out of loop? */ + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int8", + &oid_v)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "int4", + &oid_f)); + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (meta->db, + "varchar", + &oid_c)); + { + struct TALER_PQ_AmountCurrencyP am; + size_t len; + + len = TALER_PQ_make_taler_pq_amount_currency_ ( + &amounts[i], + oid_v, + oid_f, + oid_c, + &am); + GNUNET_memcpy (out, + &am, + len); + } + break; + } + case TALER_PQ_array_of_blinded_denom_sig: + { + const struct TALER_BlindedDenominationSignature *denom_sigs = data; + const struct GNUNET_CRYPTO_BlindedSignature *bs = + denom_sigs[i].blinded_sig; + uint32_t be[2]; + + be[0] = htonl ((uint32_t) bs->cipher); + be[1] = htonl (0x01); /* magic margker: blinded */ + GNUNET_memcpy (out, + &be, + sizeof(be)); + out += sizeof(be); + sz -= sizeof(be); + + switch (bs->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + /* For RSA, 'same_sized' must have been false */ + GNUNET_assert (NULL != buffers); + GNUNET_memcpy (out, + buffers[i], + sz); + break; + case GNUNET_CRYPTO_BSA_CS: + GNUNET_memcpy (out, + &bs->details.blinded_cs_answer, + sz); + break; + default: + GNUNET_assert (0); + } + break; + } + case TALER_PQ_array_of_blinded_coin_hash: + { + const struct TALER_BlindedCoinHashP *coin_hs = data; + + GNUNET_memcpy (out, + &coin_hs[i], + sizeof(struct TALER_BlindedCoinHashP)); + + break; + } + case TALER_PQ_array_of_denom_hash: + { + const struct TALER_DenominationHashP *denom_hs = data; + + GNUNET_memcpy (out, + &denom_hs[i], + sizeof(struct TALER_DenominationHashP)); + break; + } + case TALER_PQ_array_of_hash_code: + { + const struct GNUNET_HashCode *hashes = data; + + GNUNET_memcpy (out, + &hashes[i], + sizeof(struct GNUNET_HashCode)); + break; + } + default: + { + GNUNET_assert (0); + break; + } + } + out += sz; + } + } + param_values[0] = elements; + param_lengths[0] = total_size; + param_formats[0] = 1; + scratch[0] = elements; + +DONE: + if (NULL != buffers) + { + for (size_t i = 0; i<num; i++) + GNUNET_free (buffers[i]); + GNUNET_free (buffers); + } + GNUNET_free (buffer_lengths); + if (noerror) + return 1; + return -1; +} + + +/** + * Function to generate a typ specific query parameter and corresponding closure + * + * @param num Number of elements in @a elements + * @param continuous If true, @a elements is an continuous array of data + * @param elements Array of @a num elements, either continuous or pointers + * @param sizes Array of @a num sizes, one per element, may be NULL + * @param same_size If not 0, all elements in @a elements have this size + * @param typ Supported internal type of each element in @a elements + * @param oid Oid of the type to be used in Postgres + * @param[in,out] db our database handle for looking up OIDs + * @return Query parameter + */ +static struct GNUNET_PQ_QueryParam +query_param_array_generic ( + unsigned int num, + bool continuous, + const void *elements, + const size_t *sizes, + size_t same_size, + enum TALER_PQ_ArrayType typ, + Oid oid, + struct GNUNET_PQ_Context *db) +{ + struct qconv_array_cls *meta = GNUNET_new (struct qconv_array_cls); + + meta->typ = typ; + meta->oid = oid; + meta->sizes = sizes; + meta->same_size = same_size; + meta->continuous = continuous; + meta->db = db; + + { + struct GNUNET_PQ_QueryParam res = { + .conv = qconv_array, + .conv_cls = meta, + .conv_cls_cleanup = qconv_array_cls_cleanup, + .data = elements, + .size = num, + .num_params = 1, + }; + + return res; + } +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_denom_sig ( + size_t num, + const struct TALER_BlindedDenominationSignature *denom_sigs, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &oid)); + return query_param_array_generic (num, + true, + denom_sigs, + NULL, + 0, + TALER_PQ_array_of_blinded_denom_sig, + oid, + NULL); +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_blinded_coin_hash ( + size_t num, + const struct TALER_BlindedCoinHashP *coin_hs, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &oid)); + return query_param_array_generic (num, + true, + coin_hs, + NULL, + sizeof(struct TALER_BlindedCoinHashP), + TALER_PQ_array_of_blinded_coin_hash, + oid, + NULL); +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_denom_hash ( + size_t num, + const struct TALER_DenominationHashP *denom_hs, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &oid)); + return query_param_array_generic (num, + true, + denom_hs, + NULL, + sizeof(struct TALER_DenominationHashP), + TALER_PQ_array_of_denom_hash, + oid, + NULL); +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_hash_code ( + size_t num, + const struct GNUNET_HashCode *hashes, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, "gnunet_hashcode", &oid)); + return query_param_array_generic (num, + true, + hashes, + NULL, + sizeof(struct GNUNET_HashCode), + TALER_PQ_array_of_hash_code, + oid, + NULL); +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_amount ( + size_t num, + const struct TALER_Amount *amounts, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "taler_amount", + &oid)); + return query_param_array_generic ( + num, + true, + amounts, + NULL, + sizeof(struct TALER_PQ_AmountP), + TALER_PQ_array_of_amount, + oid, + db); +} + + +struct GNUNET_PQ_QueryParam +TALER_PQ_query_param_array_amount_with_currency ( + size_t num, + const struct TALER_Amount *amounts, + struct GNUNET_PQ_Context *db) +{ + Oid oid; + + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "taler_amount_currency", + &oid)); + return query_param_array_generic ( + num, + true, + amounts, + NULL, + 0, /* currency is technically variable length */ + TALER_PQ_array_of_amount_currency, + oid, + db); +} + + /* end of pq/pq_query_helper.c */ diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c index 139cf1cbf..384200cfb 100644 --- a/src/pq/pq_result_helper.c +++ b/src/pq/pq_result_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2022 Taler Systems SA + Copyright (C) 2014-2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -20,164 +20,144 @@ */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> +#include "pq_common.h" #include "taler_pq_lib.h" /** - * Extract a currency amount from a query result according to the - * given specification. + * Extract an amount from a tuple including the currency from a Postgres + * database @a result at row @a row. * - * @param result the result to extract the amount from - * @param row which row of the result to extract the amount from (needed as results can have multiple rows) - * @param currency currency to use for @a r_amount_nbo - * @param val_name name of the column with the amount's "value", must include the substring "_val". - * @param frac_name name of the column with the amount's "fractional" value, must include the substring "_frac". - * @param[out] r_amount_nbo where to store the amount, in network byte order + * @param cls closure; not used + * @param result where to extract data from + * @param row row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result * @return * #GNUNET_YES if all results could be extracted * #GNUNET_NO if at least one result was NULL * #GNUNET_SYSERR if a result was invalid (non-existing field) */ static enum GNUNET_GenericReturnValue -extract_amount_nbo_helper (PGresult *result, - int row, - const char *currency, - const char *val_name, - const char *frac_name, - struct TALER_AmountNBO *r_amount_nbo) +extract_amount_currency_tuple (void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst) { - int val_num; - int frac_num; - int len; - - /* These checks are simply to check that clients obey by our naming - conventions, and not for any functional reason */ - GNUNET_assert (NULL != - strstr (val_name, - "_val")); - GNUNET_assert (NULL != - strstr (frac_name, - "_frac")); - /* Set return value to invalid in case we don't finish */ - memset (r_amount_nbo, - 0, - sizeof (struct TALER_AmountNBO)); - val_num = PQfnumber (result, - val_name); - frac_num = PQfnumber (result, - frac_name); - if (val_num < 0) + struct TALER_Amount *r_amount = dst; + int col; + + (void) cls; + if (sizeof (struct TALER_Amount) != *dst_size) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Field `%s' does not exist in result\n", - val_name); + GNUNET_break (0); return GNUNET_SYSERR; } - if (frac_num < 0) + + /* Set return value to invalid in case we don't finish */ + memset (r_amount, + 0, + sizeof (struct TALER_Amount)); + col = PQfnumber (result, + fname); + if (col < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Field `%s' does not exist in result\n", - frac_name); + fname); return GNUNET_SYSERR; } - if ( (PQgetisnull (result, - row, - val_num)) || - (PQgetisnull (result, - row, - frac_num)) ) + if (PQgetisnull (result, + row, + col)) { return GNUNET_NO; } - /* Note that Postgres stores value in NBO internally, - so no conversion needed in this case */ - r_amount_nbo->value = *(uint64_t *) PQgetvalue (result, - row, - val_num); - r_amount_nbo->fraction = *(uint32_t *) PQgetvalue (result, - row, - frac_num); - if (GNUNET_ntohll (r_amount_nbo->value) >= TALER_AMOUNT_MAX_VALUE) + + /* Parse the tuple */ { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Field `%s' exceeds legal range\n", - val_name); - return GNUNET_SYSERR; + struct TALER_PQ_AmountCurrencyP ap; + const char *in; + size_t size; + + size = PQgetlength (result, + row, + col); + if ( (size >= sizeof (ap)) || + (size <= sizeof (ap) - TALER_CURRENCY_LEN) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect size of binary field `%s' (got %zu, expected (%zu-%zu))\n", + fname, + size, + sizeof (ap) - TALER_CURRENCY_LEN, + sizeof (ap)); + return GNUNET_SYSERR; + } + + in = PQgetvalue (result, + row, + col); + memset (&ap.c, + 0, + TALER_CURRENCY_LEN); + memcpy (&ap, + in, + size); + if (3 != ntohl (ap.cnt)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect number of elements in tuple-field `%s'\n", + fname); + return GNUNET_SYSERR; + } + /* TODO[oec]: OID-checks? */ + + r_amount->value = GNUNET_ntohll (ap.v); + r_amount->fraction = ntohl (ap.f); + memcpy (r_amount->currency, + ap.c, + TALER_CURRENCY_LEN); + if ('\0' != r_amount->currency[TALER_CURRENCY_LEN - 1]) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid currency (not 0-terminated) in tuple field `%s'\n", + fname); + /* be sure nobody uses this by accident */ + memset (r_amount, + 0, + sizeof (struct TALER_Amount)); + return GNUNET_SYSERR; + } } - if (ntohl (r_amount_nbo->fraction) >= TALER_AMOUNT_FRAC_BASE) + + if (r_amount->value >= TALER_AMOUNT_MAX_VALUE) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Field `%s' exceeds legal range\n", - frac_name); + "Value in field `%s' exceeds legal range\n", + fname); return GNUNET_SYSERR; } - len = GNUNET_MIN (TALER_CURRENCY_LEN - 1, - strlen (currency)); - memcpy (r_amount_nbo->currency, - currency, - len); - return GNUNET_OK; -} - - -/** - * Extract data from a Postgres database @a result at row @a row. - * - * @param cls closure, a `const char *` giving the currency - * @param result where to extract data from - * @param row row to extract data from - * @param fname name (or prefix) of the fields to extract from - * @param[in,out] dst_size where to store size of result, may be NULL - * @param[out] dst where to store the result - * @return - * #GNUNET_YES if all results could be extracted - * #GNUNET_NO if at least one result was NULL - * #GNUNET_SYSERR if a result was invalid (non-existing field) - */ -static enum GNUNET_GenericReturnValue -extract_amount_nbo (void *cls, - PGresult *result, - int row, - const char *fname, - size_t *dst_size, - void *dst) -{ - const char *currency = cls; - char *val_name; - char *frac_name; - enum GNUNET_GenericReturnValue ret; - - if (sizeof (struct TALER_AmountNBO) != *dst_size) + if (r_amount->fraction >= TALER_AMOUNT_FRAC_BASE) { - GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fraction in field `%s' exceeds legal range\n", + fname); return GNUNET_SYSERR; } - GNUNET_asprintf (&val_name, - "%s_val", - fname); - GNUNET_asprintf (&frac_name, - "%s_frac", - fname); - ret = extract_amount_nbo_helper (result, - row, - currency, - val_name, - frac_name, - dst); - GNUNET_free (val_name); - GNUNET_free (frac_name); - return ret; + return GNUNET_OK; } struct GNUNET_PQ_ResultSpec -TALER_PQ_result_spec_amount_nbo (const char *name, - const char *currency, - struct TALER_AmountNBO *amount) +TALER_PQ_result_spec_amount_with_currency (const char *name, + struct TALER_Amount *amount) { struct GNUNET_PQ_ResultSpec res = { - .conv = &extract_amount_nbo, - .cls = (void *) currency, + .conv = &extract_amount_currency_tuple, .dst = (void *) amount, .dst_size = sizeof (*amount), .fname = name @@ -188,7 +168,7 @@ TALER_PQ_result_spec_amount_nbo (const char *name, /** - * Extract data from a Postgres database @a result at row @a row. + * Extract an amount from a tuple from a Postgres database @a result at row @a row. * * @param cls closure, a `const char *` giving the currency * @param result where to extract data from @@ -202,47 +182,125 @@ TALER_PQ_result_spec_amount_nbo (const char *name, * #GNUNET_SYSERR if a result was invalid (non-existing field) */ static enum GNUNET_GenericReturnValue -extract_amount (void *cls, - PGresult *result, - int row, - const char *fname, - size_t *dst_size, - void *dst) +extract_amount_tuple (void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst) { - const char *currency = cls; struct TALER_Amount *r_amount = dst; - char *val_name; - char *frac_name; - struct TALER_AmountNBO amount_nbo; - enum GNUNET_GenericReturnValue ret; + const char *currency = cls; + int col; + size_t len; - if (sizeof (struct TALER_AmountNBO) != *dst_size) + if (sizeof (struct TALER_Amount) != *dst_size) { GNUNET_break (0); return GNUNET_SYSERR; } - GNUNET_asprintf (&val_name, - "%s_val", - fname); - GNUNET_asprintf (&frac_name, - "%s_frac", + + /* Set return value to invalid in case we don't finish */ + memset (r_amount, + 0, + sizeof (struct TALER_Amount)); + col = PQfnumber (result, fname); - ret = extract_amount_nbo_helper (result, - row, - currency, - val_name, - frac_name, - &amount_nbo); - if (GNUNET_OK == ret) - TALER_amount_ntoh (r_amount, - &amount_nbo); - else - memset (r_amount, - 0, - sizeof (struct TALER_Amount)); - GNUNET_free (val_name); - GNUNET_free (frac_name); - return ret; + if (col < 0) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Field `%s' does not exist in result\n", + fname); + return GNUNET_SYSERR; + } + if (PQgetisnull (result, + row, + col)) + { + return GNUNET_NO; + } + + /* Parse the tuple */ + { + struct TALER_PQ_AmountP ap; + const char *in; + size_t size; + + size = PQgetlength (result, + row, + col); + in = PQgetvalue (result, + row, + col); + if (sizeof(struct TALER_PQ_AmountNullP) == size) + { + struct TALER_PQ_AmountNullP apn; + + memcpy (&apn, + in, + size); + if ( (2 == ntohl (apn.cnt)) && + (-1 == (int32_t) ntohl (apn.sz_v)) && + (-1 == (int32_t) ntohl (apn.sz_f)) ) + { + /* is NULL! */ + return GNUNET_NO; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect size of binary field `%s' and not NULL (got %zu, expected %zu)\n", + fname, + size, + sizeof(ap)); + return GNUNET_SYSERR; + } + if (sizeof(ap) != size) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect size of binary field `%s' (got %zu, expected %zu)\n", + fname, + size, + sizeof(ap)); + return GNUNET_SYSERR; + } + + memcpy (&ap, + in, + size); + if (2 != ntohl (ap.cnt)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Incorrect number of elements in tuple-field `%s'\n", + fname); + return GNUNET_SYSERR; + } + /* TODO[oec]: OID-checks? */ + + r_amount->value = GNUNET_ntohll (ap.v); + r_amount->fraction = ntohl (ap.f); + } + + if (r_amount->value >= TALER_AMOUNT_MAX_VALUE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Value in field `%s' exceeds legal range\n", + fname); + return GNUNET_SYSERR; + } + if (r_amount->fraction >= TALER_AMOUNT_FRAC_BASE) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fraction in field `%s' exceeds legal range\n", + fname); + return GNUNET_SYSERR; + } + + len = GNUNET_MIN (TALER_CURRENCY_LEN - 1, + strlen (currency)); + + GNUNET_memcpy (r_amount->currency, + currency, + len); + return GNUNET_OK; } @@ -252,7 +310,7 @@ TALER_PQ_result_spec_amount (const char *name, struct TALER_Amount *amount) { struct GNUNET_PQ_ResultSpec res = { - .conv = &extract_amount, + .conv = &extract_amount_tuple, .cls = (void *) currency, .dst = (void *) amount, .dst_size = sizeof (*amount), @@ -388,6 +446,7 @@ extract_denom_pub (void *cls, void *dst) { struct TALER_DenominationPublicKey *pk = dst; + struct GNUNET_CRYPTO_BlindSignPublicKey *bpk; size_t len; const char *res; int fnum; @@ -420,38 +479,52 @@ extract_denom_pub (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - memcpy (be, - res, - sizeof (be)); + GNUNET_memcpy (be, + res, + sizeof (be)); res += sizeof (be); len -= sizeof (be); - pk->cipher = ntohl (be[0]); + bpk = GNUNET_new (struct GNUNET_CRYPTO_BlindSignPublicKey); + bpk->cipher = ntohl (be[0]); + bpk->rc = 1; pk->age_mask.bits = ntohl (be[1]); - switch (pk->cipher) + switch (bpk->cipher) { - case TALER_DENOMINATION_RSA: - pk->details.rsa_public_key + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + bpk->details.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (res, len); - if (NULL == pk->details.rsa_public_key) + if (NULL == bpk->details.rsa_public_key) { GNUNET_break (0); + GNUNET_free (bpk); return GNUNET_SYSERR; } + pk->bsign_pub_key = bpk; + GNUNET_CRYPTO_hash (res, + len, + &bpk->pub_key_hash); return GNUNET_OK; - case TALER_DENOMINATION_CS: - if (sizeof (pk->details.cs_public_key) != len) + case GNUNET_CRYPTO_BSA_CS: + if (sizeof (bpk->details.cs_public_key) != len) { GNUNET_break (0); + GNUNET_free (bpk); return GNUNET_SYSERR; } - memcpy (&pk->details.cs_public_key, - res, - len); + GNUNET_memcpy (&bpk->details.cs_public_key, + res, + len); + pk->bsign_pub_key = bpk; + GNUNET_CRYPTO_hash (res, + len, + &bpk->pub_key_hash); return GNUNET_OK; - default: - GNUNET_break (0); } + GNUNET_break (0); + GNUNET_free (bpk); return GNUNET_SYSERR; } @@ -511,6 +584,7 @@ extract_denom_sig (void *cls, void *dst) { struct TALER_DenominationSignature *sig = dst; + struct GNUNET_CRYPTO_UnblindedSignature *ubs; size_t len; const char *res; int fnum; @@ -543,9 +617,9 @@ extract_denom_sig (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - memcpy (&be, - res, - sizeof (be)); + GNUNET_memcpy (&be, + res, + sizeof (be)); if (0x00 != ntohl (be[1])) { GNUNET_break (0); @@ -553,32 +627,40 @@ extract_denom_sig (void *cls, } res += sizeof (be); len -= sizeof (be); - sig->cipher = ntohl (be[0]); - switch (sig->cipher) + ubs = GNUNET_new (struct GNUNET_CRYPTO_UnblindedSignature); + ubs->rc = 1; + ubs->cipher = ntohl (be[0]); + switch (ubs->cipher) { - case TALER_DENOMINATION_RSA: - sig->details.rsa_signature + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + ubs->details.rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (res, len); - if (NULL == sig->details.rsa_signature) + if (NULL == ubs->details.rsa_signature) { GNUNET_break (0); + GNUNET_free (ubs); return GNUNET_SYSERR; } + sig->unblinded_sig = ubs; return GNUNET_OK; - case TALER_DENOMINATION_CS: - if (sizeof (sig->details.cs_signature) != len) + case GNUNET_CRYPTO_BSA_CS: + if (sizeof (ubs->details.cs_signature) != len) { GNUNET_break (0); + GNUNET_free (ubs); return GNUNET_SYSERR; } - memcpy (&sig->details.cs_signature, - res, - len); + GNUNET_memcpy (&ubs->details.cs_signature, + res, + len); + sig->unblinded_sig = ubs; return GNUNET_OK; - default: - GNUNET_break (0); } + GNUNET_break (0); + GNUNET_free (ubs); return GNUNET_SYSERR; } @@ -638,6 +720,7 @@ extract_blinded_denom_sig (void *cls, void *dst) { struct TALER_BlindedDenominationSignature *sig = dst; + struct GNUNET_CRYPTO_BlindedSignature *bs; size_t len; const char *res; int fnum; @@ -670,9 +753,9 @@ extract_blinded_denom_sig (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - memcpy (&be, - res, - sizeof (be)); + GNUNET_memcpy (&be, + res, + sizeof (be)); if (0x01 != ntohl (be[1])) /* magic marker: blinded */ { GNUNET_break (0); @@ -680,32 +763,40 @@ extract_blinded_denom_sig (void *cls, } res += sizeof (be); len -= sizeof (be); - sig->cipher = ntohl (be[0]); - switch (sig->cipher) + bs = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + bs->rc = 1; + bs->cipher = ntohl (be[0]); + switch (bs->cipher) { - case TALER_DENOMINATION_RSA: - sig->details.blinded_rsa_signature + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + bs->details.blinded_rsa_signature = GNUNET_CRYPTO_rsa_signature_decode (res, len); - if (NULL == sig->details.blinded_rsa_signature) + if (NULL == bs->details.blinded_rsa_signature) { GNUNET_break (0); + GNUNET_free (bs); return GNUNET_SYSERR; } + sig->blinded_sig = bs; return GNUNET_OK; - case TALER_DENOMINATION_CS: - if (sizeof (sig->details.blinded_cs_answer) != len) + case GNUNET_CRYPTO_BSA_CS: + if (sizeof (bs->details.blinded_cs_answer) != len) { GNUNET_break (0); + GNUNET_free (bs); return GNUNET_SYSERR; } - memcpy (&sig->details.blinded_cs_answer, - res, - len); + GNUNET_memcpy (&bs->details.blinded_cs_answer, + res, + len); + sig->blinded_sig = bs; return GNUNET_OK; - default: - GNUNET_break (0); } + GNUNET_break (0); + GNUNET_free (bs); return GNUNET_SYSERR; } @@ -766,6 +857,7 @@ extract_blinded_planchet (void *cls, void *dst) { struct TALER_BlindedPlanchet *bp = dst; + struct GNUNET_CRYPTO_BlindedMessage *bm; size_t len; const char *res; int fnum; @@ -798,9 +890,9 @@ extract_blinded_planchet (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - memcpy (&be, - res, - sizeof (be)); + GNUNET_memcpy (&be, + res, + sizeof (be)); if (0x0100 != ntohl (be[1])) /* magic marker: blinded */ { GNUNET_break (0); @@ -808,29 +900,36 @@ extract_blinded_planchet (void *cls, } res += sizeof (be); len -= sizeof (be); - bp->cipher = ntohl (be[0]); - switch (bp->cipher) + bm = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage); + bm->rc = 1; + bm->cipher = ntohl (be[0]); + switch (bm->cipher) { - case TALER_DENOMINATION_RSA: - bp->details.rsa_blinded_planchet.blinded_msg_size + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: + bm->details.rsa_blinded_message.blinded_msg_size = len; - bp->details.rsa_blinded_planchet.blinded_msg + bm->details.rsa_blinded_message.blinded_msg = GNUNET_memdup (res, len); + bp->blinded_message = bm; return GNUNET_OK; - case TALER_DENOMINATION_CS: - if (sizeof (bp->details.cs_blinded_planchet) != len) + case GNUNET_CRYPTO_BSA_CS: + if (sizeof (bm->details.cs_blinded_message) != len) { GNUNET_break (0); + GNUNET_free (bm); return GNUNET_SYSERR; } - memcpy (&bp->details.cs_blinded_planchet, - res, - len); + GNUNET_memcpy (&bm->details.cs_blinded_message, + res, + len); + bp->blinded_message = bm; return GNUNET_OK; - default: - GNUNET_break (0); } + GNUNET_break (0); + GNUNET_free (bm); return GNUNET_SYSERR; } @@ -891,6 +990,7 @@ extract_exchange_withdraw_values (void *cls, void *dst) { struct TALER_ExchangeWithdrawValues *alg_values = dst; + struct GNUNET_CRYPTO_BlindingInputValues *bi; size_t len; const char *res; int fnum; @@ -923,9 +1023,9 @@ extract_exchange_withdraw_values (void *cls, GNUNET_break (0); return GNUNET_SYSERR; } - memcpy (&be, - res, - sizeof (be)); + GNUNET_memcpy (&be, + res, + sizeof (be)); if (0x010000 != ntohl (be[1])) /* magic marker: EWV */ { GNUNET_break (0); @@ -933,29 +1033,37 @@ extract_exchange_withdraw_values (void *cls, } res += sizeof (be); len -= sizeof (be); - alg_values->cipher = ntohl (be[0]); - switch (alg_values->cipher) + bi = GNUNET_new (struct GNUNET_CRYPTO_BlindingInputValues); + bi->rc = 1; + bi->cipher = ntohl (be[0]); + switch (bi->cipher) { - case TALER_DENOMINATION_RSA: + case GNUNET_CRYPTO_BSA_INVALID: + break; + case GNUNET_CRYPTO_BSA_RSA: if (0 != len) { GNUNET_break (0); + GNUNET_free (bi); return GNUNET_SYSERR; } + alg_values->blinding_inputs = bi; return GNUNET_OK; - case TALER_DENOMINATION_CS: - if (sizeof (struct TALER_DenominationCSPublicRPairP) != len) + case GNUNET_CRYPTO_BSA_CS: + if (sizeof (bi->details.cs_values) != len) { GNUNET_break (0); + GNUNET_free (bi); return GNUNET_SYSERR; } - memcpy (&alg_values->details.cs_values, - res, - len); + GNUNET_memcpy (&bi->details.cs_values, + res, + len); + alg_values->blinding_inputs = bi; return GNUNET_OK; - default: - GNUNET_break (0); } + GNUNET_break (0); + GNUNET_free (bi); return GNUNET_SYSERR; } @@ -975,4 +1083,495 @@ TALER_PQ_result_spec_exchange_withdraw_values ( } +/** + * Closure for the array result specifications. Contains type information + * for the generic parser extract_array_generic and out-pointers for the results. + */ +struct ArrayResultCls +{ + /** + * Oid of the expected type, must match the oid in the header of the PQResult struct + */ + Oid oid; + + /** + * Target type + */ + enum TALER_PQ_ArrayType typ; + + /** + * If not 0, defines the expected size of each entry + */ + size_t same_size; + + /** + * Out-pointer to write the number of elements in the array + */ + size_t *num; + + /** + * Out-pointer. If @a typ is TALER_PQ_array_of_byte and @a same_size is 0, + * allocate and put the array of @a num sizes here. NULL otherwise + */ + size_t **sizes; + + /** + * DB_connection, needed for OID-lookup for composite types + */ + const struct GNUNET_PQ_Context *db; + + /** + * Currency information for amount composites + */ + char currency[TALER_CURRENCY_LEN]; +}; + + +/** + * Extract data from a Postgres database @a result as array of a specific type + * from row @a row. The type information and optionally additional + * out-parameters are given in @a cls which is of type array_result_cls. + * + * @param cls closure of type array_result_cls + * @param result where to extract data from + * @param row row to extract data from + * @param fname name (or prefix) of the fields to extract from + * @param[in,out] dst_size where to store size of result, may be NULL + * @param[out] dst where to store the result + * @return + * #GNUNET_YES if all results could be extracted + * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) + */ +static enum GNUNET_GenericReturnValue +extract_array_generic ( + void *cls, + PGresult *result, + int row, + const char *fname, + size_t *dst_size, + void *dst) +{ + const struct ArrayResultCls *info = cls; + int data_sz; + char *data; + void *out = NULL; + struct GNUNET_PQ_ArrayHeader_P header; + int col_num; + + GNUNET_assert (NULL != dst); + *((void **) dst) = NULL; + + #define FAIL_IF(cond) \ + do { \ + if ((cond)) \ + { \ + GNUNET_break (! (cond)); \ + goto FAIL; \ + } \ + } while (0) + + col_num = PQfnumber (result, fname); + FAIL_IF (0 > col_num); + + data_sz = PQgetlength (result, row, col_num); + FAIL_IF (0 > data_sz); + FAIL_IF (sizeof(header) > (size_t) data_sz); + + data = PQgetvalue (result, row, col_num); + FAIL_IF (NULL == data); + + { + struct GNUNET_PQ_ArrayHeader_P *h = + (struct GNUNET_PQ_ArrayHeader_P *) data; + + header.ndim = ntohl (h->ndim); + header.has_null = ntohl (h->has_null); + header.oid = ntohl (h->oid); + header.dim = ntohl (h->dim); + header.lbound = ntohl (h->lbound); + + FAIL_IF (1 != header.ndim); + FAIL_IF (INT_MAX <= header.dim); + FAIL_IF (0 != header.has_null); + FAIL_IF (1 != header.lbound); + FAIL_IF (info->oid != header.oid); + } + + if (NULL != info->num) + *info->num = header.dim; + + { + char *in = data + sizeof(header); + + switch (info->typ) + { + case TALER_PQ_array_of_amount: + { + struct TALER_Amount *amounts; + if (NULL != dst_size) + *dst_size = sizeof(struct TALER_Amount) * (header.dim); + + amounts = GNUNET_new_array (header.dim, + struct TALER_Amount); + *((void **) dst) = amounts; + + for (uint32_t i = 0; i < header.dim; i++) + { + struct TALER_PQ_AmountP ap; + struct TALER_Amount *amount = &amounts[i]; + uint32_t val; + size_t sz; + + GNUNET_memcpy (&val, + in, + sizeof(val)); + sz = ntohl (val); + in += sizeof(val); + + /* total size for this array-entry */ + FAIL_IF (sizeof(ap) != sz); + + GNUNET_memcpy (&ap, + in, + sz); + FAIL_IF (2 != ntohl (ap.cnt)); + + amount->value = GNUNET_ntohll (ap.v); + amount->fraction = ntohl (ap.f); + GNUNET_memcpy (amount->currency, + info->currency, + TALER_CURRENCY_LEN); + + in += sizeof(struct TALER_PQ_AmountP); + } + return GNUNET_OK; + } + case TALER_PQ_array_of_denom_hash: + if (NULL != dst_size) + *dst_size = sizeof(struct TALER_DenominationHashP) * (header.dim); + out = GNUNET_new_array (header.dim, + struct TALER_DenominationHashP); + *((void **) dst) = out; + for (uint32_t i = 0; i < header.dim; i++) + { + uint32_t val; + size_t sz; + + GNUNET_memcpy (&val, + in, + sizeof(val)); + sz = ntohl (val); + FAIL_IF (sz != sizeof(struct TALER_DenominationHashP)); + in += sizeof(uint32_t); + *(struct TALER_DenominationHashP *) out = + *(struct TALER_DenominationHashP *) in; + in += sz; + out += sz; + } + return GNUNET_OK; + + case TALER_PQ_array_of_hash_code: + if (NULL != dst_size) + *dst_size = sizeof(struct GNUNET_HashCode) * (header.dim); + out = GNUNET_new_array (header.dim, + struct GNUNET_HashCode); + *((void **) dst) = out; + for (uint32_t i = 0; i < header.dim; i++) + { + uint32_t val; + size_t sz; + + GNUNET_memcpy (&val, + in, + sizeof(val)); + sz = ntohl (val); + FAIL_IF (sz != sizeof(struct GNUNET_HashCode)); + in += sizeof(uint32_t); + *(struct GNUNET_HashCode *) out = + *(struct GNUNET_HashCode *) in; + in += sz; + out += sz; + } + return GNUNET_OK; + + case TALER_PQ_array_of_blinded_coin_hash: + if (NULL != dst_size) + *dst_size = sizeof(struct TALER_BlindedCoinHashP) * (header.dim); + out = GNUNET_new_array (header.dim, + struct TALER_BlindedCoinHashP); + *((void **) dst) = out; + for (uint32_t i = 0; i < header.dim; i++) + { + uint32_t val; + size_t sz; + + GNUNET_memcpy (&val, + in, + sizeof(val)); + sz = ntohl (val); + FAIL_IF (sz != sizeof(struct TALER_BlindedCoinHashP)); + in += sizeof(uint32_t); + *(struct TALER_BlindedCoinHashP *) out = + *(struct TALER_BlindedCoinHashP *) in; + in += sz; + out += sz; + } + return GNUNET_OK; + + case TALER_PQ_array_of_blinded_denom_sig: + { + struct TALER_BlindedDenominationSignature *denom_sigs; + if (0 == header.dim) + { + if (NULL != dst_size) + *dst_size = 0; + break; + } + + denom_sigs = GNUNET_new_array (header.dim, + struct TALER_BlindedDenominationSignature); + *((void **) dst) = denom_sigs; + + /* copy data */ + for (uint32_t i = 0; i < header.dim; i++) + { + struct TALER_BlindedDenominationSignature *denom_sig = &denom_sigs[i]; + struct GNUNET_CRYPTO_BlindedSignature *bs; + uint32_t be[2]; + uint32_t val; + size_t sz; + + GNUNET_memcpy (&val, + in, + sizeof(val)); + sz = ntohl (val); + FAIL_IF (sizeof(be) > sz); + + in += sizeof(val); + GNUNET_memcpy (&be, + in, + sizeof(be)); + FAIL_IF (0x01 != ntohl (be[1])); /* magic marker: blinded */ + + in += sizeof(be); + sz -= sizeof(be); + bs = GNUNET_new (struct GNUNET_CRYPTO_BlindedSignature); + bs->cipher = ntohl (be[0]); + bs->rc = 1; + switch (bs->cipher) + { + case GNUNET_CRYPTO_BSA_RSA: + bs->details.blinded_rsa_signature + = GNUNET_CRYPTO_rsa_signature_decode (in, + sz); + if (NULL == bs->details.blinded_rsa_signature) + { + GNUNET_free (bs); + FAIL_IF (true); + } + break; + case GNUNET_CRYPTO_BSA_CS: + if (sizeof(bs->details.blinded_cs_answer) != sz) + { + GNUNET_free (bs); + FAIL_IF (true); + } + GNUNET_memcpy (&bs->details.blinded_cs_answer, + in, + sz); + break; + default: + GNUNET_free (bs); + FAIL_IF (true); + } + denom_sig->blinded_sig = bs; + in += sz; + } + return GNUNET_OK; + } + default: + FAIL_IF (true); + } + } +FAIL: + GNUNET_free (*(void **) dst); + return GNUNET_SYSERR; +#undef FAIL_IF +} + + +/** + * Cleanup of the data and closure of an array spec. + */ +static void +array_cleanup (void *cls, + void *rd) +{ + struct ArrayResultCls *info = cls; + void **dst = rd; + + if ((0 == info->same_size) && + (NULL != info->sizes)) + GNUNET_free (*(info->sizes)); + + /* Clean up signatures, if applicable */ + if (TALER_PQ_array_of_blinded_denom_sig == info->typ) + { + struct TALER_BlindedDenominationSignature *denom_sigs = *dst; + GNUNET_assert (NULL != info->num); + for (size_t i = 0; i < *info->num; i++) + GNUNET_free (denom_sigs[i].blinded_sig); + } + + GNUNET_free (cls); + GNUNET_free (*dst); + *dst = NULL; +} + + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_denom_sig ( + struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedDenominationSignature **denom_sigs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_blinded_denom_sig; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &info->oid)); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) denom_sigs, + .fname = name, + .cls = info + }; + return res; + +}; + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_blinded_coin_hash ( + struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_BlindedCoinHashP **h_coin_evs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_blinded_coin_hash; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &info->oid)); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) h_coin_evs, + .fname = name, + .cls = info + }; + return res; + +}; + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_denom_hash ( + struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct TALER_DenominationHashP **denom_hs) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_denom_hash; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "bytea", + &info->oid)); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) denom_hs, + .fname = name, + .cls = info + }; + return res; + +}; + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_amount ( + struct GNUNET_PQ_Context *db, + const char *name, + const char *currency, + size_t *num, + struct TALER_Amount **amounts) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_amount; + info->db = db; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "taler_amount", + &info->oid)); + + { + size_t clen = GNUNET_MIN (TALER_CURRENCY_LEN - 1, + strlen (currency)); + GNUNET_memcpy (&info->currency, + currency, + clen); + } + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) amounts, + .fname = name, + .cls = info, + }; + return res; +} + + +struct GNUNET_PQ_ResultSpec +TALER_PQ_result_spec_array_hash_code ( + struct GNUNET_PQ_Context *db, + const char *name, + size_t *num, + struct GNUNET_HashCode **hashes) +{ + struct ArrayResultCls *info = GNUNET_new (struct ArrayResultCls); + + info->num = num; + info->typ = TALER_PQ_array_of_hash_code; + info->db = db; + GNUNET_assert (GNUNET_OK == + GNUNET_PQ_get_oid_by_name (db, + "gnunet_hashcode", + &info->oid)); + + struct GNUNET_PQ_ResultSpec res = { + .conv = extract_array_generic, + .cleaner = array_cleanup, + .dst = (void *) hashes, + .fname = name, + .cls = info, + }; + return res; +} + /* end of pq_result_helper.c */ diff --git a/src/pq/test_pq.c b/src/pq/test_pq.c index 52e92b561..0fd2bfddf 100644 --- a/src/pq/test_pq.c +++ b/src/pq/test_pq.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2015, 2016 Taler Systems SA + (C) 2015, 2016, 2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -21,6 +21,7 @@ #include "platform.h" #include "taler_util.h" #include "taler_pq_lib.h" +#include <gnunet/gnunet_pq_lib.h> /** @@ -29,29 +30,29 @@ * @param db database handle to initialize * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure */ -static int +static enum GNUNET_GenericReturnValue postgres_prepare (struct GNUNET_PQ_Context *db) { struct GNUNET_PQ_PreparedStatement ps[] = { GNUNET_PQ_make_prepare ("test_insert", "INSERT INTO test_pq (" - " hamount_val" - ",hamount_frac" - ",namount_val" - ",namount_frac" + " tamount" ",json" + ",aamount" + ",tamountc" + ",hash" + ",hashes" ") VALUES " - "($1, $2, $3, $4, $5);", - 5), + "($1, $2, $3, $4, $5, $6);"), GNUNET_PQ_make_prepare ("test_select", "SELECT" - " hamount_val" - ",hamount_frac" - ",namount_val" - ",namount_frac" + " tamount" ",json" - " FROM test_pq;", - 0), + ",aamount" + ",tamountc" + ",hash" + ",hashes" + " FROM test_pq;"), GNUNET_PQ_PREPARED_STATEMENT_END }; @@ -68,33 +69,64 @@ postgres_prepare (struct GNUNET_PQ_Context *db) static int run_queries (struct GNUNET_PQ_Context *conn) { - struct TALER_Amount hamount; - struct TALER_Amount hamount2; - struct TALER_AmountNBO namount; - struct TALER_AmountNBO namount2; - PGresult *result; - int ret; + struct TALER_Amount tamount; + struct TALER_Amount aamount[3]; + struct TALER_Amount tamountc; + struct GNUNET_HashCode hc = + {{0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef, + 0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef, + 0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef, + 0xdeadbeef,0xdeadbeef,0xdeadbeef,0xdeadbeef, }}; + struct GNUNET_HashCode hcs[2] = + {{{0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f, + 0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f, + 0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f, + 0xc0feec0f,0xc0feec0f,0xc0feec0f,0xc0feec0f,}}, + {{0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf, + 0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf, + 0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf, + 0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,0xdeadbeaf,}}}; json_t *json; - json_t *json2; GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("EUR:5.5", - &hamount)); - TALER_amount_hton (&namount, - &hamount); + TALER_string_to_amount ("EUR:5.3", + &aamount[0])); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:6.4", + &aamount[1])); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("EUR:7.5", + &aamount[2])); GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("EUR:4.4", - &hamount)); + TALER_string_to_amount ("EUR:7.7", + &tamount)); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount ("FOO:8.7", + &tamountc)); json = json_object (); - json_object_set_new (json, "foo", json_integer (42)); GNUNET_assert (NULL != json); + GNUNET_assert (0 == + json_object_set_new (json, + "foo", + json_integer (42))); { struct GNUNET_PQ_QueryParam params_insert[] = { - TALER_PQ_query_param_amount (&hamount), - TALER_PQ_query_param_amount_nbo (&namount), + TALER_PQ_query_param_amount (conn, + &tamount), TALER_PQ_query_param_json (json), + TALER_PQ_query_param_array_amount (3, + aamount, + conn), + TALER_PQ_query_param_amount_with_currency (conn, + &tamountc), + GNUNET_PQ_query_param_fixed_size (&hc, + sizeof (hc)), + TALER_PQ_query_param_array_hash_code (2, + hcs, + conn), GNUNET_PQ_query_param_end }; + PGresult *result; result = GNUNET_PQ_exec_prepared (conn, "test_insert", @@ -108,55 +140,76 @@ run_queries (struct GNUNET_PQ_Context *conn) return 1; } PQclear (result); + json_decref (json); } { + struct TALER_Amount tamount2; + struct TALER_Amount tamountc2; + struct TALER_Amount *pamount; + struct GNUNET_HashCode hc2; + struct GNUNET_HashCode *hcs2; + size_t npamount; + size_t nhcs; + json_t *json2; struct GNUNET_PQ_QueryParam params_select[] = { GNUNET_PQ_query_param_end }; + struct GNUNET_PQ_ResultSpec results_select[] = { + TALER_PQ_result_spec_amount ("tamount", + "EUR", + &tamount2), + TALER_PQ_result_spec_json ("json", + &json2), + TALER_PQ_result_spec_array_amount (conn, + "aamount", + "EUR", + &npamount, + &pamount), + TALER_PQ_result_spec_amount_with_currency ("tamountc", + &tamountc2), + GNUNET_PQ_result_spec_auto_from_type ("hash", + &hc2), + TALER_PQ_result_spec_array_hash_code (conn, + "hashes", + &nhcs, + &hcs2), + GNUNET_PQ_result_spec_end + }; - result = GNUNET_PQ_exec_prepared (conn, - "test_select", - params_select); if (1 != - PQntuples (result)) + GNUNET_PQ_eval_prepared_singleton_select (conn, + "test_select", + params_select, + results_select)) { GNUNET_break (0); - PQclear (result); return 1; } - } - - { - struct GNUNET_PQ_ResultSpec results_select[] = { - TALER_PQ_result_spec_amount ("hamount", "EUR", &hamount2), - TALER_PQ_result_spec_amount_nbo ("namount", "EUR", &namount2), - TALER_PQ_result_spec_json ("json", &json2), - GNUNET_PQ_result_spec_end - }; - - ret = GNUNET_PQ_extract_result (result, - results_select, - 0); - GNUNET_break (0 == - TALER_amount_cmp (&hamount, - &hamount2)); - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("EUR:5.5", - &hamount)); - TALER_amount_ntoh (&hamount2, - &namount2); GNUNET_break (0 == - TALER_amount_cmp (&hamount, - &hamount2)); + TALER_amount_cmp (&tamount, + &tamount2)); GNUNET_break (42 == - json_integer_value (json_object_get (json2, "foo"))); + json_integer_value (json_object_get (json2, + "foo"))); + GNUNET_break (3 == npamount); + for (size_t i = 0; i < 3; i++) + { + GNUNET_break (0 == + TALER_amount_cmp (&aamount[i], + &pamount[i])); + } + GNUNET_break (0 == + TALER_amount_cmp (&tamountc, + &tamountc2)); + GNUNET_break (0 == GNUNET_memcmp (&hc,&hc2)); + for (size_t i = 0; i < 2; i++) + { + GNUNET_break (0 == + GNUNET_memcmp (&hcs[i], + &hcs2[i])); + } GNUNET_PQ_cleanup_result (results_select); - PQclear (result); } - json_decref (json); - if (GNUNET_OK != ret) - return 1; - return 0; } @@ -166,12 +219,37 @@ main (int argc, const char *const argv[]) { struct GNUNET_PQ_ExecuteStatement es[] = { + GNUNET_PQ_make_execute ("DO $$ " + " BEGIN" + " CREATE DOMAIN gnunet_hashcode AS BYTEA" + " CHECK(length(VALUE)=64);" + " EXCEPTION" + " WHEN duplicate_object THEN null;" + " END " + "$$;"), + GNUNET_PQ_make_execute ("DO $$ " + " BEGIN" + " CREATE TYPE taler_amount AS" + " (val INT8, frac INT4);" + " EXCEPTION" + " WHEN duplicate_object THEN null;" + " END " + "$$;"), + GNUNET_PQ_make_execute ("DO $$ " + " BEGIN" + " CREATE TYPE taler_amount_currency AS" + " (val INT8, frac INT4, curr VARCHAR(12));" + " EXCEPTION" + " WHEN duplicate_object THEN null;" + " END " + "$$;"), GNUNET_PQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_pq (" - " hamount_val INT8 NOT NULL" - ",hamount_frac INT4 NOT NULL" - ",namount_val INT8 NOT NULL" - ",namount_frac INT4 NOT NULL" + " tamount taler_amount NOT NULL" ",json VARCHAR NOT NULL" + ",aamount taler_amount[]" + ",tamountc taler_amount_currency" + ",hash gnunet_hashcode" + ",hashes gnunet_hashcode[]" ")"), GNUNET_PQ_EXECUTE_STATEMENT_END }; @@ -196,6 +274,7 @@ main (int argc, GNUNET_PQ_disconnect (conn); return 1; } + ret = run_queries (conn); { struct GNUNET_PQ_ExecuteStatement ds[] = { |