gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-exchange] branch master updated: combine deposit confirmation sig


From: gnunet
Subject: [taler-exchange] branch master updated: combine deposit confirmation signatures into one big signature
Date: Sun, 24 Sep 2023 19:03:38 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

The following commit(s) were added to refs/heads/master by this push:
     new c6f7bd46 combine deposit confirmation signatures into one big signature
c6f7bd46 is described below

commit c6f7bd46fe73236dd3464cc6d87a5bce92c1ef16
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Sun Sep 24 19:03:30 2023 +0200

    combine deposit confirmation signatures into one big signature
---
 .../taler-auditor-httpd_deposit-confirmation.c     | 113 +++++++++++++++++---
 src/auditor/taler-helper-auditor-deposits.c        |  29 ++---
 src/auditordb/auditor-0001.sql                     |   7 +-
 src/auditordb/pg_get_deposit_confirmations.c       |  45 ++++++--
 src/auditordb/pg_insert_deposit_confirmation.c     |  18 ++--
 src/auditordb/plugin_auditordb_postgres.c          |   2 +-
 src/exchange/taler-exchange-httpd_batch-deposit.c  |  82 +++++---------
 src/include/taler_auditor_service.h                |  18 ++--
 src/include/taler_auditordb_plugin.h               |  21 ++--
 src/include/taler_crypto_lib.h                     |  20 ++--
 src/include/taler_exchange_service.h               |   9 +-
 src/include/taler_testing_lib.h                    |  17 ++-
 src/lib/auditor_api_deposit_confirmation.c         |  84 ++++++++++-----
 src/lib/exchange_api_batch_deposit.c               | 118 +++++++++------------
 src/testing/test_auditor_api.c                     |   2 +-
 .../testing_api_cmd_auditor_deposit_confirmation.c |  48 ++++++---
 src/testing/testing_api_cmd_batch_deposit.c        |  31 +++---
 src/testing/testing_api_cmd_deposit.c              |  15 ++-
 src/util/exchange_signatures.c                     |  48 ++++++---
 19 files changed, 432 insertions(+), 295 deletions(-)

diff --git a/src/auditor/taler-auditor-httpd_deposit-confirmation.c 
b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
index a19e7c1f..020d43d5 100644
--- a/src/auditor/taler-auditor-httpd_deposit-confirmation.c
+++ b/src/auditor/taler-auditor-httpd_deposit-confirmation.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2020 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 Affero General Public License as published by the Free 
Software
@@ -115,6 +115,11 @@ verify_and_execute_deposit_confirmation (
     .end = GNUNET_TIME_timestamp_hton (es->ep_end),
     .signkey_pub = es->exchange_pub
   };
+  const struct TALER_CoinSpendSignatureP *coin_sigps[
+    GNUNET_NZL (dc->num_coins)];
+
+  for (unsigned int i = 0; i<dc->num_coins; i++)
+    coin_sigps[i] = &dc->coin_sigs[i];
 
   if (GNUNET_TIME_absolute_is_future (es->ep_start.abs_time) ||
       GNUNET_TIME_absolute_is_past (es->ep_expire.abs_time) )
@@ -231,8 +236,9 @@ verify_and_execute_deposit_confirmation (
         dc->exchange_timestamp,
         dc->wire_deadline,
         dc->refund_deadline,
-        &dc->amount_without_fee,
-        &dc->coin_pub,
+        &dc->total_without_fee,
+        dc->num_coins,
+        coin_sigps,
         &dc->merchant,
         &dc->exchange_pub,
         &dc->exchange_sig))
@@ -265,16 +271,19 @@ verify_and_execute_deposit_confirmation (
 
 
 MHD_RESULT
-TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
+TAH_DEPOSIT_CONFIRMATION_handler (
+  struct TAH_RequestHandler *rh,
+  struct MHD_Connection *connection,
+  void **connection_cls,
+  const char *upload_data,
+  size_t *upload_data_size)
 {
   struct TALER_AUDITORDB_DepositConfirmation dc = {
     .refund_deadline = GNUNET_TIME_UNIT_ZERO_TS
   };
   struct TALER_AUDITORDB_ExchangeSigningKey es;
+  const json_t *jcoin_sigs;
+  const json_t *jcoin_pubs;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
                                  &dc.h_contract_terms),
@@ -290,11 +299,13 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct 
TAH_RequestHandler *rh,
       NULL),
     GNUNET_JSON_spec_timestamp ("wire_deadline",
                                 &dc.wire_deadline),
-    TALER_JSON_spec_amount ("amount_without_fee",
+    TALER_JSON_spec_amount ("total_without_fee",
                             TAH_currency,
-                            &dc.amount_without_fee),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                 &dc.coin_pub),
+                            &dc.total_without_fee),
+    GNUNET_JSON_spec_array_const ("coin_pubs",
+                                  &jcoin_pubs),
+    GNUNET_JSON_spec_array_const ("coin_sigs",
+                                  &jcoin_sigs),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
                                  &dc.merchant),
     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
@@ -313,13 +324,14 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct 
TAH_RequestHandler *rh,
                                  &es.master_sig),
     GNUNET_JSON_spec_end ()
   };
+  unsigned int num_coins;
+  json_t *json;
 
   (void) rh;
   (void) connection_cls;
   (void) upload_data;
   (void) upload_data_size;
   {
-    json_t *json;
     enum GNUNET_GenericReturnValue res;
 
     res = TALER_MHD_parse_post_json (connection,
@@ -335,22 +347,89 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct 
TAH_RequestHandler *rh,
     res = TALER_MHD_parse_json_data (connection,
                                      json,
                                      spec);
-    json_decref (json);
     if (GNUNET_SYSERR == res)
+    {
+      json_decref (json);
       return MHD_NO; /* hard failure */
+    }
     if (GNUNET_NO == res)
+    {
+      json_decref (json);
       return MHD_YES; /* failure */
+    }
   }
-
-  es.exchange_pub = dc.exchange_pub; /* used twice! */
-  dc.master_public_key = es.master_public_key;
+  num_coins = json_array_size (jcoin_sigs);
+  if (num_coins != json_array_size (jcoin_pubs))
   {
+    GNUNET_break_op (0);
+    json_decref (json);
+    return TALER_MHD_reply_with_ec (
+      connection,
+      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+      "coin_pubs.length != coin_sigs.length");
+  }
+  if (0 == num_coins)
+  {
+    GNUNET_break_op (0);
+    json_decref (json);
+    return TALER_MHD_reply_with_ec (
+      connection,
+      TALER_EC_GENERIC_PARAMETER_MALFORMED,
+      "coin_pubs array is empty");
+  }
+  {
+    struct TALER_CoinSpendPublicKeyP coin_pubs[num_coins];
+    struct TALER_CoinSpendSignatureP coin_sigs[num_coins];
     MHD_RESULT res;
 
+    for (unsigned int i = 0; i<num_coins; i++)
+    {
+      json_t *jpub = json_array_get (jcoin_pubs,
+                                     i);
+      json_t *jsig = json_array_get (jcoin_sigs,
+                                     i);
+      const char *ps = json_string_value (jpub);
+      const char *ss = json_string_value (jsig);
+
+      if ( (NULL == ps) ||
+           (GNUNET_OK !=
+            GNUNET_STRINGS_string_to_data (ps,
+                                           strlen (ps),
+                                           &coin_pubs[i],
+                                           sizeof (coin_pubs[i]))) )
+      {
+        GNUNET_break_op (0);
+        json_decref (json);
+        return TALER_MHD_reply_with_ec (
+          connection,
+          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+          "coin_pub[] malformed");
+      }
+      if ( (NULL == ss) ||
+           (GNUNET_OK !=
+            GNUNET_STRINGS_string_to_data (ss,
+                                           strlen (ss),
+                                           &coin_sigs[i],
+                                           sizeof (coin_sigs[i]))) )
+      {
+        GNUNET_break_op (0);
+        json_decref (json);
+        return TALER_MHD_reply_with_ec (
+          connection,
+          TALER_EC_GENERIC_PARAMETER_MALFORMED,
+          "coin_sig[] malformed");
+      }
+    }
+    dc.num_coins = num_coins;
+    dc.coin_pubs = coin_pubs;
+    dc.coin_sigs = coin_sigs;
+    es.exchange_pub = dc.exchange_pub; /* used twice! */
+    dc.master_public_key = es.master_public_key;
     res = verify_and_execute_deposit_confirmation (connection,
                                                    &dc,
                                                    &es);
     GNUNET_JSON_parse_free (spec);
+    json_decref (json);
     return res;
   }
 }
