aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <grothoff@gnunet.org>2022-04-01 16:39:07 +0200
committerChristian Grothoff <grothoff@gnunet.org>2022-04-01 16:39:07 +0200
commitcaf66486e70d896b1dd4eca3785fd42185a540cd (patch)
treebc4323b6fc7d1e256b485daaf7c20bdfa9037cfd /src
parent747ae5ef094731650911838a51e49db778b18ab6 (diff)
work on purse creation logic
Diffstat (limited to 'src')
-rw-r--r--src/exchange/taler-exchange-httpd_deposit.c12
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c2
-rw-r--r--src/exchange/taler-exchange-httpd_purses_create.c249
-rw-r--r--src/exchange/taler-exchange-httpd_withdraw.c1
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c23
-rw-r--r--src/include/taler_crypto_lib.h27
-rw-r--r--src/include/taler_exchangedb_plugin.h23
-rw-r--r--src/include/taler_signatures.h5
-rw-r--r--src/util/exchange_signatures.c113
9 files changed, 398 insertions, 57 deletions
diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c
index 59d25904f..011f5f159 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -178,7 +178,6 @@ deposit_transaction (void *cls,
}
if (in_conflict)
{
- TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
@@ -188,7 +187,6 @@ deposit_transaction (void *cls,
}
if (! balance_ok)
{
- TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
@@ -323,6 +321,16 @@ TEH_handler_deposit (struct MHD_Connection *connection,
GNUNET_JSON_parse_free (spec);
return mret;
}
+ if (0 > TALER_amount_cmp (&dk->meta.value,
+ &deposit.amount_with_fee))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
+ NULL);
+ }
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
{
/* This denomination is past the expiration time for deposits */
diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c
index 6fe97c8f2..2ff03023a 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -183,7 +183,6 @@ melt_transaction (void *cls,
if (rmc->zombie_required)
{
GNUNET_break_op (0);
- TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
@@ -193,7 +192,6 @@ melt_transaction (void *cls,
if (! balance_ok)
{
GNUNET_break_op (0);
- TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
diff --git a/src/exchange/taler-exchange-httpd_purses_create.c b/src/exchange/taler-exchange-httpd_purses_create.c
index ee822b259..75cb448a2 100644
--- a/src/exchange/taler-exchange-httpd_purses_create.c
+++ b/src/exchange/taler-exchange-httpd_purses_create.c
@@ -58,6 +58,11 @@ struct Coin
* Deposit fee applicable for this coin.
*/
struct TALER_Amount deposit_fee;
+
+ /**
+ * Amount to be put into the purse from this coin.
+ */
+ struct TALER_Amount amount_minus_fee;
};
@@ -147,22 +152,22 @@ reply_create_success (struct MHD_Connection *connection,
{
struct TALER_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig;
- // FIXME: define what exactly we sign over!
- struct TALER_DepositConfirmationPS dc = {
- .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION),
- .purpose.size = htonl (sizeof (dc)),
- .h_contract_terms = pcc->h_contract_terms,
- .purse_pub = *pcc->purse_pub
- };
enum TALER_ErrorCode ec;
- TALER_amount_hton (&dc.amount_without_fee,
- &pcc->amount);
if (TALER_EC_NONE !=
- (ec = TEH_keys_exchange_sign (&dc,
- &pub,
- &sig)))
+ (ec = TALER_exchange_online_purse_created_sign (
+ &TEH_keys_exchange_sign_,
+ pcc->exchange_timestamp,
+ pcc->purse_expiration,
+ &pcc->amount,
+ &pcc->total_deposited,
+ pcc->purse_pub,
+ &pcc->merge_pub,
+ &pcc->h_contract_terms,
+ &pub,
+ &sig)))
{
+ GNUNET_break (0);
return TALER_MHD_reply_with_ec (connection,
ec,
NULL);
@@ -170,8 +175,10 @@ reply_create_success (struct MHD_Connection *connection,
return TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_OK,
- // GNUNET_JSON_pack_timestamp ("exchange_timestamp",
- // pcc->exchange_timestamp),
+ GNUNET_JSON_pack_amount ("total_deposited",
+ &pcc->deposit_total),
+ GNUNET_JSON_pack_timestamp ("exchange_timestamp",
+ pcc->exchange_timestamp),
GNUNET_JSON_pack_data_auto ("exchange_sig",
&sig),
GNUNET_JSON_pack_data_auto ("exchange_pub",
@@ -199,43 +206,143 @@ create_transaction (void *cls,
{
struct PurseCreateContext *pcc = cls;
enum GNUNET_DB_QueryStatus qs;
- bool balance_ok;
- bool in_conflict;
-
- // 1) create purse
- // 2) deposit all coins
- // 3) if present, persist contract
- // Also: nicely report errors...
- qs = TEH_plugin->XXX (TEH_plugin->cls, ...);
+ bool in_conflict = true;
+
+ /* 1) create purse */
+ qs = TEH_plugin->insert_purse_request (TEH_plugin->cls,
+ &pcc->purse_pub,
+ &pcc->merge_pub,
+ pcc->purse_expiration,
+ &pcc->h_contract_terms,
+ pcc->min_age,
+ &pcc->amount,
+ &pcc->purse_sig,
+ &in_conflict);
if (qs < 0)
{
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
- TALER_LOG_WARNING ("Failed to store purse information in database\n");
- *mhd_ret = TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "purse create");
+ TALER_LOG_WARNING (
+ "Failed to store create purse information in database\n");
+ *mhd_ret =
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "purse create");
return qs;
}
if (in_conflict)
{
+ struct TALER_PurseMergePublicKeyP merge_pub;
+ struct GNUNET_TIME_Timestamp purse_expiration;
+ struct TALER_PrivateContractHashP h_contract_terms;
+ struct TALER_Amount target_amount;
+ struct TALER_Amount balance;
+ struct TALER_PurseContractSignatureP purse_sig;
+
TEH_plugin->rollback (TEH_plugin->cls);
+ qs = TEH_plugin->select_purse_request (TEH_plugin->cls,
+ pcc->purse_pub,
+ &merge_pub,
+ &purse_expiration,
+ &h_contract_terms,
+ &target_amount,
+ &balance,
+ &purse_sig);
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+ TALER_LOG_WARNING ("Failed to fetch purse information from database\n");
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "select purse request");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
*mhd_ret
- = TEH_RESPONSE_reply_coin_insufficient_funds (
+ = return TALER_MHD_REPLY_JSON_PACK (
connection,
- TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_CONTRACT,
- &coin->pci.coin_pub);
+ MHD_HTTP_CONFLICT,
+ TALER_JSON_pack_ec (
+ TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA),
+ GNUNET_JSON_pack_amount ("amount",
+ &amount),
+ GNUNET_JSON_pack_timestamp ("purse_expiration",
+ purse_expiration),
+ GNUNET_JSON_pack_data_auto ("purse_sig",
+ &purse_sig),
+ GNUNET_JSON_pack_data_auto ("h_contract_terms",
+ &h_contract_terms),
+ GNUNET_JSON_pack_data_auto ("merge_pub",
+ &merge_pub));
return GNUNET_DB_STATUS_HARD_ERROR;
}
- if (! balance_ok)
+ /* 2) deposit all coins */
+ for (unsigned int i = 0; i<pcc->num_coins; i++)
{
- TEH_plugin->rollback (TEH_plugin->cls);
+ struct Coin *coin = &pcc->coins[i];
+ bool balance_ok = false;
+
+ qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls,
+ &pcc->purse_pub,
+ &coin->cpi.coin_pub,
+ &coin->amount,
+ &coin->coin_sig,
+ &coin->amount_minus_fee,
+ &balance_ok);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ TALER_LOG_WARNING (
+ "Failed to store purse deposit information in database\n");
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "purse create deposit");
+ return qs;
+ }
+ if (! balance_ok)
+ {
+ *mhd_ret
+ = TEH_RESPONSE_reply_coin_insufficient_funds (
+ connection,
+ TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
+ &coin->pci.coin_pub);
+ 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->contract_pub,
+ pcc->econtract_size,
+ pcc->econtract,
+ &in_conflict);
+ if (qs < 0)
+ {
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ return qs;
+ TALER_LOG_WARNING ("Failed to store purse information in database\n");
+ *mhd_ret = TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_DB_STORE_FAILED,
+ "purse create contract");
+ return qs;
+ }
+ if (in_conflict)
+ {
+ /* FIXME-DESIGN: we have no proof that the
+ client is at fault here!
+ => need 2nd client signature over the encrypted
+ contract (and ECDHE public key)!!! */
*mhd_ret
- = TEH_RESPONSE_reply_coin_insufficient_funds (
+ = TEH_RESPONSE_reply_with_error (
connection,
- TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
- &coin->pci.coin_pub);
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_CONTRACT_STORED,
+ NULL);
return GNUNET_DB_STATUS_HARD_ERROR;
}
return qs;
@@ -289,7 +396,7 @@ parse_coin (struct MHD_Connection *connection,
}
if (GNUNET_OK !=
- TALER_wallet_purse_deposit_verify ("http://FIXME-URL",
+ TALER_wallet_purse_deposit_verify (TEH_base_url,
&pcc->purse_pub,
&pcc->amount,
&coin->coin_pub,
@@ -301,7 +408,7 @@ parse_coin (struct MHD_Connection *connection,
TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED,
TALER_EC_EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID,
- NULL))
+ TEH_base_url))
? GNUNET_NO : GNUNET_SYSERR;
}
/* check denomination exists and is valid */
@@ -317,6 +424,18 @@ parse_coin (struct MHD_Connection *connection,
GNUNET_JSON_parse_free (spec);
return (MHD_YES == mret) ? GNUNET_NO : GNUNET_SYSERR:
}
+ if (0 > TALER_amount_cmp (&dk->meta.value,
+ &coin->amount))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return (MHD_YES ==
+ TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
+ NULL))
+ ? GNUNET_NO : GNUNET_SYSERR;
+ }
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
{
/* This denomination is past the expiration time for deposits */
@@ -366,6 +485,20 @@ parse_coin (struct MHD_Connection *connection,
}
coin->deposit_fee = dk->meta.fees.deposit;
+ if (0 < TALER_amount_cmp (&coin->deposit_fee,
+ &coin->amount))
+ {
+ GNUNET_break_op (0);
+ GNUNET_JSON_parse_free (spec);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
+ NULL);
+ }
+ GNUNET_assert (0 <=
+ TALER_amount_subtact (&coin->amount_minus_fee,
+ &coin->amount,
+ &coin->deposit_fee));
/* check coin signature */
switch (dk->denom_pub.cipher)
{
@@ -391,6 +524,17 @@ parse_coin (struct MHD_Connection *connection,
NULL))
? GNUNET_NO : GNUNET_SYSERR;
}
+ if (0 >
+ TALER_amount_add (&pcc->deposit_total,
+ &pcc->deposit_total,
+ &coin->amount_minus_fee))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
+ "total deposit contribution");
+ }
}
{
MHD_RESULT mhd_ret = MHD_NO;
@@ -425,10 +569,10 @@ parse_coin (struct MHD_Connection *connection,
MHD_RESULT
-TEH_handler_purses_create (struct MHD_Connection *connection,
- const struct
- TALER_PurseContractPublicKeyP *purse_pub,
- const json_t *root)
+TEH_handler_purses_create (
+ struct MHD_Connection *connection,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const json_t *root)
{
struct PurseCreateContext pcc = {
.purse_pub = purse_pub,
@@ -461,6 +605,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
&pcc.purse_expiration),
GNUNET_JSON_spec_end ()
};
+ const struct TEH_GlobalFee *gf;
{
enum GNUNET_GenericReturnValue res;
@@ -479,7 +624,9 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
return MHD_YES; /* failure */
}
}
-
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (TEH_currency,
+ &pcc.deposit_total));
if (GNUNET_TIME_timestamp_cmp (pcc.purse_expiration,
<,
pcc.exchange_timestamp))
@@ -511,6 +658,17 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_GENERIC_PARAMETER_MALFORMED,
"deposits");
}
+ gf = TEH_keys_global_fee_by_time (TEH_keys_get_state (),
+ pcc.exchange_timestamp);
+ if (NULL == gf)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Cannot create purse: global fees not configured!\n");
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_INTERNAL_SERVER_ERROR,
+ TALER_EC_EXCHANGE_GENERIC_GLOBAL_FEES_MISSING,
+ NULL);
+ }
/* parse deposits */
pcc.coins = GNUNET_new_array (struct Coin,
pcc.num_coins);
@@ -531,8 +689,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
}
}
- // FIXME: get purse_fee!
- if (0 < TALER_amount_cmp (&purse_fee,
+ if (0 < TALER_amount_cmp (&gf->fees.purse,
&pcc.deposit_total))
{
GNUNET_break_op (0);
@@ -543,7 +700,6 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE,
NULL);
}
-
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK !=
@@ -560,7 +716,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
GNUNET_free (pcc.coins);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED,
- TALER_EC_EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID,
+ TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
NULL);
}
@@ -568,6 +724,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
TEH_plugin->preflight (TEH_plugin->cls))
{
GNUNET_break (0);
+ GNUNET_JSON_parse_free (spec);
GNUNET_free (pcc.coins);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c
index 5765181b2..d5ecd3386 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -256,7 +256,6 @@ withdraw_transaction (void *cls,
}
if (! below_limit)
{
- TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TALER_MHD_REPLY_JSON_PACK (
connection,
MHD_HTTP_ACCEPTED,
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c
index ec26a6dd0..8f0f5e22c 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -12668,6 +12668,10 @@ postgres_insert_partner (void *cls,
* @param pub_ckey ephemeral key for DH used to encrypt the contract
* @param econtract_size number of bytes in @a econtract
* @param econtract the encrypted contract
+ * @param[out] in_conflict set to true if @a econtract
+ * conflicts with an existing contract;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
@@ -12675,7 +12679,8 @@ postgres_insert_contract (void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_ContractDiffiePublicP *pub_ckey,
size_t econtract_size,
- const void *econtract)
+ const void *econtract,
+ bool *in_conflict)
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -12715,6 +12720,10 @@ postgres_select_contract (void *cls,
* @param age_limit age limit to enforce for payments into the purse
* @param amount target amount (with fees) to be put into the purse
* @param purse_sig signature with @a purse_pub's private key affirming the above
+ * @param[out] in_conflict set to true if the meta data
+ * conflicts with an existing purse;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
@@ -12726,7 +12735,8 @@ postgres_insert_purse_request (
const struct TALER_PrivateContractHashP *h_contract_terms,
uint32_t age_limit,
const struct TALER_Amount *amount,
- const struct TALER_PurseContractSignatureP *purse_sig)
+ const struct TALER_PurseContractSignatureP *purse_sig,
+ bool *in_conflict)
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
@@ -12806,6 +12816,11 @@ postgres_select_purse_by_merge_pub (
* @param coin_pub coin to deposit (debit)
* @param amount fraction of the coin's value to deposit
* @param coin_sig signature affirming the operation
+ * @param amount_minus_fee amount to add to the purse
+ * @param[out] balance_ok set to false if the coin's
+ * remaining balance is below @a amount;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
static enum GNUNET_DB_QueryStatus
@@ -12814,7 +12829,9 @@ postgres_do_purse_deposit (
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount,
- const struct TALER_CoinSpendSignatureP *coin_sig)
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ const struct TALER_Amount *amount_minus_fee,
+ bool *balance_ok)
{
GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index bafcca08e..6b554dfb1 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -3811,6 +3811,33 @@ TALER_exchange_online_reserve_closed_verify (
const struct TALER_ExchangeSignatureP *sig);
+enum TALER_ErrorCode
+TALER_exchange_online_purse_created_sign (
+ TALER_ExchangeSignCallback scb,
+ struct GNUNET_TIME_Timestamp exchange_time,
+ struct GNUNET_TIME_Timestamp purse_expiration,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_Amount *total_deposited,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseMergePublicKeyP *merge_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ struct TALER_ExchangePublicKeyP *pub,
+ struct TALER_ExchangeSignatureP *sig);
+
+
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_purse_created_verify (
+ struct GNUNET_TIME_Timestamp exchange_time,
+ struct GNUNET_TIME_Timestamp purse_expiration,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_Amount *total_deposited,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseMergePublicKeyP *merge_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_ExchangePublicKeyP *pub,
+ const struct TALER_ExchangeSignatureP *sig);
+
+
/* ********************* offline signing ************************** */
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 6b86dc3ce..488ffd519 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4389,6 +4389,10 @@ struct TALER_EXCHANGEDB_Plugin
* @param pub_ckey ephemeral key for DH used to encrypt the contract
* @param econtract_size number of bytes in @a econtract
* @param econtract the encrypted contract
+ * @param[out] in_conflict set to true if @a econtract
+ * conflicts with an existing contract;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -4396,7 +4400,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_ContractDiffiePublicP *pub_ckey,
size_t econtract_size,
- const void *econtract);
+ const void *econtract,
+ bool *in_conflict);
/**
@@ -4428,6 +4433,10 @@ struct TALER_EXCHANGEDB_Plugin
* @param age_limit age limit to enforce for payments into the purse
* @param amount target amount (with fees) to be put into the purse
* @param purse_sig signature with @a purse_pub's private key affirming the above
+ * @param[out] in_conflict set to true if the meta data
+ * conflicts with an existing purse;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -4439,7 +4448,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PrivateContractHashP *h_contract_terms,
uint32_t age_limit,
const struct TALER_Amount *amount,
- const struct TALER_PurseContractSignatureP *purse_sig);
+ const struct TALER_PurseContractSignatureP *purse_sig,
+ bool *in_conflict);
/**
@@ -4507,6 +4517,11 @@ struct TALER_EXCHANGEDB_Plugin
* @param coin_pub coin to deposit (debit)
* @param amount fraction of the coin's value to deposit
* @param coin_sig signature affirming the operation
+ * @param amount_minus_fee amount to add to the purse
+ * @param[out] balance_ok set to false if the coin's
+ * remaining balance is below @a amount;
+ * in this case, the return value will be
+ * #GNUNET_DB_STATUS_SUCCESS_ONE despite the failure
* @return transaction status code
*/
enum GNUNET_DB_QueryStatus
@@ -4515,7 +4530,9 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount,
- const struct TALER_CoinSpendSignatureP *coin_sig);
+ const struct TALER_CoinSpendSignatureP *coin_sig,
+ const struct TALER_Amount *amount_minus_fee,
+ bool *balance_ok);
/**
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index 52c294cef..70c917b67 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -176,6 +176,11 @@
*/
#define TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS 1044
+/**
+ * Signature by which the exchange affirms that a purse
+ * was created with a certain amount deposited into it.
+ */
+#define TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION 1045
/**********************/
/* Auditor signatures */
diff --git a/src/util/exchange_signatures.c b/src/util/exchange_signatures.c
index 4bfa822f1..eeec0d613 100644
--- a/src/util/exchange_signatures.c
+++ b/src/util/exchange_signatures.c
@@ -1261,4 +1261,117 @@ TALER_exchange_online_reserve_closed_verify (
}
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Response by which the exchange affirms that it has
+ * received funds deposited into a purse.
+ */
+struct TALER_PurseCreateDepositConfirmationPS
+{
+
+ /**
+ * Purpose is #TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION
+ */
+ struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+ /**
+ * When did the exchange receive the deposits.
+ */
+ struct GNUNET_TIME_TimestampNBO exchange_time;
+
+ /**
+ * When will the purse expire?
+ */
+ struct GNUNET_TIME_TimestampNBO purse_expiration;
+
+ /**
+ * How much should the purse ultimately contain.
+ */
+ struct TALER_AmountNBO amount_without_fee;
+
+ /**
+ * How much was deposited so far.
+ */
+ struct TALER_AmountNBO total_deposited;
+
+ /**
+ * Public key of the purse.
+ */
+ struct TALER_PurseContractPublicKeyP purse_pub;
+
+ /**
+ * Public key of the merge capability.
+ */
+ struct TALER_PurseMergePublicKeyP merge_pub;
+
+ /**
+ * Hash of the contract of the purse.
+ */
+ struct TALER_PrivateContractHashP h_contract_terms;
+
+};
+
+GNUNET_NETWORK_STRUCT_END
+
+
+enum TALER_ErrorCode
+TALER_exchange_online_purse_created_sign (
+ TALER_ExchangeSignCallback scb,
+ struct GNUNET_TIME_Timestamp exchange_time,
+ struct GNUNET_TIME_Timestamp purse_expiration,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_Amount *total_deposited,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseMergePublicKeyP *merge_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ struct TALER_ExchangePublicKeyP *pub,
+ struct TALER_ExchangeSignatureP *sig)
+{
+ struct TALER_PurseCreateDepositConfirmationPS dc = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION),
+ .purpose.size = htonl (sizeof (dc)),
+ .h_contract_terms = *h_contract_terms,
+ .purse_pub = *purse_pub,
+ .merge_pub = *merge_pub,
+ .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
+ .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
+ };
+
+ return scb (&dc.purpose,
+ pub,
+ sig);
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_exchange_online_purse_created_verify (
+ struct GNUNET_TIME_Timestamp exchange_time,
+ struct GNUNET_TIME_Timestamp purse_expiration,
+ const struct TALER_Amount *amount_without_fee,
+ const struct TALER_Amount *total_deposited,
+ const struct TALER_PurseContractPublicKeyP *purse_pub,
+ const struct TALER_PurseMergePublicKeyP *merge_pub,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_ExchangePublicKeyP *pub,
+ const struct TALER_ExchangeSignatureP *sig)
+{
+ struct TALER_PurseCreateDepositConfirmationPS dc = {
+ .purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION),
+ .purpose.size = htonl (sizeof (dc)),
+ .h_contract_terms = *h_contract_terms,
+ .purse_pub = *purse_pub,
+ .merge_pub = *merge_pub,
+ .purse_expiration = GNUNET_TIME_timestamp_hton (purse_expiration),
+ .exchange_time = GNUNET_TIME_timestamp_hton (exchange_time)
+ };
+
+ return
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PURSE_CREATION,
+ &dc,
+ &sig->eddsa_signature,
+ &pub->eddsa_pub);
+}
+
+
/* end of exchange_signatures.c */