work on purse creation logic

This commit is contained in:
Christian Grothoff 2022-04-01 16:39:07 +02:00
parent 747ae5ef09
commit caf66486e7
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
10 changed files with 400 additions and 59 deletions

@ -1 +1 @@
Subproject commit 25eb78f2d0e20a137020dd0ab1c6474123843dbe Subproject commit b799c63db9beda99e9151e7611a1ac4e810786ab

View File

@ -178,7 +178,6 @@ deposit_transaction (void *cls,
} }
if (in_conflict) if (in_conflict)
{ {
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret *mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
@ -188,7 +187,6 @@ deposit_transaction (void *cls,
} }
if (! balance_ok) if (! balance_ok)
{ {
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret *mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,
@ -323,6 +321,16 @@ TEH_handler_deposit (struct MHD_Connection *connection,
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return mret; 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)) if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
{ {
/* This denomination is past the expiration time for deposits */ /* This denomination is past the expiration time for deposits */

View File

@ -183,7 +183,6 @@ melt_transaction (void *cls,
if (rmc->zombie_required) if (rmc->zombie_required)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TALER_MHD_reply_with_error (connection, *mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE, TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
@ -193,7 +192,6 @@ melt_transaction (void *cls,
if (! balance_ok) if (! balance_ok)
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret *mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_coin_insufficient_funds (
connection, connection,

View File

@ -58,6 +58,11 @@ struct Coin
* Deposit fee applicable for this coin. * Deposit fee applicable for this coin.
*/ */
struct TALER_Amount deposit_fee; 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_ExchangePublicKeyP pub;
struct TALER_ExchangeSignatureP sig; 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; enum TALER_ErrorCode ec;
TALER_amount_hton (&dc.amount_without_fee,
&pcc->amount);
if (TALER_EC_NONE != if (TALER_EC_NONE !=
(ec = TEH_keys_exchange_sign (&dc, (ec = TALER_exchange_online_purse_created_sign (
&pub, &TEH_keys_exchange_sign_,
&sig))) 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, return TALER_MHD_reply_with_ec (connection,
ec, ec,
NULL); NULL);
@ -170,8 +175,10 @@ reply_create_success (struct MHD_Connection *connection,
return TALER_MHD_REPLY_JSON_PACK ( return TALER_MHD_REPLY_JSON_PACK (
connection, connection,
MHD_HTTP_OK, MHD_HTTP_OK,
// GNUNET_JSON_pack_timestamp ("exchange_timestamp", GNUNET_JSON_pack_amount ("total_deposited",
// pcc->exchange_timestamp), &pcc->deposit_total),
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
pcc->exchange_timestamp),
GNUNET_JSON_pack_data_auto ("exchange_sig", GNUNET_JSON_pack_data_auto ("exchange_sig",
&sig), &sig),
GNUNET_JSON_pack_data_auto ("exchange_pub", GNUNET_JSON_pack_data_auto ("exchange_pub",
@ -199,14 +206,120 @@ create_transaction (void *cls,
{ {
struct PurseCreateContext *pcc = cls; struct PurseCreateContext *pcc = cls;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
bool balance_ok; bool in_conflict = true;
bool in_conflict;
// 1) create purse /* 1) create purse */
// 2) deposit all coins qs = TEH_plugin->insert_purse_request (TEH_plugin->cls,
// 3) if present, persist contract &pcc->purse_pub,
// Also: nicely report errors... &pcc->merge_pub,
qs = TEH_plugin->XXX (TEH_plugin->cls, ...); 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 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
= return TALER_MHD_REPLY_JSON_PACK (
connection,
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;
}
/* 2) deposit all coins */
for (unsigned int i = 0; i<pcc->num_coins; i++)
{
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 (qs < 0)
{ {
if (GNUNET_DB_STATUS_SOFT_ERROR == qs) if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
@ -215,27 +328,21 @@ create_transaction (void *cls,
*mhd_ret = TALER_MHD_reply_with_error (connection, *mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_GENERIC_DB_STORE_FAILED, TALER_EC_GENERIC_DB_STORE_FAILED,
"purse create"); "purse create contract");
return qs; return qs;
} }
if (in_conflict) if (in_conflict)
{ {
TEH_plugin->rollback (TEH_plugin->cls); /* 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 *mhd_ret
= TEH_RESPONSE_reply_coin_insufficient_funds ( = TEH_RESPONSE_reply_with_error (
connection, connection,
TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_CONTRACT, MHD_HTTP_INTERNAL_SERVER_ERROR,
&coin->pci.coin_pub); TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_CONTRACT_STORED,
return GNUNET_DB_STATUS_HARD_ERROR; NULL);
}
if (! balance_ok)
{
TEH_plugin->rollback (TEH_plugin->cls);
*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; return GNUNET_DB_STATUS_HARD_ERROR;
} }
return qs; return qs;
@ -289,7 +396,7 @@ parse_coin (struct MHD_Connection *connection,
} }
if (GNUNET_OK != if (GNUNET_OK !=
TALER_wallet_purse_deposit_verify ("http://FIXME-URL", TALER_wallet_purse_deposit_verify (TEH_base_url,
&pcc->purse_pub, &pcc->purse_pub,
&pcc->amount, &pcc->amount,
&coin->coin_pub, &coin->coin_pub,
@ -301,7 +408,7 @@ parse_coin (struct MHD_Connection *connection,
TALER_MHD_reply_with_error (connection, TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED, MHD_HTTP_UNAUTHORIZED,
TALER_EC_EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID, TALER_EC_EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID,
NULL)) TEH_base_url))
? GNUNET_NO : GNUNET_SYSERR; ? GNUNET_NO : GNUNET_SYSERR;
} }
/* check denomination exists and is valid */ /* check denomination exists and is valid */
@ -317,6 +424,18 @@ parse_coin (struct MHD_Connection *connection,
GNUNET_JSON_parse_free (spec); 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))
{
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)) if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
{ {
/* This denomination is past the expiration time for deposits */ /* 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; 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 */ /* check coin signature */
switch (dk->denom_pub.cipher) switch (dk->denom_pub.cipher)
{ {
@ -391,6 +524,17 @@ parse_coin (struct MHD_Connection *connection,
NULL)) NULL))
? GNUNET_NO : GNUNET_SYSERR; ? 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; MHD_RESULT mhd_ret = MHD_NO;
@ -425,10 +569,10 @@ parse_coin (struct MHD_Connection *connection,
MHD_RESULT MHD_RESULT
TEH_handler_purses_create (struct MHD_Connection *connection, TEH_handler_purses_create (
const struct struct MHD_Connection *connection,
TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseContractPublicKeyP *purse_pub,
const json_t *root) const json_t *root)
{ {
struct PurseCreateContext pcc = { struct PurseCreateContext pcc = {
.purse_pub = purse_pub, .purse_pub = purse_pub,
@ -461,6 +605,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
&pcc.purse_expiration), &pcc.purse_expiration),
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
const struct TEH_GlobalFee *gf;
{ {
enum GNUNET_GenericReturnValue res; enum GNUNET_GenericReturnValue res;
@ -479,7 +624,9 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
return MHD_YES; /* failure */ 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, if (GNUNET_TIME_timestamp_cmp (pcc.purse_expiration,
<, <,
pcc.exchange_timestamp)) pcc.exchange_timestamp))
@ -511,6 +658,17 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_GENERIC_PARAMETER_MALFORMED, TALER_EC_EXCHANGE_GENERIC_PARAMETER_MALFORMED,
"deposits"); "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 */ /* parse deposits */
pcc.coins = GNUNET_new_array (struct Coin, pcc.coins = GNUNET_new_array (struct Coin,
pcc.num_coins); pcc.num_coins);
@ -531,8 +689,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
} }
} }
// FIXME: get purse_fee! if (0 < TALER_amount_cmp (&gf->fees.purse,
if (0 < TALER_amount_cmp (&purse_fee,
&pcc.deposit_total)) &pcc.deposit_total))
{ {
GNUNET_break_op (0); 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, TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE,
NULL); NULL);
} }
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
if (GNUNET_OK != if (GNUNET_OK !=
@ -560,7 +716,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
GNUNET_free (pcc.coins); GNUNET_free (pcc.coins);
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_UNAUTHORIZED, MHD_HTTP_UNAUTHORIZED,
TALER_EC_EXCHANGE_PURSE_CREATE_COIN_SIGNATURE_INVALID, TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
NULL); NULL);
} }
@ -568,6 +724,7 @@ TEH_handler_purses_create (struct MHD_Connection *connection,
TEH_plugin->preflight (TEH_plugin->cls)) TEH_plugin->preflight (TEH_plugin->cls))
{ {
GNUNET_break (0); GNUNET_break (0);
GNUNET_JSON_parse_free (spec);
GNUNET_free (pcc.coins); GNUNET_free (pcc.coins);
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,

View File

@ -256,7 +256,6 @@ withdraw_transaction (void *cls,
} }
if (! below_limit) if (! below_limit)
{ {
TEH_plugin->rollback (TEH_plugin->cls);
*mhd_ret = TALER_MHD_REPLY_JSON_PACK ( *mhd_ret = TALER_MHD_REPLY_JSON_PACK (
connection, connection,
MHD_HTTP_ACCEPTED, MHD_HTTP_ACCEPTED,

View File

@ -12668,6 +12668,10 @@ postgres_insert_partner (void *cls,
* @param pub_ckey ephemeral key for DH used to encrypt the contract * @param pub_ckey ephemeral key for DH used to encrypt the contract
* @param econtract_size number of bytes in @a econtract * @param econtract_size number of bytes in @a econtract
* @param econtract the encrypted contract * @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 * @return transaction status code
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
@ -12675,7 +12679,8 @@ postgres_insert_contract (void *cls,
const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_ContractDiffiePublicP *pub_ckey, const struct TALER_ContractDiffiePublicP *pub_ckey,
size_t econtract_size, size_t econtract_size,
const void *econtract) const void *econtract,
bool *in_conflict)
{ {
GNUNET_break (0); GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR; 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 age_limit age limit to enforce for payments into the purse
* @param amount target amount (with fees) to be put 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 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 * @return transaction status code
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
@ -12726,7 +12735,8 @@ postgres_insert_purse_request (
const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_PrivateContractHashP *h_contract_terms,
uint32_t age_limit, uint32_t age_limit,
const struct TALER_Amount *amount, const struct TALER_Amount *amount,
const struct TALER_PurseContractSignatureP *purse_sig) const struct TALER_PurseContractSignatureP *purse_sig,
bool *in_conflict)
{ {
GNUNET_break (0); GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;
@ -12806,6 +12816,11 @@ postgres_select_purse_by_merge_pub (
* @param coin_pub coin to deposit (debit) * @param coin_pub coin to deposit (debit)
* @param amount fraction of the coin's value to deposit * @param amount fraction of the coin's value to deposit
* @param coin_sig signature affirming the operation * @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 * @return transaction status code
*/ */
static enum GNUNET_DB_QueryStatus static enum GNUNET_DB_QueryStatus
@ -12814,7 +12829,9 @@ postgres_do_purse_deposit (
const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount, 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); GNUNET_break (0);
return GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_DB_STATUS_HARD_ERROR;

View File

@ -3811,6 +3811,33 @@ TALER_exchange_online_reserve_closed_verify (
const struct TALER_ExchangeSignatureP *sig); 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 ************************** */ /* ********************* offline signing ************************** */

View File

@ -4389,6 +4389,10 @@ struct TALER_EXCHANGEDB_Plugin
* @param pub_ckey ephemeral key for DH used to encrypt the contract * @param pub_ckey ephemeral key for DH used to encrypt the contract
* @param econtract_size number of bytes in @a econtract * @param econtract_size number of bytes in @a econtract
* @param econtract the encrypted contract * @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 * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -4396,7 +4400,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_ContractDiffiePublicP *pub_ckey, const struct TALER_ContractDiffiePublicP *pub_ckey,
size_t econtract_size, 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 age_limit age limit to enforce for payments into the purse
* @param amount target amount (with fees) to be put 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 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 * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -4439,7 +4448,8 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PrivateContractHashP *h_contract_terms, const struct TALER_PrivateContractHashP *h_contract_terms,
uint32_t age_limit, uint32_t age_limit,
const struct TALER_Amount *amount, 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 coin_pub coin to deposit (debit)
* @param amount fraction of the coin's value to deposit * @param amount fraction of the coin's value to deposit
* @param coin_sig signature affirming the operation * @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 * @return transaction status code
*/ */
enum GNUNET_DB_QueryStatus enum GNUNET_DB_QueryStatus
@ -4515,7 +4530,9 @@ struct TALER_EXCHANGEDB_Plugin
const struct TALER_PurseContractPublicKeyP *purse_pub, const struct TALER_PurseContractPublicKeyP *purse_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount, 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);
/** /**

View File

@ -176,6 +176,11 @@
*/ */
#define TALER_SIGNATURE_EXCHANGE_ACCOUNT_SETUP_SUCCESS 1044 #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 */ /* Auditor signatures */

View File

@ -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 */ /* end of exchange_signatures.c */