diff --git a/src/auditor/taler-helper-auditor-deposits.c 
b/src/auditor/taler-helper-auditor-deposits.c
index c08727d3..fc739599 100644
--- a/src/auditor/taler-helper-auditor-deposits.c
+++ b/src/auditor/taler-helper-auditor-deposits.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2016-2021 Taler Systems SA
+  Copyright (C) 2016-2023 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero Public License as published by the Free Software
@@ -108,8 +108,10 @@ test_dc (void *cls,
          const struct TALER_AUDITORDB_DepositConfirmation *dc)
 {
   struct DepositConfirmationContext *dcc = cls;
+  bool missing = false;
 
   dcc->last_seen_coin_serial = serial_id;
+  for (unsigned int i = 0; i<dc->num_coins; i++)
   {
     enum GNUNET_DB_QueryStatus qs;
     struct GNUNET_TIME_Timestamp exchange_timestamp;
@@ -118,20 +120,12 @@ test_dc (void *cls,
     qs = TALER_ARL_edb->have_deposit2 (TALER_ARL_edb->cls,
                                        &dc->h_contract_terms,
                                        &dc->h_wire,
-                                       &dc->coin_pub,
+                                       &dc->coin_pubs[i],
                                        &dc->merchant,
                                        dc->refund_deadline,
                                        &deposit_fee,
                                        &exchange_timestamp);
-    if (qs > 0)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Found deposit %s in exchange database\n",
-                  GNUNET_h2s (&dc->h_contract_terms.hash));
-      if (TALER_ARL_do_abort ())
-        return GNUNET_SYSERR;
-      return GNUNET_OK; /* found, all good */
-    }
+    missing |= (0 == qs);
     if (qs < 0)
     {
       GNUNET_break (0); /* DB error, complain */
@@ -139,6 +133,15 @@ test_dc (void *cls,
       return GNUNET_SYSERR;
     }
   }
+  if (! missing)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Found deposit %s in exchange database\n",
+                GNUNET_h2s (&dc->h_contract_terms.hash));
+    if (TALER_ARL_do_abort ())
+      return GNUNET_SYSERR;
+    return GNUNET_OK; /* all coins found, all good */
+  }
   /* deposit confirmation missing! report! */
   TALER_ARL_report (
     report_deposit_confirmation_inconsistencies,
@@ -146,7 +149,7 @@ test_dc (void *cls,
       TALER_JSON_pack_time_abs_human ("timestamp",
                                       dc->exchange_timestamp.abs_time),
       TALER_JSON_pack_amount ("amount",
-                              &dc->amount_without_fee),
+                              &dc->total_without_fee),
       GNUNET_JSON_pack_uint64 ("rowid",
                                serial_id),
       GNUNET_JSON_pack_data_auto ("account",
@@ -156,7 +159,7 @@ test_dc (void *cls,
   dcc->missed_count++;
   TALER_ARL_amount_add (&dcc->missed_amount,
                         &dcc->missed_amount,
-                        &dc->amount_without_fee);
+                        &dc->total_without_fee);
   if (TALER_ARL_do_abort ())
     return GNUNET_SYSERR;
   return GNUNET_OK;
diff --git a/src/auditordb/auditor-0001.sql b/src/auditordb/auditor-0001.sql
index a167e855..b755da4b 100644
--- a/src/auditordb/auditor-0001.sql
+++ b/src/auditordb/auditor-0001.sql
@@ -289,13 +289,14 @@ CREATE TABLE IF NOT EXISTS deposit_confirmations
   ,exchange_timestamp INT8 NOT NULL
   ,refund_deadline INT8 NOT NULL
   ,wire_deadline INT8 NOT NULL
-  ,amount_without_fee taler_amount NOT NULL
-  ,coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)
+  ,total_without_fee taler_amount NOT NULL
+  ,coin_pubs BYTEA[] NOT NULL CHECK (CARDINALITY(coin_pubs)>0)
+  ,coin_sigs BYTEA[] NOT NULL CHECK 
(CARDINALITY(coin_sigs)=CARDINALITY(coin_pubs))
   ,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)
   ,exchange_sig BYTEA NOT NULL CHECK (LENGTH(exchange_sig)=64)
   ,exchange_pub BYTEA NOT NULL CHECK (LENGTH(exchange_pub)=32)
   ,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
-  ,PRIMARY KEY 
(h_contract_terms,h_wire,coin_pub,merchant_pub,exchange_sig,exchange_pub,master_sig)
+  ,PRIMARY KEY 
(h_contract_terms,h_wire,merchant_pub,exchange_sig,exchange_pub,master_sig)
   );
 COMMENT ON TABLE deposit_confirmations
   IS 'deposit confirmation sent to us by merchants; we must check that the 
exchange reported these properly.';
diff --git a/src/auditordb/pg_get_deposit_confirmations.c 
b/src/auditordb/pg_get_deposit_confirmations.c
index 56306e10..86b6cdd8 100644
--- a/src/auditordb/pg_get_deposit_confirmations.c
+++ b/src/auditordb/pg_get_deposit_confirmations.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2022 Taler Systems SA
+   Copyright (C) 2022-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
@@ -82,6 +82,10 @@ deposit_confirmation_cb (void *cls,
     struct TALER_AUDITORDB_DepositConfirmation dc = {
       .master_public_key = *dcc->master_pub
     };
+    struct TALER_CoinSpendPublicKeyP *coin_pubs = NULL;
+    struct TALER_CoinSpendSignatureP *coin_sigs = NULL;
+    size_t num_pubs = 0;
+    size_t num_sigs = 0;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("serial_id",
                                     &serial_id),
@@ -97,10 +101,16 @@ deposit_confirmation_cb (void *cls,
                                        &dc.refund_deadline),
       GNUNET_PQ_result_spec_timestamp ("wire_deadline",
                                        &dc.wire_deadline),
-      TALER_PQ_RESULT_SPEC_AMOUNT ("amount_without_fee",
-                                   &dc.amount_without_fee),
-      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                            &dc.coin_pub),
+      TALER_PQ_RESULT_SPEC_AMOUNT ("total_without_fee",
+                                   &dc.total_without_fee),
+      GNUNET_PQ_result_spec_auto_array_from_type (pg->conn,
+                                                  "coin_pub",
+                                                  &num_pubs,
+                                                  coin_pubs),
+      GNUNET_PQ_result_spec_auto_array_from_type (pg->conn,
+                                                  "coin_sigs",
+                                                  &num_sigs,
+                                                  coin_sigs),
       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
                                             &dc.merchant),
       GNUNET_PQ_result_spec_auto_from_type ("exchange_sig",
@@ -111,6 +121,7 @@ deposit_confirmation_cb (void *cls,
                                             &dc.master_sig),
       GNUNET_PQ_result_spec_end
     };
