From 4a36ed7fbfcaa220d1b2605851b38fc1a386e7d3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 2 Oct 2022 22:47:28 +0200 Subject: [PATCH] complete taler-exchange-httpd_reserves_open.c logic (first pass, still without DB logic or tests) --- .../taler-exchange-httpd_reserves_open.c | 96 +++++++++++++++++-- src/include/taler_crypto_lib.h | 12 +-- src/include/taler_exchangedb_plugin.h | 49 ++++++++++ src/lib/exchange_api_reserves_open.c | 15 ++- src/util/wallet_signatures.c | 21 ++-- 5 files changed, 152 insertions(+), 41 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_reserves_open.c b/src/exchange/taler-exchange-httpd_reserves_open.c index 2bc450ee2..c9f5e4019 100644 --- a/src/exchange/taler-exchange-httpd_reserves_open.c +++ b/src/exchange/taler-exchange-httpd_reserves_open.c @@ -83,6 +83,11 @@ struct ReserveOpenContext */ struct TALER_Amount open_cost; + /** + * Total amount that was deposited. + */ + struct TALER_Amount total; + /** * Information about payments by coin. */ @@ -111,9 +116,16 @@ static MHD_RESULT reply_reserve_open_success (struct MHD_Connection *connection, const struct ReserveOpenContext *rsc) { + unsigned int status; + + status = MHD_HTTP_OK; + if (GNUNET_TIME_timestamp_cmp (rsc->reserve_expiration, + <, + rsc->desired_expiration)) + status = MHD_HTTP_PAYMENT_REQUIRED; return TALER_MHD_REPLY_JSON_PACK ( connection, - MHD_HTTP_OK, + status, GNUNET_JSON_pack_timestamp ("reserve_expiration", rsc->reserve_expiration), TALER_JSON_pack_amount ("open_cost", @@ -150,7 +162,7 @@ cleanup_rsc (struct ReserveOpenContext *rsc) * @param cls a `struct ReserveOpenContext *` * @param connection MHD request which triggered the transaction * @param[out] mhd_ret set to MHD response status for @a connection, - * if transaction failed (!); unused + * if transaction failed (!) * @return transaction status */ static enum GNUNET_DB_QueryStatus @@ -161,15 +173,63 @@ reserve_open_transaction (void *cls, struct ReserveOpenContext *rsc = cls; enum GNUNET_DB_QueryStatus qs; - (void) rsc; -#if 0 - // FIXME: implement! + for (unsigned int i = 0; ipayments_len; i++) + { + struct TEH_PurseDepositedCoin *coin = &rsc->payments[i]; + bool insufficient_funds = true; + + qs = TEH_make_coin_known (&coin->cpi, + connection, + &coin->known_coin_id, + mhd_ret); + if (qs < 0) + return qs; + qs = TEH_plugin->insert_reserve_open_deposit ( + TEH_plugin->cls, + &coin->cpi, + &coin->coin_sig, + coin->known_coin_id, + &coin->amount, + &rsc->reserve_sig, + &insufficient_funds); + /* 0 == qs is fine, then the coin was already + spent for this very operation as identified + by reserve_sig! */ + if (qs < 0) + { + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + return qs; + GNUNET_break (0); + *mhd_ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "insert_reserve_open_deposit"); + return qs; + } + if (insufficient_funds) + { + *mhd_ret + = TEH_RESPONSE_reply_coin_insufficient_funds ( + connection, + TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS, + &coin->cpi.denom_pub_hash, + &coin->cpi.coin_pub); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } + qs = TEH_plugin->do_reserve_open (TEH_plugin->cls, + /* inputs */ rsc->reserve_pub, - ...); -#else - qs = GNUNET_DB_STATUS_HARD_ERROR; -#endif + &rsc->total, + rsc->purse_limit, + &rsc->reserve_sig, + rsc->desired_expiration, + rsc->timestamp, + &rsc->gf->fees.account, + /* outputs */ + &rsc->open_cost, + &rsc->reserve_expiration); switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: @@ -178,7 +238,7 @@ reserve_open_transaction (void *cls, = TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, - "get_reserve_open"); + "do_reserve_open"); return GNUNET_DB_STATUS_HARD_ERROR; case GNUNET_DB_STATUS_SOFT_ERROR: return qs; @@ -258,6 +318,7 @@ TEH_handler_reserves_open (struct TEH_RequestContext *rc, rsc.payments_len = json_array_size (payments); rsc.payments = GNUNET_new_array (rsc.payments_len, struct TEH_PurseDepositedCoin); + rsc.total = rsc.reserve_payment; for (unsigned int i = 0; iamount_minus_fee and + thereby charge the deposit fee even when paying the reserve-open fee. + To be decided... */ + if (0 > + TALER_amount_add (&rsc.total, + &rsc.total, + &coin->amount)) + { + GNUNET_break (0); + cleanup_rsc (&rsc); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT, + NULL); + } } { diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 09e386cd4..a861563b2 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -3107,16 +3107,14 @@ TALER_wallet_reserve_open_verify ( * Sign to deposit coin to pay for keeping a reserve open. * * @param coin_contribution how much the coin should contribute - * @param reserve_pub public key of the reserve - * @param request_timestamp time of the open request + * @param reserve_sig signature over the reserve open operation * @param coin_priv private key of the coin * @param[out] coin_sig signature by the coin */ void TALER_wallet_reserve_open_deposit_sign ( const struct TALER_Amount *coin_contribution, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig); @@ -3125,8 +3123,7 @@ TALER_wallet_reserve_open_deposit_sign ( * Verify signature that deposits coin to pay for keeping a reserve open. * * @param coin_contribution how much the coin should contribute - * @param reserve_pub public key of the reserve - * @param request_timestamp time of the open request + * @param reserve_sig signature over the reserve open operation * @param coin_pub public key of the coin * @param coin_sig signature by the coin * @return #GNUNET_OK if the signature is valid @@ -3134,8 +3131,7 @@ TALER_wallet_reserve_open_deposit_sign ( enum GNUNET_GenericReturnValue TALER_wallet_reserve_open_deposit_verify ( const struct TALER_Amount *coin_contribution, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 261ffb184..e6c38ad9b 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -4043,6 +4043,55 @@ struct TALER_EXCHANGEDB_Plugin void *rec_cls); + /** + * Insert reserve open coin deposit data into database. + * Subtracts the @a coin_total from the coin's balance. + * + * @param cls closure + * @param cpi public information about the coin + * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT + * @param known_coin_id ID of the coin in the known_coins table + * @param coin_total amount to be spent of the coin (including deposit fee) + * @param reserve_sig signature by the reserve affirming the open operation + * @param[out] insufficient_funds set to true if the coin's balance is insufficient, otherwise to false + * @return transaction status code, 0 if operation is already in the DB + */ + enum GNUNET_DB_QueryStatus + (*insert_reserve_open_deposit)( + void *cls, + const struct TALER_CoinPublicInfo *cpi, + const struct TALER_CoinSpendSignatureP *coin_sig, + uint64_t known_coin_id, + const struct TALER_Amount *coin_total, + const struct TALER_ReserveSignatureP *reserve_sig, + bool *insufficient_funds); + + + /** + * Insert reserve close operation into database. + * + * @param cls closure + * @param reserve_pub which reserve is this about? + * @param execution_date when did we perform the transfer? + * @param receiver_account to which account do we transfer, in payto://-format + * @param wtid identifier for the wire transfer + * @param amount_with_fee amount we charged to the reserve + * @param closing_fee how high is the closing fee + * @return transaction status code + */ + enum GNUNET_DB_QueryStatus + (*do_reserve_open)(void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_Amount *total_paid, + uint32_t min_purse_limit, + const struct TALER_ReserveSignatureP *reserve_sig, + struct GNUNET_TIME_Timestamp desired_expiration, + struct GNUNET_TIME_Timestamp now, + const struct TALER_Amount *open_fee, + struct TALER_Amount *open_cost, + const struct GNUNET_TIME_Timestamp *final_expiration); + + /** * Insert reserve close operation into database. * diff --git a/src/lib/exchange_api_reserves_open.c b/src/lib/exchange_api_reserves_open.c index eb63f8fe9..08d267fb7 100644 --- a/src/lib/exchange_api_reserves_open.c +++ b/src/lib/exchange_api_reserves_open.c @@ -393,6 +393,12 @@ TALER_EXCHANGE_reserves_open ( GNUNET_free (roh); return NULL; } + TALER_wallet_reserve_open_sign (reserve_contribution, + roh->ts, + expiration_time, + min_purses, + reserve_priv, + &roh->reserve_sig); cpa = json_array (); GNUNET_assert (NULL != cpa); for (unsigned int i = 0; iamount, - &roh->reserve_pub, - roh->ts, + &roh->reserve_sig, &pd->coin_priv, &coin_sig); GNUNET_CRYPTO_eddsa_key_get_public (&pd->coin_priv.eddsa_priv, @@ -437,12 +442,6 @@ TALER_EXCHANGE_reserves_open ( json_array_append_new (cpa, cp)); } - TALER_wallet_reserve_open_sign (reserve_contribution, - roh->ts, - expiration_time, - min_purses, - reserve_priv, - &roh->reserve_sig); { json_t *open_obj = GNUNET_JSON_PACK ( GNUNET_JSON_pack_timestamp ("request_timestamp", diff --git a/src/util/wallet_signatures.c b/src/util/wallet_signatures.c index 4e4932a2b..c57506bd8 100644 --- a/src/util/wallet_signatures.c +++ b/src/util/wallet_signatures.c @@ -1342,14 +1342,9 @@ struct TALER_ReserveOpenDepositPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * When was the request created. + * Which reserve's opening signature should be paid for? */ - struct GNUNET_TIME_TimestampNBO request_timestamp; - - /** - * Which reserve's opening should be paid for? - */ - struct TALER_ReservePublicKeyP reserve_pub; + struct TALER_ReserveSignatureP reserve_sig; /** * Specifies how much of the coin's value should be spent on opening this @@ -1364,16 +1359,14 @@ GNUNET_NETWORK_STRUCT_END void TALER_wallet_reserve_open_deposit_sign ( const struct TALER_Amount *coin_contribution, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_CoinSpendSignatureP *coin_sig) { struct TALER_ReserveOpenDepositPS rod = { .purpose.size = htonl (sizeof (rod)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT), - .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp), - .reserve_pub = *reserve_pub + .reserve_sig = *reserve_sig }; TALER_amount_hton (&rod.coin_contribution, @@ -1388,16 +1381,14 @@ TALER_wallet_reserve_open_deposit_sign ( enum GNUNET_GenericReturnValue TALER_wallet_reserve_open_deposit_verify ( const struct TALER_Amount *coin_contribution, - const struct TALER_ReservePublicKeyP *reserve_pub, - struct GNUNET_TIME_Timestamp request_timestamp, + const struct TALER_ReserveSignatureP *reserve_sig, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig) { struct TALER_ReserveOpenDepositPS rod = { .purpose.size = htonl (sizeof (rod)), .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_OPEN_DEPOSIT), - .request_timestamp = GNUNET_TIME_timestamp_hton (request_timestamp), - .reserve_pub = *reserve_pub + .reserve_sig = *reserve_sig }; TALER_amount_hton (&rod.coin_contribution,