commit 74388fd3718dc67a7fbf712f6983d71443685b7f
parent acd6092a1acc43f3efbf6cf0a539341ee933baed
Author: Christian Grothoff <christian@grothoff.org>
Date: Fri, 7 Aug 2026 00:32:02 +0200
fix warning
Diffstat:
2 files changed, 188 insertions(+), 10 deletions(-)
diff --git a/src/donaudb/meson.build b/src/donaudb/meson.build
@@ -135,6 +135,7 @@ test_donaudb = executable(
talerpq_dep,
gnunetutil_dep,
gnunetjson_dep,
+ gnunetpq_dep,
pq_dep,
json_dep,
],
diff --git a/src/donaudb/test_donaudb.c b/src/donaudb/test_donaudb.c
@@ -98,6 +98,120 @@ static int result;
static struct DONAUDB_PostgresContext *ctx;
/**
+ * Denomination key pair used to manufacture blinded donation unit
+ * signatures for the regression tests below. Created on demand.
+ */
+static struct TALER_DenominationPrivateKey test_denom_priv;
+
+/**
+ * Public key matching #test_denom_priv.
+ */
+static struct TALER_DenominationPublicKey test_denom_pub;
+
+
+/**
+ * Create a blinded donation unit signature usable as filler data
+ * for `receipts_issued.blinded_sig'.
+ *
+ * @return freshly allocated blinded signature
+ */
+static struct GNUNET_CRYPTO_BlindedSignature *
+make_blinded_sig (void)
+{
+ struct GNUNET_CRYPTO_BlindedMessage *rp;
+ struct GNUNET_CRYPTO_BlindedSignature *bs;
+
+ if (NULL == test_denom_priv.bsign_priv_key)
+ GNUNET_assert (GNUNET_OK ==
+ TALER_denom_priv_create (&test_denom_priv,
+ &test_denom_pub,
+ GNUNET_CRYPTO_BSA_RSA,
+ RSA_KEY_SIZE));
+ rp = GNUNET_new (struct GNUNET_CRYPTO_BlindedMessage);
+ rp->cipher = GNUNET_CRYPTO_BSA_RSA;
+ rp->rc = 1;
+ rp->details.rsa_blinded_message.blinded_msg_size = 32;
+ rp->details.rsa_blinded_message.blinded_msg = GNUNET_malloc (32);
+ GNUNET_CRYPTO_random_block (rp->details.rsa_blinded_message.blinded_msg,
+ 32);
+ bs = GNUNET_CRYPTO_blind_sign (test_denom_priv.bsign_priv_key,
+ "rw",
+ rp);
+ GNUNET_assert (NULL != bs);
+ GNUNET_CRYPTO_blinded_message_decref (rp);
+ return bs;
+}
+
+
+/**
+ * Register a fresh charity with a random public key.
+ *
+ * @param max_per_year annual donation limit to use
+ * @param[out] charity_id set to the ID of the new charity
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+make_charity (const char *max_per_year,
+ uint64_t *charity_id)
+{
+ struct DONAU_CharityPublicKeyP charity_pub;
+ struct TALER_Amount max;
+
+ RND_BLK (&charity_pub);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (max_per_year,
+ &max));
+ return (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+ DONAUDB_insert_charity (ctx,
+ &charity_pub,
+ "test charity",
+ "https://charity.example.com/",
+ &max,
+ charity_id))
+ ? GNUNET_OK
+ : GNUNET_SYSERR;
+}
+
+
+/**
+ * Issue one (fresh, never seen before) receipt for @a charity_id.
+ *
+ * @param charity_id charity to issue for
+ * @param year year to attribute the receipt to
+ * @param amount amount of the receipt
+ * @param[out] under_limit set to true if the charity stayed below its limit
+ * @return database status of the operation
+ */
+static enum GNUNET_DB_QueryStatus
+issue_receipt (uint64_t charity_id,
+ uint32_t year,
+ const char *amount,
+ bool *under_limit)
+{
+ struct DONAU_BlindedDonationUnitSignature du_sigs[1];
+ struct DONAU_DonationReceiptHashP h_receipt;
+ struct TALER_Amount amt;
+ enum GNUNET_DB_QueryStatus qs;
+
+ RND_BLK (&h_receipt);
+ GNUNET_assert (GNUNET_OK ==
+ TALER_string_to_amount (amount,
+ &amt));
+ du_sigs[0].blinded_sig = make_blinded_sig ();
+ qs = DONAUDB_do_insert_receipt_issued (ctx,
+ year,
+ 1,
+ du_sigs,
+ charity_id,
+ &h_receipt,
+ &amt,
+ under_limit);
+ GNUNET_CRYPTO_blinded_sig_decref (du_sigs[0].blinded_sig);
+ return qs;
+}
+
+
+/**
* Return charities information.
*
* @param cls closure
@@ -765,17 +879,17 @@ run (void *cls)
struct TALER_Amount expected;
size_t conflict_index = 42;
size_t unknown_index = 42;
- uint64_t year = GNUNET_TIME_get_current_year ();
+ uint64_t this_year = GNUNET_TIME_get_current_year ();
RND_BLK (&donor);
RND_BLK (&nonce);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
make_donation_unit (&du_a,
- year,
+ this_year,
CURRENCY ":10"));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
make_donation_unit (&du_b,
- year,
+ this_year,
CURRENCY ":25"));
dr.h_donation_unit_pub = du_a;
@@ -786,7 +900,7 @@ run (void *cls)
&donor,
1,
&dr,
- year,
+ this_year,
&conflict_index,
&unknown_index));
FAILIF (1 != conflict_index); /* == num_dr, i.e. no conflict */
@@ -801,7 +915,7 @@ run (void *cls)
&donor,
1,
&dr,
- year,
+ this_year,
&conflict_index,
&unknown_index));
FAILIF (0 != conflict_index);
@@ -810,7 +924,7 @@ run (void *cls)
/* Only the first receipt is deductible; the second one was dropped. */
FAILIF (0 >
DONAUDB_get_receipts_submitted_total (ctx,
- year,
+ this_year,
&donor,
&total));
GNUNET_assert (GNUNET_OK ==
@@ -833,12 +947,12 @@ run (void *cls)
struct TALER_Amount zero;
size_t conflict_index = 42;
size_t unknown_index = 42;
- uint64_t year = GNUNET_TIME_get_current_year ();
+ uint64_t this_year = GNUNET_TIME_get_current_year ();
RND_BLK (&donor);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
make_donation_unit (&known_du,
- year,
+ this_year,
CURRENCY ":10"));
drs[0].h_donation_unit_pub = known_du;
RND_BLK (&drs[0].nonce);
@@ -853,7 +967,7 @@ run (void *cls)
&donor,
2,
drs,
- year,
+ this_year,
&conflict_index,
&unknown_index));
FAILIF (1 != unknown_index);
@@ -863,7 +977,7 @@ run (void *cls)
/* The valid receipt in the same batch must NOT have been stored. */
FAILIF (0 >
DONAUDB_get_receipts_submitted_total (ctx,
- year,
+ this_year,
&donor,
&total));
GNUNET_assert (GNUNET_OK ==
@@ -873,6 +987,64 @@ run (void *cls)
&zero));
}
+ /* Everything below runs in autocommit, which is what donau-httpd does. */
+
+ /* D-1: amount_add()'s overflow guard must trigger at 2^52, not at
+ 2^20. A charity with a realistic annual limit must be able to
+ book receipts well beyond 1048576 units of currency. */
+ {
+ uint64_t big_charity_id;
+ bool under_limit = false;
+
+ FAILIF (GNUNET_OK !=
+ make_charity (CURRENCY ":4000000000",
+ &big_charity_id));
+ FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+ issue_receipt (big_charity_id,
+ GNUNET_TIME_get_current_year (),
+ CURRENCY ":2000000",
+ &under_limit));
+ FAILIF (! under_limit);
+ }
+
+ /* D-8: receipts_issued.receipt_id must not be settable from outside.
+ If it is, an operator bulk load or a partial restore can leave the
+ identity sequence behind the largest stored value, after which every
+ insert fails on receipts_issued_receipt_id_key -- which donau-httpd
+ used to answer with an assertion failure, i.e. abort(). */
+ {
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_end
+ };
+
+ /* Control: the very same insert without receipt_id is accepted. */
+ FAILIF (GNUNET_OK !=
+ GNUNET_PQ_prepare_anon (
+ ctx->conn,
+ "INSERT INTO receipts_issued"
+ " (blinded_sig,charity_id,receipt_hash,amount)"
+ " SELECT r.blinded_sig, r.charity_id,"
+ " sha512(r.receipt_hash), r.amount"
+ " FROM receipts_issued r"
+ " LIMIT 1;"));
+ /* Supplying receipt_id explicitly must be refused. With GENERATED
+ ALWAYS PostgreSQL already rejects it at PREPARE time; if a server
+ accepts the statement, executing it must fail. */
+ if (GNUNET_OK ==
+ GNUNET_PQ_prepare_anon (
+ ctx->conn,
+ "INSERT INTO receipts_issued"
+ " (receipt_id,blinded_sig,charity_id,receipt_hash,amount)"
+ " SELECT 424242, r.blinded_sig, r.charity_id,"
+ " sha512(r.receipt_hash), r.amount"
+ " FROM receipts_issued r"
+ " LIMIT 1;"))
+ FAILIF (0 <=
+ GNUNET_PQ_eval_prepared_non_select (ctx->conn,
+ "",
+ params));
+ }
+
result = 0;
drop:
@@ -881,6 +1053,11 @@ drop:
GNUNET_break (GNUNET_OK ==
DONAUDB_drop_tables (ctx));
cleanup:
+ if (NULL != test_denom_priv.bsign_priv_key)
+ {
+ TALER_denom_priv_free (&test_denom_priv);
+ TALER_denom_pub_free (&test_denom_pub);
+ }
DONAUDB_disconnect (ctx);
ctx = NULL;
}