+    enum GNUNET_GenericReturnValue rval;
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -121,11 +132,22 @@ deposit_confirmation_cb (void *cls,
       dcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return;
     }
+    if (num_sigs != num_pubs)
+    {
+      GNUNET_break (0);
+      dcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      GNUNET_PQ_cleanup_result (rs);
+      return;
+    }
     dcc->qs = i + 1;
-    if (GNUNET_OK !=
-        dcc->cb (dcc->cb_cls,
-                 serial_id,
-                 &dc))
+    dc.coin_pubs = coin_pubs;
+    dc.coin_sigs = coin_sigs;
+    dc.num_coins = num_sigs;
+    rval = dcc->cb (dcc->cb_cls,
+                    serial_id,
+                    &dc);
+    GNUNET_PQ_cleanup_result (rs);
+    if (GNUNET_OK != rval)
       break;
   }
 }
@@ -163,8 +185,9 @@ TAH_PG_get_deposit_confirmations (
            ",exchange_timestamp"
            ",wire_deadline"
            ",refund_deadline"
-           ",amount_without_fee"
-           ",coin_pub"
+           ",total_without_fee"
+           ",coin_pubs"
+           ",coin_sigs"
            ",merchant_pub"
            ",exchange_sig"
            ",exchange_pub"
diff --git a/src/auditordb/pg_insert_deposit_confirmation.c 
b/src/auditordb/pg_insert_deposit_confirmation.c
index 7c54494d..b811e267 100644
--- a/src/auditordb/pg_insert_deposit_confirmation.c
+++ b/src/auditordb/pg_insert_deposit_confirmation.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2022 Taler Systems SA
+   Copyright (C) 2022, 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
@@ -41,8 +41,13 @@ TAH_PG_insert_deposit_confirmation (
     GNUNET_PQ_query_param_timestamp (&dc->wire_deadline),
     GNUNET_PQ_query_param_timestamp (&dc->refund_deadline),
     TALER_PQ_query_param_amount (pg->conn,
-                                 &dc->amount_without_fee),
-    GNUNET_PQ_query_param_auto_from_type (&dc->coin_pub),
+                                 &dc->total_without_fee),
+    GNUNET_PQ_query_param_array_auto_from_type (dc->num_coins,
+                                                dc->coin_pubs,
+                                                pg->conn),
+    GNUNET_PQ_query_param_array_auto_from_type (dc->num_coins,
+                                                dc->coin_sigs,
+                                                pg->conn),
     GNUNET_PQ_query_param_auto_from_type (&dc->merchant),
     GNUNET_PQ_query_param_auto_from_type (&dc->exchange_sig),
     GNUNET_PQ_query_param_auto_from_type (&dc->exchange_pub),
@@ -60,13 +65,14 @@ TAH_PG_insert_deposit_confirmation (
            ",exchange_timestamp"
            ",wire_deadline"
            ",refund_deadline"
-           ",amount_without_fee"
-           ",coin_pub"
+           ",total_without_fee"
+           ",coin_pubs"
+           ",coin_sigs"
            ",merchant_pub"
            ",exchange_sig"
            ",exchange_pub"
            ",master_sig"                  /* master_sig could be normalized... 
*/
-           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13);");
+           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14);");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
                                              
"auditor_deposit_confirmation_insert",
                                              params);
diff --git a/src/auditordb/plugin_auditordb_postgres.c 
b/src/auditordb/plugin_auditordb_postgres.c
index 24d1768b..2b722a5e 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.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
diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c 
b/src/exchange/taler-exchange-httpd_batch-deposit.c
index 894f8fb4..bc8b20de 100644
--- a/src/exchange/taler-exchange-httpd_batch-deposit.c
+++ b/src/exchange/taler-exchange-httpd_batch-deposit.c
@@ -108,65 +108,33 @@ reply_batch_deposit_success (
   const struct BatchDepositContext *dc)
 {
   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &dc->bd;
-  json_t *arr;
+  const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (bd->num_cdis)];
+  enum TALER_ErrorCode ec;
   struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
 
-again:
-  arr = json_array ();
-  GNUNET_assert (NULL != arr);
   for (unsigned int i = 0; i<bd->num_cdis; i++)
+    csigs[i] = &bd->cdis[i].csig;
+  if (TALER_EC_NONE !=
+      (ec = TALER_exchange_online_deposit_confirmation_sign (
+         &TEH_keys_exchange_sign_,
+         &bd->h_contract_terms,
+         &dc->h_wire,
+         NULL != dc->policy_json ? &dc->h_policy : NULL,
+         dc->exchange_timestamp,
+         bd->wire_deadline,
+         bd->refund_deadline,
+         &dc->policy_details.accumulated_total,   /* excludes fees */
+         bd->num_cdis,
+         csigs,
+         &dc->bd.merchant_pub,
+         &pub,
+         &sig)))
   {
-    const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
-      = &bd->cdis[i];
-    struct TALER_ExchangePublicKeyP pubi;
-    struct TALER_ExchangeSignatureP sig;
-    enum TALER_ErrorCode ec;
-    struct TALER_Amount amount_without_fee;
-
-    GNUNET_assert (0 <=
-                   TALER_amount_subtract (&amount_without_fee,
-                                          &cdi->amount_with_fee,
-                                          &dc->deposit_fees[i]));
-    if (TALER_EC_NONE !=
-        (ec = TALER_exchange_online_deposit_confirmation_sign (
-           &TEH_keys_exchange_sign_,
-           &bd->h_contract_terms,
-           &dc->h_wire,
-           NULL != dc->policy_json ? &dc->h_policy : NULL,
-           dc->exchange_timestamp,
-           bd->wire_deadline,
-           bd->refund_deadline,
-           &amount_without_fee,
-           &cdi->coin.coin_pub,
-           &dc->bd.merchant_pub,
-           &pubi,
-           &sig)))
-    {
-      GNUNET_break (0);
-      return TALER_MHD_reply_with_ec (connection,
-                                      ec,
-                                      NULL);
-    }
-    if (0 == i)
-      pub = pubi;
-    if (0 !=
-        GNUNET_memcmp (&pub,
-                       &pubi))
-    {
-      /* note: in the future, maybe have batch sign API to avoid having to
-         handle key rollover... */
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Exchange public key changed during batch deposit, trying 
again\n");
-      json_decref (arr);
-      goto again;
-    }
-    GNUNET_assert (
-      0 ==
-      json_array_append_new (arr,
-                             GNUNET_JSON_PACK (
-                               GNUNET_JSON_pack_data_auto (
-                                 "exchange_sig",
-                                 &sig))));
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
   return TALER_MHD_REPLY_JSON_PACK (
     connection,
@@ -175,8 +143,8 @@ again:
                                 dc->exchange_timestamp),
     GNUNET_JSON_pack_data_auto ("exchange_pub",
                                 &pub),
-    GNUNET_JSON_pack_array_steal ("exchange_sigs",
-                                  arr));
+    GNUNET_JSON_pack_data_auto ("exchange_sig",
+                                &sig));
 }
 
 
