summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2022-04-05 17:15:50 +0200
committerChristian Grothoff <christian@grothoff.org>2022-04-05 17:15:50 +0200
commitf3a4b00907410444055f47d4ff2074b44f981ad3 (patch)
treecb8f191a285152649e0b780aad8154f5b819f924 /src
parent71916414069d18ec5b7d5901d52d62a2aaa1403d (diff)
downloadexchange-f3a4b00907410444055f47d4ff2074b44f981ad3.tar.gz
exchange-f3a4b00907410444055f47d4ff2074b44f981ad3.tar.bz2
exchange-f3a4b00907410444055f47d4ff2074b44f981ad3.zip
adapt to latest GNUnet API: GNUNET_JSON_spec_mark_optional() changed
Diffstat (limited to 'src')
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c6
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c19
-rw-r--r--src/exchange/taler-exchange-httpd_metrics.h3
-rw-r--r--src/exchange/taler-exchange-httpd_purses_create.c154
-rw-r--r--src/exchange/taler-exchange-httpd_purses_create.h47
-rw-r--r--src/exchange/taler-exchange-httpd_recoup-refresh.c6
-rw-r--r--src/exchange/taler-exchange-httpd_recoup.c6
-rw-r--r--src/exchange/taler-exchange-httpd_refreshes_reveal.c20
-rw-r--r--src/exchangedb/exchange-0001-part.sql96
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c104
-rw-r--r--src/include/taler_exchangedb_plugin.h27
-rw-r--r--src/lib/exchange_api_common.c22
-rw-r--r--src/lib/exchange_api_deposit.c3
-rw-r--r--src/lib/exchange_api_handle.c9
-rw-r--r--src/lib/exchange_api_link.c9
-rw-r--r--src/lib/exchange_api_purse_create_with_deposit.c5
-rw-r--r--src/lib/exchange_api_refund.c12
-rw-r--r--src/testing/testing_api_cmd_auditor_deposit_confirmation.c3
19 files changed, 440 insertions, 112 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index dd8edfb90..c4c1656a5 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -103,6 +103,7 @@ taler_exchange_httpd_SOURCES = \
taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \
taler-exchange-httpd_metrics.c taler-exchange-httpd_metrics.h \
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
+ taler-exchange-httpd_purses_create.c taler-exchange-httpd_purses_create.h \
taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
taler-exchange-httpd_recoup-refresh.c taler-exchange-httpd_recoup-refresh.h \
taler-exchange-httpd_refreshes_reveal.c taler-exchange-httpd_refreshes_reveal.h \
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 011f5f159..0ce5add33 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -224,14 +224,16 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.h_contract_terms),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &deposit.coin.h_age_commitment)),
+ &deposit.coin.h_age_commitment),
+ &deposit.coin.no_age_commitment),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&deposit.csig),
GNUNET_JSON_spec_timestamp ("timestamp",
&deposit.timestamp),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("refund_deadline",
- &deposit.refund_deadline)),
+ &deposit.refund_deadline),
+ NULL),
GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
&deposit.wire_deadline),
GNUNET_JSON_spec_end ()
diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c
index 2ff03023a..3d6f05c0a 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -120,9 +120,9 @@ struct MeltContext
bool coin_is_dirty;
/**
- * True if @e rms is set.
+ * True if @e rms is missing.
*/
- bool have_rms;
+ bool no_rms;
};
@@ -161,9 +161,9 @@ melt_transaction (void *cls,
if (0 >
(qs = TEH_plugin->do_melt (TEH_plugin->cls,
- rmc->have_rms
- ? &rmc->rms
- : NULL,
+ rmc->no_rms
+ ? NULL
+ : &rmc->rms,
&rmc->refresh_session,
rmc->known_coin_id,
&rmc->zombie_required,
@@ -443,7 +443,8 @@ TEH_handler_melt (struct MHD_Connection *connection,
&rmc.refresh_session.coin.denom_pub_hash),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("age_commitment_hash",
- &rmc.refresh_session.coin.h_age_commitment)),
+ &rmc.refresh_session.coin.h_age_commitment),
+ &rmc.refresh_session.coin.no_age_commitment),
GNUNET_JSON_spec_fixed_auto ("confirm_sig",
&rmc.refresh_session.coin_sig),
TALER_JSON_spec_amount ("value_with_fee",
@@ -453,7 +454,8 @@ TEH_handler_melt (struct MHD_Connection *connection,
&rmc.refresh_session.rc),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("rms",
- &rmc.rms)),
+ &rmc.rms),
+ &rmc.no_rms),
GNUNET_JSON_spec_end ()
};
@@ -469,9 +471,6 @@ TEH_handler_melt (struct MHD_Connection *connection,
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
}
- rmc.have_rms = (NULL != json_object_get (root,
- "rms"));
-
{
MHD_RESULT res;
diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h
index a74fa44fd..d1d20d9aa 100644
--- a/src/exchange/taler-exchange-httpd_metrics.h
+++ b/src/exchange/taler-exchange-httpd_metrics.h
@@ -35,7 +35,8 @@ enum TEH_MetricTypeRequest
TEH_MT_REQUEST_DEPOSIT = 1,
TEH_MT_REQUEST_WITHDRAW = 2,
TEH_MT_REQUEST_MELT = 3,
- TEH_MT_REQUEST_COUNT = 4 /* MUST BE LAST! */
+ TEH_MT_REQUEST_PURSE_CREATE = 4,
+ TEH_MT_REQUEST_COUNT = 5 /* MUST BE LAST! */
};
/**
diff --git a/src/exchange/taler-exchange-httpd_purses_create.c b/src/exchange/taler-exchange-httpd_purses_create.c
index 326dad5c3..610320da0 100644
--- a/src/exchange/taler-exchange-httpd_purses_create.c
+++ b/src/exchange/taler-exchange-httpd_purses_create.c
@@ -63,6 +63,11 @@ struct Coin
* Amount to be put into the purse from this coin.
*/
struct TALER_Amount amount_minus_fee;
+
+ /**
+ * ID of the coin in known_coins.
+ */
+ uint64_t known_coin_id;
};
@@ -141,6 +146,10 @@ struct PurseCreateContext
*/
unsigned int num_coins;
+ /**
+ * Minimum age for deposits into this purse.
+ */
+ uint32_t min_age;
};
@@ -165,7 +174,7 @@ reply_create_success (struct MHD_Connection *connection,
pcc->exchange_timestamp,
pcc->purse_expiration,
&pcc->amount,
- &pcc->total_deposited,
+ &pcc->deposit_total,
pcc->purse_pub,
&pcc->merge_pub,
&pcc->h_contract_terms,
@@ -180,8 +189,8 @@ reply_create_success (struct MHD_Connection *connection,
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
- GNUNET_JSON_pack_amount ("total_deposited",
- &pcc->deposit_total),
+ TALER_JSON_pack_amount ("total_deposited",
+ &pcc->deposit_total),
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
pcc->exchange_timestamp),
GNUNET_JSON_pack_data_auto ("exchange_sig",
@@ -215,7 +224,7 @@ create_transaction (void *cls,
/* 1) create purse */
qs = TEH_plugin->insert_purse_request (TEH_plugin->cls,
- &pcc->purse_pub,
+ pcc->purse_pub,
&pcc->merge_pub,
pcc->purse_expiration,
&pcc->h_contract_terms,
@@ -244,6 +253,7 @@ create_transaction (void *cls,
struct TALER_Amount target_amount;
struct TALER_Amount balance;
struct TALER_PurseContractSignatureP purse_sig;
+ uint32_t min_age;
TEH_plugin->rollback (TEH_plugin->cls);
qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
@@ -251,6 +261,7 @@ create_transaction (void *cls,
&merge_pub,
&purse_expiration,
&h_contract_terms,
+ &min_age,
&target_amount,
&balance,
&purse_sig);
@@ -270,8 +281,10 @@ create_transaction (void *cls,
MHD_HTTP_CONFLICT,
TALER_JSON_pack_ec (
TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA),
- GNUNET_JSON_pack_amount ("amount",
- &amount),
+ TALER_JSON_pack_amount ("amount",
+ &target_amount),
+ GNUNET_JSON_pack_uint64 ("min_age",
+ min_age),
GNUNET_JSON_pack_timestamp ("purse_expiration",
purse_expiration),
GNUNET_JSON_pack_data_auto ("purse_sig",
@@ -287,15 +300,17 @@ create_transaction (void *cls,
{
struct Coin *coin = &pcc->coins[i];
bool balance_ok = false;
+ bool conflict = true;
qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls,
- &pcc->purse_pub,
+ pcc->purse_pub,
&coin->cpi.coin_pub,
&coin->amount,
&coin->coin_sig,
&coin->amount_minus_fee,
- &balance_ok);
- if (qs < 0)
+ &balance_ok,
+ &conflict);
+ if (qs <= 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
@@ -313,14 +328,58 @@ create_transaction (void *cls,
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
- &coin->pci.coin_pub);
+ &coin->cpi.coin_pub);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ if (conflict)
+ {
+ struct TALER_Amount amount;
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+ struct TALER_CoinSpendSignatureP coin_sig;
+ char *partner_url = NULL;
+
+ TEH_plugin->rollback (TEH_plugin->cls);
+ qs = TEH_plugin->get_purse_deposit (TEH_plugin->cls,
+ pcc->purse_pub,
+ &coin->cpi.coin_pub,
+ &amount,
+ &coin_sig,
+ &partner_url);
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+ TALER_LOG_WARNING (
+ "Failed to fetch purse deposit information from database\n");
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "get purse deposit");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+
+ *mhd_ret
+ = TALER_MHD_REPLY_JSON_PACK (
+ connection,
+ MHD_HTTP_CONFLICT,
+ TALER_JSON_pack_ec (
+ TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA),
+ GNUNET_JSON_pack_data_auto ("coin_pub",
+ &coin_pub),
+ GNUNET_JSON_pack_data_auto ("coin_sig",
+ &coin_sig),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("partner_url",
+ partner_url)),
+ TALER_JSON_pack_amount ("amount",
+ &amount));
+ GNUNET_free (partner_url);
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
/* 3) if present, persist contract */
in_conflict = true;
qs = TEH_plugin->insert_contract (TEH_plugin->cls,
- &pcc->purse_pub,
+ pcc->purse_pub,
&pcc->contract_pub,
pcc->econtract_size,
pcc->econtract,
@@ -345,12 +404,12 @@ create_transaction (void *cls,
void *econtract;
struct GNUNET_HashCode h_econtract;
- qs = select_contract (cls,
- &pcc->purse_pub,
- &pub_ckey,
- &econtract_sig,
- &econtract_size,
- &econtract);
+ qs = TEH_plugin->select_contract (TEH_plugin->cls,
+ pcc->purse_pub,
+ &pub_ckey,
+ &econtract_sig,
+ &econtract_size,
+ &econtract);
if (qs <= 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@@ -411,8 +470,8 @@ parse_coin (struct MHD_Connection *connection,
&coin->cpi.denom_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &coin->cpi.h_age_commitment)),
- &coin->cpi.no_age_commitment),
+ &coin->cpi.h_age_commitment),
+ &coin->cpi.no_age_commitment),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&coin->coin_sig),
GNUNET_JSON_spec_fixed_auto ("coin_pub",
@@ -432,10 +491,10 @@ parse_coin (struct MHD_Connection *connection,
if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify (TEH_base_url,
- &pcc->purse_pub,
+ pcc->purse_pub,
&pcc->amount,
- &coin->coin_pub,
- &coin->csig))
+ &coin->cpi.coin_pub,
+ &coin->coin_sig))
{
TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
GNUNET_JSON_parse_free (spec);
@@ -451,13 +510,13 @@ parse_coin (struct MHD_Connection *connection,
struct TEH_DenominationKey *dk;
MHD_RESULT mret;
- dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash,
+ dk = TEH_keys_denomination_by_hash (&coin->cpi.denom_pub_hash,
connection,
&mret);
if (NULL == dk)
{
GNUNET_JSON_parse_free (spec);
- return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR:
+ return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR;
}
if (0 > TALER_amount_cmp (&dk->meta.value,
&coin->amount))
@@ -478,10 +537,10 @@ parse_coin (struct MHD_Connection *connection,
return (MHD_YES ==
TEH_RESPONSE_reply_expired_denom_pub_hash (
connection,
- &coin->denom_pub_hash,
+ &coin->cpi.denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
"PURSE CREATE"))
- ? GNUNET_NO : GNUNET_SYSERR:
+ ? GNUNET_NO : GNUNET_SYSERR;
}
if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
{
@@ -490,7 +549,7 @@ parse_coin (struct MHD_Connection *connection,
return (MHD_YES ==
TEH_RESPONSE_reply_expired_denom_pub_hash (
connection,
- &coin->denom_pub_hash,
+ &coin->cpi.denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
"PURSE CREATE"))
? GNUNET_NO : GNUNET_SYSERR;
@@ -502,12 +561,12 @@ parse_coin (struct MHD_Connection *connection,
return (MHD_YES ==
TEH_RESPONSE_reply_expired_denom_pub_hash (
connection,
- &deposit.coin.denom_pub_hash,
+ &coin->cpi.denom_pub_hash,
TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
"PURSE CREATE"))
? GNUNET_NO : GNUNET_SYSERR;
}
- if (dk->denom_pub.cipher != deposit.coin.denom_sig.cipher)
+ if (dk->denom_pub.cipher != coin->cpi.denom_sig.cipher)
{
/* denomination cipher and denomination signature cipher not the same */
GNUNET_JSON_parse_free (spec);
@@ -531,9 +590,9 @@ parse_coin (struct MHD_Connection *connection,
NULL);
}
GNUNET_assert (0 <=
- TALER_amount_subtact (&coin->amount_minus_fee,
- &coin->amount,
- &coin->deposit_fee));
+ TALER_amount_subtract (&coin->amount_minus_fee,
+ &coin->amount,
+ &coin->deposit_fee));
/* check coin signature */
switch (dk->denom_pub.cipher)
{
@@ -547,7 +606,7 @@ parse_coin (struct MHD_Connection *connection,
break;
}
if (GNUNET_YES !=
- TALER_test_coin_valid (&deposit.coin,
+ TALER_test_coin_valid (&coin->cpi,
&dk->denom_pub))
{
TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
@@ -578,9 +637,9 @@ parse_coin (struct MHD_Connection *connection,
/* make sure coin is 'known' in database */
for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++)
{
- qs = TEH_make_coin_known (&deposit.coin,
+ qs = TEH_make_coin_known (&coin->cpi,
connection,
- &pcc.known_coin_id,
+ &coin->known_coin_id,
&mhd_ret);
/* no transaction => no serialization failures should be possible */
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
@@ -611,7 +670,7 @@ TEH_handler_purses_create (
{
struct PurseCreateContext pcc = {
.purse_pub = purse_pub,
- .exchange_timestamp = GNUNET_TIME_timestamp_get ();
+ .exchange_timestamp = GNUNET_TIME_timestamp_get ()
};
json_t *deposits;
json_t *deposit;
@@ -623,15 +682,18 @@ TEH_handler_purses_create (
GNUNET_JSON_spec_uint32 ("min_age",
&pcc.min_age),
GNUNET_JSON_spec_mark_optional (
- GNUNET_JSON_spec_var_size ("econtract",
- &pcc.econtract,
- &pcc.ecotract_size)),
+ GNUNET_JSON_spec_varsize ("econtract",
+ &pcc.econtract,
+ &pcc.econtract_size),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("econtract_sig",
- &pcc.econtract_sig)),
+ &pcc.econtract_sig),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("contract_pub",
- &pcc.contract_pub)),
+ &pcc.contract_pub),
+ NULL),
GNUNET_JSON_spec_fixed_auto ("merge_pub",
&pcc.merge_pub),
GNUNET_JSON_spec_fixed_auto ("purse_sig",
@@ -694,7 +756,7 @@ TEH_handler_purses_create (
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
- TALER_EC_EXCHANGE_GENERIC_PARAMETER_MALFORMED,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
"deposits");
}
gf = TEH_keys_global_fee_by_time (TEH_keys_get_state (),
@@ -709,8 +771,8 @@ TEH_handler_purses_create (
NULL);
}
/* parse deposits */
- pcc.coins = GNUNET_new_array (struct Coin,
- pcc.num_coins);
+ pcc.coins = GNUNET_new_array (pcc.num_coins,
+ struct Coin);
json_array_foreach (deposits, idx, deposit)
{
enum GNUNET_GenericReturnValue res;
@@ -742,12 +804,12 @@ TEH_handler_purses_create (
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
- TALER_wallet_purse_create_verify (&pcc.purse_expiration,
+ TALER_wallet_purse_create_verify (pcc.purse_expiration,
&pcc.h_contract_terms,
&pcc.merge_pub,
pcc.min_age,
&pcc.amount,
- &pcc.purse_pub,
+ pcc.purse_pub,
&pcc.purse_sig))
{
TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
diff --git a/src/exchange/taler-exchange-httpd_purses_create.h b/src/exchange/taler-exchange-httpd_purses_create.h
new file mode 100644
index 000000000..4dfbabf9b
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_purses_create.h
@@ -0,0 +1,47 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2022 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
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_purses_create.h
+ * @brief Handle /purses/$PID/create requests
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_PURSES_CREATE_H
+#define TALER_EXCHANGE_HTTPD_PURSES_CREATE_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/purses/$PURSE_PUB/create" request. Parses the JSON, and, if
+ * successful, passes the JSON data to #create_transaction() to further check
+ * the details of the operation specified. If everything checks out, this
+ * will ultimately lead to the "purses create" being executed, or rejected.
+ *
+ * @param connection the MHD connection to handle
+ * @param purse_pub public key of the purse
+ * @param root uploaded JSON data
+ * @return MHD result code
+ */
+MHD_RESULT
+TEH_handler_purses_create (struct MHD_Connection *connection,
+ const struct
+ TALER_PurseContractPublicKeyP *purse_pub,
+ const json_t *root);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_recoup-refresh.c b/src/exchange/taler-exchange-httpd_recoup-refresh.c
index dbeadfbd4..a00eeba4e 100644
--- a/src/exchange/taler-exchange-httpd_recoup-refresh.c
+++ b/src/exchange/taler-exchange-httpd_recoup-refresh.c
@@ -390,10 +390,12 @@ TEH_handler_recoup_refresh (struct MHD_Connection *connection,
&coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &coin.h_age_commitment)),
+ &coin.h_age_commitment),
+ &coin.no_age_commitment),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
- &nonce)),
+ &nonce),
+ NULL),
GNUNET_JSON_spec_end ()
};
diff --git a/src/exchange/taler-exchange-httpd_recoup.c b/src/exchange/taler-exchange-httpd_recoup.c
index 101e7bffd..6bda8af9e 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -403,10 +403,12 @@ TEH_handler_recoup (struct MHD_Connection *connection,
&coin_sig),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &coin.h_age_commitment)),
+ &coin.h_age_commitment),
+ &coin.no_age_commitment),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
- &nonce)),
+ &nonce),
+ NULL),
GNUNET_JSON_spec_end ()
};
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index e1cf6ab2c..8a5d8a802 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -132,9 +132,9 @@ struct RevealContext
unsigned int num_fresh_coins;
/**
- * True if @e rms was provided.
+ * True if @e rms was not provided.
*/
- bool have_rms;
+ bool no_rms;
};
@@ -337,9 +337,9 @@ check_commitment (struct RevealContext *rctx,
}
TALER_refresh_get_commitment (&rc_expected,
TALER_CNC_KAPPA,
- rctx->have_rms
- ? &rctx->rms
- : NULL,
+ rctx->no_rms
+ ? NULL
+ : &rctx->rms,
rctx->num_fresh_coins,
rcs,
&rctx->melt.session.coin.coin_pub,
@@ -484,7 +484,7 @@ resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
if (NULL == dks[i])
return ret;
if ( (TALER_DENOMINATION_CS == dks[i]->denom_pub.cipher) &&
- (! rctx->have_rms) )
+ (rctx->no_rms) )
{
return TALER_MHD_reply_with_error (
connection,
@@ -937,10 +937,12 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
&new_denoms_h),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("old_age_commitment",
- &old_age_commitment)),
+ &old_age_commitment),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("rms",
- &rctx.rms)),
+ &rctx.rms),
+ &rctx.no_rms),
GNUNET_JSON_spec_end ()
};
@@ -981,8 +983,6 @@ TEH_handler_reveal (struct TEH_RequestContext *rc,
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
}
- rctx.have_rms = (NULL != json_object_get (root,
- "rms"));
/* Check we got enough transfer private keys */
/* Note we do +1 as 1 row (cut-and-choose!) is missing! */
diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql
index c068ff6f9..2c416f03e 100644
--- a/src/exchangedb/exchange-0001-part.sql
+++ b/src/exchangedb/exchange-0001-part.sql
@@ -2469,22 +2469,108 @@ END $$;
-
-
-
-
CREATE OR REPLACE FUNCTION exchange_do_purse_deposit(
+ IN in_partner_id INT8,
IN in_purse_pub BYTEA,
IN in_amount_with_fee_val INT8,
IN in_amount_with_fee_frac INT4,
IN in_coin_pub BYTEA,
IN in_coin_sig BYTEA,
+ IN in_amount_without_fee_val INT8,
+ IN in_amount_without_fee_frac INT4,
OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN)
LANGUAGE plpgsql
AS $$
BEGIN
- -- FIXME
+
+-- Store the deposit request.
+INSERT INTO purse_deposits
+ (partner_serial_id
+ ,purse_pub
+ ,coin_pub
+ ,amount_with_fee_val
+ ,amount_with_fee_frac
+ ,coin_sig)
+ VALUES
+ (in_partner_id
+ ,in_purse_pub
+ ,in_coin_pub
+ ,in_amount_with_fee_val
+ ,in_amount_with_fee_frac
+ ,in_coin_sig)
+ ON CONFLICT DO NOTHING;
+
+IF NOT FOUND
+THEN
+ -- Idempotency check: check if coin_sig is the same,
+ -- if so, success, otherwise conflict!
+ SELECT
+ 1
+ FROM purse_deposits
+ WHERE coin_pub = in_coin_pub
+ AND purse_pub = in_purse_pub
+ AND coin_sig = in_cion_sig;
+ IF NOT FOUND
+ THEN
+ -- Deposit exists, but with differences. Not allowed.
+ out_balance_ok=FALSE;
+ out_conflict=TRUE;
+ RETURN;
+ END IF;
+END IF;
+
+
+-- Debit the coin
+-- Check and update balance of the coin.
+UPDATE known_coins
+ SET
+ remaining_frac=remaining_frac-in_amount_with_fee_frac
+ + CASE
+ WHEN remaining_frac < in_amount_with_fee_frac
+ THEN 100000000
+ ELSE 0
+ END,
+ remaining_val=remaining_val-in_amount_with_fee_val
+ - CASE
+ WHEN remaining_frac < in_amount_with_fee_frac
+ THEN 1
+ ELSE 0
+ END
+ WHERE coin_pub=in_coin_pub
+ AND ( (remaining_val > in_amount_with_fee_val) OR
+ ( (remaining_frac >= in_amount_with_fee_frac) AND
+ (remaining_val >= in_amount_with_fee_val) ) );
+
+IF NOT FOUND
+THEN
+ -- Insufficient balance.
+ out_balance_ok=FALSE;
+ out_conflict=FALSE;
+ RETURN;
+END IF;
+
+
+-- Credit the purse.
+UPDATE purse_requests
+ SET
+ balance_frac=balance_frac+in_amount_without_fee_frac
+ - CASE
+ WHEN balance_frac+in_amount_without_fee_frac >= 100000000
+ THEN 100000000
+ ELSE 0
+ END,
+ balance_val=balance_val+in_amount_without_fee_val
+ + CASE
+ WHEN balance_frac+in_amount_without_fee_frac >= 100000000
+ THEN 1
+ ELSE 0
+ END
+ WHERE purse_pub=in_purse_pub;
+
+out_conflict=FALSE;
+out_balance_ok=TRUE;
+
END $$;
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index 64cc0a77e..aed69725b 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -799,6 +799,15 @@ prepare_statements (struct PostgresClosure *pg)
" FROM exchange_do_deposit"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17);",
17),
+ /* used in postgres_do_purse_deposit() */
+ GNUNET_PQ_make_prepare (
+ "call_purse_deposit",
+ "SELECT "
+ " out_balance_ok AS balance_ok"
+ ",out_conflict AS conflict"
+ " FROM exchange_do_purse_deposit"
+ " ($1,$2,$3,$4,$5,$6,$7,$8);",
+ 8),
/* Used in #postgres_do_melt() to melt a coin. */
GNUNET_PQ_make_prepare (
"call_melt",
@@ -3342,13 +3351,19 @@ prepare_statements (struct PostgresClosure *pg)
" FROM purse_requests"
" WHERE merge_pub=$1;",
1),
- /* Used in #postgres_do_purse_deposit() */
+ /* Used in #postgres_get_purse_deposit */
GNUNET_PQ_make_prepare (
- "call_purse_deposit",
- "SELECT 1"
- " FROM exchange_do_purse_deposit"
- " ($1, $2, $3, $4, $5);",
- 5),
+ "select_purse_deposit_by_coin_pub",
+ "SELECT "
+ " coin_sig"
+ ",amount_with_fee_val"
+ ",amount_with_fee_frac"
+ ",partner_base_url"
+ " FROM purse_deposits"
+ " LEFT JOIN partners USING (partner_serial_id)"
+ " WHERE coin_pub=$2"
+ " AND purse_pub=$1;",
+ 2),
/* Used in #postgres_do_purse_merge() */
GNUNET_PQ_make_prepare (
"call_purse_merge",
@@ -13181,10 +13196,79 @@ postgres_do_purse_deposit (
const struct TALER_Amount *amount,
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_minus_fee,
- bool *balance_ok)
+ bool *balance_ok,
+ bool *conflict)
{
- GNUNET_break (0); // FIXME
- return GNUNET_DB_STATUS_HARD_ERROR;
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_uint64 (0), /* FIXME: partner ID */
+ GNUNET_PQ_query_param_auto_from_type (purse_pub),
+ TALER_PQ_query_param_amount (amount),
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_auto_from_type (coin_sig),
+ TALER_PQ_query_param_amount (amount_minus_fee),
+ GNUNET_PQ_query_param_end
+ };
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_bool ("balance_ok",
+ balance_ok),
+ GNUNET_PQ_result_spec_bool ("conflict",
+ conflict),
+ GNUNET_PQ_result_spec_end
+ };
+
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "call_purse_deposit",
+ params,
+ rs);
+}
+
+
+/**
+ * Function called to obtain a coin deposit data from
+ * depositing the coin into a purse.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param purse_pub purse to credit
+ * @param coin_pub coin to deposit (debit)
+ * @param[out] amount set fraction of the coin's value that was deposited (with fee)
+ * @param[out] coin_sig set to signature affirming the operation
+ * @param[out] partner_url set to the URL of the partner exchange, or NULL for ourselves, must be freed by caller
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_purse_deposit (
+ void *cls,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_Amount *amount,
+ struct TALER_CoinSpendSignatureP *coin_sig,
+ char **partner_url)
+{
+ struct PostgresClosure *pg = cls;
+ struct GNUNET_PQ_QueryParam params[] = {
+ GNUNET_PQ_query_param_auto_from_type (purse_pub),
+ GNUNET_PQ_query_param_auto_from_type (coin_pub),
+ GNUNET_PQ_query_param_end
+ };
+ bool is_null;
+ struct GNUNET_PQ_ResultSpec rs[] = {
+ GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+ coin_sig),
+ TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
+ amount),
+ GNUNET_PQ_result_spec_allow_null (
+ GNUNET_PQ_result_spec_string ("partner_base_url",
+ partner_url),
+ &is_null),
+ GNUNET_PQ_result_spec_end
+ };
+
+ *partner_url = NULL;
+ return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+ "select_purse_deposit_by_coin_pub",
+ params,
+ rs);
}
@@ -13589,6 +13673,8 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
= &postgres_select_purse_by_merge_pub;
plugin->do_purse_deposit
= &postgres_do_purse_deposit;
+ plugin->get_purse_deposit
+ = &postgres_get_purse_deposit;
plugin->do_purse_merge
= &postgres_do_purse_merge;
plugin->select_purse_merge
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 9c9410d66..c065f581f 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4567,6 +4567,8 @@ struct TALER_EXCHANGEDB_Plugin
* remaining balance is below @a amount;
* in this case, the return value will be
* #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
+ * @param[out] conflict the same coin was deposited into
+ * this purse with a different amount already
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -4577,7 +4579,30 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_Amount *amount,
const struct TALER_CoinSpendSignatureP *coin_sig,
const struct TALER_Amount *amount_minus_fee,
- bool *balance_ok);
+ bool *balance_ok,
+ bool *conflict);
+
+
+ /**
+ * Function called to obtain a coin deposit data from
+ * depositing the coin into a purse.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param purse_pub purse to credit
+ * @param coin_pub coin to deposit (debit)
+ * @param[out] amount set fraction of the coin's value that was deposited (with fee)
+ * @param[out] coin_sig set to signature affirming the operation
+ * @param[out] partner_url set to the URL of the partner exchange, or NULL for ourselves, must be freed by caller
+ * @return transaction status code
+ */
+ enum GNUNET_DB_QueryStatus
+ (*get_purse_deposit)(
+ void *cls,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ struct TALER_Amount *amount,
+ struct TALER_CoinSpendSignatureP *coin_sig,
+ char **partner_url);
/**
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index df1c43e4f..04f2a4df8 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -447,7 +447,8 @@ TALER_EXCHANGE_verify_coin_history (
struct TALER_MerchantPublicKeyP merchant_pub;
struct GNUNET_TIME_Timestamp refund_deadline = {0};
struct TALER_CoinSpendSignatureP sig;
- struct TALER_AgeCommitmentHash hac = {0};
+ struct TALER_AgeCommitmentHash hac;
+ bool no_hac;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig),
@@ -459,12 +460,14 @@ TALER_EXCHANGE_verify_coin_history (
h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &hac)),
+ &hac),
+ &no_hac),
GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("refund_deadline",
- &refund_deadline)),
+ &refund_deadline),
+ NULL),
TALER_JSON_spec_amount_any ("deposit_fee",
&fee),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
@@ -486,7 +489,7 @@ TALER_EXCHANGE_verify_coin_history (
&fee,
&h_wire,
&h_contract_terms,
- TALER_AgeCommitmentHash_isNullOrZero (&hac) ? NULL : &hac,
+ no_hac ? NULL : &hac,
NULL /* h_extensions! */,
h_denom_pub,
wallet_timestamp,
@@ -519,7 +522,8 @@ TALER_EXCHANGE_verify_coin_history (
{
struct TALER_CoinSpendSignatureP sig;
struct TALER_RefreshCommitmentP rc;
- struct TALER_AgeCommitmentHash h_age_commitment = {0};
+ struct TALER_AgeCommitmentHash h_age_commitment;
+ bool no_hac;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&sig),
@@ -529,7 +533,8 @@ TALER_EXCHANGE_verify_coin_history (
h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &h_age_commitment)),
+ &h_age_commitment),
+ &no_hac),
TALER_JSON_spec_amount_any ("melt_fee",
&fee),
GNUNET_JSON_spec_end ()
@@ -566,8 +571,9 @@ TALER_EXCHANGE_verify_coin_history (
&fee,
&rc,
h_denom_pub,
- TALER_AgeCommitmentHash_isNullOrZero (&h_age_commitment) ?
- NULL : &h_age_commitment,
+ no_hac
+ ? NULL
+ : &h_age_commitment,
coin_pub,
&sig))
{
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 947a42561..67f595bfb 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -333,7 +333,8 @@ handle_deposit_finished (void *cls,
&dh->exchange_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_string ("transaction_base_url",
- &dr.details.success.transaction_base_url)),
+ &dr.details.success.transaction_base_url),
+ NULL),
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
&dh->exchange_timestamp),
GNUNET_JSON_spec_end ()
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index feab4ac44..6528c56d1 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -750,7 +750,8 @@ decode_keys_json (const json_t *resp_obj,
&currency),
GNUNET_JSON_spec_mark_optional (
TALER_JSON_spec_amount_any ("wallet_balance_limit_without_kyc",
- &key_data->wallet_balance_limit_without_kyc)),
+ &key_data->wallet_balance_limit_without_kyc),
+ NULL),
GNUNET_JSON_spec_end ()
};
@@ -895,11 +896,13 @@ decode_keys_json (const json_t *resp_obj,
struct GNUNET_JSON_Specification ext_spec[] = {
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_json ("extensions",
- &extensions)),
+ &extensions),
+ NULL),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto (
"extensions_sig",
- &extensions_sig)),
+ &extensions_sig),
+ NULL),
GNUNET_JSON_spec_end ()
};
diff --git a/src/lib/exchange_api_link.c b/src/lib/exchange_api_link.c
index 5840cac63..ddc763c33 100644
--- a/src/lib/exchange_api_link.c
+++ b/src/lib/exchange_api_link.c
@@ -97,6 +97,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
union TALER_DenominationBlindingKeyP bks;
struct TALER_ExchangeWithdrawValues alg_values;
struct TALER_CsNonce nonce;
+ bool no_nonce;
uint32_t coin_idx;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_denom_pub ("denom_pub",
@@ -111,7 +112,8 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
&coin_idx),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("cs_nonce",
- &nonce)),
+ &nonce),
+ &no_nonce),
GNUNET_JSON_spec_end ()
};
struct TALER_TransferSecretP secret;
@@ -119,9 +121,6 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
struct TALER_CoinPubHashP c_hash;
/* parse reply */
- memset (&nonce,
- 0,
- sizeof (nonce));
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
@@ -180,7 +179,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
}
if (TALER_DENOMINATION_CS == alg_values.cipher)
{
- if (GNUNET_is_zero (&nonce))
+ if (no_nonce)
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
diff --git a/src/lib/exchange_api_purse_create_with_deposit.c b/src/lib/exchange_api_purse_create_with_deposit.c
index d8f86de0d..f5871fa34 100644
--- a/src/lib/exchange_api_purse_create_with_deposit.c
+++ b/src/lib/exchange_api_purse_create_with_deposit.c
@@ -259,7 +259,7 @@ TALER_EXCHANGE_purse_create_with_deposit (
struct TALER_ContractDiffiePublicP contract_pub;
char arg_str[sizeof (pch->purse_pub) * 2 + 32];
char *url;
- uint32_t min_age;
+ uint32_t min_age = 0;
pch = GNUNET_new (struct TALER_EXCHANGE_PurseCreateDepositHandle);
pch->exchange = exchange;
@@ -272,7 +272,8 @@ TALER_EXCHANGE_purse_create_with_deposit (
&pch->purse_value_after_fees),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_uint32 ("minimum_age",
- &min_age)),
+ &min_age),
+ NULL),
GNUNET_JSON_spec_end ()
};
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index 6369a5bd6..004661b00 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -231,7 +231,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
struct TALER_Amount deposit_fee;
struct TALER_MerchantWireHashP h_wire;
struct TALER_PrivateContractHashP h_contract_terms;
- struct TALER_AgeCommitmentHash h_age_commitment = {{{0}}};
+ struct TALER_AgeCommitmentHash h_age_commitment;
+ bool no_hac;
// struct TALER_ExtensionContractHashP h_extensions; // FIXME!
struct TALER_DenominationHashP h_denom_pub;
struct GNUNET_TIME_Timestamp wallet_timestamp;
@@ -249,7 +250,8 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&h_denom_pub),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
- &h_age_commitment)),
+ &h_age_commitment),
+ &no_hac),
GNUNET_JSON_spec_timestamp ("timestamp",
&wallet_timestamp),
GNUNET_JSON_spec_timestamp ("refund_deadline",
@@ -275,8 +277,10 @@ verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
&deposit_fee,
&h_wire,
&h_contract_terms,
- &h_age_commitment,
- NULL /* h_extensions! */,
+ no_hac
+ ? NULL
+ : &h_age_commitment,
+ NULL /* FIXME-OEC: h_extensions! */,
&h_denom_pub,
wallet_timestamp,
&merchant_pub,
diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index beeeb551c..d99b12937 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -291,7 +291,8 @@ deposit_confirmation_run (void *cls,
&timestamp),
GNUNET_JSON_spec_mark_optional (
GNUNET_JSON_spec_timestamp ("refund_deadline",
- &refund_deadline)),
+ &refund_deadline),
+ NULL),
GNUNET_JSON_spec_end ()
};