From 8adde040ab5c3fdc40b06537d3bab1aa60ecc5e5 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Thu, 29 Jan 2015 00:09:48 +0100 Subject: [PATCH] towards proper implementation of /withdraw/status --- src/mint/key_io.h | 1 + src/mint/mint_db.c | 228 ++++++++++++-------------- src/mint/mint_db.h | 182 ++++++++++++-------- src/mint/taler-mint-httpd_db.c | 227 +++++++++++++------------ src/mint/taler-mint-httpd_responses.c | 119 +++++++++++++- src/mint/taler-mint-httpd_responses.h | 21 ++- src/util/amount.c | 42 ++++- 7 files changed, 495 insertions(+), 325 deletions(-) diff --git a/src/mint/key_io.h b/src/mint/key_io.h index 44665e379..a14866f47 100644 --- a/src/mint/key_io.h +++ b/src/mint/key_io.h @@ -42,6 +42,7 @@ struct TALER_MINT_SignKeyIssuePriv { struct GNUNET_CRYPTO_EddsaPrivateKey signkey_priv; + struct TALER_MINT_SignKeyIssue issue; }; diff --git a/src/mint/mint_db.c b/src/mint/mint_db.c index 5da2a5d46..0e448bf09 100644 --- a/src/mint/mint_db.c +++ b/src/mint/mint_db.c @@ -103,128 +103,6 @@ TALER_TALER_DB_extract_amount (PGresult *result, } - -int -TALER_MINT_DB_get_reserve (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub, - struct Reserve *reserve) -{ - PGresult *result; - int res; - struct TALER_DB_QueryParam params[] = { - TALER_DB_QUERY_PARAM_PTR (reserve_pub), - TALER_DB_QUERY_PARAM_END - }; - - result = TALER_DB_exec_prepared (db_conn, "get_reserve", params); - - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result)); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 == PQntuples (result)) - { - PQclear (result); - return GNUNET_NO; - } - - reserve->reserve_pub = *reserve_pub; - - struct TALER_DB_ResultSpec rs[] = { - TALER_DB_RESULT_SPEC("status_sig", &reserve->status_sig), - TALER_DB_RESULT_SPEC("status_sign_pub", &reserve->status_sign_pub), - TALER_DB_RESULT_SPEC_END - }; - - res = TALER_DB_extract_result (result, rs, 0); - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - - { - int fnums[] = { - PQfnumber (result, "balance_value"), - PQfnumber (result, "balance_fraction"), - PQfnumber (result, "balance_currency"), - }; - if (GNUNET_OK != TALER_TALER_DB_extract_amount_nbo (result, 0, fnums, &reserve->balance)) - { - GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; - } - } - - /* FIXME: Add expiration?? */ - - PQclear (result); - return GNUNET_OK; -} - - -/* If fresh is GNUNET_YES, set some fields to NULL as they are not actually valid */ -int -TALER_MINT_DB_update_reserve (PGconn *db_conn, - const struct Reserve *reserve, - int fresh) -{ - PGresult *result; - uint64_t stamp_sec; - - stamp_sec = GNUNET_ntohll (GNUNET_TIME_absolute_ntoh (reserve->expiration).abs_value_us / 1000000); - - struct TALER_DB_QueryParam params[] = { - TALER_DB_QUERY_PARAM_PTR (&reserve->reserve_pub), - TALER_DB_QUERY_PARAM_PTR (&reserve->balance.value), - TALER_DB_QUERY_PARAM_PTR (&reserve->balance.fraction), - TALER_DB_QUERY_PARAM_PTR_SIZED (&reserve->balance.currency, - strlen (reserve->balance.currency)), - TALER_DB_QUERY_PARAM_PTR (&reserve->status_sig), - TALER_DB_QUERY_PARAM_PTR (&reserve->status_sign_pub), - TALER_DB_QUERY_PARAM_PTR (&stamp_sec), - TALER_DB_QUERY_PARAM_END - }; - - /* set some fields to NULL if they are not actually valid */ - - if (GNUNET_YES == fresh) - { - unsigned i; - for (i = 4; i <= 7; i += 1) - { - params[i].data = NULL; - params[i].size = 0; - } - } - - result = TALER_DB_exec_prepared (db_conn, "update_reserve", params); - - if (PGRES_COMMAND_OK != PQresultStatus (result)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Query failed: %s\n", PQresultErrorMessage (result)); - PQclear (result); - return GNUNET_SYSERR; - } - - if (0 != strcmp ("1", PQcmdTuples (result))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Update failed (updated '%s' tupes instead of '1')\n", - PQcmdTuples (result)); - return GNUNET_SYSERR; - } - - PQclear (result); - return GNUNET_OK; -} - - - int TALER_MINT_DB_prepare (PGconn *db_conn) { @@ -1560,9 +1438,6 @@ TALER_db_get_transfer (PGconn *db_conn, - - - /** * Close thread-local database connection when a thread is destroyed. * @@ -1712,6 +1587,10 @@ TALER_MINT_DB_commit (PGconn *db_conn) } + + + + /** * Locate the response for a /withdraw request under the * key of the hash of the blinded message. @@ -1729,6 +1608,7 @@ TALER_MINT_DB_get_collectable_blindcoin (PGconn *db_conn, const struct GNUNET_HashCode *h_blind, struct CollectableBlindcoin *collectable) { + // FIXME: check logic! PGresult *result; struct TALER_DB_QueryParam params[] = { TALER_DB_QUERY_PARAM_PTR (h_blind), @@ -1792,6 +1672,7 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, const struct GNUNET_HashCode *h_blind, const struct CollectableBlindcoin *collectable) { + // FIXME: check logic! PGresult *result; char *sig_buf; size_t sig_buf_size; @@ -1834,6 +1715,95 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, } +/** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param db_conn connection to use + * @param reserve_pub public key of the reserve + * @return known transaction history (NULL if reserve is unknown) + */ +struct ReserveHistory * +TALER_MINT_DB_get_reserve_history (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) +{ + // FIXME: implement logic! + PGresult *result; + // int res; + struct TALER_DB_QueryParam params[] = { + TALER_DB_QUERY_PARAM_PTR (reserve_pub), + TALER_DB_QUERY_PARAM_END + }; + + result = TALER_DB_exec_prepared (db_conn, "get_reserve", params); + + if (PGRES_TUPLES_OK != PQresultStatus (result)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Query failed: %s\n", + PQresultErrorMessage (result)); + PQclear (result); + return NULL; + } + + if (0 == PQntuples (result)) + { + PQclear (result); + return NULL; + } +#if 0 + reserve->reserve_pub = *reserve_pub; + + struct TALER_DB_ResultSpec rs[] = { + TALER_DB_RESULT_SPEC("status_sig", &reserve->status_sig), + TALER_DB_RESULT_SPEC("status_sign_pub", &reserve->status_sign_pub), + TALER_DB_RESULT_SPEC_END + }; + + res = TALER_DB_extract_result (result, rs, 0); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + { + int fnums[] = { + PQfnumber (result, "balance_value"), + PQfnumber (result, "balance_fraction"), + PQfnumber (result, "balance_currency"), + }; + if (GNUNET_OK != TALER_TALER_DB_extract_amount_nbo (result, 0, fnums, &reserve->balance)) + { + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + } + + /* FIXME: Add expiration?? */ + + PQclear (result); + return GNUNET_OK; +#endif + return NULL; +} + + +/** + * Free memory associated with the given reserve history. + * + * @param rh history to free. + */ +void +TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh) +{ + // FIXME: implement + GNUNET_assert (0); +} + + /** * Check if we have the specified deposit already in the database. * @@ -1846,6 +1816,7 @@ int TALER_MINT_DB_have_deposit (PGconn *db_conn, const struct Deposit *deposit) { + // FIXME: check logic! struct TALER_DB_QueryParam params[] = { TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), // FIXME TALER_DB_QUERY_PARAM_END @@ -1884,6 +1855,7 @@ int TALER_MINT_DB_insert_deposit (PGconn *db_conn, const struct Deposit *deposit) { + // FIXME: check logic! struct TALER_DB_QueryParam params[]= { TALER_DB_QUERY_PARAM_PTR (&deposit->coin.coin_pub), TALER_DB_QUERY_PARAM_PTR (&deposit->coin.denom_pub), // FIXME! @@ -1928,6 +1900,7 @@ int TALER_MINT_DB_have_refresh_melt (PGconn *db_conn, const struct RefreshMelt *melt) { + // FIXME: check logic! uint16_t oldcoin_index_nbo = htons (melt->oldcoin_index); struct TALER_DB_QueryParam params[] = { TALER_DB_QUERY_PARAM_PTR(&melt->session_pub), @@ -1956,7 +1929,6 @@ TALER_MINT_DB_have_refresh_melt (PGconn *db_conn, } - /** * Store the given /refresh/melt request in the database. * @@ -1969,6 +1941,7 @@ int TALER_MINT_DB_insert_refresh_melt (PGconn *db_conn, const struct RefreshMelt *melt) { + // FIXME: check logic! uint16_t oldcoin_index_nbo = htons (melt->oldcoin_index); char *buf; size_t buf_size; @@ -2014,6 +1987,7 @@ TALER_MINT_DB_get_refresh_melt (PGconn *db_conn, uint16_t oldcoin_index, struct RefreshMelt *melt) { + // FIXME: check logic! GNUNET_break (0); return GNUNET_SYSERR; } @@ -2031,6 +2005,7 @@ struct TALER_MINT_DB_TransactionList * TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, const struct GNUNET_CRYPTO_EcdsaPublicKey *coin_pub) { + // FIXME: check logic! GNUNET_break (0); // FIXME: implement! return NULL; } @@ -2044,6 +2019,7 @@ TALER_MINT_DB_get_coin_transactions (PGconn *db_conn, void TALER_MINT_DB_free_coin_transaction_list (struct TALER_MINT_DB_TransactionList *list) { + // FIXME: check logic! GNUNET_break (0); } diff --git a/src/mint/mint_db.h b/src/mint/mint_db.h index ff14ba1e4..4cb2a5f1e 100644 --- a/src/mint/mint_db.h +++ b/src/mint/mint_db.h @@ -38,77 +38,6 @@ int TALER_MINT_DB_prepare (PGconn *db_conn); -/** - * Reserve row. Corresponds to table 'reserves' in the mint's - * database. FIXME: not sure this is how we want to store this - * information. Also, may currently used in different ways in the - * code, so we might need to separate the struct into different ones - * depending on the context it is used in. - */ -struct Reserve -{ - /** - * Signature over the purse. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EddsaSignature status_sig; - - /** - * Signature with purpose TALER_SIGNATURE_PURSE. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EccSignaturePurpose status_sig_purpose; - - /** - * Signing key used to sign the purse. - * Only valid if (blind_session_missing==GNUNET_YES). - */ - struct GNUNET_CRYPTO_EddsaPublicKey status_sign_pub; - - /** - * Withdraw public key, identifies the purse. - * Only the customer knows the corresponding private key. - */ - struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; - - /** - * Remaining balance in the purse. // FIXME: do not use NBO here! - */ - struct TALER_AmountNBO balance; - - /** - * Expiration date for the purse. - */ - struct GNUNET_TIME_AbsoluteNBO expiration; -}; - - -int -TALER_MINT_DB_get_reserve (PGconn *db_conn, - const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub, - struct Reserve *reserve_res); - - -/** - * Update information about a reserve. - * - * @param db_conn - * @param reserve current reserve status - * @param fresh FIXME - * @return #GNUNET_OK on success - */ -int -TALER_MINT_DB_update_reserve (PGconn *db_conn, - const struct Reserve *reserve, - int fresh); - - - - - - - - int TALER_MINT_DB_insert_refresh_order (PGconn *db_conn, uint16_t newcoin_index, @@ -328,6 +257,34 @@ void TALER_MINT_DB_rollback (PGconn *db_conn); +/** + * Information we keep on a bank transfer that + * established a reserve. + */ +struct BankTransfer +{ + + /** + * Public key of the reserve that was filled. + */ + struct GNUNET_CRYPTO_EddsaPublicKey reserve_pub; + + /** + * Amount that was transferred to the mint. + */ + struct TALER_Amount amount; + + /** + * Detailed wire information about the transaction. + */ + const json_t *wire; + +}; + + +/* FIXME: add functions to add bank transfers to our DB + (and to test if we already did add one) */ + /** * Information we keep for a withdrawn coin to reproduce @@ -360,6 +317,9 @@ struct CollectableBlindcoin }; +/* FIXME: need call to convert CollectableBlindcoin to JSON (#3527) */ + + /** * Locate the response for a /withdraw request under the * key of the hash of the blinded message. @@ -396,6 +356,86 @@ TALER_MINT_DB_insert_collectable_blindcoin (PGconn *db_conn, const struct CollectableBlindcoin *collectable); + +/** + * Types of operations on a reserved. + */ +enum TALER_MINT_DB_ReserveOperation +{ + /** + * Money was deposited into the reserve via a bank transfer. + */ + TALER_MINT_DB_RO_BANK_TO_MINT = 0, + + /** + * A Coin was withdrawn from the reserve using /withdraw. + */ + TALER_MINT_DB_RO_WITHDRAW_COIN = 1 +}; + + +/** + * Reserve history as a linked list. Lists all of the transactions + * associated with this reserve (such as the bank transfers that + * established the reserve and all /withdraw operations we have done + * since). + */ +struct ReserveHistory +{ + + /** + * Next entry in the reserve history. + */ + struct ReserveHistory *next; + + /** + * Type of the event, determins @e details. + */ + enum TALER_MINT_DB_ReserveOperation type; + + /** + * Details of the operation, depending on @e type. + */ + union + { + + /** + * Details about a bank transfer to the mint. + */ + struct BankTransfer *bank; + + /** + * Details about a /withdraw operation. + */ + struct CollectableBlindcoin *withdraw; + + } details; + +}; + + +/** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param db_conn connection to use + * @param reserve_pub public key of the reserve + * @return known transaction history (NULL if reserve is unknown) + */ +struct ReserveHistory * +TALER_MINT_DB_get_reserve_history (PGconn *db_conn, + const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub); + + +/** + * Free memory associated with the given reserve history. + * + * @param rh history to free. + */ +void +TALER_MINT_DB_free_reserve_history (struct ReserveHistory *rh); + + /** * Specification for a /deposit operation. */ diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index c6f0fe2cb..bf10cd29e 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -22,12 +22,31 @@ * - actually abstract DB implementation (i.e. via plugin logic) * (this file should remain largely unchanged with the exception * of the PQ-specific DB handle types) - * - /deposit: properly check existing deposits - * - /deposit: properly perform commit (check return value) - * - /deposit: check for leaks - * - ALL: check API: given structs are usually not perfect, as they - * often contain too many fields for the context - * - ALL: check transactional behavior + * - /withdraw/sign: all + * + properly check all conditions and handle errors + * + properly check transaction logic + * + check for leaks + * + check low-level API + * - /refresh/melt: all + * + properly check all conditions and handle errors + * + properly check transaction logic + * + check for leaks + * + check low-level API + * - /refresh/commit: all + * + properly check all conditions and handle errors + * + properly check transaction logic + * + check for leaks + * + check low-level API + * - /refresh/reveal: all + * + properly check all conditions and handle errors + * + properly check transaction logic + * + check for leaks + * + check low-level API + * - /refresh/link: all + * + properly check all conditions and handle errors + * + properly check transaction logic + * + check for leaks + * + check low-level API */ #include "platform.h" #include @@ -42,6 +61,26 @@ #include "taler-mint-httpd_keystate.h" +/** + * Get an amount in the mint's currency that is zero. + * + * @return zero amount in the mint's currency + */ +static struct TALER_Amount +mint_amount_native_zero () +{ + struct TALER_Amount amount; + + memset (&amount, + 0, + sizeof (amount)); + memcpy (amount.currency, + MINT_CURRENCY, + strlen (MINT_CURRENCY) + 1); + return amount; +} + + /** * Execute a deposit. The validity of the coin and signature * have already been checked. The database must now check that @@ -58,6 +97,15 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, { PGconn *db_conn; struct TALER_MINT_DB_TransactionList *tl; + struct TALER_MINT_DB_TransactionList *pos; + struct TALER_Amount spent; + struct TALER_Amount value; + struct TALER_Amount fee_deposit; + struct TALER_Amount fee_withdraw; + struct TALER_Amount fee_refresh; + struct MintKeyState *mks; + struct TALER_MINT_DenomKeyIssuePriv *dki; + int ret; if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) { @@ -76,6 +124,14 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, &deposit->merchant_pub, &deposit->amount); } + mks = TALER_MINT_key_state_acquire (); + dki = TALER_MINT_get_denom_key (mks, + deposit->coin.denom_pub); + value = TALER_amount_ntoh (dki->issue.value); + fee_deposit = TALER_amount_ntoh (dki->issue.fee_deposit); + fee_refresh = TALER_amount_ntoh (dki->issue.fee_refresh); + TALER_MINT_key_state_release (mks); + if (GNUNET_OK != TALER_MINT_DB_transaction (db_conn)) { @@ -84,19 +140,48 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, } tl = TALER_MINT_DB_get_coin_transactions (db_conn, &deposit->coin.coin_pub); - if (NULL != tl) + spent = fee_withdraw; /* fee for THIS transaction */ + /* FIXME: need to deal better with integer overflows + in the logic that follows! (change amount.c API!) */ + spent = TALER_amount_add (spent, + deposit->amount); + + for (pos = tl; NULL != pos; pos = pos->next) { - // FIXME: in the future, check if there's enough credits - // left on the coin. For now: refuse - // FIXME: return more information here - TALER_MINT_DB_rollback (db_conn); - return TALER_MINT_reply_json_pack (connection, - MHD_HTTP_FORBIDDEN, - "{s:s}", - "error", "insufficient funds"); + switch (pos->type) + { + case TALER_MINT_DB_TT_DEPOSIT: + spent = TALER_amount_add (spent, + pos->details.deposit->amount); + spent = TALER_amount_add (spent, + fee_deposit); + break; + case TALER_MINT_DB_TT_REFRESH_MELT: + spent = TALER_amount_add (spent, + pos->details.melt->amount); + spent = TALER_amount_add (spent, + fee_refresh); + break; + case TALER_MINT_DB_TT_LOCK: + /* should check if lock is still active, + and if it is for THIS operation; if + lock is inactive, delete it; if lock + is for THIS operation, ignore it; + if lock is for another operation, + count it! */ + GNUNET_assert (0); // FIXME: not implemented! + break; + } } - + if (0 < TALER_amount_cmp (spent, value)) + { + TALER_MINT_DB_rollback (db_conn); + ret = TALER_MINT_reply_insufficient_funds (connection, + tl); + TALER_MINT_DB_free_coin_transaction_list (tl); + return ret; + } TALER_MINT_DB_free_coin_transaction_list (tl); if (GNUNET_OK != @@ -124,37 +209,6 @@ TALER_MINT_db_execute_deposit (struct MHD_Connection *connection, } - - - - - - - - -/** - * Sign a reserve's status with the current signing key. - * FIXME: not sure why we do this. Should just return - * existing list of operations on the reserve. - * - * @param reserve the reserve to sign - * @param key_state the key state containing the current - * signing private key - */ -static void -sign_reserve (struct Reserve *reserve, - struct MintKeyState *key_state) -{ - reserve->status_sign_pub = key_state->current_sign_key_issue.issue.signkey_pub; - reserve->status_sig_purpose.purpose = htonl (TALER_SIGNATURE_RESERVE_STATUS); - reserve->status_sig_purpose.size = htonl (sizeof (struct Reserve) - - offsetof (struct Reserve, status_sig_purpose)); - GNUNET_CRYPTO_eddsa_sign (&key_state->current_sign_key_issue.signkey_priv, - &reserve->status_sig_purpose, - &reserve->status_sig); -} - - /** * Execute a /withdraw/status. * @@ -167,50 +221,25 @@ TALER_MINT_db_execute_withdraw_status (struct MHD_Connection *connection, const struct GNUNET_CRYPTO_EddsaPublicKey *reserve_pub) { PGconn *db_conn; + struct ReserveHistory *rh; int res; - struct Reserve reserve; - struct MintKeyState *key_state; - int must_update = GNUNET_NO; if (NULL == (db_conn = TALER_MINT_DB_get_connection ())) { GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } - res = TALER_MINT_DB_get_reserve (db_conn, - reserve_pub, - &reserve); - /* check if these are really the matching error codes, - seems odd... */ - if (GNUNET_SYSERR == res) + rh = TALER_MINT_DB_get_reserve_history (db_conn, + reserve_pub); + if (NULL == rh) return TALER_MINT_reply_json_pack (connection, MHD_HTTP_NOT_FOUND, "{s:s}", - "error", - "Reserve not found"); - if (GNUNET_OK != res) - { - GNUNET_break (0); - return TALER_MINT_reply_internal_error (connection, - "Internal error"); - } - key_state = TALER_MINT_key_state_acquire (); - if (0 != memcmp (&key_state->current_sign_key_issue.issue.signkey_pub, - &reserve.status_sign_pub, - sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) - { - sign_reserve (&reserve, key_state); - must_update = GNUNET_YES; - } - if ((GNUNET_YES == must_update) && - (GNUNET_OK != TALER_MINT_DB_update_reserve (db_conn, &reserve, !must_update))) - { - GNUNET_break (0); - return MHD_YES; - } - return TALER_MINT_reply_withdraw_status_success (connection, - TALER_amount_ntoh (reserve.balance), - GNUNET_TIME_absolute_ntoh (reserve.expiration)); + "error", "Reserve not found"); + res = TALER_MINT_reply_withdraw_status_success (connection, + rh); + TALER_MINT_DB_free_reserve_history (rh); + return res; } @@ -234,7 +263,7 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, const struct GNUNET_CRYPTO_EddsaSignature *signature) { PGconn *db_conn; - struct Reserve db_reserve; + struct ReserveHistory *rh; struct MintKeyState *key_state; struct CollectableBlindcoin collectable; struct TALER_MINT_DenomKeyIssuePriv *dki; @@ -270,15 +299,9 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, return res; } GNUNET_assert (GNUNET_NO == res); - res = TALER_MINT_DB_get_reserve (db_conn, - reserve, - &db_reserve); - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - return TALER_MINT_reply_internal_db_error (connection); - } - if (GNUNET_NO == res) + rh = TALER_MINT_DB_get_reserve_history (db_conn, + reserve); + if (NULL == rh) return TALER_MINT_reply_json_pack (connection, MHD_HTTP_NOT_FOUND, "{s:s}", @@ -298,6 +321,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, amount_required = TALER_amount_add (TALER_amount_ntoh (dki->issue.value), TALER_amount_ntoh (dki->issue.fee_withdraw)); + // FIX LOGIC! +#if 0 if (0 < TALER_amount_cmp (amount_required, TALER_amount_ntoh (db_reserve.balance))) return TALER_MINT_reply_json_pack (connection, @@ -329,6 +354,8 @@ TALER_MINT_db_execute_withdraw_sign (struct MHD_Connection *connection, GNUNET_break (0); return TALER_MINT_reply_internal_db_error (connection); } +#endif + collectable.denom_pub = (struct GNUNET_CRYPTO_rsa_PublicKey *) denomination_pub; collectable.sig = sig; collectable.reserve_pub = *reserve; @@ -401,21 +428,6 @@ refresh_accept_denoms (struct MHD_Connection *connection, } -/** - * Get an amount in the mint's currency that is zero. - * - * @return zero amount in the mint's currency - */ -static struct TALER_Amount -mint_amount_native_zero () -{ - struct TALER_Amount amount; - - memset (&amount, 0, sizeof (amount)); - memcpy (amount.currency, MINT_CURRENCY, strlen (MINT_CURRENCY) + 1); - - return amount; -} /** @@ -1290,3 +1302,6 @@ TALER_MINT_db_execute_refresh_link (struct MHD_Connection *connection, json_decref (root); return res; } + + +/* end of taler-mint-httpd_db.c */ diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index 307e6ec17..bad87429c 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -308,32 +308,133 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, } +/** + * Send proof that a /deposit, /refresh/melt or /lock request is + * invalid to client. This function will create a message with all of + * the operations affecting the coin that demonstrate that the coin + * has insufficient value. + * + * @param connection connection to the client + * @param tl transaction list to use to build reply + * @return MHD result code + */ +int +TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINT_DB_TransactionList *tl) +{ + const struct TALER_MINT_DB_TransactionList *pos; + int ret; + + // FIXME: implement properly! + for (pos = tl; NULL != pos; pos = pos->next) + { + switch (pos->type) + { + case TALER_MINT_DB_TT_DEPOSIT: + /* FIXME: add operation details to json reply */ + break; + case TALER_MINT_DB_TT_REFRESH_MELT: + /* FIXME: add operation details to json reply */ + break; + case TALER_MINT_DB_TT_LOCK: + /* FIXME: add operation details to json reply */ + break; + } + } + + ret = TALER_MINT_reply_json_pack (connection, + MHD_HTTP_FORBIDDEN, + "{s:s}", + "error", "insufficient funds"); + return ret; +} + + /** * Send reserve status information to client. * * @param connection connection to the client - * @param balance current reserve balance - * @param expiration when will the reserve expire + * @param rh reserve history to return * @return MHD result code */ int TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, - const struct TALER_Amount balance, - struct GNUNET_TIME_Absolute expiration) + const struct ReserveHistory *rh) { + struct TALER_Amount deposit_total; + struct TALER_Amount withdraw_total; + struct TALER_Amount balance; + struct TALER_Amount value; json_t *json_balance; - json_t *json_expiration; + json_t *json_history; int ret; + struct MintKeyState *key_state; + const struct ReserveHistory *pos; + struct TALER_MINT_DenomKeyIssuePriv *dki; + json_history = json_array (); + ret = 0; + for (pos = rh; NULL != pos; pos = pos->next) + { + switch (pos->type) + { + case TALER_MINT_DB_RO_BANK_TO_MINT: + if (0 == ret) + deposit_total = pos->details.bank->amount; + else + deposit_total = TALER_amount_add (deposit_total, + pos->details.bank->amount); + ret = 1; + json_array_append_new (json_history, + json_pack ("{s:s, s:o, s:o}", + "type", "DEPOSIT", + "wire", pos->details.bank->wire, + "amount", TALER_JSON_from_amount (pos->details.bank->amount))); + break; + case TALER_MINT_DB_RO_WITHDRAW_COIN: + break; + } + } + + key_state = TALER_MINT_key_state_acquire (); + ret = 0; + for (pos = rh; NULL != pos; pos = pos->next) + { + switch (pos->type) + { + case TALER_MINT_DB_RO_BANK_TO_MINT: + break; + case TALER_MINT_DB_RO_WITHDRAW_COIN: + dki = TALER_MINT_get_denom_key (key_state, + pos->details.withdraw->denom_pub); + value = TALER_amount_ntoh (dki->issue.value); + if (0 == ret) + withdraw_total = value; + else + withdraw_total = TALER_amount_add (withdraw_total, + value); + ret = 1; + /* FIXME: add `struct CollectableBlindcoin` as JSON here as well! (#3527) */ + json_array_append_new (json_history, + json_pack ("{s:s, s:o, s:o}", + "type", "WITHDRAW", + "amount", TALER_JSON_from_amount (value))); + + break; + } + } + TALER_MINT_key_state_release (key_state); + + balance = TALER_amount_subtract (deposit_total, + withdraw_total); json_balance = TALER_JSON_from_amount (balance); - json_expiration = TALER_JSON_from_abs (expiration); ret = TALER_MINT_reply_json_pack (connection, MHD_HTTP_OK, "{s:o, s:o}", "balance", json_balance, - "expiration", json_expiration); + "history", json_history); + json_decref (json_history); json_decref (json_balance); - json_decref (json_expiration); return ret; } @@ -354,7 +455,7 @@ TALER_MINT_reply_withdraw_sign_success (struct MHD_Connection *connection, char *sig_buf; int ret; - /* FIXME: use TALER_JSON_from_sig here instead! */ + /* FIXME: use TALER_JSON_from_sig here instead!? */ sig_buf_size = GNUNET_CRYPTO_rsa_signature_encode (collectable->sig, &sig_buf); sig_json = TALER_JSON_from_data (sig_buf, diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index f8a671e18..5e2f98638 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -181,18 +181,31 @@ TALER_MINT_reply_deposit_success (struct MHD_Connection *connection, const struct TALER_Amount *amount); +/** + * Send proof that a /deposit, /refresh/melt or /lock request is + * invalid to client. This function will create a message with all of + * the operations affecting the coin that demonstrate that the coin + * has insufficient value. + * + * @param connection connection to the client + * @param tl transaction list to use to build reply + * @return MHD result code + */ +int +TALER_MINT_reply_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINT_DB_TransactionList *tl); + + /** * Send reserve status information to client. * * @param connection connection to the client - * @param balance current reserve balance - * @param expiration when will the reserve expire + * @param rh reserve history to return * @return MHD result code */ int TALER_MINT_reply_withdraw_status_success (struct MHD_Connection *connection, - struct TALER_Amount balance, - struct GNUNET_TIME_Absolute expiration); + const struct ReserveHistory *rh); /** diff --git a/src/util/amount.c b/src/util/amount.c index bb5bf0d5b..65fac78e3 100644 --- a/src/util/amount.c +++ b/src/util/amount.c @@ -19,6 +19,12 @@ * @author Sree Harsha Totakura * @author Florian Dold * @author Benedikt Mueller + * + * TODO: + * - the way this library currently deals with underflow/overflow + * is insufficient; just going for UINT32_MAX on overflow + * will not do; similar issues for incompatible currencies; + * we need some more explicit logic to say 'bogus value', */ #include "platform.h" #include "taler_util.h" @@ -169,7 +175,8 @@ TALER_amount_ntoh (struct TALER_AmountNBO dn) * @return result of the comparison */ int -TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2) +TALER_amount_cmp (struct TALER_Amount a1, + struct TALER_Amount a2) { a1 = TALER_amount_normalize (a1); a2 = TALER_amount_normalize (a2); @@ -195,7 +202,8 @@ TALER_amount_cmp (struct TALER_Amount a1, struct TALER_Amount a2) * @return (a1-a2) or 0 if a2>=a1 */ struct TALER_Amount -TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2) +TALER_amount_subtract (struct TALER_Amount a1, + struct TALER_Amount a2) { a1 = TALER_amount_normalize (a1); a2 = TALER_amount_normalize (a2); @@ -233,7 +241,8 @@ TALER_amount_subtract (struct TALER_Amount a1, struct TALER_Amount a2) * @return sum of a1 and a2 */ struct TALER_Amount -TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2) +TALER_amount_add (struct TALER_Amount a1, + struct TALER_Amount a2) { a1 = TALER_amount_normalize (a1); a2 = TALER_amount_normalize (a2); @@ -243,17 +252,25 @@ TALER_amount_add (struct TALER_Amount a1, struct TALER_Amount a2) if (0 == a1.currency[0]) { - memcpy (a2.currency, a1.currency, TALER_CURRENCY_LEN); + memcpy (a2.currency, + a1.currency, + TALER_CURRENCY_LEN); } if (0 == a2.currency[0]) { - memcpy (a1.currency, a2.currency, TALER_CURRENCY_LEN); + memcpy (a1.currency, + a2.currency, + TALER_CURRENCY_LEN); } - if (0 != a1.currency[0] && 0 != memcmp (a1.currency, a2.currency, TALER_CURRENCY_LEN)) + if ( (0 != a1.currency[0]) && + (0 != memcmp (a1.currency, + a2.currency, + TALER_CURRENCY_LEN)) ) { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "adding mismatching currencies\n"); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "adding mismatching currencies\n"); } if (a1.value < a2.value) @@ -312,11 +329,18 @@ TALER_amount_to_string (struct TALER_Amount amount) n = (n * 10) % (AMOUNT_FRAC_BASE); } tail[i] = 0; - len = GNUNET_asprintf (&result, "%s:%lu.%s", curr, (unsigned long) amount.value, tail); + len = GNUNET_asprintf (&result, + "%s:%lu.%s", + curr, + (unsigned long) amount.value, + tail); } else { - len = GNUNET_asprintf (&result, "%s:%lu", curr, (unsigned long) amount.value); + len = GNUNET_asprintf (&result, + "%s:%lu", + curr, + (unsigned long) amount.value); } GNUNET_assert (len > 0); return result;