diff --git a/src/include/taler_auditor_service.h 
b/src/include/taler_auditor_service.h
index 3d8ca9ef..409faca7 100644
--- a/src/include/taler_auditor_service.h
+++ b/src/include/taler_auditor_service.h
@@ -226,11 +226,11 @@ TALER_AUDITOR_get_config (struct GNUNET_CURL_Context *ctx,
 /**
  * Cancel auditor config request.
  *
- * @param auditor the auditor handle
+ * @param[in] auditor the auditor handle
  */
 void
-TALER_AUDITOR_get_config_cancel (struct
-                                 TALER_AUDITOR_GetConfigHandle *auditor);
+TALER_AUDITOR_get_config_cancel (
+  struct TALER_AUDITOR_GetConfigHandle *auditor);
 
 
 /**
@@ -284,8 +284,10 @@ typedef void
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far in the future
  * @param wire_deadline date until which the exchange should wire the funds
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the auditor (can be zero if refunds are not allowed); must not 
be after the @a wire_deadline
- * @param amount_without_fee the amount confirmed to be wired by the exchange 
to the merchant
- * @param coin_pub coin’s public key
+ * @param total_without_fee the amount confirmed to be wired by the exchange 
to the merchant
+ * @param num_coins number of coins involved in the batch deposit
+ * @param coin_pubs array of the coin’s public keys
+ * @param coin_sigs array of the original deposit signatures of the coins in 
the batch
  * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
  * @param exchange_sig the signature made with purpose 
#TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT
  * @param exchange_pub the public key of the exchange that matches @a 
exchange_sig
@@ -309,8 +311,10 @@ TALER_AUDITOR_deposit_confirmation (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendPublicKeyP *coin_pubs[static num_coins],
+  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   const struct TALER_ExchangePublicKeyP *exchange_pub,
   const struct TALER_ExchangeSignatureP *exchange_sig,
diff --git a/src/include/taler_auditordb_plugin.h 
b/src/include/taler_auditordb_plugin.h
index eebb31e0..31e6723a 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -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
@@ -421,14 +421,23 @@ struct TALER_AUDITORDB_DepositConfirmation
    * Amount to be deposited, excluding fee.  Calculated from the
    * amount with fee and the fee from the deposit request.
    */
-  struct TALER_Amount amount_without_fee;
+  struct TALER_Amount total_without_fee;
 
   /**
-   * The coin's public key.  This is the value that must have been
-   * signed (blindly) by the Exchange.  The deposit request is to be
-   * signed by the corresponding private key (using EdDSA).
+   * Length of the @e coin_pubs and @e coin_sigs arrays.
    */
-  struct TALER_CoinSpendPublicKeyP coin_pub;
+  unsigned int num_coins;
+
+  /**
+   * Array of the coin public keys involved in the
+   * batch deposit operation.
+   */
+  const struct TALER_CoinSpendPublicKeyP *coin_pubs;
+
+  /**
+   * Array of coin deposit signatures from the deposit operation.
+   */
+  const struct TALER_CoinSpendSignatureP *coin_sigs;
 
   /**
    * The Merchant's public key.  Allows the merchant to later refund
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index b7666610..cef8f55a 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -4045,8 +4045,9 @@ typedef enum TALER_ErrorCode
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far off
  * @param wire_deadline date until which the exchange should wire the funds
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the exchange (can be zero if refunds are not allowed); must 
not be after the @a wire_deadline
- * @param amount_without_fee the amount to be deposited after fees
- * @param coin_pub public key of the deposited coin
+ * @param total_without_fee the total amount to be deposited after fees over 
all coins
+ * @param num_coins length of @a coin_sigs array
+ * @param coin_sigs signatures of the deposited coins
  * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
  * @param[out] pub where to write the public key
  * @param[out] sig where to write the signature
@@ -4061,8 +4062,9 @@ TALER_exchange_online_deposit_confirmation_sign (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   struct TALER_ExchangePublicKeyP *pub,
   struct TALER_ExchangeSignatureP *sig);
@@ -4077,8 +4079,9 @@ TALER_exchange_online_deposit_confirmation_sign (
  * @param exchange_timestamp timestamp when the contract was finalized, must 
not be too far off
  * @param wire_deadline date until which the exchange should wire the funds
  * @param refund_deadline date until which the merchant can issue a refund to 
the customer via the exchange (can be zero if refunds are not allowed); must 
not be after the @a wire_deadline
- * @param amount_without_fee the amount to be deposited after fees
- * @param coin_pub public key of the deposited coin
+ * @param total_without_fee the total amount to be deposited after fees over 
all coins
+ * @param num_coins length of @a coin_sigs array
+ * @param coin_sigs signatures of the deposited coins
  * @param merchant_pub the public key of the merchant (used to identify the 
merchant for refund requests)
  * @param pub where to write the public key
  * @param sig where to write the signature
@@ -4092,8 +4095,9 @@ TALER_exchange_online_deposit_confirmation_verify (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   const struct TALER_ExchangePublicKeyP *pub,
   const struct TALER_ExchangeSignatureP *sig);
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index c344a93a..662700d4 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1143,9 +1143,9 @@ struct TALER_EXCHANGE_BatchDepositResult
       struct GNUNET_TIME_Timestamp deposit_timestamp;
 
       /**
-       * Array of signatures provided by the exchange
+       * Deposit confirmation signature provided by the exchange
        */
-      const struct TALER_ExchangeSignatureP *exchange_sigs;
+      const struct TALER_ExchangeSignatureP *exchange_sig;
 
       /**
        * exchange key used to sign @a exchange_sig.
@@ -1158,11 +1158,6 @@ struct TALER_EXCHANGE_BatchDepositResult
        */
       const char *transaction_base_url;
 
-      /**
-       * Length of the @e exchange_sigs array.
-       */
-      unsigned int num_signatures;
-
     } ok;
 
     /**
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 5506f025..02679802 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -811,20 +811,18 @@ TALER_TESTING_cmd_exec_auditor_dbinit (const char *label,
  * @param label command label.
  * @param deposit_reference reference to any operation that can
  *        provide a coin.
- * @param coin_index if @a deposit_reference offers an array of
- *        coins, this parameter selects which one in that array.
- *        This value is currently ignored, as only one-coin
- *        deposits are implemented.
+ * @param num_coins number of coins expected in the batch deposit
  * @param amount_without_fee deposited amount without the fee
  * @param expected_response_code expected HTTP response code.
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_deposit_confirmation (const char *label,
-                                        const char *deposit_reference,
-                                        unsigned int coin_index,
-                                        const char *amount_without_fee,
-                                        unsigned int expected_response_code);
+TALER_TESTING_cmd_deposit_confirmation (
+  const char *label,
+  const char *deposit_reference,
+  unsigned int num_coins,
+  const char *amount_without_fee,
+  unsigned int expected_response_code);
 
 
 /**
@@ -2729,6 +2727,7 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait 
*traits,
   op (exchange_wd_value, const struct TALER_ExchangeWithdrawValues)     \
   op (coin_priv, const struct TALER_CoinSpendPrivateKeyP)               \
   op (coin_pub, const struct TALER_CoinSpendPublicKeyP)                 \
+  op (coin_sig, const struct TALER_CoinSpendSignatureP)                 \
   op (absolute_time, const struct GNUNET_TIME_Absolute)                 \
   op (timestamp, const struct GNUNET_TIME_Timestamp)                    \
   op (wire_deadline, const struct GNUNET_TIME_Timestamp)                \
diff --git a/src/lib/auditor_api_deposit_confirmation.c 
b/src/lib/auditor_api_deposit_confirmation.c
index 0b0f4dd4..1e2ecc6c 100644
--- a/src/lib/auditor_api_deposit_confirmation.c
+++ b/src/lib/auditor_api_deposit_confirmation.c
@@ -167,22 +167,25 @@ handle_deposit_confirmation_finished (void *cls,
  * @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not
  */
 static enum GNUNET_GenericReturnValue
-verify_signatures (const struct TALER_MerchantWireHashP *h_wire,
-                   const struct TALER_ExtensionPolicyHashP *h_policy,
-                   const struct TALER_PrivateContractHashP *h_contract_terms,
-                   struct GNUNET_TIME_Timestamp exchange_timestamp,
-                   struct GNUNET_TIME_Timestamp wire_deadline,
-                   struct GNUNET_TIME_Timestamp refund_deadline,
-                   const struct TALER_Amount *amount_without_fee,
-                   const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                   const struct TALER_MerchantPublicKeyP *merchant_pub,
-                   const struct TALER_ExchangePublicKeyP *exchange_pub,
-                   const struct TALER_ExchangeSignatureP *exchange_sig,
-                   const struct TALER_MasterPublicKeyP *master_pub,
-                   struct GNUNET_TIME_Timestamp ep_start,
-                   struct GNUNET_TIME_Timestamp ep_expire,
-                   struct GNUNET_TIME_Timestamp ep_end,
-                   const struct TALER_MasterSignatureP *master_sig)
+verify_signatures (
+  const struct TALER_MerchantWireHashP *h_wire,
+  const struct TALER_ExtensionPolicyHashP *h_policy,
+  const struct TALER_PrivateContractHashP *h_contract_terms,
+  struct GNUNET_TIME_Timestamp exchange_timestamp,
+  struct GNUNET_TIME_Timestamp wire_deadline,
+  struct GNUNET_TIME_Timestamp refund_deadline,
+  const struct TALER_Amount *amount_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendSignatureP *coin_sigs[
+    static num_coins],
+  const struct TALER_MerchantPublicKeyP *merchant_pub,
+  const struct TALER_ExchangePublicKeyP *exchange_pub,
+  const struct TALER_ExchangeSignatureP *exchange_sig,
+  const struct TALER_MasterPublicKeyP *master_pub,
+  struct GNUNET_TIME_Timestamp ep_start,
+  struct GNUNET_TIME_Timestamp ep_expire,
+  struct GNUNET_TIME_Timestamp ep_end,
+  const struct TALER_MasterSignatureP *master_sig)
 {
   if (GNUNET_OK !=
       TALER_exchange_online_deposit_confirmation_verify (
@@ -193,7 +196,8 @@ verify_signatures (const struct TALER_MerchantWireHashP 
*h_wire,
         wire_deadline,
         refund_deadline,
         amount_without_fee,
-        coin_pub,
+        num_coins,
+        coin_sigs,
         merchant_pub,
         exchange_pub,
         exchange_sig))
@@ -241,8 +245,12 @@ TALER_AUDITOR_deposit_confirmation (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendPublicKeyP *coin_pubs[
+    static num_coins],
+  const struct TALER_CoinSpendSignatureP *coin_sigs[
+    static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   const struct TALER_ExchangePublicKeyP *exchange_pub,
   const struct TALER_ExchangeSignatureP *exchange_sig,
@@ -257,7 +265,14 @@ TALER_AUDITOR_deposit_confirmation (
   struct TALER_AUDITOR_DepositConfirmationHandle *dh;
   json_t *deposit_confirmation_obj;
   CURL *eh;
+  json_t *jcoin_sigs;
+  json_t *jcoin_pubs;
 
+  if (0 == num_coins)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   if (GNUNET_OK !=
       verify_signatures (h_wire,
                          h_policy,
@@ -265,8 +280,9 @@ TALER_AUDITOR_deposit_confirmation (
                          exchange_timestamp,
                          wire_deadline,
                          refund_deadline,
-                         amount_without_fee,
-                         coin_pub,
+                         total_without_fee,
+                         num_coins,
+                         coin_sigs,
                          merchant_pub,
                          exchange_pub,
                          exchange_sig,
@@ -279,7 +295,21 @@ TALER_AUDITOR_deposit_confirmation (
     GNUNET_break_op (0);
     return NULL;
   }
-
+  jcoin_sigs = json_array ();
+  GNUNET_assert (NULL != jcoin_sigs);
+  jcoin_pubs = json_array ();
+  GNUNET_assert (NULL != jcoin_pubs);
+  for (unsigned int i = 0; i<num_coins; i++)
+  {
+    GNUNET_assert (0 ==
+                   json_array_append_new (jcoin_sigs,
+                                          GNUNET_JSON_from_data_auto (
+                                            coin_sigs[i])));
+    GNUNET_assert (0 ==
+                   json_array_append_new (jcoin_pubs,
+                                          GNUNET_JSON_from_data_auto (
+                                            coin_pubs[i])));
+  }
   deposit_confirmation_obj
     = GNUNET_JSON_PACK (
         GNUNET_JSON_pack_data_auto ("h_wire",
@@ -295,10 +325,12 @@ TALER_AUDITOR_deposit_confirmation (
                                       refund_deadline)),
         GNUNET_JSON_pack_timestamp ("wire_deadline",
                                     wire_deadline),
-        TALER_JSON_pack_amount ("amount_without_fee",
-                                amount_without_fee),
-        GNUNET_JSON_pack_data_auto ("coin_pub",
-                                    coin_pub),
+        TALER_JSON_pack_amount ("total_without_fee",
+                                total_without_fee),
+        GNUNET_JSON_pack_array_steal ("coin_pubs",
+                                      jcoin_pubs),
+        GNUNET_JSON_pack_array_steal ("coin_sigs",
+                                      jcoin_sigs),
         GNUNET_JSON_pack_data_auto ("merchant_pub",
                                     merchant_pub),
         GNUNET_JSON_pack_data_auto ("exchange_sig",
diff --git a/src/lib/exchange_api_batch_deposit.c 
b/src/lib/exchange_api_batch_deposit.c
index 273b25e8..f9d505dc 100644
--- a/src/lib/exchange_api_batch_deposit.c
+++ b/src/lib/exchange_api_batch_deposit.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
@@ -146,9 +146,9 @@ struct TALER_EXCHANGE_BatchDepositHandle
   struct GNUNET_TIME_Timestamp exchange_timestamp;
 
   /**
-   * Exchange signatures, set for #auditor_cb.
+   * Exchange signature, set for #auditor_cb.
    */
-  struct TALER_ExchangeSignatureP *exchange_sigs;
+  struct TALER_ExchangeSignatureP exchange_sig;
 
   /**
    * Head of DLL of interactions with this auditor.
@@ -170,6 +170,11 @@ struct TALER_EXCHANGE_BatchDepositHandle
    */
   struct TALER_ExchangePublicKeyP exchange_pub;
 
+  /**
+   * Total amount deposited without fees as calculated by us.
+   */
+  struct TALER_Amount total_without_fee;
+
   /**
    * Response object to free at the end.
    */
@@ -251,9 +256,20 @@ auditor_cb (void *cls,
   struct TALER_EXCHANGE_BatchDepositHandle *dh = cls;
   const struct TALER_EXCHANGE_SigningPublicKey *spk;
   struct TEAH_AuditorInteractionEntry *aie;
-  struct TALER_Amount amount_without_fee;
   const struct TALER_EXCHANGE_DenomPublicKey *dki;
   unsigned int coin;
+  const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (
+                                                  dh->num_cdds)];
+  const struct TALER_CoinSpendPublicKeyP *cpubs[GNUNET_NZL (
+                                                  dh->num_cdds)];
+
+  for (unsigned int i = 0; i<dh->num_cdds; i++)
+  {
+    const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &dh->cdds[i];
+
+    csigs[i] = &cdd->coin_sig;
+    cpubs[i] = &cdd->coin_pub;
+  }
 
   if (0 !=
       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
@@ -278,10 +294,6 @@ auditor_cb (void *cls,
     GNUNET_break_op (0);
     return;
   }
-  GNUNET_assert (0 <=
-                 TALER_amount_subtract (&amount_without_fee,
-                                        &dh->cdds[coin].amount,
-                                        &dki->fees.deposit));
   aie = GNUNET_new (struct TEAH_AuditorInteractionEntry);
   aie->dh = dh;
   aie->auditor_url = auditor_url;
@@ -294,11 +306,13 @@ auditor_cb (void *cls,
     dh->exchange_timestamp,
     dh->dcd.wire_deadline,
     dh->dcd.refund_deadline,
-    &amount_without_fee,
-    &dh->cdds[coin].coin_pub,
+    &dh->total_without_fee,
+    dh->num_cdds,
+    cpubs,
+    csigs,
     &dh->dcd.merchant_pub,
     &dh->exchange_pub,
-    &dh->exchange_sigs[coin],
+    &dh->exchange_sig,
     &dh->keys->master_pub,
     spk->valid_from,
     spk->valid_until,
@@ -340,12 +354,9 @@ handle_deposit_finished (void *cls,
     break;
   case MHD_HTTP_OK:
     {
-      const json_t *sigs;
-      json_t *sig;
-      unsigned int idx;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_array_const ("exchange_sigs",
-                                      &sigs),
+        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+                                     &dh->exchange_sig),
         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
                                      &dh->exchange_pub),
         GNUNET_JSON_spec_mark_optional (
@@ -367,15 +378,6 @@ handle_deposit_finished (void *cls,
         dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
         break;
       }
-      if (json_array_size (sigs) != dh->num_cdds)
-      {
-        GNUNET_break_op (0);
-        dr->hr.http_status = 0;
-        dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-        break;
-      }
-      dh->exchange_sigs = GNUNET_new_array (dh->num_cdds,
-                                            struct TALER_ExchangeSignatureP);
       if (GNUNET_OK !=
           TALER_EXCHANGE_test_signing_key (dh->keys,
                                            &dh->exchange_pub))
@@ -385,35 +387,12 @@ handle_deposit_finished (void *cls,
         dr->hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
         break;
       }
-      json_array_foreach (sigs, idx, sig)
       {
-        struct GNUNET_JSON_Specification ispec[] = {
-          GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                       &dh->exchange_sigs[idx]),
-          GNUNET_JSON_spec_end ()
-        };
-        struct TALER_Amount amount_without_fee;
-        const struct TALER_EXCHANGE_DenomPublicKey *dki;
-
-        if (GNUNET_OK !=
-            GNUNET_JSON_parse (sig,
-                               ispec,
-                               NULL, NULL))
-        {
-          GNUNET_break_op (0);
-          dr->hr.http_status = 0;
-          dr->hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
-          break;
-        }
-        dki = TALER_EXCHANGE_get_denomination_key_by_hash (dh->keys,
-                                                           &dh->cdds[idx].
-                                                           h_denom_pub);
-        GNUNET_assert (NULL != dki);
-        GNUNET_assert (0 <=
-                       TALER_amount_subtract (&amount_without_fee,
-                                              &dh->cdds[idx].amount,
-                                              &dki->fees.deposit));
+        const struct TALER_CoinSpendSignatureP *csigs[
+          GNUNET_NZL (dh->num_cdds)];
 
+        for (unsigned int i = 0; i<dh->num_cdds; i++)
+          csigs[i] = &dh->cdds[i].coin_sig;
         if (GNUNET_OK !=
             TALER_exchange_online_deposit_confirmation_verify (
               &dh->dcd.h_contract_terms,
@@ -422,11 +401,12 @@ handle_deposit_finished (void *cls,
               dh->exchange_timestamp,
               dh->dcd.wire_deadline,
               dh->dcd.refund_deadline,
-              &amount_without_fee,
-              &dh->cdds[idx].coin_pub,
+              &dh->total_without_fee,
+              dh->num_cdds,
+              csigs,
               &dh->dcd.merchant_pub,
               &dh->exchange_pub,
-              &dh->exchange_sigs[idx]))
+              &dh->exchange_sig))
         {
           GNUNET_break_op (0);
           dr->hr.http_status = 0;
@@ -438,10 +418,9 @@ handle_deposit_finished (void *cls,
                                 &auditor_cb,
                                 dh);
     }
-    dr->details.ok.exchange_sigs = dh->exchange_sigs;
+    dr->details.ok.exchange_sig = &dh->exchange_sig;
     dr->details.ok.exchange_pub = &dh->exchange_pub;
     dr->details.ok.deposit_timestamp = dh->exchange_timestamp;
-    dr->details.ok.num_signatures = dh->num_cdds;
     break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the exchange is buggy
@@ -531,9 +510,13 @@ TALER_EXCHANGE_batch_deposit (
   json_t *deposit_obj;
   json_t *deposits;
   CURL *eh;
-  struct TALER_Amount amount_without_fee;
   const struct GNUNET_HashCode *wallet_data_hashp;
 
+  if (0 == num_cdds)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   if (GNUNET_TIME_timestamp_cmp (dcd->refund_deadline,
                                  >,
                                  dcd->wire_deadline))
@@ -547,8 +530,7 @@ TALER_EXCHANGE_batch_deposit (
   dh->cb = cb;
   dh->cb_cls = cb_cls;
   dh->cdds = GNUNET_memdup (cdds,
-                            num_cdds
-                            * sizeof (*cdds));
+                            num_cdds * sizeof (*cdds));
   dh->num_cdds = num_cdds;
   dh->dcd = *dcd;
   if (NULL != dcd->policy_details)
@@ -559,11 +541,15 @@ TALER_EXCHANGE_batch_deposit (
                                       &dh->h_wire);
   deposits = json_array ();
   GNUNET_assert (NULL != deposits);
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_set_zero (cdds[0].amount.currency,
+                                        &dh->total_without_fee));
   for (unsigned int i = 0; i<num_cdds; i++)
   {
     const struct TALER_EXCHANGE_CoinDepositDetail *cdd = &cdds[i];
     const struct TALER_EXCHANGE_DenomPublicKey *dki;
     const struct TALER_AgeCommitmentHash *h_age_commitmentp;
+    struct TALER_Amount amount_without_fee;
 
     dki = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
                                                        &cdd->h_denom_pub);
@@ -580,17 +566,14 @@ TALER_EXCHANGE_batch_deposit (
     {
       *ec = TALER_EC_EXCHANGE_DEPOSIT_FEE_ABOVE_AMOUNT;
       GNUNET_break_op (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Amount: %s\n",
-                  TALER_amount2s (&cdd->amount));
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Fee: %s\n",
-                  TALER_amount2s (&dki->fees.deposit));
       GNUNET_free (dh->cdds);
       GNUNET_free (dh);
       return NULL;
     }
-
+    GNUNET_assert (0 <=
+                   TALER_amount_add (&dh->total_without_fee,
+                                     &dh->total_without_fee,
+                                     &amount_without_fee));
     if (GNUNET_OK !=
         TALER_EXCHANGE_verify_deposit_signature_ (dcd,
                                                   &dh->h_policy,
@@ -737,7 +720,6 @@ TALER_EXCHANGE_batch_deposit_cancel (
   TALER_EXCHANGE_keys_decref (deposit->keys);
   GNUNET_free (deposit->url);
   GNUNET_free (deposit->cdds);
-  GNUNET_free (deposit->exchange_sigs);
   TALER_curl_easy_post_finished (&deposit->post_ctx);
   json_decref (deposit->response);
   GNUNET_free (deposit);
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 8c45f732..ac147d93 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -635,7 +635,7 @@ run (void *cls,
       MHD_HTTP_OK),
     TALER_TESTING_cmd_deposit_confirmation ("deposit-confirmation",
                                             "massive-deposit-10",
-                                            0,
+                                            1,
                                             "EUR:0.99",
                                             MHD_HTTP_OK),
     CMD_RUN_AUDITOR ("massive-auditor"),
diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c 
b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index 1e5a6344..9477a5d7 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -59,9 +59,9 @@ struct DepositConfirmationState
   const char *amount_without_fee;
 
   /**
-   * Which coin of the @e deposit_reference should we confirm.
+   * How many coins were there in the @e deposit_reference?
    */
-  unsigned int coin_index;
+  unsigned int num_coins;
 
   /**
    * DepositConfirmation handle while operation is running.
@@ -201,14 +201,15 @@ deposit_confirmation_run (void *cls,
   struct GNUNET_TIME_Timestamp refund_deadline
     = GNUNET_TIME_UNIT_ZERO_TS;
   struct TALER_Amount amount_without_fee;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct TALER_CoinSpendPublicKeyP coin_pubs[dcs->num_coins];
+  const struct TALER_CoinSpendPublicKeyP *coin_pubps[dcs->num_coins];
+  const struct TALER_CoinSpendSignatureP *coin_sigps[dcs->num_coins];
   const struct TALER_MerchantPrivateKeyP *merchant_priv;
   struct TALER_MerchantPublicKeyP merchant_pub;
   const struct TALER_ExchangePublicKeyP *exchange_pub;
   const struct TALER_ExchangeSignatureP *exchange_sig;
   const json_t *wire_details;
   const json_t *contract_terms;
-  const struct TALER_CoinSpendPrivateKeyP *coin_priv;
   const struct TALER_EXCHANGE_Keys *keys;
   const struct TALER_EXCHANGE_SigningPublicKey *spk;
   const char *auditor_url;
@@ -249,19 +250,19 @@ deposit_confirmation_run (void *cls,
 
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_exchange_pub (deposit_cmd,
-                                                       dcs->coin_index,
+                                                       0,
                                                        &exchange_pub));
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_exchange_sig (deposit_cmd,
-                                                       dcs->coin_index,
+                                                       0,
                                                        &exchange_sig));
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_timestamp (deposit_cmd,
-                                                    dcs->coin_index,
+                                                    0,
                                                     &exchange_timestamp));
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_wire_deadline (deposit_cmd,
-                                                        dcs->coin_index,
+                                                        0,
                                                         &wire_deadline));
   GNUNET_assert (NULL != exchange_timestamp);
   keys = TALER_TESTING_get_keys (is);
@@ -283,12 +284,23 @@ deposit_confirmation_run (void *cls,
   GNUNET_assert (GNUNET_OK ==
                  TALER_JSON_merchant_wire_signature_hash (wire_details,
                                                           &h_wire));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_TESTING_get_trait_coin_priv (deposit_cmd,
-                                                    dcs->coin_index,
-                                                    &coin_priv));
-  GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
-                                      &coin_pub.eddsa_pub);
+
+  for (unsigned int i = 0; i<dcs->num_coins; i++)
+  {
+    const struct TALER_CoinSpendPrivateKeyP *coin_priv;
+
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_TESTING_get_trait_coin_priv (deposit_cmd,
+                                                      i,
+                                                      &coin_priv));
+    GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
+                                        &coin_pubs[i].eddsa_pub);
+    coin_pubps[i] = &coin_pubs[i];
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_TESTING_get_trait_coin_sig (deposit_cmd,
+                                                     i,
+                                                     &coin_sigps[i]));
+  }
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_merchant_priv (deposit_cmd,
                                                         &merchant_priv));
@@ -331,7 +343,9 @@ deposit_confirmation_run (void *cls,
     *wire_deadline,
     refund_deadline,
     &amount_without_fee,
-    &coin_pub,
+    dcs->num_coins,
+    coin_pubps,
+    coin_sigps,
     &merchant_pub,
     exchange_pub,
     exchange_sig,
@@ -385,7 +399,7 @@ deposit_confirmation_cleanup (void *cls,
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_deposit_confirmation (const char *label,
                                         const char *deposit_reference,
-                                        unsigned int coin_index,
+                                        unsigned int num_coins,
                                         const char *amount_without_fee,
                                         unsigned int expected_response_code)
 {
@@ -393,7 +407,7 @@ TALER_TESTING_cmd_deposit_confirmation (const char *label,
 
   dcs = GNUNET_new (struct DepositConfirmationState);
   dcs->deposit_reference = deposit_reference;
-  dcs->coin_index = coin_index;
+  dcs->num_coins = num_coins;
   dcs->amount_without_fee = amount_without_fee;
   dcs->expected_response_code = expected_response_code;
 
diff --git a/src/testing/testing_api_cmd_batch_deposit.c 
b/src/testing/testing_api_cmd_batch_deposit.c
index a3c25e35..ef54e6a0 100644
--- a/src/testing/testing_api_cmd_batch_deposit.c
+++ b/src/testing/testing_api_cmd_batch_deposit.c
@@ -58,6 +58,11 @@ struct Coin
    */
   struct TALER_Amount deposit_fee;
 
+  /**
+   * Our coin signature.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * Reference to any command that is able to provide a coin,
    * possibly using $LABEL#$INDEX notation.
@@ -151,10 +156,9 @@ struct BatchDepositState
   struct GNUNET_SCHEDULER_Task *retry_task;
 
   /**
-   * Array of @e num_coins signatures from the exchange on the
-   * deposit confirmation.
+   * Deposit confirmation signature from the exchange.
    */
-  struct TALER_ExchangeSignatureP *exchange_sigs;
+  struct TALER_ExchangeSignatureP exchange_sig;
 
   /**
    * Reference to previous deposit operation.
@@ -205,19 +209,10 @@ batch_deposit_cb (void *cls,
   }
   if (MHD_HTTP_OK == dr->hr.http_status)
   {
-    if (ds->num_coins != dr->details.ok.num_signatures)
-    {
-      GNUNET_break (0);
-      TALER_TESTING_interpreter_fail (ds->is);
-      return;
-    }
     ds->deposit_succeeded = GNUNET_YES;
     ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
     ds->exchange_pub = *dr->details.ok.exchange_pub;
-    ds->exchange_sigs = GNUNET_memdup (dr->details.ok.exchange_sigs,
-                                       dr->details.ok.num_signatures
-                                       * sizeof (struct
-                                                 TALER_ExchangeSignatureP));
+    ds->exchange_sig = *dr->details.ok.exchange_sig;
   }
   TALER_TESTING_interpreter_next (ds->is);
 }
@@ -373,6 +368,7 @@ batch_deposit_run (void *cls,
                                ds->refund_deadline,
                                coin_priv,
                                &cdd->coin_sig);
+    coin->coin_sig = cdd->coin_sig;
   }
 
   GNUNET_assert (NULL == ds->dh);
@@ -439,7 +435,6 @@ batch_deposit_cleanup (void *cls,
   for (unsigned int i = 0; i<ds->num_coins; i++)
     GNUNET_free (ds->coins[i].coin_reference);
   GNUNET_free (ds->coins);
-  GNUNET_free (ds->exchange_sigs);
   json_decref (ds->wire_details);
   json_decref (ds->contract_terms);
   GNUNET_free (ds);
@@ -495,10 +490,10 @@ batch_deposit_traits (void *cls,
     struct TALER_TESTING_Trait traits[] = {
       /* First two traits are only available if
          ds->traits is #GNUNET_YES */
-      TALER_TESTING_make_trait_exchange_pub (index,
+      TALER_TESTING_make_trait_exchange_pub (0,
                                              &ds->exchange_pub),
-      TALER_TESTING_make_trait_exchange_sig (index,
-                                             &ds->exchange_sigs[index]),
+      TALER_TESTING_make_trait_exchange_sig (0,
+                                             &ds->exchange_sig),
       /* These traits are always available */
       TALER_TESTING_make_trait_wire_details (ds->wire_details),
       TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
@@ -507,6 +502,8 @@ batch_deposit_traits (void *cls,
                                                      age_commitment_proof),
       TALER_TESTING_make_trait_coin_priv (index,
                                           coin_spent_priv),
+      TALER_TESTING_make_trait_coin_sig (index,
+                                         &coin->coin_sig),
       TALER_TESTING_make_trait_deposit_amount (index,
                                                &coin->amount),
       TALER_TESTING_make_trait_deposit_fee_amount (index,
diff --git a/src/testing/testing_api_cmd_deposit.c 
b/src/testing/testing_api_cmd_deposit.c
index 0ee6aa44..61074afa 100644
--- a/src/testing/testing_api_cmd_deposit.c
+++ b/src/testing/testing_api_cmd_deposit.c
@@ -68,6 +68,11 @@ struct DepositState
    */
   unsigned int coin_index;
 
+  /**
+   * Our coin signature.
+   */
+  struct TALER_CoinSpendSignatureP coin_sig;
+
   /**
    * Wire details of who is depositing -- this would be merchant
    * wire details in a normal scenario.
@@ -258,11 +263,10 @@ deposit_cb (void *cls,
   }
   if (MHD_HTTP_OK == dr->hr.http_status)
   {
-    GNUNET_assert (1 == dr->details.ok.num_signatures);
     ds->deposit_succeeded = GNUNET_YES;
     ds->exchange_timestamp = dr->details.ok.deposit_timestamp;
     ds->exchange_pub = *dr->details.ok.exchange_pub;
-    ds->exchange_sig = dr->details.ok.exchange_sigs[0];
+    ds->exchange_sig = *dr->details.ok.exchange_sig;
   }
   TALER_TESTING_interpreter_next (ds->is);
 }
@@ -287,7 +291,6 @@ deposit_run (void *cls,
   const struct TALER_AgeCommitmentHash *phac;
   const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
   const struct TALER_DenominationSignature *denom_pub_sig;
-  struct TALER_CoinSpendSignatureP coin_sig;
   struct TALER_MerchantPublicKeyP merchant_pub;
   struct TALER_PrivateContractHashP h_contract_terms;
   enum TALER_ErrorCode ec;
@@ -449,14 +452,14 @@ deposit_run (void *cls,
                                &merchant_pub,
                                ds->refund_deadline,
                                coin_priv,
-                               &coin_sig);
+                               &ds->coin_sig);
   }
   GNUNET_assert (NULL == ds->dh);
   {
     struct TALER_EXCHANGE_CoinDepositDetail cdd = {
       .amount = ds->amount,
       .coin_pub = coin_pub,
-      .coin_sig = coin_sig,
+      .coin_sig = ds->coin_sig,
       .denom_sig = *denom_pub_sig,
       .h_denom_pub = denom_pub->h_key,
       .h_age_commitment = {{{0}}},
@@ -595,6 +598,8 @@ deposit_traits (void *cls,
       /* These traits are always available */
       TALER_TESTING_make_trait_coin_priv (0,
                                           coin_spent_priv),
+      TALER_TESTING_make_trait_coin_sig (0,
+                                         &ds->coin_sig),
       TALER_TESTING_make_trait_age_commitment_proof (0,
                                                      age_commitment_proof),
       TALER_TESTING_make_trait_h_age_commitment (0,
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index 3aa464aa..bc5fe439 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2021, 2022 Taler Systems SA
+  Copyright (C) 2021-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
@@ -78,12 +78,12 @@ struct TALER_DepositConfirmationPS
    * Amount to be deposited, excluding fee.  Calculated from the
    * amount with fee and the fee from the deposit request.
    */
-  struct TALER_AmountNBO amount_without_fee;
+  struct TALER_AmountNBO total_without_fee;
 
   /**
-   * The public key of the coin that was deposited.
+   * Hash over all of the coin signatures.
    */
-  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct GNUNET_HashCode h_coin_sigs;
 
   /**
    * The Merchant's public key.  Allows the merchant to later refund
@@ -105,8 +105,9 @@ TALER_exchange_online_deposit_confirmation_sign (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   struct TALER_ExchangePublicKeyP *pub,
   struct TALER_ExchangeSignatureP *sig)
@@ -119,14 +120,21 @@ TALER_exchange_online_deposit_confirmation_sign (
     .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp),
     .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline),
     .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
-    .coin_pub = *coin_pub,
     .merchant_pub = *merchant_pub
   };
-
+  struct GNUNET_HashContext *hc;
+
+  hc = GNUNET_CRYPTO_hash_context_start ();
+  for (unsigned int i = 0; i<num_coins; i++)
+    GNUNET_CRYPTO_hash_context_read (hc,
+                                     coin_sigs[i],
+                                     sizeof (*coin_sigs[i]));
+  GNUNET_CRYPTO_hash_context_finish (hc,
+                                     &dcs.h_coin_sigs);
   if (NULL != h_policy)
     dcs.h_policy = *h_policy;
-  TALER_amount_hton (&dcs.amount_without_fee,
-                     amount_without_fee);
+  TALER_amount_hton (&dcs.total_without_fee,
+                     total_without_fee);
   return scb (&dcs.purpose,
               pub,
               sig);
@@ -141,8 +149,9 @@ TALER_exchange_online_deposit_confirmation_verify (
   struct GNUNET_TIME_Timestamp exchange_timestamp,
   struct GNUNET_TIME_Timestamp wire_deadline,
   struct GNUNET_TIME_Timestamp refund_deadline,
-  const struct TALER_Amount *amount_without_fee,
-  const struct TALER_CoinSpendPublicKeyP *coin_pub,
+  const struct TALER_Amount *total_without_fee,
+  unsigned int num_coins,
+  const struct TALER_CoinSpendSignatureP *coin_sigs[static num_coins],
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   const struct TALER_ExchangePublicKeyP *exchange_pub,
   const struct TALER_ExchangeSignatureP *exchange_sig)
@@ -155,14 +164,21 @@ TALER_exchange_online_deposit_confirmation_verify (
     .exchange_timestamp = GNUNET_TIME_timestamp_hton (exchange_timestamp),
     .wire_deadline = GNUNET_TIME_timestamp_hton (wire_deadline),
     .refund_deadline = GNUNET_TIME_timestamp_hton (refund_deadline),
-    .coin_pub = *coin_pub,
     .merchant_pub = *merchant_pub
   };
-
+  struct GNUNET_HashContext *hc;
+
+  hc = GNUNET_CRYPTO_hash_context_start ();
+  for (unsigned int i = 0; i<num_coins; i++)
+    GNUNET_CRYPTO_hash_context_read (hc,
+                                     coin_sigs[i],
+                                     sizeof (*coin_sigs[i]));
+  GNUNET_CRYPTO_hash_context_finish (hc,
+                                     &dcs.h_coin_sigs);
   if (NULL != h_policy)
     dcs.h_policy = *h_policy;
-  TALER_amount_hton (&dcs.amount_without_fee,
-                     amount_without_fee);
+  TALER_amount_hton (&dcs.total_without_fee,
+                     total_without_fee);
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT,
                                   &dcs,

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]