From 6501118f6c345fc1a4810611f8631172b105c279 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 14 Sep 2015 11:56:37 +0200 Subject: remove /lock skeleton logic (#3625) --- src/mintdb/plugin_mintdb_postgres.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 6e26c82e..f47e31b4 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -2666,7 +2666,7 @@ postgres_insert_refresh_commit_links (void *cls, PQclear (result); return GNUNET_SYSERR; } - + if (0 != strcmp ("1", PQcmdTuples (result))) { GNUNET_break (0); @@ -2734,7 +2734,7 @@ postgres_get_refresh_commit_links (void *cls, &links[i].shared_secret_enc), TALER_PQ_result_spec_end }; - + if (GNUNET_YES != TALER_PQ_extract_result (result, rs, 0)) { @@ -3286,8 +3286,6 @@ libtaler_plugin_mintdb_postgres_init (void *cls) plugin->get_link_data_list = &postgres_get_link_data_list; plugin->free_link_data_list = &common_free_link_data_list; plugin->get_transfer = &postgres_get_transfer; - // plugin->have_lock = &postgres_have_lock; /* #3625 */ - // plugin->insert_lock = &postgres_insert_lock; /* #3625 */ plugin->get_coin_transactions = &postgres_get_coin_transactions; plugin->free_coin_transaction_list = &common_free_coin_transaction_list; return plugin; -- cgit v1.2.3 From f8a730a0ab6db9ca37c652493b844a7a5ff503e0 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Sep 2015 20:02:21 +0200 Subject: fixing #3816: adding 'id' field to uniquely identify each deposit --- src/include/taler_mintdb_plugin.h | 52 +++++++++++++++++++++++++++++++++++-- src/mintdb/plugin_mintdb_postgres.c | 29 +++++++++++++++------ 2 files changed, 71 insertions(+), 10 deletions(-) (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 14518d28..4e06fc86 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -516,6 +516,31 @@ struct TALER_MINTDB_MeltCommitment struct TALER_MINTDB_Session; +/** + * Function called with details about deposits that + * have been made, with the goal of executing the + * corresponding wire transaction. + * + * @param cls closure + * @param id transaction ID (used as future `min_id` to avoid + * iterating over transactions more than once) + * @param amount_with_fee amount that was deposited including fee + * @param deposit_fee amount the mint gets to keep as transaction fees + * @param transaction_id unique transaction ID chosen by the merchant + * @param h_contract hash of the contract between merchant and customer + * @param wire wire details for the merchant + * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop + */ +typedef int +(*TALER_MINTDB_DepositIterator)(void *cls, + uint64_t id, + const struct TALER_Amount *amount_with_fee, + const struct TALER_Amount *deposit_fee, + uint64_t transaction_id, + const struct GNUNET_HashCode *h_contract, + const char *wire); + + /** * Function called with the session hashes and transfer secret * information for a given coin. @@ -780,8 +805,7 @@ struct TALER_MINTDB_Plugin /** - * Insert information about deposited coin into the - * database. + * Insert information about deposited coin into the database. * * @param cls the @e cls of this struct with the plugin-specific state * @param sesssion connection to the database @@ -794,6 +818,30 @@ struct TALER_MINTDB_Plugin const struct TALER_MINTDB_Deposit *deposit); + /** + * Obtain information about deposits. Iterates over all deposits + * above a certain ID. Use a @a min_id of 0 to start at the beginning. + * This operation is executed in its own transaction in transaction + * mode "READ COMMITTED", i.e. we should only see valid deposits. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param sesssion connection to the database + * @param min_id deposit to start at + * @param limit maximum number of transactions to fetch + * @param deposit_cb function to call for each deposit + * @param deposit_cb_cls closure for @a deposit_cb + * @return number of rows processed, 0 if none exist, + * #GNUNET_SYSERR on error + */ + int + (*iterate_deposits) (void *cls, + struct TALER_MINTDB_Session *sesssion, + uint64_t min_id, + unsigned int limit, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls); + + /** * Lookup refresh session data under the given @a session_hash. * diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index f47e31b4..efb73a32 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -424,15 +424,10 @@ postgres_create_tables (void *cls, ",ev_sig BYTEA NOT NULL" ")"); /* This table contains the wire transfers the mint is supposed to - execute to transmit funds to the merchants (and manage refunds). - TODO: we might want to generate some other primary key - to internally identify outgoing transactions, as "coin_pub" - may not be unique if a wallet chooses not to refresh. The - resulting transaction ID should then be returned to the merchant - and could be used by the mearchant for further inquriries about - the deposit's execution. (#3816); */ + execute to transmit funds to the merchants (and manage refunds). */ SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " - "(coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" + "(id BIGSERIAL" + ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" ",denom_pub BYTEA NOT NULL REFERENCES denominations (pub)" ",denom_sig BYTEA NOT NULL" ",transaction_id INT8 NOT NULL" @@ -853,6 +848,24 @@ postgres_prepare (PGconn *db_conn) " (merchant_pub=$3)" " )", 3, NULL); + + /* Used in #postgres_iterate_deposits() */ + PREPARE ("deposits_iterate", + "SELECT" + " id" + " amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",deposit_fee_val" + ",deposit_fee_frac" + ",deposit_fee_curr" + ",transaction_id" + ",h_contract" + ",wire" + " FROM deposits" + " WHERE id>=$1" + " LIMIT $2;", + 2, NULL); /* Used in #postgres_get_coin_transactions() to obtain information about how a coin has been spend with /deposit requests. */ PREPARE ("get_deposit_with_coin_pub", -- cgit v1.2.3 From cc47c5c701340c9be0acc6b7394aa2afad0cd0d3 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Sep 2015 20:28:37 +0200 Subject: implement #3838 (as required for wire transfers) --- src/include/taler_mintdb_plugin.h | 102 +++++++++++++++--------------- src/mintdb/plugin_mintdb_postgres.c | 123 ++++++++++++++++++++++++++++++++++-- 2 files changed, 170 insertions(+), 55 deletions(-) (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 4e06fc86..851ed526 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -538,7 +538,7 @@ typedef int const struct TALER_Amount *deposit_fee, uint64_t transaction_id, const struct GNUNET_HashCode *h_contract, - const char *wire); + const json_t *wire); /** @@ -628,23 +628,23 @@ struct TALER_MINTDB_Plugin * Commit a transaction. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use * @return #GNUNET_OK on success */ int (*commit) (void *cls, - struct TALER_MINTDB_Session *sesssion); + struct TALER_MINTDB_Session *session); /** * Abort/rollback a transaction. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use */ void (*rollback) (void *cls, - struct TALER_MINTDB_Session *sesssion); + struct TALER_MINTDB_Session *session); /** @@ -653,7 +653,7 @@ struct TALER_MINTDB_Plugin * with this key have. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use * @param denom_pub the public key used for signing coins of this denomination * @param issue issuing information with value, fees and other info about the coin * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure @@ -669,7 +669,7 @@ struct TALER_MINTDB_Plugin * Fetch information about a denomination key. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use * @param denom_pub the public key used for signing coins of this denomination * @param[out] issue set to issue information with value, fees and other info about the coin, can be NULL * @return #GNUNET_OK on success; #GNUNET_NO if no record was found, #GNUNET_SYSERR on failure @@ -728,7 +728,7 @@ struct TALER_MINTDB_Plugin * key of the hash of the blinded message. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param h_blind hash of the blinded coin to be signed (will match * `h_coin_envelope` in the @a collectable to be returned) * @param collectable corresponding collectable coin (blind signature) @@ -739,7 +739,7 @@ struct TALER_MINTDB_Plugin */ int (*get_withdraw_info) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *h_blind, struct TALER_MINTDB_CollectableBlindcoin *collectable); @@ -749,7 +749,7 @@ struct TALER_MINTDB_Plugin * hash of the blinded message. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param collectable corresponding collectable coin (blind signature) * if a coin is found * @return #GNUNET_SYSERR on internal error @@ -758,7 +758,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_withdraw_info) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_MINTDB_CollectableBlindcoin *collectable); @@ -767,13 +767,13 @@ struct TALER_MINTDB_Plugin * reserve. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to use + * @param session connection to use * @param reserve_pub public key of the reserve * @return known transaction history (NULL if reserve is unknown) */ struct TALER_MINTDB_ReserveHistory * (*get_reserve_history) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_ReservePublicKeyP *reserve_pub); @@ -792,7 +792,7 @@ struct TALER_MINTDB_Plugin * Check if we have the specified deposit already in the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param deposit deposit to search for * @return #GNUNET_YES if we know this operation, * #GNUNET_NO if this exact deposit is unknown to us, @@ -800,7 +800,7 @@ struct TALER_MINTDB_Plugin */ int (*have_deposit) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_MINTDB_Deposit *deposit); @@ -808,13 +808,13 @@ struct TALER_MINTDB_Plugin * Insert information about deposited coin into the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to the database + * @param session connection to the database * @param deposit deposit information to store * @return #GNUNET_OK on success, #GNUNET_SYSERR on error */ int (*insert_deposit) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_MINTDB_Deposit *deposit); @@ -822,10 +822,10 @@ struct TALER_MINTDB_Plugin * Obtain information about deposits. Iterates over all deposits * above a certain ID. Use a @a min_id of 0 to start at the beginning. * This operation is executed in its own transaction in transaction - * mode "READ COMMITTED", i.e. we should only see valid deposits. + * mode "REPEATABLE READ", i.e. we should only see valid deposits. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion connection to the database + * @param session connection to the database * @param min_id deposit to start at * @param limit maximum number of transactions to fetch * @param deposit_cb function to call for each deposit @@ -835,9 +835,9 @@ struct TALER_MINTDB_Plugin */ int (*iterate_deposits) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, uint64_t min_id, - unsigned int limit, + uint32_t limit, TALER_MINTDB_DepositIterator deposit_cb, void *deposit_cb_cls); @@ -846,7 +846,7 @@ struct TALER_MINTDB_Plugin * Lookup refresh session data under the given @a session_hash. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database handle to use + * @param session database handle to use * @param session_hash hash over the melt to use for the lookup * @param[out] refresh_session where to store the result * @return #GNUNET_YES on success, @@ -855,7 +855,7 @@ struct TALER_MINTDB_Plugin */ int (*get_refresh_session) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, struct TALER_MINTDB_RefreshSession *refresh_session); @@ -864,7 +864,7 @@ struct TALER_MINTDB_Plugin * Store new refresh session data under the given @a session_hash. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database handle to use + * @param session database handle to use * @param session_hash hash over the melt to use to locate the session * @param refresh_session session data to store * @return #GNUNET_YES on success, @@ -872,7 +872,7 @@ struct TALER_MINTDB_Plugin */ int (*create_refresh_session) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, const struct TALER_MINTDB_RefreshSession *refresh_session); @@ -881,7 +881,7 @@ struct TALER_MINTDB_Plugin * Store the given /refresh/melt request in the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param oldcoin_index index of the coin to store * @param melt coin melt operation details to store; includes * the session hash of the melt @@ -890,7 +890,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_melt) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, uint16_t oldcoin_index, const struct TALER_MINTDB_RefreshMelt *melt); @@ -899,7 +899,7 @@ struct TALER_MINTDB_Plugin * Get information about melted coin details from the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param session_hash hash to identify refresh session * @param oldcoin_index index of the coin to retrieve * @param melt melt data to fill in, can be NULL @@ -908,7 +908,7 @@ struct TALER_MINTDB_Plugin */ int (*get_refresh_melt) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t oldcoin_index, struct TALER_MINTDB_RefreshMelt *melt); @@ -919,7 +919,7 @@ struct TALER_MINTDB_Plugin * in a given refresh operation. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param session_hash hash to identify refresh session * @param num_newcoins number of coins to generate, size of the @a denom_pubs array * @param denom_pubs array denominations of the coins to create @@ -928,7 +928,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_order) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t num_newcoins, const struct TALER_DenominationPublicKey *denom_pubs); @@ -939,7 +939,7 @@ struct TALER_MINTDB_Plugin * create in the given refresh operation. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param session_hash hash to identify refresh session * @param num_newcoins size of the @a denom_pubs array * @param[out] denom_pubs where to write @a num_newcoins denomination keys @@ -948,7 +948,7 @@ struct TALER_MINTDB_Plugin */ int (*get_refresh_order) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t num_newcoins, struct TALER_DenominationPublicKey *denom_pubs); @@ -959,7 +959,7 @@ struct TALER_MINTDB_Plugin * for the given refresh session in the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @param cnc_index cut and choose index (1st dimension), relating to #TALER_CNC_KAPPA * @param num_newcoins coin index size of the @a commit_coins array @@ -969,7 +969,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_commit_coins) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t cnc_index, uint16_t num_newcoins, @@ -981,7 +981,7 @@ struct TALER_MINTDB_Plugin * given coin of the given refresh session from the database. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @param cnc_index cut and choose set index (1st dimension) * @param num_coins size of the @a commit_coins array @@ -992,7 +992,7 @@ struct TALER_MINTDB_Plugin */ int (*get_refresh_commit_coins) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t cnc_index, uint16_t num_coins, @@ -1004,7 +1004,7 @@ struct TALER_MINTDB_Plugin * for the given refresh session. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @param cnc_index cut and choose index (1st dimension), relating to #TALER_CNC_KAPPA * @param num_links size of the @a commit_link array @@ -1013,7 +1013,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_commit_links) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t cnc_index, uint16_t num_links, @@ -1024,7 +1024,7 @@ struct TALER_MINTDB_Plugin * for the given refresh session. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @param cnc_index cut and choose index (1st dimension) * @param num_links size of the @a links array to return @@ -1035,7 +1035,7 @@ struct TALER_MINTDB_Plugin */ int (*get_refresh_commit_links) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t cnc_index, uint16_t num_links, @@ -1046,14 +1046,14 @@ struct TALER_MINTDB_Plugin * Get all of the information from the given melt commit operation. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection to use + * @param session database connection to use * @param session_hash hash to identify refresh session * @return NULL if the @a session_hash does not correspond to any known melt * operation */ struct TALER_MINTDB_MeltCommitment * (*get_melt_commitment) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash); @@ -1075,7 +1075,7 @@ struct TALER_MINTDB_Plugin * be used to try to obtain the private keys during "/refresh/link". * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param session_hash hash to identify refresh session * @param newcoin_index coin index * @param ev_sig coin signature @@ -1083,7 +1083,7 @@ struct TALER_MINTDB_Plugin */ int (*insert_refresh_out) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash, uint16_t newcoin_index, const struct TALER_DenominationSignature *ev_sig); @@ -1094,13 +1094,13 @@ struct TALER_MINTDB_Plugin * information, the denomination keys and the signatures. * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param session_hash session to get linkage data for * @return all known link data for the session */ struct TALER_MINTDB_LinkDataList * (*get_link_data_list) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct GNUNET_HashCode *session_hash); @@ -1123,7 +1123,7 @@ struct TALER_MINTDB_Plugin * * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param coin_pub public key of the coin * @param tdc function to call for each session the coin was melted into * @param tdc_cls closure for @a tdc @@ -1133,7 +1133,7 @@ struct TALER_MINTDB_Plugin */ int (*get_transfer) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_CoinSpendPublicKeyP *coin_pub, TALER_MINTDB_TransferDataCallback tdc, void *tdc_cls); @@ -1144,13 +1144,13 @@ struct TALER_MINTDB_Plugin * with the given coin (/refresh/melt and /deposit operations). * * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection + * @param session database connection * @param coin_pub coin to investigate * @return list of transactions, NULL if coin is fresh */ struct TALER_MINTDB_TransactionList * (*get_coin_transactions) (void *cls, - struct TALER_MINTDB_Session *sesssion, + struct TALER_MINTDB_Session *session, const struct TALER_CoinSpendPublicKeyP *coin_pub); diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index efb73a32..86db0a11 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -426,7 +426,7 @@ postgres_create_tables (void *cls, /* This table contains the wire transfers the mint is supposed to execute to transmit funds to the merchants (and manage refunds). */ SQLEXEC("CREATE TABLE IF NOT EXISTS deposits " - "(id BIGSERIAL" + "(serial_id BIGSERIAL" ",coin_pub BYTEA NOT NULL CHECK (LENGTH(coin_pub)=32)" ",denom_pub BYTEA NOT NULL REFERENCES denominations (pub)" ",denom_sig BYTEA NOT NULL" @@ -852,8 +852,8 @@ postgres_prepare (PGconn *db_conn) /* Used in #postgres_iterate_deposits() */ PREPARE ("deposits_iterate", "SELECT" - " id" - " amount_with_fee_val" + " serial_id" + ",amount_with_fee_val" ",amount_with_fee_frac" ",amount_with_fee_curr" ",deposit_fee_val" @@ -863,7 +863,8 @@ postgres_prepare (PGconn *db_conn) ",h_contract" ",wire" " FROM deposits" - " WHERE id>=$1" + " WHERE serial_id>=$1" + " ORDER BY serial_id ASC" " LIMIT $2;", 2, NULL); /* Used in #postgres_get_coin_transactions() to obtain information @@ -1894,6 +1895,119 @@ postgres_have_deposit (void *cls, } +/** + * Obtain information about deposits. Iterates over all deposits + * above a certain ID. Use a @a min_id of 0 to start at the beginning. + * This operation is executed in its own transaction in transaction + * mode "REPEATABLE READ", i.e. we should only see valid deposits. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param session connection to the database + * @param min_id deposit to start at + * @param limit maximum number of transactions to fetch + * @param deposit_cb function to call for each deposit + * @param deposit_cb_cls closure for @a deposit_cb + * @return number of rows processed, 0 if none exist, + * #GNUNET_SYSERR on error + */ +static int +postgres_iterate_deposits (void *cls, + struct TALER_MINTDB_Session *session, + uint64_t min_id, + uint32_t limit, + TALER_MINTDB_DepositIterator deposit_cb, + void *deposit_cb_cls) +{ + struct TALER_PQ_QueryParam params[] = { + TALER_PQ_query_param_uint64 (&min_id), + TALER_PQ_query_param_uint32 (&limit), + TALER_PQ_query_param_end + }; + PGresult *result; + unsigned int i; + unsigned int n; + + if (GNUNET_OK != + postgres_start (cls, session)) + return GNUNET_SYSERR; + result = PQexec (session->conn, + "SET TRANSACTION REPEATABLE READ"); + if (PGRES_COMMAND_OK != + PQresultStatus (result)) + { + TALER_LOG_ERROR ("Failed to set transaction to REPEATABL EREAD: %s\n", + PQresultErrorMessage (result)); + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + + result = TALER_PQ_exec_prepared (session->conn, + "deposits_iterate", + params); + if (PGRES_TUPLES_OK != + PQresultStatus (result)) + { + BREAK_DB_ERR (result); + PQclear (result); + postgres_rollback (cls, session); + return GNUNET_SYSERR; + } + if (0 == (n = PQntuples (result))) + { + PQclear (result); + postgres_rollback (cls, session); + return 0; + } + for (i=0;iget_reserve_history = &postgres_get_reserve_history; plugin->free_reserve_history = &common_free_reserve_history; plugin->have_deposit = &postgres_have_deposit; + plugin->iterate_deposits = &postgres_iterate_deposits; plugin->insert_deposit = &postgres_insert_deposit; plugin->get_refresh_session = &postgres_get_refresh_session; -- cgit v1.2.3 From 1eadd66ae0c4abe6867321bcac0ad2f9832a0baf Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 19 Sep 2015 22:08:49 +0200 Subject: renaming /withdraw to /reserve (#3968) --- src/include/taler_mint_service.h | 64 +- src/mint-lib/Makefile.am | 4 +- src/mint-lib/mint_api_handle.c | 2 +- src/mint-lib/mint_api_handle.h | 2 +- src/mint-lib/mint_api_reserve.c | 927 +++++++++++++++++++++++++++++ src/mint-lib/mint_api_withdraw.c | 927 ----------------------------- src/mint-lib/test_mint_api.c | 178 +++--- src/mint/Makefile.am | 2 +- src/mint/taler-mint-httpd.c | 14 +- src/mint/taler-mint-httpd_db.c | 30 +- src/mint/taler-mint-httpd_db.h | 20 +- src/mint/taler-mint-httpd_keystate.h | 2 +- src/mint/taler-mint-httpd_reserve.c | 185 ++++++ src/mint/taler-mint-httpd_reserve.h | 73 +++ src/mint/taler-mint-httpd_responses.c | 12 +- src/mint/taler-mint-httpd_responses.h | 14 +- src/mint/taler-mint-httpd_withdraw.c | 180 ------ src/mint/taler-mint-httpd_withdraw.h | 73 --- src/mintdb/perf_taler_mintdb.c | 12 +- src/mintdb/perf_taler_mintdb_interpreter.h | 2 +- src/mintdb/plugin_mintdb_postgres.c | 10 +- 21 files changed, 1369 insertions(+), 1364 deletions(-) create mode 100644 src/mint-lib/mint_api_reserve.c delete mode 100644 src/mint-lib/mint_api_withdraw.c create mode 100644 src/mint/taler-mint-httpd_reserve.c create mode 100644 src/mint/taler-mint-httpd_reserve.h delete mode 100644 src/mint/taler-mint-httpd_withdraw.c delete mode 100644 src/mint/taler-mint-httpd_withdraw.h (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/include/taler_mint_service.h b/src/include/taler_mint_service.h index 8d569d4b..e36dcf69 100644 --- a/src/include/taler_mint_service.h +++ b/src/include/taler_mint_service.h @@ -547,13 +547,13 @@ void TALER_MINT_deposit_cancel (struct TALER_MINT_DepositHandle *deposit); -/* ********************* /withdraw/status *********************** */ +/* ********************* /reserve/status *********************** */ /** - * @brief A /withdraw/status Handle + * @brief A /reserve/status Handle */ -struct TALER_MINT_WithdrawStatusHandle; +struct TALER_MINT_ReserveStatusHandle; /** @@ -623,12 +623,12 @@ struct TALER_MINT_ReserveHistory * @param history detailed transaction history, NULL on error */ typedef void -(*TALER_MINT_WithdrawStatusResultCallback) (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_MINT_ReserveHistory *history); +(*TALER_MINT_ReserveStatusResultCallback) (void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_MINT_ReserveHistory *history); /** @@ -647,11 +647,11 @@ typedef void * @return a handle for this request; NULL if the inputs are invalid (i.e. * signatures fail to verify). In this case, the callback is not called. */ -struct TALER_MINT_WithdrawStatusHandle * -TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MINT_WithdrawStatusResultCallback cb, - void *cb_cls); +struct TALER_MINT_ReserveStatusHandle * +TALER_MINT_reserve_status (struct TALER_MINT_Handle *mint, + const struct TALER_ReservePublicKeyP *reserve_pub, + TALER_MINT_ReserveStatusResultCallback cb, + void *cb_cls); /** @@ -661,16 +661,16 @@ TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, * @param wsh the withdraw status request handle */ void -TALER_MINT_withdraw_status_cancel (struct TALER_MINT_WithdrawStatusHandle *wsh); +TALER_MINT_reserve_status_cancel (struct TALER_MINT_ReserveStatusHandle *wsh); -/* ********************* /withdraw/sign *********************** */ +/* ********************* /reserve/withdraw *********************** */ /** - * @brief A /withdraw/sign Handle + * @brief A /reserve/withdraw Handle */ -struct TALER_MINT_WithdrawSignHandle; +struct TALER_MINT_ReserveWithdrawHandle; /** @@ -684,14 +684,14 @@ struct TALER_MINT_WithdrawSignHandle; * @param full_response full response from the mint (for logging, in case of errors) */ typedef void -(*TALER_MINT_WithdrawSignResultCallback) (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - json_t *full_response); +(*TALER_MINT_ReserveWithdrawResultCallback) (void *cls, + unsigned int http_status, + const struct TALER_DenominationSignature *sig, + json_t *full_response); /** - * Withdraw a coin from the mint using a /withdraw/sign request. This + * Withdraw a coin from the mint using a /reserve/withdraw request. This * API is typically used by a wallet. Note that to ensure that no * money is lost in case of hardware failures, the caller must have * committed (most of) the arguments to disk before calling, and be @@ -711,14 +711,14 @@ typedef void * if the inputs are invalid (i.e. denomination key not with this mint). * In this case, the callback is not called. */ -struct TALER_MINT_WithdrawSignHandle * -TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, - const struct TALER_MINT_DenomPublicKey *pk, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKey *blinding_key, - TALER_MINT_WithdrawSignResultCallback res_cb, - void *res_cb_cls); +struct TALER_MINT_ReserveWithdrawHandle * +TALER_MINT_reserve_withdraw (struct TALER_MINT_Handle *mint, + const struct TALER_MINT_DenomPublicKey *pk, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_DenominationBlindingKey *blinding_key, + TALER_MINT_ReserveWithdrawResultCallback res_cb, + void *res_cb_cls); /** @@ -728,7 +728,7 @@ TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, * @param sign the withdraw sign request handle */ void -TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign); +TALER_MINT_reserve_withdraw_cancel (struct TALER_MINT_ReserveWithdrawHandle *sign); /* ********************* /refresh/melt+reveal ***************************** */ diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am index 2729177f..ccea4ec5 100644 --- a/src/mint-lib/Makefile.am +++ b/src/mint-lib/Makefile.am @@ -22,8 +22,8 @@ libtalermint_la_SOURCES = \ mint_api_deposit.c \ mint_api_refresh.c \ mint_api_refresh_link.c \ - mint_api_wire.c \ - mint_api_withdraw.c + mint_api_reserve.c \ + mint_api_wire.c libtalermint_la_LIBADD = \ -lgnunetutil \ diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c index 9eaa6171..61291389 100644 --- a/src/mint-lib/mint_api_handle.c +++ b/src/mint-lib/mint_api_handle.c @@ -686,7 +686,7 @@ MAH_handle_is_ready (struct TALER_MINT_Handle *h) * Obtain the URL to use for an API request. * * @param h the mint handle to query - * @param path Taler API path (i.e. "/withdraw/sign") + * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h index aeaeeb59..fae30a30 100644 --- a/src/mint-lib/mint_api_handle.h +++ b/src/mint-lib/mint_api_handle.h @@ -48,7 +48,7 @@ MAH_handle_is_ready (struct TALER_MINT_Handle *h); * Obtain the URL to use for an API request. * * @param h the mint handle to query - * @param path Taler API path (i.e. "/withdraw/sign") + * @param path Taler API path (i.e. "/reserve/withdraw") * @return the full URI to use with cURL */ char * diff --git a/src/mint-lib/mint_api_reserve.c b/src/mint-lib/mint_api_reserve.c new file mode 100644 index 00000000..57e8552c --- /dev/null +++ b/src/mint-lib/mint_api_reserve.c @@ -0,0 +1,927 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, If not, see + +*/ +/** + * @file mint-lib/mint_api_reserve.c + * @brief Implementation of the /reserve requests of the mint's HTTP API + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include /* just for HTTP status codes */ +#include +#include "taler_mint_service.h" +#include "mint_api_json.h" +#include "mint_api_context.h" +#include "mint_api_handle.h" +#include "taler_signatures.h" + + +/* ********************** /reserve/status ********************** */ + +/** + * @brief A Withdraw Status Handle + */ +struct TALER_MINT_ReserveStatusHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_ReserveStatusResultCallback cb; + + /** + * Public key of the reserve we are querying. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + +}; + + +/** + * Parse history given in JSON format and return it in binary + * format. + * + * @param[in] history JSON array with the history + * @param reserve_pub public key of the reserve to inspect + * @param currency currency we expect the balance to be in + * @param[out] balance final balance + * @param history_length number of entries in @a history + * @param[out] rhistory array of length @a history_length, set to the + * parsed history entries + * @return #GNUNET_OK if history was valid and @a rhistory and @a balance + * were set, + * #GNUNET_SYSERR if there was a protocol violation in @a history + */ +static int +parse_reserve_history (json_t *history, + const struct TALER_ReservePublicKeyP *reserve_pub, + const char *currency, + struct TALER_Amount *balance, + unsigned int history_length, + struct TALER_MINT_ReserveHistory *rhistory) +{ + struct GNUNET_HashCode uuid[history_length]; + unsigned int uuid_off; + struct TALER_Amount total_in; + struct TALER_Amount total_out; + size_t off; + + TALER_amount_get_zero (currency, + &total_in); + TALER_amount_get_zero (currency, + &total_out); + uuid_off = 0; + for (off=0;offeddsa_pub), + MAJ_spec_end + }; + unsigned int i; + + rhistory[off].type = TALER_MINT_RTT_WITHDRAWAL; + if (GNUNET_OK != + MAJ_parse_json (transaction, + withdraw_spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Check that the signature actually signed a withdraw request */ + if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || + (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) + { + GNUNET_break_op (0); + MAJ_parse_free (withdraw_spec); + return GNUNET_SYSERR; + } + withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; + TALER_amount_ntoh (&amount_from_purpose, + &withdraw_purpose->amount_with_fee); + if (0 != TALER_amount_cmp (&amount, + &amount_from_purpose)) + { + GNUNET_break_op (0); + MAJ_parse_free (withdraw_spec); + return GNUNET_SYSERR; + } + rhistory[off].details.out_authorization_sig = json_object_get (transaction, + "signature"); + /* Check check that the same withdraw transaction + isn't listed twice by the mint. We use the + "uuid" array to remember the hashes of all + purposes, and compare the hashes to find + duplicates. */ + GNUNET_CRYPTO_hash (withdraw_purpose, + ntohl (withdraw_purpose->purpose.size), + &uuid[uuid_off]); + for (i=0;ijob = NULL; + json = MAC_download_get_result (&wsh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + { + /* TODO: move into separate function... */ + json_t *history; + unsigned int len; + struct TALER_Amount balance; + struct TALER_Amount balance_from_history; + struct MAJ_Specification spec[] = { + MAJ_spec_amount ("balance", &balance), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + history = json_object_get (json, + "history"); + if (NULL == history) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + len = json_array_size (history); + { + struct TALER_MINT_ReserveHistory rhistory[len]; + + if (GNUNET_OK != + parse_reserve_history (history, + &wsh->reserve_pub, + balance.currency, + &balance_from_history, + len, + rhistory)) + { + GNUNET_break_op (0); + response_code = 0; + break; + } + if (0 != + TALER_amount_cmp (&balance_from_history, + &balance)) + { + /* mint cannot add up balances!? */ + GNUNET_break_op (0); + response_code = 0; + break; + } + wsh->cb (wsh->cb_cls, + response_code, + json, + &balance, + len, + rhistory); + wsh->cb = NULL; + } + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, this should never + happen, we should pass the JSON reply to the application */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != wsh->cb) + wsh->cb (wsh->cb_cls, + response_code, + json, + NULL, + 0, NULL); + json_decref (json); + TALER_MINT_reserve_status_cancel (wsh); +} + + +/** + * Submit a request to obtain the transaction history of a reserve + * from the mint. Note that while we return the full response to the + * caller for further processing, we do already verify that the + * response is well-formed (i.e. that signatures included in the + * response are all valid and add up to the balance). If the mint's + * reply is not well-formed, we return an HTTP status code of zero to + * @a cb. + * + * @param mint the mint handle; the mint must be ready to operate + * @param reserve_pub public key of the reserve to inspect + * @param cb the callback to call when a reply for this request is available + * @param cb_cls closure for the above callback + * @return a handle for this request; NULL if the inputs are invalid (i.e. + * signatures fail to verify). In this case, the callback is not called. + */ +struct TALER_MINT_ReserveStatusHandle * +TALER_MINT_reserve_status (struct TALER_MINT_Handle *mint, + const struct TALER_ReservePublicKeyP *reserve_pub, + TALER_MINT_ReserveStatusResultCallback cb, + void *cb_cls) +{ + struct TALER_MINT_ReserveStatusHandle *wsh; + struct TALER_MINT_Context *ctx; + CURL *eh; + char *pub_str; + char *arg_str; + + if (GNUNET_YES != + MAH_handle_is_ready (mint)) + { + GNUNET_break (0); + return NULL; + } + pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)); + GNUNET_asprintf (&arg_str, + "/reserve/status?reserve_pub=%s", + pub_str); + GNUNET_free (pub_str); + wsh = GNUNET_new (struct TALER_MINT_ReserveStatusHandle); + wsh->mint = mint; + wsh->cb = cb; + wsh->cb_cls = cb_cls; + wsh->reserve_pub = *reserve_pub; + wsh->url = MAH_path_to_url (mint, + arg_str); + GNUNET_free (arg_str); + + eh = curl_easy_init (); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wsh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wsh->db)); + ctx = MAH_handle_to_context (mint); + wsh->job = MAC_job_add (ctx, + eh, + GNUNET_NO, + &handle_reserve_status_finished, + wsh); + return wsh; +} + + +/** + * Cancel a withdraw status request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param wsh the withdraw status request handle + */ +void +TALER_MINT_reserve_status_cancel (struct TALER_MINT_ReserveStatusHandle *wsh) +{ + if (NULL != wsh->job) + { + MAC_job_cancel (wsh->job); + wsh->job = NULL; + } + GNUNET_free_non_null (wsh->db.buf); + GNUNET_free (wsh->url); + GNUNET_free (wsh); +} + + +/* ********************** /reserve/withdraw ********************** */ + +/** + * @brief A Withdraw Sign Handle + */ +struct TALER_MINT_ReserveWithdrawHandle +{ + + /** + * The connection to mint this request handle will use + */ + struct TALER_MINT_Handle *mint; + + /** + * The url for this request. + */ + char *url; + + /** + * JSON encoding of the request to POST. + */ + char *json_enc; + + /** + * Handle for the request. + */ + struct MAC_Job *job; + + /** + * Function to call with the result. + */ + TALER_MINT_ReserveWithdrawResultCallback cb; + + /** + * Key used to blind the value. + */ + const struct TALER_DenominationBlindingKey *blinding_key; + + /** + * Denomination key we are withdrawing. + */ + const struct TALER_MINT_DenomPublicKey *pk; + + /** + * Closure for @a cb. + */ + void *cb_cls; + + /** + * Download buffer + */ + struct MAC_DownloadBuffer db; + + /** + * Hash of the public key of the coin we are signing. + */ + struct GNUNET_HashCode c_hash; + + /** + * Public key of the reserve we are withdrawing from. + */ + struct TALER_ReservePublicKeyP reserve_pub; + +}; + + +/** + * We got a 200 OK response for the /reserve/withdraw operation. + * Extract the coin's signature and return it to the caller. + * The signature we get from the mint is for the blinded value. + * Thus, we first must unblind it and then should verify its + * validity against our coin's hash. + * + * If everything checks out, we return the unblinded signature + * to the application via the callback. + * + * @param wsh operation handle + * @param json reply from the mint + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static int +reserve_withdraw_ok (struct TALER_MINT_ReserveWithdrawHandle *wsh, + json_t *json) +{ + struct GNUNET_CRYPTO_rsa_Signature *blind_sig; + struct GNUNET_CRYPTO_rsa_Signature *sig; + struct TALER_DenominationSignature dsig; + struct MAJ_Specification spec[] = { + MAJ_spec_rsa_signature ("ev_sig", &blind_sig), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, + wsh->blinding_key->rsa_blinding_key, + wsh->pk->key.rsa_public_key); + GNUNET_CRYPTO_rsa_signature_free (blind_sig); + if (GNUNET_OK != + GNUNET_CRYPTO_rsa_verify (&wsh->c_hash, + sig, + wsh->pk->key.rsa_public_key)) + { + GNUNET_break_op (0); + GNUNET_CRYPTO_rsa_signature_free (sig); + return GNUNET_SYSERR; + } + /* signature is valid, return it to the application */ + dsig.rsa_signature = sig; + wsh->cb (wsh->cb_cls, + MHD_HTTP_OK, + &dsig, + json); + /* make sure callback isn't called again after return */ + wsh->cb = NULL; + GNUNET_CRYPTO_rsa_signature_free (sig); + return GNUNET_OK; +} + + +/** + * We got a 402 PAYMENT REQUIRED response for the /reserve/withdraw operation. + * Check the signatures on the withdraw transactions in the provided + * history and that the balances add up. We don't do anything directly + * with the information, as the JSON will be returned to the application. + * However, our job is ensuring that the mint followed the protocol, and + * this in particular means checking all of the signatures in the history. + * + * @param wsh operation handle + * @param json reply from the mint + * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors + */ +static int +reserve_withdraw_payment_required (struct TALER_MINT_ReserveWithdrawHandle *wsh, + json_t *json) +{ + struct TALER_Amount balance; + struct TALER_Amount balance_from_history; + struct TALER_Amount requested_amount; + json_t *history; + size_t len; + struct MAJ_Specification spec[] = { + MAJ_spec_amount ("balance", &balance), + MAJ_spec_end + }; + + if (GNUNET_OK != + MAJ_parse_json (json, + spec)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + history = json_object_get (json, + "history"); + if (NULL == history) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + + /* go over transaction history and compute + total incoming and outgoing amounts */ + len = json_array_size (history); + { + struct TALER_MINT_ReserveHistory rhistory[len]; + + if (GNUNET_OK != + parse_reserve_history (history, + &wsh->reserve_pub, + balance.currency, + &balance_from_history, + len, + rhistory)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + } + + if (0 != + TALER_amount_cmp (&balance_from_history, + &balance)) + { + /* mint cannot add up balances!? */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Compute how much we expected to charge to the reserve */ + if (GNUNET_OK != + TALER_amount_add (&requested_amount, + &wsh->pk->value, + &wsh->pk->fee_withdraw)) + { + /* Overflow here? Very strange, our CPU must be fried... */ + GNUNET_break (0); + return GNUNET_SYSERR; + } + /* Check that funds were really insufficient */ + if (0 >= TALER_amount_cmp (&requested_amount, + &balance)) + { + /* Requested amount is smaller or equal to reported balance, + so this should not have failed. */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called when we're done processing the + * HTTP /reserve/withdraw request. + * + * @param cls the `struct TALER_MINT_ReserveWithdrawHandle` + * @param eh curl handle of the request that finished + */ +static void +handle_reserve_withdraw_finished (void *cls, + CURL *eh) +{ + struct TALER_MINT_ReserveWithdrawHandle *wsh = cls; + long response_code; + json_t *json; + + wsh->job = NULL; + json = MAC_download_get_result (&wsh->db, + eh, + &response_code); + switch (response_code) + { + case 0: + break; + case MHD_HTTP_OK: + if (GNUNET_OK != + reserve_withdraw_ok (wsh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } + break; + case MHD_HTTP_BAD_REQUEST: + /* This should never happen, either us or the mint is buggy + (or API version conflict); just pass JSON reply to the application */ + break; + case MHD_HTTP_PAYMENT_REQUIRED: + /* The mint says that the reserve has insufficient funds; + check the signatures in the history... */ + if (GNUNET_OK != + reserve_withdraw_payment_required (wsh, + json)) + { + GNUNET_break_op (0); + response_code = 0; + } + break; + case MHD_HTTP_UNAUTHORIZED: + GNUNET_break (0); + /* Nothing really to verify, mint says one of the signatures is + invalid; as we checked them, this should never happen, we + should pass the JSON reply to the application */ + break; + case MHD_HTTP_NOT_FOUND: + /* Nothing really to verify, the mint basically just says + that it doesn't know this reserve. Can happen if we + query before the wire transfer went through. + We should simply pass the JSON reply to the application. */ + break; + case MHD_HTTP_INTERNAL_SERVER_ERROR: + /* Server had an internal issue; we should retry, but this API + leaves this to the application */ + break; + default: + /* unexpected response code */ + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected response code %u\n", + response_code); + GNUNET_break (0); + response_code = 0; + break; + } + if (NULL != wsh->cb) + wsh->cb (wsh->cb_cls, + response_code, + NULL, + json); + json_decref (json); + TALER_MINT_reserve_withdraw_cancel (wsh); +} + + +/** + * Withdraw a coin from the mint using a /reserve/withdraw request. Note + * that to ensure that no money is lost in case of hardware failures, + * the caller must have committed (most of) the arguments to disk + * before calling, and be ready to repeat the request with the same + * arguments in case of failures. + * + * @param mint the mint handle; the mint must be ready to operate + * @param pk kind of coin to create + * @param reserve_priv private key of the reserve to withdraw from + * @param coin_priv where to store the coin's private key, + * caller must have committed this value to disk before the call (with @a pk) + * @param blinding_key where to store the coin's blinding key + * caller must have committed this value to disk before the call (with @a pk) + * @param res_cb the callback to call when the final result for this request is available + * @param res_cb_cls closure for the above callback + * @return #GNUNET_OK on success, #GNUNET_SYSERR + * if the inputs are invalid (i.e. denomination key not with this mint). + * In this case, the callback is not called. + */ +struct TALER_MINT_ReserveWithdrawHandle * +TALER_MINT_reserve_withdraw (struct TALER_MINT_Handle *mint, + const struct TALER_MINT_DenomPublicKey *pk, + const struct TALER_ReservePrivateKeyP *reserve_priv, + const struct TALER_CoinSpendPrivateKeyP *coin_priv, + const struct TALER_DenominationBlindingKey *blinding_key, + TALER_MINT_ReserveWithdrawResultCallback res_cb, + void *res_cb_cls) +{ + struct TALER_MINT_ReserveWithdrawHandle *wsh; + struct TALER_WithdrawRequestPS req; + struct TALER_ReserveSignatureP reserve_sig; + struct TALER_CoinSpendPublicKeyP coin_pub; + struct TALER_MINT_Context *ctx; + struct TALER_Amount amount_with_fee; + char *coin_ev; + size_t coin_ev_size; + json_t *withdraw_obj; + CURL *eh; + + wsh = GNUNET_new (struct TALER_MINT_ReserveWithdrawHandle); + wsh->mint = mint; + wsh->cb = res_cb; + wsh->cb_cls = res_cb_cls; + wsh->pk = pk; + + GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, + &coin_pub.eddsa_pub); + GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + &wsh->c_hash); + coin_ev_size = GNUNET_CRYPTO_rsa_blind (&wsh->c_hash, + blinding_key->rsa_blinding_key, + pk->key.rsa_public_key, + &coin_ev); + GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, + &wsh->reserve_pub.eddsa_pub); + req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); + req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); + req.reserve_pub = wsh->reserve_pub; + if (GNUNET_OK != + TALER_amount_add (&amount_with_fee, + &pk->fee_withdraw, + &pk->value)) + { + /* mint gave us denomination keys that overflow like this!? */ + GNUNET_break_op (0); + GNUNET_free (coin_ev); + GNUNET_free (wsh); + return NULL; + } + TALER_amount_hton (&req.amount_with_fee, + &amount_with_fee); + TALER_amount_hton (&req.withdraw_fee, + &pk->fee_withdraw); + GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key, + &req.h_denomination_pub); + GNUNET_CRYPTO_hash (coin_ev, + coin_ev_size, + &req.h_coin_envelope); + GNUNET_assert (GNUNET_OK == + GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, + &req.purpose, + &reserve_sig.eddsa_signature)); + withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */ + " s:o, s:o}",/* reserve_pub and reserve_sig */ + "denom_pub", TALER_json_from_rsa_public_key (pk->key.rsa_public_key), + "coin_ev", TALER_json_from_data (coin_ev, + coin_ev_size), + "reserve_pub", TALER_json_from_data (&wsh->reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)), + "reserve_sig", TALER_json_from_data (&reserve_sig, + sizeof (reserve_sig))); + GNUNET_free (coin_ev); + + wsh->blinding_key = blinding_key; + wsh->url = MAH_path_to_url (mint, "/reserve/withdraw"); + + eh = curl_easy_init (); + GNUNET_assert (NULL != (wsh->json_enc = + json_dumps (withdraw_obj, + JSON_COMPACT))); + json_decref (withdraw_obj); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + wsh->url)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDS, + wsh->json_enc)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_POSTFIELDSIZE, + strlen (wsh->json_enc))); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEFUNCTION, + &MAC_download_cb)); + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_WRITEDATA, + &wsh->db)); + ctx = MAH_handle_to_context (mint); + wsh->job = MAC_job_add (ctx, + eh, + GNUNET_YES, + &handle_reserve_withdraw_finished, + wsh); + return wsh; +} + + +/** + * Cancel a withdraw status request. This function cannot be used + * on a request handle if a response is already served for it. + * + * @param sign the withdraw sign request handle + */ +void +TALER_MINT_reserve_withdraw_cancel (struct TALER_MINT_ReserveWithdrawHandle *sign) +{ + if (NULL != sign->job) + { + MAC_job_cancel (sign->job); + sign->job = NULL; + } + GNUNET_free_non_null (sign->db.buf); + GNUNET_free (sign->url); + GNUNET_free (sign->json_enc); + GNUNET_free (sign); +} + + +/* end of mint_api_reserve.c */ diff --git a/src/mint-lib/mint_api_withdraw.c b/src/mint-lib/mint_api_withdraw.c deleted file mode 100644 index ddabb811..00000000 --- a/src/mint-lib/mint_api_withdraw.c +++ /dev/null @@ -1,927 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors) - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, If not, see - -*/ -/** - * @file mint-lib/mint_api_withdraw.c - * @brief Implementation of the /withdraw requests of the mint's HTTP API - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include /* just for HTTP status codes */ -#include -#include "taler_mint_service.h" -#include "mint_api_json.h" -#include "mint_api_context.h" -#include "mint_api_handle.h" -#include "taler_signatures.h" - - -/* ********************** /withdraw/status ********************** */ - -/** - * @brief A Withdraw Status Handle - */ -struct TALER_MINT_WithdrawStatusHandle -{ - - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this request. - */ - char *url; - - /** - * Handle for the request. - */ - struct MAC_Job *job; - - /** - * Function to call with the result. - */ - TALER_MINT_WithdrawStatusResultCallback cb; - - /** - * Public key of the reserve we are querying. - */ - struct TALER_ReservePublicKeyP reserve_pub; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Download buffer - */ - struct MAC_DownloadBuffer db; - -}; - - -/** - * Parse history given in JSON format and return it in binary - * format. - * - * @param[in] history JSON array with the history - * @param reserve_pub public key of the reserve to inspect - * @param currency currency we expect the balance to be in - * @param[out] balance final balance - * @param history_length number of entries in @a history - * @param[out] rhistory array of length @a history_length, set to the - * parsed history entries - * @return #GNUNET_OK if history was valid and @a rhistory and @a balance - * were set, - * #GNUNET_SYSERR if there was a protocol violation in @a history - */ -static int -parse_reserve_history (json_t *history, - const struct TALER_ReservePublicKeyP *reserve_pub, - const char *currency, - struct TALER_Amount *balance, - unsigned int history_length, - struct TALER_MINT_ReserveHistory *rhistory) -{ - struct GNUNET_HashCode uuid[history_length]; - unsigned int uuid_off; - struct TALER_Amount total_in; - struct TALER_Amount total_out; - size_t off; - - TALER_amount_get_zero (currency, - &total_in); - TALER_amount_get_zero (currency, - &total_out); - uuid_off = 0; - for (off=0;offeddsa_pub), - MAJ_spec_end - }; - unsigned int i; - - rhistory[off].type = TALER_MINT_RTT_WITHDRAWAL; - if (GNUNET_OK != - MAJ_parse_json (transaction, - withdraw_spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* Check that the signature actually signed a withdraw request */ - if ( (ntohl (purpose->purpose) != TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW) || - (ntohl (purpose->size) != sizeof (struct TALER_WithdrawRequestPS)) ) - { - GNUNET_break_op (0); - MAJ_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - withdraw_purpose = (const struct TALER_WithdrawRequestPS *) purpose; - TALER_amount_ntoh (&amount_from_purpose, - &withdraw_purpose->amount_with_fee); - if (0 != TALER_amount_cmp (&amount, - &amount_from_purpose)) - { - GNUNET_break_op (0); - MAJ_parse_free (withdraw_spec); - return GNUNET_SYSERR; - } - rhistory[off].details.out_authorization_sig = json_object_get (transaction, - "signature"); - /* Check check that the same withdraw transaction - isn't listed twice by the mint. We use the - "uuid" array to remember the hashes of all - purposes, and compare the hashes to find - duplicates. */ - GNUNET_CRYPTO_hash (withdraw_purpose, - ntohl (withdraw_purpose->purpose.size), - &uuid[uuid_off]); - for (i=0;ijob = NULL; - json = MAC_download_get_result (&wsh->db, - eh, - &response_code); - switch (response_code) - { - case 0: - break; - case MHD_HTTP_OK: - { - /* TODO: move into separate function... */ - json_t *history; - unsigned int len; - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; - struct MAJ_Specification spec[] = { - MAJ_spec_amount ("balance", &balance), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - history = json_object_get (json, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - len = json_array_size (history); - { - struct TALER_MINT_ReserveHistory rhistory[len]; - - if (GNUNET_OK != - parse_reserve_history (history, - &wsh->reserve_pub, - balance.currency, - &balance_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - response_code = 0; - break; - } - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) - { - /* mint cannot add up balances!? */ - GNUNET_break_op (0); - response_code = 0; - break; - } - wsh->cb (wsh->cb_cls, - response_code, - json, - &balance, - len, - rhistory); - wsh->cb = NULL; - } - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the mint is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, this should never - happen, we should pass the JSON reply to the application */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - response_code); - GNUNET_break (0); - response_code = 0; - break; - } - if (NULL != wsh->cb) - wsh->cb (wsh->cb_cls, - response_code, - json, - NULL, - 0, NULL); - json_decref (json); - TALER_MINT_withdraw_status_cancel (wsh); -} - - -/** - * Submit a request to obtain the transaction history of a reserve - * from the mint. Note that while we return the full response to the - * caller for further processing, we do already verify that the - * response is well-formed (i.e. that signatures included in the - * response are all valid and add up to the balance). If the mint's - * reply is not well-formed, we return an HTTP status code of zero to - * @a cb. - * - * @param mint the mint handle; the mint must be ready to operate - * @param reserve_pub public key of the reserve to inspect - * @param cb the callback to call when a reply for this request is available - * @param cb_cls closure for the above callback - * @return a handle for this request; NULL if the inputs are invalid (i.e. - * signatures fail to verify). In this case, the callback is not called. - */ -struct TALER_MINT_WithdrawStatusHandle * -TALER_MINT_withdraw_status (struct TALER_MINT_Handle *mint, - const struct TALER_ReservePublicKeyP *reserve_pub, - TALER_MINT_WithdrawStatusResultCallback cb, - void *cb_cls) -{ - struct TALER_MINT_WithdrawStatusHandle *wsh; - struct TALER_MINT_Context *ctx; - CURL *eh; - char *pub_str; - char *arg_str; - - if (GNUNET_YES != - MAH_handle_is_ready (mint)) - { - GNUNET_break (0); - return NULL; - } - pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)); - GNUNET_asprintf (&arg_str, - "/withdraw/status?reserve_pub=%s", - pub_str); - GNUNET_free (pub_str); - wsh = GNUNET_new (struct TALER_MINT_WithdrawStatusHandle); - wsh->mint = mint; - wsh->cb = cb; - wsh->cb_cls = cb_cls; - wsh->reserve_pub = *reserve_pub; - wsh->url = MAH_path_to_url (mint, - arg_str); - GNUNET_free (arg_str); - - eh = curl_easy_init (); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_URL, - wsh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEFUNCTION, - &MAC_download_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEDATA, - &wsh->db)); - ctx = MAH_handle_to_context (mint); - wsh->job = MAC_job_add (ctx, - eh, - GNUNET_NO, - &handle_withdraw_status_finished, - wsh); - return wsh; -} - - -/** - * Cancel a withdraw status request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param wsh the withdraw status request handle - */ -void -TALER_MINT_withdraw_status_cancel (struct TALER_MINT_WithdrawStatusHandle *wsh) -{ - if (NULL != wsh->job) - { - MAC_job_cancel (wsh->job); - wsh->job = NULL; - } - GNUNET_free_non_null (wsh->db.buf); - GNUNET_free (wsh->url); - GNUNET_free (wsh); -} - - -/* ********************** /withdraw/sign ********************** */ - -/** - * @brief A Withdraw Sign Handle - */ -struct TALER_MINT_WithdrawSignHandle -{ - - /** - * The connection to mint this request handle will use - */ - struct TALER_MINT_Handle *mint; - - /** - * The url for this request. - */ - char *url; - - /** - * JSON encoding of the request to POST. - */ - char *json_enc; - - /** - * Handle for the request. - */ - struct MAC_Job *job; - - /** - * Function to call with the result. - */ - TALER_MINT_WithdrawSignResultCallback cb; - - /** - * Key used to blind the value. - */ - const struct TALER_DenominationBlindingKey *blinding_key; - - /** - * Denomination key we are withdrawing. - */ - const struct TALER_MINT_DenomPublicKey *pk; - - /** - * Closure for @a cb. - */ - void *cb_cls; - - /** - * Download buffer - */ - struct MAC_DownloadBuffer db; - - /** - * Hash of the public key of the coin we are signing. - */ - struct GNUNET_HashCode c_hash; - - /** - * Public key of the reserve we are withdrawing from. - */ - struct TALER_ReservePublicKeyP reserve_pub; - -}; - - -/** - * We got a 200 OK response for the /withdraw/sign operation. - * Extract the coin's signature and return it to the caller. - * The signature we get from the mint is for the blinded value. - * Thus, we first must unblind it and then should verify its - * validity against our coin's hash. - * - * If everything checks out, we return the unblinded signature - * to the application via the callback. - * - * @param wsh operation handle - * @param json reply from the mint - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static int -withdraw_sign_ok (struct TALER_MINT_WithdrawSignHandle *wsh, - json_t *json) -{ - struct GNUNET_CRYPTO_rsa_Signature *blind_sig; - struct GNUNET_CRYPTO_rsa_Signature *sig; - struct TALER_DenominationSignature dsig; - struct MAJ_Specification spec[] = { - MAJ_spec_rsa_signature ("ev_sig", &blind_sig), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - sig = GNUNET_CRYPTO_rsa_unblind (blind_sig, - wsh->blinding_key->rsa_blinding_key, - wsh->pk->key.rsa_public_key); - GNUNET_CRYPTO_rsa_signature_free (blind_sig); - if (GNUNET_OK != - GNUNET_CRYPTO_rsa_verify (&wsh->c_hash, - sig, - wsh->pk->key.rsa_public_key)) - { - GNUNET_break_op (0); - GNUNET_CRYPTO_rsa_signature_free (sig); - return GNUNET_SYSERR; - } - /* signature is valid, return it to the application */ - dsig.rsa_signature = sig; - wsh->cb (wsh->cb_cls, - MHD_HTTP_OK, - &dsig, - json); - /* make sure callback isn't called again after return */ - wsh->cb = NULL; - GNUNET_CRYPTO_rsa_signature_free (sig); - return GNUNET_OK; -} - - -/** - * We got a 402 PAYMENT REQUIRED response for the /withdraw/sign operation. - * Check the signatures on the withdraw transactions in the provided - * history and that the balances add up. We don't do anything directly - * with the information, as the JSON will be returned to the application. - * However, our job is ensuring that the mint followed the protocol, and - * this in particular means checking all of the signatures in the history. - * - * @param wsh operation handle - * @param json reply from the mint - * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors - */ -static int -withdraw_sign_payment_required (struct TALER_MINT_WithdrawSignHandle *wsh, - json_t *json) -{ - struct TALER_Amount balance; - struct TALER_Amount balance_from_history; - struct TALER_Amount requested_amount; - json_t *history; - size_t len; - struct MAJ_Specification spec[] = { - MAJ_spec_amount ("balance", &balance), - MAJ_spec_end - }; - - if (GNUNET_OK != - MAJ_parse_json (json, - spec)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - history = json_object_get (json, - "history"); - if (NULL == history) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - - /* go over transaction history and compute - total incoming and outgoing amounts */ - len = json_array_size (history); - { - struct TALER_MINT_ReserveHistory rhistory[len]; - - if (GNUNET_OK != - parse_reserve_history (history, - &wsh->reserve_pub, - balance.currency, - &balance_from_history, - len, - rhistory)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - } - - if (0 != - TALER_amount_cmp (&balance_from_history, - &balance)) - { - /* mint cannot add up balances!? */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - /* Compute how much we expected to charge to the reserve */ - if (GNUNET_OK != - TALER_amount_add (&requested_amount, - &wsh->pk->value, - &wsh->pk->fee_withdraw)) - { - /* Overflow here? Very strange, our CPU must be fried... */ - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* Check that funds were really insufficient */ - if (0 >= TALER_amount_cmp (&requested_amount, - &balance)) - { - /* Requested amount is smaller or equal to reported balance, - so this should not have failed. */ - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called when we're done processing the - * HTTP /withdraw/sign request. - * - * @param cls the `struct TALER_MINT_WithdrawSignHandle` - * @param eh curl handle of the request that finished - */ -static void -handle_withdraw_sign_finished (void *cls, - CURL *eh) -{ - struct TALER_MINT_WithdrawSignHandle *wsh = cls; - long response_code; - json_t *json; - - wsh->job = NULL; - json = MAC_download_get_result (&wsh->db, - eh, - &response_code); - switch (response_code) - { - case 0: - break; - case MHD_HTTP_OK: - if (GNUNET_OK != - withdraw_sign_ok (wsh, - json)) - { - GNUNET_break_op (0); - response_code = 0; - } - break; - case MHD_HTTP_BAD_REQUEST: - /* This should never happen, either us or the mint is buggy - (or API version conflict); just pass JSON reply to the application */ - break; - case MHD_HTTP_PAYMENT_REQUIRED: - /* The mint says that the reserve has insufficient funds; - check the signatures in the history... */ - if (GNUNET_OK != - withdraw_sign_payment_required (wsh, - json)) - { - GNUNET_break_op (0); - response_code = 0; - } - break; - case MHD_HTTP_UNAUTHORIZED: - GNUNET_break (0); - /* Nothing really to verify, mint says one of the signatures is - invalid; as we checked them, this should never happen, we - should pass the JSON reply to the application */ - break; - case MHD_HTTP_NOT_FOUND: - /* Nothing really to verify, the mint basically just says - that it doesn't know this reserve. Can happen if we - query before the wire transfer went through. - We should simply pass the JSON reply to the application. */ - break; - case MHD_HTTP_INTERNAL_SERVER_ERROR: - /* Server had an internal issue; we should retry, but this API - leaves this to the application */ - break; - default: - /* unexpected response code */ - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unexpected response code %u\n", - response_code); - GNUNET_break (0); - response_code = 0; - break; - } - if (NULL != wsh->cb) - wsh->cb (wsh->cb_cls, - response_code, - NULL, - json); - json_decref (json); - TALER_MINT_withdraw_sign_cancel (wsh); -} - - -/** - * Withdraw a coin from the mint using a /withdraw/sign request. Note - * that to ensure that no money is lost in case of hardware failures, - * the caller must have committed (most of) the arguments to disk - * before calling, and be ready to repeat the request with the same - * arguments in case of failures. - * - * @param mint the mint handle; the mint must be ready to operate - * @param pk kind of coin to create - * @param reserve_priv private key of the reserve to withdraw from - * @param coin_priv where to store the coin's private key, - * caller must have committed this value to disk before the call (with @a pk) - * @param blinding_key where to store the coin's blinding key - * caller must have committed this value to disk before the call (with @a pk) - * @param res_cb the callback to call when the final result for this request is available - * @param res_cb_cls closure for the above callback - * @return #GNUNET_OK on success, #GNUNET_SYSERR - * if the inputs are invalid (i.e. denomination key not with this mint). - * In this case, the callback is not called. - */ -struct TALER_MINT_WithdrawSignHandle * -TALER_MINT_withdraw_sign (struct TALER_MINT_Handle *mint, - const struct TALER_MINT_DenomPublicKey *pk, - const struct TALER_ReservePrivateKeyP *reserve_priv, - const struct TALER_CoinSpendPrivateKeyP *coin_priv, - const struct TALER_DenominationBlindingKey *blinding_key, - TALER_MINT_WithdrawSignResultCallback res_cb, - void *res_cb_cls) -{ - struct TALER_MINT_WithdrawSignHandle *wsh; - struct TALER_WithdrawRequestPS req; - struct TALER_ReserveSignatureP reserve_sig; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct TALER_MINT_Context *ctx; - struct TALER_Amount amount_with_fee; - char *coin_ev; - size_t coin_ev_size; - json_t *withdraw_obj; - CURL *eh; - - wsh = GNUNET_new (struct TALER_MINT_WithdrawSignHandle); - wsh->mint = mint; - wsh->cb = res_cb; - wsh->cb_cls = res_cb_cls; - wsh->pk = pk; - - GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub, - sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), - &wsh->c_hash); - coin_ev_size = GNUNET_CRYPTO_rsa_blind (&wsh->c_hash, - blinding_key->rsa_blinding_key, - pk->key.rsa_public_key, - &coin_ev); - GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv, - &wsh->reserve_pub.eddsa_pub); - req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); - req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - req.reserve_pub = wsh->reserve_pub; - if (GNUNET_OK != - TALER_amount_add (&amount_with_fee, - &pk->fee_withdraw, - &pk->value)) - { - /* mint gave us denomination keys that overflow like this!? */ - GNUNET_break_op (0); - GNUNET_free (coin_ev); - GNUNET_free (wsh); - return NULL; - } - TALER_amount_hton (&req.amount_with_fee, - &amount_with_fee); - TALER_amount_hton (&req.withdraw_fee, - &pk->fee_withdraw); - GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key, - &req.h_denomination_pub); - GNUNET_CRYPTO_hash (coin_ev, - coin_ev_size, - &req.h_coin_envelope); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv, - &req.purpose, - &reserve_sig.eddsa_signature)); - withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub and coin_ev */ - " s:o, s:o}",/* reserve_pub and reserve_sig */ - "denom_pub", TALER_json_from_rsa_public_key (pk->key.rsa_public_key), - "coin_ev", TALER_json_from_data (coin_ev, - coin_ev_size), - "reserve_pub", TALER_json_from_data (&wsh->reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)), - "reserve_sig", TALER_json_from_data (&reserve_sig, - sizeof (reserve_sig))); - GNUNET_free (coin_ev); - - wsh->blinding_key = blinding_key; - wsh->url = MAH_path_to_url (mint, "/withdraw/sign"); - - eh = curl_easy_init (); - GNUNET_assert (NULL != (wsh->json_enc = - json_dumps (withdraw_obj, - JSON_COMPACT))); - json_decref (withdraw_obj); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_URL, - wsh->url)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDS, - wsh->json_enc)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDSIZE, - strlen (wsh->json_enc))); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEFUNCTION, - &MAC_download_cb)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_WRITEDATA, - &wsh->db)); - ctx = MAH_handle_to_context (mint); - wsh->job = MAC_job_add (ctx, - eh, - GNUNET_YES, - &handle_withdraw_sign_finished, - wsh); - return wsh; -} - - -/** - * Cancel a withdraw status request. This function cannot be used - * on a request handle if a response is already served for it. - * - * @param sign the withdraw sign request handle - */ -void -TALER_MINT_withdraw_sign_cancel (struct TALER_MINT_WithdrawSignHandle *sign) -{ - if (NULL != sign->job) - { - MAC_job_cancel (sign->job); - sign->job = NULL; - } - GNUNET_free_non_null (sign->db.buf); - GNUNET_free (sign->url); - GNUNET_free (sign->json_enc); - GNUNET_free (sign); -} - - -/* end of mint_api_withdraw.c */ diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c index 96a15207..b2f833d0 100644 --- a/src/mint-lib/test_mint_api.c +++ b/src/mint-lib/test_mint_api.c @@ -131,7 +131,7 @@ struct MeltDetails const char *amount; /** - * Reference to withdraw_sign operations for coin to + * Reference to reserve_withdraw operations for coin to * be used for the /refresh/melt operation. */ const char *coin_ref; @@ -243,14 +243,14 @@ struct Command /** * Set to the API's handle during the operation. */ - struct TALER_MINT_WithdrawStatusHandle *wsh; + struct TALER_MINT_ReserveStatusHandle *wsh; /** * Expected reserve balance. */ const char *expected_balance; - } withdraw_status; + } reserve_status; /** * Information for a #OC_WITHDRAW_SIGN command. @@ -296,9 +296,9 @@ struct Command /** * Withdraw handle (while operation is running). */ - struct TALER_MINT_WithdrawSignHandle *wsh; + struct TALER_MINT_ReserveWithdrawHandle *wsh; - } withdraw_sign; + } reserve_withdraw; /** * Information for a #OC_DEPOSIT command. @@ -312,7 +312,7 @@ struct Command const char *amount; /** - * Reference to a withdraw_sign operation for a coin to + * Reference to a reserve_withdraw operation for a coin to * be used for the /deposit operation. */ const char *coin_ref; @@ -649,8 +649,8 @@ compare_admin_add_incoming_history (const struct TALER_MINT_ReserveHistory *h, * @return #GNUNET_OK if they match, #GNUNET_SYSERR if not */ static int -compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, - const struct Command *cmd) +compare_reserve_withdraw_history (const struct TALER_MINT_ReserveHistory *h, + const struct Command *cmd) { struct TALER_Amount amount; struct TALER_Amount amount_with_fee; @@ -661,12 +661,12 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, return GNUNET_SYSERR; } GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.withdraw_sign.amount, + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, &amount)); GNUNET_assert (GNUNET_OK == TALER_amount_add (&amount_with_fee, &amount, - &cmd->details.withdraw_sign.pk->fee_withdraw)); + &cmd->details.reserve_withdraw.pk->fee_withdraw)); if (0 != TALER_amount_cmp (&amount_with_fee, &h->amount)) { @@ -678,7 +678,7 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, /** - * Function called with the result of a /withdraw/status request. + * Function called with the result of a /reserve/status request. * * @param cls closure with the interpreter state * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -689,12 +689,12 @@ compare_withdraw_sign_history (const struct TALER_MINT_ReserveHistory *h, * @param history detailed transaction history, NULL on error */ static void -withdraw_status_cb (void *cls, - unsigned int http_status, - json_t *json, - const struct TALER_Amount *balance, - unsigned int history_length, - const struct TALER_MINT_ReserveHistory *history) +reserve_status_cb (void *cls, + unsigned int http_status, + json_t *json, + const struct TALER_Amount *balance, + unsigned int history_length, + const struct TALER_MINT_ReserveHistory *history) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; @@ -703,7 +703,7 @@ withdraw_status_cb (void *cls, unsigned int j; struct TALER_Amount amount; - cmd->details.withdraw_status.wsh = NULL; + cmd->details.reserve_status.wsh = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -727,10 +727,10 @@ withdraw_status_cb (void *cls, { case OC_ADMIN_ADD_INCOMING: if ( ( (NULL != rel->label) && - (0 == strcmp (cmd->details.withdraw_status.reserve_reference, + (0 == strcmp (cmd->details.reserve_status.reserve_reference, rel->label) ) ) || ( (NULL != rel->details.admin_add_incoming.reserve_reference) && - (0 == strcmp (cmd->details.withdraw_status.reserve_reference, + (0 == strcmp (cmd->details.reserve_status.reserve_reference, rel->details.admin_add_incoming.reserve_reference) ) ) ) { if (GNUNET_OK != @@ -745,11 +745,11 @@ withdraw_status_cb (void *cls, } break; case OC_WITHDRAW_SIGN: - if (0 == strcmp (cmd->details.withdraw_status.reserve_reference, - rel->details.withdraw_sign.reserve_reference)) + if (0 == strcmp (cmd->details.reserve_status.reserve_reference, + rel->details.reserve_withdraw.reserve_reference)) { if (GNUNET_OK != - compare_withdraw_sign_history (&history[j], + compare_reserve_withdraw_history (&history[j], rel)) { GNUNET_break (0); @@ -770,10 +770,10 @@ withdraw_status_cb (void *cls, fail (is); return; } - if (NULL != cmd->details.withdraw_status.expected_balance) + if (NULL != cmd->details.reserve_status.expected_balance) { GNUNET_assert (GNUNET_OK == - TALER_string_to_amount (cmd->details.withdraw_status.expected_balance, + TALER_string_to_amount (cmd->details.reserve_status.expected_balance, &amount)); if (0 != TALER_amount_cmp (&amount, balance)) @@ -796,7 +796,7 @@ withdraw_status_cb (void *cls, /** - * Function called upon completion of our /withdraw/sign request. + * Function called upon completion of our /reserve/withdraw request. * * @param cls closure with the interpreter state * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request @@ -805,15 +805,15 @@ withdraw_status_cb (void *cls, * @param full_response full response from the mint (for logging, in case of errors) */ static void -withdraw_sign_cb (void *cls, - unsigned int http_status, - const struct TALER_DenominationSignature *sig, - json_t *full_response) +reserve_withdraw_cb (void *cls, + unsigned int http_status, + const struct TALER_DenominationSignature *sig, + json_t *full_response) { struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; - cmd->details.withdraw_sign.wsh = NULL; + cmd->details.reserve_withdraw.wsh = NULL; if (cmd->expected_response_code != http_status) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -834,7 +834,7 @@ withdraw_sign_cb (void *cls, fail (is); return; } - cmd->details.withdraw_sign.sig.rsa_signature + cmd->details.reserve_withdraw.sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (sig->rsa_signature); break; case MHD_HTTP_PAYMENT_REQUIRED: @@ -1309,44 +1309,44 @@ interpreter_run (void *cls, return; case OC_WITHDRAW_STATUS: GNUNET_assert (NULL != - cmd->details.withdraw_status.reserve_reference); + cmd->details.reserve_status.reserve_reference); ref = find_command (is, - cmd->details.withdraw_status.reserve_reference); + cmd->details.reserve_status.reserve_reference); GNUNET_assert (NULL != ref); GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); GNUNET_CRYPTO_eddsa_key_get_public (&ref->details.admin_add_incoming.reserve_priv.eddsa_priv, &reserve_pub.eddsa_pub); - cmd->details.withdraw_status.wsh - = TALER_MINT_withdraw_status (mint, - &reserve_pub, - &withdraw_status_cb, - is); + cmd->details.reserve_status.wsh + = TALER_MINT_reserve_status (mint, + &reserve_pub, + &reserve_status_cb, + is); trigger_context_task (); return; case OC_WITHDRAW_SIGN: GNUNET_assert (NULL != - cmd->details.withdraw_sign.reserve_reference); + cmd->details.reserve_withdraw.reserve_reference); ref = find_command (is, - cmd->details.withdraw_sign.reserve_reference); + cmd->details.reserve_withdraw.reserve_reference); GNUNET_assert (NULL != ref); GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc); - if (NULL != cmd->details.withdraw_sign.amount) + if (NULL != cmd->details.reserve_withdraw.amount) { if (GNUNET_OK != - TALER_string_to_amount (cmd->details.withdraw_sign.amount, + TALER_string_to_amount (cmd->details.reserve_withdraw.amount, &amount)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to parse amount `%s' at %u\n", - cmd->details.withdraw_sign.amount, + cmd->details.reserve_withdraw.amount, is->ip); fail (is); return; } - cmd->details.withdraw_sign.pk = find_pk (is->keys, - &amount); + cmd->details.reserve_withdraw.pk = find_pk (is->keys, + &amount); } - if (NULL == cmd->details.withdraw_sign.pk) + if (NULL == cmd->details.reserve_withdraw.pk) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to determine denomination key at %u\n", @@ -1360,22 +1360,22 @@ interpreter_run (void *cls, struct GNUNET_CRYPTO_EddsaPrivateKey *priv; priv = GNUNET_CRYPTO_eddsa_key_create (); - cmd->details.withdraw_sign.coin_priv.eddsa_priv = *priv; + cmd->details.reserve_withdraw.coin_priv.eddsa_priv = *priv; GNUNET_free (priv); } - GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.withdraw_sign.coin_priv.eddsa_priv, + GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.reserve_withdraw.coin_priv.eddsa_priv, &coin_pub.eddsa_pub); - cmd->details.withdraw_sign.blinding_key.rsa_blinding_key - = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.withdraw_sign.pk->key.rsa_public_key)); - cmd->details.withdraw_sign.wsh - = TALER_MINT_withdraw_sign (mint, - cmd->details.withdraw_sign.pk, - &ref->details.admin_add_incoming.reserve_priv, - &cmd->details.withdraw_sign.coin_priv, - &cmd->details.withdraw_sign.blinding_key, - &withdraw_sign_cb, - is); - if (NULL == cmd->details.withdraw_sign.wsh) + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key + = GNUNET_CRYPTO_rsa_blinding_key_create (GNUNET_CRYPTO_rsa_public_key_len (cmd->details.reserve_withdraw.pk->key.rsa_public_key)); + cmd->details.reserve_withdraw.wsh + = TALER_MINT_reserve_withdraw (mint, + cmd->details.reserve_withdraw.pk, + &ref->details.admin_add_incoming.reserve_priv, + &cmd->details.reserve_withdraw.coin_priv, + &cmd->details.reserve_withdraw.blinding_key, + &reserve_withdraw_cb, + is); + if (NULL == cmd->details.reserve_withdraw.wsh) { GNUNET_break (0); fail (is); @@ -1404,9 +1404,9 @@ interpreter_run (void *cls, switch (ref->oc) { case OC_WITHDRAW_SIGN: - coin_priv = &ref->details.withdraw_sign.coin_priv; - coin_pk = ref->details.withdraw_sign.pk; - coin_pk_sig = &ref->details.withdraw_sign.sig; + coin_priv = &ref->details.reserve_withdraw.coin_priv; + coin_pk = ref->details.reserve_withdraw.pk; + coin_pk_sig = &ref->details.reserve_withdraw.sig; break; case OC_REFRESH_REVEAL: { @@ -1549,7 +1549,7 @@ interpreter_run (void *cls, GNUNET_assert (NULL != ref); GNUNET_assert (OC_WITHDRAW_SIGN == ref->oc); - melt_privs[i] = ref->details.withdraw_sign.coin_priv; + melt_privs[i] = ref->details.reserve_withdraw.coin_priv; if (GNUNET_OK != TALER_string_to_amount (md->amount, &melt_amounts[i])) @@ -1561,8 +1561,8 @@ interpreter_run (void *cls, fail (is); return; } - melt_sigs[i] = ref->details.withdraw_sign.sig; - melt_pks[i] = *ref->details.withdraw_sign.pk; + melt_sigs[i] = ref->details.reserve_withdraw.sig; + melt_pks[i] = *ref->details.reserve_withdraw.pk; } for (i=0;idetails.withdraw_sign.amount, + cmd->details.reserve_withdraw.amount, is->ip); fail (is); return; @@ -1639,7 +1639,7 @@ interpreter_run (void *cls, /* find melt command */ ref = find_command (is, ref->details.refresh_reveal.melt_ref); - /* find withdraw_sign command */ + /* find reserve_withdraw command */ { unsigned int idx; const struct MeltDetails *md; @@ -1658,7 +1658,7 @@ interpreter_run (void *cls, /* finally, use private key from withdraw sign command */ cmd->details.refresh_link.rlh = TALER_MINT_refresh_link (mint, - &ref->details.withdraw_sign.coin_priv, + &ref->details.reserve_withdraw.coin_priv, &link_cb, is); if (NULL == cmd->details.refresh_link.rlh) @@ -1724,35 +1724,35 @@ do_shutdown (void *cls, } break; case OC_WITHDRAW_STATUS: - if (NULL != cmd->details.withdraw_status.wsh) + if (NULL != cmd->details.reserve_status.wsh) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Command %u (%s) did not complete\n", i, cmd->label); - TALER_MINT_withdraw_status_cancel (cmd->details.withdraw_status.wsh); - cmd->details.withdraw_status.wsh = NULL; + TALER_MINT_reserve_status_cancel (cmd->details.reserve_status.wsh); + cmd->details.reserve_status.wsh = NULL; } break; case OC_WITHDRAW_SIGN: - if (NULL != cmd->details.withdraw_sign.wsh) + if (NULL != cmd->details.reserve_withdraw.wsh) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Command %u (%s) did not complete\n", i, cmd->label); - TALER_MINT_withdraw_sign_cancel (cmd->details.withdraw_sign.wsh); - cmd->details.withdraw_sign.wsh = NULL; + TALER_MINT_reserve_withdraw_cancel (cmd->details.reserve_withdraw.wsh); + cmd->details.reserve_withdraw.wsh = NULL; } - if (NULL != cmd->details.withdraw_sign.sig.rsa_signature) + if (NULL != cmd->details.reserve_withdraw.sig.rsa_signature) { - GNUNET_CRYPTO_rsa_signature_free (cmd->details.withdraw_sign.sig.rsa_signature); - cmd->details.withdraw_sign.sig.rsa_signature = NULL; + GNUNET_CRYPTO_rsa_signature_free (cmd->details.reserve_withdraw.sig.rsa_signature); + cmd->details.reserve_withdraw.sig.rsa_signature = NULL; } - if (NULL != cmd->details.withdraw_sign.blinding_key.rsa_blinding_key) + if (NULL != cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key) { - GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.withdraw_sign.blinding_key.rsa_blinding_key); - cmd->details.withdraw_sign.blinding_key.rsa_blinding_key = NULL; + GNUNET_CRYPTO_rsa_blinding_key_free (cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key); + cmd->details.reserve_withdraw.blinding_key.rsa_blinding_key = NULL; } break; case OC_DEPOSIT: @@ -2018,15 +2018,15 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "withdraw-coin-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_sign.reserve_reference = "create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Check that deposit and withdraw operation are in history, and that the balance is now at zero */ { .oc = OC_WITHDRAW_STATUS, .label = "withdraw-status-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_status.reserve_reference = "create-reserve-1", - .details.withdraw_status.expected_balance = "EUR:0" }, + .details.reserve_status.reserve_reference = "create-reserve-1", + .details.reserve_status.expected_balance = "EUR:0" }, /* Try to deposit the 5 EUR coin (in full) */ { .oc = OC_DEPOSIT, .label = "deposit-simple", @@ -2041,8 +2041,8 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "withdraw-coin-2", .expected_response_code = MHD_HTTP_PAYMENT_REQUIRED, - .details.withdraw_sign.reserve_reference = "create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Try to double-spend the 5 EUR coin with different wire details */ { .oc = OC_DEPOSIT, @@ -2086,8 +2086,8 @@ run (void *cls, { .oc = OC_WITHDRAW_SIGN, .label = "refresh-withdraw-coin-1", .expected_response_code = MHD_HTTP_OK, - .details.withdraw_sign.reserve_reference = "refresh-create-reserve-1", - .details.withdraw_sign.amount = "EUR:5" }, + .details.reserve_withdraw.reserve_reference = "refresh-create-reserve-1", + .details.reserve_withdraw.amount = "EUR:5" }, /* Try to partially spend (deposit) 1 EUR of the 5 EUR coin (in full) (merchant would receive EUR:0.99 due to 1 ct deposit fee) */ { .oc = OC_DEPOSIT, diff --git a/src/mint/Makefile.am b/src/mint/Makefile.am index 7d75f9b6..e5d0c656 100644 --- a/src/mint/Makefile.am +++ b/src/mint/Makefile.am @@ -18,7 +18,7 @@ taler_mint_httpd_SOURCES = \ taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \ taler-mint-httpd_admin.c taler-mint-httpd_admin.h \ taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \ - taler-mint-httpd_withdraw.c taler-mint-httpd_withdraw.h \ + taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \ taler-mint-httpd_wire.c taler-mint-httpd_wire.h \ taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h taler_mint_httpd_LDADD = \ diff --git a/src/mint/taler-mint-httpd.c b/src/mint/taler-mint-httpd.c index e68cd242..cd2f6276 100644 --- a/src/mint/taler-mint-httpd.c +++ b/src/mint/taler-mint-httpd.c @@ -30,7 +30,7 @@ #include "taler-mint-httpd_mhd.h" #include "taler-mint-httpd_admin.h" #include "taler-mint-httpd_deposit.h" -#include "taler-mint-httpd_withdraw.h" +#include "taler-mint-httpd_reserve.h" #include "taler-mint-httpd_wire.h" #include "taler-mint-httpd_refresh.h" #include "taler-mint-httpd_keystate.h" @@ -190,17 +190,17 @@ handle_mhd_request (void *cls, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, /* Withdrawing coins / interaction with reserves */ - { "/withdraw/status", MHD_HTTP_METHOD_GET, "application/json", + { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json", NULL, 0, - &TMH_WITHDRAW_handler_withdraw_status, MHD_HTTP_OK }, - { "/withdraw/status", NULL, "text/plain", + &TMH_RESERVE_handler_reserve_status, MHD_HTTP_OK }, + { "/reserve/status", NULL, "text/plain", "Only GET is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, - { "/withdraw/sign", MHD_HTTP_METHOD_POST, "application/json", + { "/reserve/withdraw", MHD_HTTP_METHOD_POST, "application/json", NULL, 0, - &TMH_WITHDRAW_handler_withdraw_sign, MHD_HTTP_OK }, - { "/withdraw/sign", NULL, "text/plain", + &TMH_RESERVE_handler_reserve_withdraw, MHD_HTTP_OK }, + { "/reserve/withdraw", NULL, "text/plain", "Only POST is allowed", 0, &TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED }, diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 27031da2..021a3155 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -203,7 +203,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, /** - * Execute a /withdraw/status. Given the public key of a reserve, + * Execute a /reserve/status. Given the public key of a reserve, * return the associated transaction history. * * @param connection the MHD connection to handle @@ -211,8 +211,8 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub) +TMH_DB_execute_reserve_status (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve_pub) { struct TALER_MINTDB_Session *session; struct TALER_MINTDB_ReserveHistory *rh; @@ -233,7 +233,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, "{s:s, s:s}", "error", "Reserve not found", "parameter", "withdraw_pub"); - res = TMH_RESPONSE_reply_withdraw_status_success (connection, + res = TMH_RESPONSE_reply_reserve_status_success (connection, rh); TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); @@ -242,7 +242,7 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, /** - * Execute a "/withdraw/sign". Given a reserve and a properly signed + * Execute a "/reserve/withdraw". Given a reserve and a properly signed * request to withdraw a coin, check the balance of the reserve and * if it is sufficient, store the request and return the signed * blinded envelope. @@ -256,12 +256,12 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve, - const struct TALER_DenominationPublicKey *denomination_pub, - const char *blinded_msg, - size_t blinded_msg_len, - const struct TALER_ReserveSignatureP *signature) +TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature) { struct TALER_MINTDB_Session *session; struct TALER_MINTDB_ReserveHistory *rh; @@ -303,7 +303,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, /* Don't sign again if we have already signed the coin */ if (GNUNET_YES == res) { - res = TMH_RESPONSE_reply_withdraw_sign_success (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, &collectable); GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature); GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key); @@ -431,7 +431,7 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, TMH_KS_release (key_state); TMH_plugin->rollback (TMH_plugin->cls, session); - res = TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection, rh); TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); @@ -475,10 +475,10 @@ TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, TMH_plugin->commit (TMH_plugin->cls, session)) { - TALER_LOG_WARNING ("/withdraw/sign transaction commit failed\n"); + TALER_LOG_WARNING ("/reserve/withdraw transaction commit failed\n"); return TMH_RESPONSE_reply_commit_error (connection); } - res = TMH_RESPONSE_reply_withdraw_sign_success (connection, + res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, &collectable); GNUNET_CRYPTO_rsa_signature_free (sig); return res; diff --git a/src/mint/taler-mint-httpd_db.h b/src/mint/taler-mint-httpd_db.h index 59976217..f1d9fbfb 100644 --- a/src/mint/taler-mint-httpd_db.h +++ b/src/mint/taler-mint-httpd_db.h @@ -41,7 +41,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, /** - * Execute a "/withdraw/status". Given the public key of a reserve, + * Execute a "/reserve/status". Given the public key of a reserve, * return the associated transaction history. * * @param connection the MHD connection to handle @@ -49,12 +49,12 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub); +TMH_DB_execute_reserve_status (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve_pub); /** - * Execute a "/withdraw/sign". Given a reserve and a properly signed + * Execute a "/reserve/withdraw". Given a reserve and a properly signed * request to withdraw a coin, check the balance of the reserve and * if it is sufficient, store the request and return the signed * blinded envelope. @@ -68,12 +68,12 @@ TMH_DB_execute_withdraw_status (struct MHD_Connection *connection, * @return MHD result code */ int -TMH_DB_execute_withdraw_sign (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve, - const struct TALER_DenominationPublicKey *denomination_pub, - const char *blinded_msg, - size_t blinded_msg_len, - const struct TALER_ReserveSignatureP *signature); +TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature); /** diff --git a/src/mint/taler-mint-httpd_keystate.h b/src/mint/taler-mint-httpd_keystate.h index 62b041e9..9529de8f 100644 --- a/src/mint/taler-mint-httpd_keystate.h +++ b/src/mint/taler-mint-httpd_keystate.h @@ -65,7 +65,7 @@ TMH_KS_release (struct TMH_KS_StateHandle *key_state); enum TMH_KS_DenominationKeyUse { /** - * The key is to be used for a /withdraw/sign or /refresh (mint) + * The key is to be used for a /reserve/withdraw or /refresh (mint) * operation. */ TMH_KS_DKU_WITHDRAW, diff --git a/src/mint/taler-mint-httpd_reserve.c b/src/mint/taler-mint-httpd_reserve.c new file mode 100644 index 00000000..44527809 --- /dev/null +++ b/src/mint/taler-mint-httpd_reserve.c @@ -0,0 +1,185 @@ +/* + This file is part of TALER + Copyright (C) 2014,2015 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_reserve.c + * @brief Handle /reserve/ requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler-mint-httpd_reserve.h" +#include "taler-mint-httpd_parsing.h" +#include "taler-mint-httpd_responses.h" +#include "taler-mint-httpd_keystate.h" + + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_status (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + struct TALER_ReservePublicKeyP reserve_pub; + int res; + + res = TMH_PARSE_mhd_request_arg_data (connection, + "reserve_pub", + &reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* parse error */ + return TMH_DB_execute_reserve_status (connection, + &reserve_pub); +} + + +/** + * Handle a "/reserve/withdraw" request. Parses the "reserve_pub" + * EdDSA key of the reserve and the requested "denom_pub" which + * specifies the key/value of the coin to be withdrawn, and checks + * that the signature "reserve_sig" makes this a valid withdrawl + * request from the specified reserve. If so, the envelope + * with the blinded coin "coin_ev" is passed down to execute the + * withdrawl operation. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + json_t *root; + struct TALER_WithdrawRequestPS wsrd; + int res; + struct TALER_DenominationPublicKey denomination_pub; + char *blinded_msg; + size_t blinded_msg_len; + struct TALER_Amount amount; + struct TALER_Amount amount_with_fee; + struct TALER_Amount fee_withdraw; + struct TALER_ReserveSignatureP signature; + struct TALER_MINTDB_DenominationKeyIssueInformation *dki; + struct TMH_KS_StateHandle *ks; + + struct TMH_PARSE_FieldSpecification spec[] = { + TMH_PARSE_member_variable ("coin_ev", + (void **) &blinded_msg, + &blinded_msg_len), + TMH_PARSE_member_fixed ("reserve_pub", + &wsrd.reserve_pub), + TMH_PARSE_member_fixed ("reserve_sig", + &signature), + TMH_PARSE_member_denomination_public_key ("denom_pub", + &denomination_pub), + TMH_PARSE_MEMBER_END + }; + + res = TMH_PARSE_post_json (connection, + connection_cls, + upload_data, + upload_data_size, + &root); + if (GNUNET_SYSERR == res) + return MHD_NO; + if ( (GNUNET_NO == res) || (NULL == root) ) + return MHD_YES; + res = TMH_PARSE_json_data (connection, + root, + spec); + json_decref (root); + if (GNUNET_OK != res) + return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + ks = TMH_KS_acquire (); + dki = TMH_KS_denomination_key_lookup (ks, + &denomination_pub, + TMH_KS_DKU_WITHDRAW); + if (NULL == dki) + { + TMH_PARSE_release_data (spec); + return TMH_RESPONSE_reply_arg_unknown (connection, + "denom_pub"); + } + TALER_amount_ntoh (&amount, + &dki->issue.properties.value); + TALER_amount_ntoh (&fee_withdraw, + &dki->issue.properties.fee_withdraw); + GNUNET_assert (GNUNET_OK == + TALER_amount_add (&amount_with_fee, + &amount, + &fee_withdraw)); + TALER_amount_hton (&wsrd.amount_with_fee, + &amount_with_fee); + TALER_amount_hton (&wsrd.withdraw_fee, + &fee_withdraw); + TMH_KS_release (ks); + /* verify signature! */ + wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); + wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); + + GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key, + &wsrd.h_denomination_pub); + GNUNET_CRYPTO_hash (blinded_msg, + blinded_msg_len, + &wsrd.h_coin_envelope); + if (GNUNET_OK != + GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, + &wsrd.purpose, + &signature.eddsa_signature, + &wsrd.reserve_pub.eddsa_pub)) + { + TALER_LOG_WARNING ("Client supplied invalid signature for /reserve/withdraw request\n"); + TMH_PARSE_release_data (spec); + return TMH_RESPONSE_reply_signature_invalid (connection, + "reserve_sig"); + } + res = TMH_DB_execute_reserve_withdraw (connection, + &wsrd.reserve_pub, + &denomination_pub, + blinded_msg, + blinded_msg_len, + &signature); + TMH_PARSE_release_data (spec); + return res; +} + +/* end of taler-mint-httpd_reserve.c */ diff --git a/src/mint/taler-mint-httpd_reserve.h b/src/mint/taler-mint-httpd_reserve.h new file mode 100644 index 00000000..71a779fe --- /dev/null +++ b/src/mint/taler-mint-httpd_reserve.h @@ -0,0 +1,73 @@ +/* + This file is part of TALER + Copyright (C) 2014 GNUnet e.V. + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, If not, see +*/ +/** + * @file taler-mint-httpd_reserve.h + * @brief Handle /reserve/ requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_MINT_HTTPD_RESERVE_H +#define TALER_MINT_HTTPD_RESERVE_H + +#include +#include "taler-mint-httpd.h" + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_status (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + + +/** + * Handle a "/reserve/withdraw" request. Parses the "reserve_pub" + * EdDSA key of the reserve and the requested "denom_pub" which + * specifies the key/value of the coin to be withdrawn, and checks + * that the signature "reserve_sig" makes this a valid withdrawl + * request from the specified reserve. If so, the envelope + * with the blinded coin "coin_ev" is passed down to execute the + * withdrawl operation. + * + * @param rh context of the handler + * @param connection the MHD connection to handle + * @param[in,out] connection_cls the connection's closure (can be updated) + * @param upload_data upload data + * @param[in,out] upload_data_size number of bytes (left) in @a upload_data + * @return MHD result code + */ +int +TMH_RESERVE_handler_reserve_withdraw (struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +#endif diff --git a/src/mint/taler-mint-httpd_responses.c b/src/mint/taler-mint-httpd_responses.c index ce04fa41..367b1904 100644 --- a/src/mint/taler-mint-httpd_responses.c +++ b/src/mint/taler-mint-httpd_responses.c @@ -630,8 +630,8 @@ compile_reserve_history (const struct TALER_MINTDB_ReserveHistory *rh, * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh) +TMH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh) { json_t *json_balance; json_t *json_history; @@ -654,15 +654,15 @@ TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, /** * Send reserve status information to client with the * message that we have insufficient funds for the - * requested /withdraw/sign operation. + * requested /reserve/withdraw operation. * * @param connection connection to the client * @param rh reserve history to return * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh) +TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh) { json_t *json_balance; json_t *json_history; @@ -691,7 +691,7 @@ TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *conn * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection, +TMH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection, const struct TALER_MINTDB_CollectableBlindcoin *collectable) { json_t *sig_json; diff --git a/src/mint/taler-mint-httpd_responses.h b/src/mint/taler-mint-httpd_responses.h index a3535638..deb7dd63 100644 --- a/src/mint/taler-mint-httpd_responses.h +++ b/src/mint/taler-mint-httpd_responses.h @@ -245,22 +245,22 @@ TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_status_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh); +TMH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh); /** * Send reserve status information to client with the * message that we have insufficient funds for the - * requested /withdraw/sign operation. + * requested /reserve/withdraw operation. * * @param connection connection to the client * @param rh reserve history to return * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *connection, - const struct TALER_MINTDB_ReserveHistory *rh); +TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection, + const struct TALER_MINTDB_ReserveHistory *rh); /** @@ -271,8 +271,8 @@ TMH_RESPONSE_reply_withdraw_sign_insufficient_funds (struct MHD_Connection *conn * @return MHD result code */ int -TMH_RESPONSE_reply_withdraw_sign_success (struct MHD_Connection *connection, - const struct TALER_MINTDB_CollectableBlindcoin *collectable); +TMH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection, + const struct TALER_MINTDB_CollectableBlindcoin *collectable); /** diff --git a/src/mint/taler-mint-httpd_withdraw.c b/src/mint/taler-mint-httpd_withdraw.c deleted file mode 100644 index 4f558164..00000000 --- a/src/mint/taler-mint-httpd_withdraw.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014,2015 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-httpd_withdraw.c - * @brief Handle /withdraw/ requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include "taler-mint-httpd_withdraw.h" -#include "taler-mint-httpd_parsing.h" -#include "taler-mint-httpd_responses.h" -#include "taler-mint-httpd_keystate.h" - - -/** - * Handle a "/withdraw/status" request. Parses the - * given "reserve_pub" argument (which should contain the - * EdDSA public key of a reserve) and then respond with the - * status of the reserve. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - struct TALER_ReservePublicKeyP reserve_pub; - int res; - - res = TMH_PARSE_mhd_request_arg_data (connection, - "reserve_pub", - &reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)); - if (GNUNET_SYSERR == res) - return MHD_NO; /* internal error */ - if (GNUNET_NO == res) - return MHD_YES; /* parse error */ - return TMH_DB_execute_withdraw_status (connection, - &reserve_pub); -} - - -/** - * Handle a "/withdraw/sign" request. Parses the "reserve_pub" - * EdDSA key of the reserve and the requested "denom_pub" which - * specifies the key/value of the coin to be withdrawn, and checks - * that the signature "reserve_sig" makes this a valid withdrawl - * request from the specified reserve. If so, the envelope - * with the blinded coin "coin_ev" is passed down to execute the - * withdrawl operation. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - json_t *root; - struct TALER_WithdrawRequestPS wsrd; - int res; - struct TALER_DenominationPublicKey denomination_pub; - char *blinded_msg; - size_t blinded_msg_len; - struct TALER_Amount amount; - struct TALER_Amount amount_with_fee; - struct TALER_Amount fee_withdraw; - struct TALER_ReserveSignatureP signature; - struct TALER_MINTDB_DenominationKeyIssueInformation *dki; - struct TMH_KS_StateHandle *ks; - - struct TMH_PARSE_FieldSpecification spec[] = { - TMH_PARSE_member_variable ("coin_ev", (void **) &blinded_msg, &blinded_msg_len), - TMH_PARSE_member_fixed ("reserve_pub", &wsrd.reserve_pub), - TMH_PARSE_member_fixed ("reserve_sig", &signature), - TMH_PARSE_member_denomination_public_key ("denom_pub", &denomination_pub), - TMH_PARSE_MEMBER_END - }; - - res = TMH_PARSE_post_json (connection, - connection_cls, - upload_data, - upload_data_size, - &root); - if (GNUNET_SYSERR == res) - return MHD_NO; - if ( (GNUNET_NO == res) || (NULL == root) ) - return MHD_YES; - res = TMH_PARSE_json_data (connection, - root, - spec); - json_decref (root); - if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - ks = TMH_KS_acquire (); - dki = TMH_KS_denomination_key_lookup (ks, - &denomination_pub, - TMH_KS_DKU_WITHDRAW); - if (NULL == dki) - { - TMH_PARSE_release_data (spec); - return TMH_RESPONSE_reply_arg_unknown (connection, - "denom_pub"); - } - TALER_amount_ntoh (&amount, - &dki->issue.properties.value); - TALER_amount_ntoh (&fee_withdraw, - &dki->issue.properties.fee_withdraw); - GNUNET_assert (GNUNET_OK == - TALER_amount_add (&amount_with_fee, - &amount, - &fee_withdraw)); - TALER_amount_hton (&wsrd.amount_with_fee, - &amount_with_fee); - TALER_amount_hton (&wsrd.withdraw_fee, - &fee_withdraw); - TMH_KS_release (ks); - /* verify signature! */ - wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS)); - wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW); - - GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key, - &wsrd.h_denomination_pub); - GNUNET_CRYPTO_hash (blinded_msg, - blinded_msg_len, - &wsrd.h_coin_envelope); - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW, - &wsrd.purpose, - &signature.eddsa_signature, - &wsrd.reserve_pub.eddsa_pub)) - { - TALER_LOG_WARNING ("Client supplied invalid signature for /withdraw/sign request\n"); - TMH_PARSE_release_data (spec); - return TMH_RESPONSE_reply_signature_invalid (connection, - "reserve_sig"); - } - res = TMH_DB_execute_withdraw_sign (connection, - &wsrd.reserve_pub, - &denomination_pub, - blinded_msg, - blinded_msg_len, - &signature); - TMH_PARSE_release_data (spec); - return res; -} - -/* end of taler-mint-httpd_withdraw.c */ diff --git a/src/mint/taler-mint-httpd_withdraw.h b/src/mint/taler-mint-httpd_withdraw.h deleted file mode 100644 index 668178b1..00000000 --- a/src/mint/taler-mint-httpd_withdraw.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014 GNUnet e.V. - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, If not, see -*/ -/** - * @file taler-mint-httpd_withdraw.h - * @brief Handle /withdraw/ requests - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_MINT_HTTPD_WITHDRAW_H -#define TALER_MINT_HTTPD_WITHDRAW_H - -#include -#include "taler-mint-httpd.h" - -/** - * Handle a "/withdraw/status" request. Parses the - * given "reserve_pub" argument (which should contain the - * EdDSA public key of a reserve) and then respond with the - * status of the reserve. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_status (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size); - - -/** - * Handle a "/withdraw/sign" request. Parses the "reserve_pub" - * EdDSA key of the reserve and the requested "denom_pub" which - * specifies the key/value of the coin to be withdrawn, and checks - * that the signature "reserve_sig" makes this a valid withdrawl - * request from the specified reserve. If so, the envelope - * with the blinded coin "coin_ev" is passed down to execute the - * withdrawl operation. - * - * @param rh context of the handler - * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @return MHD result code - */ -int -TMH_WITHDRAW_handler_withdraw_sign (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size); - -#endif diff --git a/src/mintdb/perf_taler_mintdb.c b/src/mintdb/perf_taler_mintdb.c index fd96e797..73708a6c 100644 --- a/src/mintdb/perf_taler_mintdb.c +++ b/src/mintdb/perf_taler_mintdb.c @@ -287,28 +287,28 @@ main (int argc, char ** argv) PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of transaction loading"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("27 - start"), - PERF_TALER_MINTDB_INIT_CMD_LOOP ("27 - /withdraw/sign", + PERF_TALER_MINTDB_INIT_CMD_LOOP ("27 - /reserve/withdraw", NB_WITHDRAW_SAVE), PERF_TALER_MINTDB_INIT_CMD_LOAD_ARRAY ("27 - reserve", - "27 - /withdraw/sign", + "27 - /reserve/withdraw", "02 - save reserve"), PERF_TALER_MINTDB_INIT_CMD_LOAD_ARRAY ("27 - dki", - "27 - /withdraw/sign", + "27 - /reserve/withdraw", "01 - save denomination"), PERF_TALER_MINTDB_INIT_CMD_WITHDRAW_SIGN ("", "27 - dki", "27 - reserve"), PERF_TALER_MINTDB_INIT_CMD_END_LOOP ("", - "27 - /withdraw/sign"), + "27 - /reserve/withdraw"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("27 - end"), PERF_TALER_MINTDB_INIT_CMD_GAUGER ("", "27 - start", "27 - end", "POSTGRES", - "Number of /withdraw/sign per second", + "Number of /reserve/withdraw per second", "item/sec", NB_WITHDRAW_SAVE), - PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of /withdraw/sign"), + PERF_TALER_MINTDB_INIT_CMD_DEBUG ("End of /reserve/withdraw"), PERF_TALER_MINTDB_INIT_CMD_GET_TIME ("28 - start"), PERF_TALER_MINTDB_INIT_CMD_LOOP ("28 - /deposit", diff --git a/src/mintdb/perf_taler_mintdb_interpreter.h b/src/mintdb/perf_taler_mintdb_interpreter.h index b9283e84..722d1a07 100644 --- a/src/mintdb/perf_taler_mintdb_interpreter.h +++ b/src/mintdb/perf_taler_mintdb_interpreter.h @@ -363,7 +363,7 @@ /** - * The /withdraw/sign api call + * The /reserve/withdraw api call * * Exposes #PERF_TALER_MINTDB_COIN * diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 86db0a11..0cc76f79 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -593,7 +593,7 @@ postgres_prepare (PGconn *db_conn) 1, NULL); /* Used in #postgres_insert_withdraw_info() to store the signature of a blinded coin with the blinded coin's - details before returning it during /withdraw/sign. We store + details before returning it during /reserve/withdraw. We store the coin's denomination information (public key, signature) and the blinded message as well as the reserve that the coin is being withdrawn from and the signature of the message @@ -616,9 +616,9 @@ postgres_prepare (PGconn *db_conn) "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);", 12, NULL); /* Used in #postgres_get_withdraw_info() to - locate the response for a /withdraw/sign request + locate the response for a /reserve/withdraw request using the hash of the blinded message. Used to - make sure /withdraw/sign requests are idempotent. */ + make sure /reserve/withdraw requests are idempotent. */ PREPARE ("get_withdraw_info", "SELECT" " denom_pub" @@ -636,7 +636,7 @@ postgres_prepare (PGconn *db_conn) " WHERE h_blind_ev=$1", 1, NULL); /* Used during #postgres_get_reserve_history() to - obtain all of the /withdraw/sign operations that + obtain all of the /reserve/withdraw operations that have been performed on a given reserve. (i.e. to demonstrate double-spending) */ PREPARE ("get_reserves_out", @@ -1502,7 +1502,7 @@ postgres_reserves_in_insert (void *cls, /** - * Locate the response for a /withdraw/sign request under the + * Locate the response for a /reserve/withdraw request under the * key of the hash of the blinded message. * * @param cls the `struct PostgresClosure` with the plugin-specific state -- cgit v1.2.3 From 70c28e53d0615bc6694ea2339a739d387f8d8691 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Sep 2015 10:59:48 +0200 Subject: indenting --- src/mintdb/plugin_mintdb_postgres.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 0cc76f79..42aac19f 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -990,7 +990,7 @@ postgres_get_session (void *cls, PQstatus (db_conn)) { TALER_LOG_ERROR ("Database connection failed: %s\n", - PQerrorMessage (db_conn)); + PQerrorMessage (db_conn)); GNUNET_break (0); return NULL; } -- cgit v1.2.3 From 93a84d5e5aee9df869ac9e78a88c294d6311a9ef Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 21 Sep 2015 14:36:18 +0200 Subject: retry transactions on serialization/dead-lock failures (#3990) --- src/include/taler_mintdb_plugin.h | 3 +- src/mint/taler-mint-httpd_db.c | 466 ++++++++++++++++++++-------------- src/mint/test_taler_mint_httpd_afl.sh | 33 +++ src/mintdb/plugin_mintdb_postgres.c | 26 +- 4 files changed, 332 insertions(+), 196 deletions(-) create mode 100755 src/mint/test_taler_mint_httpd_afl.sh (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index 851ed526..c8013acc 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -629,7 +629,8 @@ struct TALER_MINTDB_Plugin * * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use - * @return #GNUNET_OK on success + * @return #GNUNET_OK on success, #GNUNET_NO if the transaction + * can be retried, #GNUNET_SYSERR on hard failures */ int (*commit) (void *cls, diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 021a3155..da656abc 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -24,6 +24,67 @@ #include "taler-mint-httpd_responses.h" #include "taler-mint-httpd_keystate.h" +/** + * How often should we retry a transaction before giving up + * (for transactions resulting in serialization/dead locks only). + */ +#define MAX_TRANSACTION_COMMIT_RETRIES 3 + +/** + * Code to begin a transaction, must be inline as we define a block + * that ends with #COMMIT_TRANSACTION() within which we perform a number + * of retries. Note that this code may call "return" internally, so + * it must be called within a function where any cleanup will be done + * by the caller. Furthermore, the function's return value must + * match that of a #TMH_RESPONSE_reply_internal_db_error() status code. + * + * @param session session handle + * @param connection connection handle + */ +#define START_TRANSACTION(session,connection) \ +{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\ + unsigned int transaction_retries = 0; \ + int transaction_commit_result; \ +transaction_start_label: /* we will use goto for retries */ \ + if (GNUNET_OK != \ + TMH_plugin->start (TMH_plugin->cls, \ + session)) \ + { \ + GNUNET_break (0); \ + return TMH_RESPONSE_reply_internal_db_error (connection); \ + } + +/** + * Code to conclude a transaction, dual to #START_TRANSACTION(). Note + * that this code may call "return" internally, so it must be called + * within a function where any cleanup will be done by the caller. + * Furthermore, the function's return value must match that of a + * #TMH_RESPONSE_reply_internal_db_error() status code. + * + * @param session session handle + * @param connection connection handle + */ +#define COMMIT_TRANSACTION(session,connection) \ + transaction_commit_result = \ + TMH_plugin->commit (TMH_plugin->cls, \ + session); \ + if (GNUNET_SYSERR == transaction_commit_result) \ + { \ + TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \ + return TMH_RESPONSE_reply_commit_error (connection); \ + } \ + if (GNUNET_NO == transaction_commit_result) \ + { \ + TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \ + if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \ + goto transaction_start_label; \ + TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \ + transaction_retries, \ + __FUNCTION__); \ + return TMH_RESPONSE_reply_commit_error (connection); \ + } \ +} /* end of scope opened by BEGIN_TRANSACTION */ + /** * Calculate the total value of all transactions performed. @@ -130,13 +191,8 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, &dki->issue.properties.value); TMH_KS_release (mks); - if (GNUNET_OK != - TMH_plugin->start (TMH_plugin->cls, - session)) - { - GNUNET_break (0); - return TMH_RESPONSE_reply_internal_db_error (connection); - } + START_TRANSACTION (session, connection); + /* fee for THIS transaction */ spent = deposit->amount_with_fee; /* add cost of all previous transactions */ @@ -179,13 +235,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection, return TMH_RESPONSE_reply_internal_db_error (connection); } - if (GNUNET_OK != - TMH_plugin->commit (TMH_plugin->cls, - session)) - { - TALER_LOG_WARNING ("/deposit transaction commit failed\n"); - return TMH_RESPONSE_reply_commit_error (connection); - } + COMMIT_TRANSACTION(session, connection); GNUNET_assert (GNUNET_OK == TALER_amount_subtract (&amount_without_fee, &deposit->amount_with_fee, @@ -242,35 +292,36 @@ TMH_DB_execute_reserve_status (struct MHD_Connection *connection, /** - * Execute a "/reserve/withdraw". Given a reserve and a properly signed - * request to withdraw a coin, check the balance of the reserve and - * if it is sufficient, store the request and return the signed - * blinded envelope. + * Try to execute /reserve/withdraw transaction. * - * @param connection the MHD connection to handle - * @param reserve public key of the reserve + * @param connection request we are handling + * @param session database session we are using + * @param reserve reserve to withdraw from * @param denomination_pub public key of the denomination requested + * @param dki denomination to withdraw * @param blinded_msg blinded message to be signed * @param blinded_msg_len number of bytes in @a blinded_msg * @param signature signature over the withdraw request, to be stored in DB + * @param denom_sig[out] where to write the resulting signature + * (used to release memory in case of transaction failure * @return MHD result code */ -int -TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve, - const struct TALER_DenominationPublicKey *denomination_pub, - const char *blinded_msg, - size_t blinded_msg_len, - const struct TALER_ReserveSignatureP *signature) +static int +execute_reserve_withdraw_transaction (struct MHD_Connection *connection, + struct TALER_MINTDB_Session *session, + struct TMH_KS_StateHandle *key_state, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const struct TALER_MINTDB_DenominationKeyIssueInformation *dki, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature, + struct TALER_DenominationSignature *denom_sig) { - struct TALER_MINTDB_Session *session; struct TALER_MINTDB_ReserveHistory *rh; const struct TALER_MINTDB_ReserveHistory *pos; - struct TMH_KS_StateHandle *key_state; - struct TALER_MINTDB_CollectableBlindcoin collectable; - struct TALER_MINTDB_DenominationKeyIssueInformation *dki; struct TALER_MINTDB_DenominationKeyIssueInformation *tdki; - struct GNUNET_CRYPTO_rsa_Signature *sig; + struct TALER_MINTDB_CollectableBlindcoin collectable; struct TALER_Amount amount_required; struct TALER_Amount deposit_total; struct TALER_Amount withdraw_total; @@ -280,60 +331,8 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, struct GNUNET_HashCode h_blind; int res; - GNUNET_CRYPTO_hash (blinded_msg, - blinded_msg_len, - &h_blind); - - if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, - TMH_test_mode))) - { - GNUNET_break (0); - return TMH_RESPONSE_reply_internal_db_error (connection); - } - res = TMH_plugin->get_withdraw_info (TMH_plugin->cls, - session, - &h_blind, - &collectable); - if (GNUNET_SYSERR == res) - { - GNUNET_break (0); - return TMH_RESPONSE_reply_internal_db_error (connection); - } - - /* Don't sign again if we have already signed the coin */ - if (GNUNET_YES == res) - { - res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, - &collectable); - GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature); - GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key); - return res; - } - GNUNET_assert (GNUNET_NO == res); - /* Check if balance is sufficient */ - key_state = TMH_KS_acquire (); - dki = TMH_KS_denomination_key_lookup (key_state, - denomination_pub, - TMH_KS_DKU_WITHDRAW); - if (NULL == dki) - { - TMH_KS_release (key_state); - return TMH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "Denomination not found"); - } - if (GNUNET_OK != - TMH_plugin->start (TMH_plugin->cls, - session)) - { - GNUNET_break (0); - TMH_KS_release (key_state); - return TMH_RESPONSE_reply_internal_db_error (connection); - } - + START_TRANSACTION (session, connection); rh = TMH_plugin->get_reserve_history (TMH_plugin->cls, session, reserve); @@ -341,7 +340,6 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, { TMH_plugin->rollback (TMH_plugin->cls, session); - TMH_KS_release (key_state); return TMH_RESPONSE_reply_arg_unknown (connection, "reserve_pub"); } @@ -359,7 +357,6 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, { TMH_plugin->rollback (TMH_plugin->cls, session); - TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); } @@ -380,7 +377,6 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, { TMH_plugin->rollback (TMH_plugin->cls, session); - TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); } res |= 1; @@ -401,7 +397,6 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, { TMH_plugin->rollback (TMH_plugin->cls, session); - TMH_KS_release (key_state); return TMH_RESPONSE_reply_internal_db_error (connection); } res |= 2; @@ -428,11 +423,10 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, if (0 < TALER_amount_cmp (&amount_required, &balance)) { - TMH_KS_release (key_state); TMH_plugin->rollback (TMH_plugin->cls, session); res = TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection, - rh); + rh); TMH_plugin->free_reserve_history (TMH_plugin->cls, rh); return res; @@ -441,11 +435,11 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, rh); /* Balance is good, sign the coin! */ - sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key, - blinded_msg, - blinded_msg_len); - TMH_KS_release (key_state); - if (NULL == sig) + denom_sig->rsa_signature + = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key, + blinded_msg, + blinded_msg_len); + if (NULL == denom_sig->rsa_signature) { GNUNET_break (0); TMH_plugin->rollback (TMH_plugin->cls, @@ -453,7 +447,7 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, return TMH_RESPONSE_reply_internal_error (connection, "Internal error"); } - collectable.sig.rsa_signature = sig; + collectable.sig = *denom_sig; collectable.denom_pub = *denomination_pub; collectable.amount_with_fee = amount_required; collectable.withdraw_fee = fee_withdraw; @@ -466,21 +460,105 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, &collectable)) { GNUNET_break (0); - GNUNET_CRYPTO_rsa_signature_free (sig); TMH_plugin->rollback (TMH_plugin->cls, session); return TMH_RESPONSE_reply_internal_db_error (connection); } - if (GNUNET_OK != - TMH_plugin->commit (TMH_plugin->cls, - session)) + COMMIT_TRANSACTION (session, connection); + + return TMH_RESPONSE_reply_reserve_withdraw_success (connection, + &collectable); +} + + + +/** + * Execute a "/reserve/withdraw". Given a reserve and a properly signed + * request to withdraw a coin, check the balance of the reserve and + * if it is sufficient, store the request and return the signed + * blinded envelope. + * + * @param connection the MHD connection to handle + * @param reserve public key of the reserve + * @param denomination_pub public key of the denomination requested + * @param blinded_msg blinded message to be signed + * @param blinded_msg_len number of bytes in @a blinded_msg + * @param signature signature over the withdraw request, to be stored in DB + * @return MHD result code + */ +int +TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection, + const struct TALER_ReservePublicKeyP *reserve, + const struct TALER_DenominationPublicKey *denomination_pub, + const char *blinded_msg, + size_t blinded_msg_len, + const struct TALER_ReserveSignatureP *signature) +{ + struct TALER_MINTDB_Session *session; + struct TMH_KS_StateHandle *key_state; + struct TALER_MINTDB_DenominationKeyIssueInformation *dki; + struct TALER_MINTDB_CollectableBlindcoin collectable; + struct TALER_DenominationSignature denom_sig; + struct GNUNET_HashCode h_blind; + int res; + + GNUNET_CRYPTO_hash (blinded_msg, + blinded_msg_len, + &h_blind); + + if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls, + TMH_test_mode))) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + res = TMH_plugin->get_withdraw_info (TMH_plugin->cls, + session, + &h_blind, + &collectable); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + + /* Don't sign again if we have already signed the coin */ + if (GNUNET_YES == res) { - TALER_LOG_WARNING ("/reserve/withdraw transaction commit failed\n"); - return TMH_RESPONSE_reply_commit_error (connection); + res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, + &collectable); + GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature); + GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key); + return res; } - res = TMH_RESPONSE_reply_reserve_withdraw_success (connection, - &collectable); - GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_assert (GNUNET_NO == res); + + key_state = TMH_KS_acquire (); + dki = TMH_KS_denomination_key_lookup (key_state, + denomination_pub, + TMH_KS_DKU_WITHDRAW); + if (NULL == dki) + { + TMH_KS_release (key_state); + return TMH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s}", + "error", + "Denomination not found"); + } + denom_sig.rsa_signature = NULL; + res = execute_reserve_withdraw_transaction (connection, + session, + key_state, + reserve, + denomination_pub, + dki, + blinded_msg, + blinded_msg_len, + signature, + &denom_sig); + GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature); + TMH_KS_release (key_state); return res; } @@ -633,13 +711,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, GNUNET_break (0); return TMH_RESPONSE_reply_internal_db_error (connection); } - if (GNUNET_OK != - TMH_plugin->start (TMH_plugin->cls, - session)) - { - GNUNET_break (0); - return TMH_RESPONSE_reply_internal_db_error (connection); - } + START_TRANSACTION (session, connection); res = TMH_plugin->get_refresh_session (TMH_plugin->cls, session, session_hash, @@ -741,13 +813,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection, } } - if (GNUNET_OK != - TMH_plugin->commit (TMH_plugin->cls, - session)) - { - TALER_LOG_WARNING ("/refresh/melt transaction commit failed\n"); - return TMH_RESPONSE_reply_commit_error (connection); - } + COMMIT_TRANSACTION (session, connection); return TMH_RESPONSE_reply_refresh_melt_success (connection, session_hash, refresh_session.noreveal_index); @@ -1038,10 +1104,10 @@ refresh_mint_coin (struct MHD_Connection *connection, } if (GNUNET_OK != TMH_plugin->insert_refresh_out (TMH_plugin->cls, - session, - session_hash, - coin_off, - &ev_sig)) + session, + session_hash, + coin_off, + &ev_sig)) { GNUNET_break (0); GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature); @@ -1051,6 +1117,74 @@ refresh_mint_coin (struct MHD_Connection *connection, } +/** + * The client request was well-formed, now execute the DB transaction + * of a "/refresh/reveal" operation. We use the @a ev_sigs and + * @a commit_coins to clean up resources after this function returns + * as we might experience retries of the database transaction. + * + * @param connection the MHD connection to handle + * @param session database session + * @param session_hash hash identifying the refresh session + * @param refresh_session information about the refresh operation we are doing + * @param denom_pubs array of "num_newcoins" denomination keys for the new coins + * @param ev_sigs[out] where to store generated signatures for the new coins, + * array of length "num_newcoins", memory released by the + * caller + * @param commit_coins[out] array of length "num_newcoins" to be used for + * information about the new coins from the commitment. + * @return MHD result code + */ +static int +execute_refresh_reveal_transaction (struct MHD_Connection *connection, + struct TALER_MINTDB_Session *session, + const struct GNUNET_HashCode *session_hash, + struct TALER_MINTDB_RefreshSession *refresh_session, + struct TALER_MINTDB_RefreshMelt *melts, + struct TALER_DenominationPublicKey *denom_pubs, + struct TALER_DenominationSignature *ev_sigs, + struct TALER_MINTDB_RefreshCommitCoin *commit_coins) +{ + unsigned int j; + struct TMH_KS_StateHandle *key_state; + + START_TRANSACTION (session, connection); + if (GNUNET_OK != + TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls, + session, + session_hash, + refresh_session->noreveal_index, + refresh_session->num_newcoins, + commit_coins)) + { + GNUNET_break (0); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + key_state = TMH_KS_acquire (); + for (j=0;jnum_newcoins;j++) + { + if (NULL == ev_sigs[j].rsa_signature) /* could be non-NULL during retries */ + ev_sigs[j] = refresh_mint_coin (connection, + session, + session_hash, + key_state, + &denom_pubs[j], + &commit_coins[j], + j); + if (NULL == ev_sigs[j].rsa_signature) + { + TMH_KS_release (key_state); + return TMH_RESPONSE_reply_internal_db_error (connection); + } + } + TMH_KS_release (key_state); + COMMIT_TRANSACTION (session, connection); + return TMH_RESPONSE_reply_refresh_reveal_success (connection, + refresh_session->num_newcoins, + ev_sigs); +} + + /** * Execute a "/refresh/reveal". The client is revealing to us the * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the @@ -1074,7 +1208,6 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, int res; struct TALER_MINTDB_Session *session; struct TALER_MINTDB_RefreshSession refresh_session; - struct TMH_KS_StateHandle *key_state; struct TALER_MINTDB_RefreshMelt *melts; struct TALER_DenominationPublicKey *denom_pubs; struct TALER_DenominationSignature *ev_sigs; @@ -1164,82 +1297,27 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, GNUNET_free (melts); /* Client request OK, start transaction */ - if (GNUNET_OK != - TMH_plugin->start (TMH_plugin->cls, - session)) - { - GNUNET_break (0); - for (j=0;jget_refresh_commit_coins (TMH_plugin->cls, - session, - session_hash, - refresh_session.noreveal_index, - refresh_session.num_newcoins, - commit_coins)) - { - GNUNET_break (0); - GNUNET_free (commit_coins); - for (j=0;jcommit (TMH_plugin->cls, - session)) - { - TALER_LOG_WARNING ("/refresh/reveal transaction commit failed\n"); - for (i=0;i +# +# +# This script uses 'curl' to POST various ill-formed requests to the +# taler-mint-httpd. Basically, the goal is to make sure that the +# HTTP server survives (and produces the 'correct' error code). +# +# We read the JSON snippets from afl-tests/ +# +# Setup keys. +taler-mint-keyup -d test-mint-home -m test-mint-home/master.priv +# Run test... +for n in afl-tests/*.req +do + echo -n "Test $n" + taler-mint-httpd -d test-mint-home/ -t 1 -f $n -C > /dev/null || { echo "FAIL!"; exit 1; } + echo "OK" +done +exit 0 diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 42aac19f..62110802 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -1095,7 +1095,31 @@ postgres_commit (void *cls, if (PGRES_COMMAND_OK != PQresultStatus (result)) { - GNUNET_break (0); + const char *sqlstate; + + sqlstate = PQresultErrorField (result, + PG_DIAG_SQLSTATE); + if (NULL == sqlstate) + { + /* very unexpected... */ + GNUNET_break (0); + PQclear (result); + return GNUNET_SYSERR; + } + /* 40P01: deadlock, 40001: serialization failure */ + if ( (0 == strcmp (sqlstate, + "40P01")) || + (0 == strcmp (sqlstate, + "40001")) ) + { + /* These two can be retried and have a fair chance of working + the next time */ + PQclear (result); + return GNUNET_NO; + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Database commit failure: %s\n", + sqlstate); PQclear (result); return GNUNET_SYSERR; } -- cgit v1.2.3 From 8aebcf283a4aecb1d058db8b4f021439e45d3685 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 22 Sep 2015 10:16:01 +0200 Subject: more leaks --- src/mint/taler-mint-httpd_db.c | 20 ++++++++++++++++++++ src/mintdb/mintdb_plugin.c | 2 ++ src/mintdb/plugin_mintdb_common.c | 33 ++++++++++++++++++++++++--------- src/mintdb/plugin_mintdb_postgres.c | 36 +++++++++++------------------------- 4 files changed, 57 insertions(+), 34 deletions(-) (limited to 'src/mintdb/plugin_mintdb_postgres.c') diff --git a/src/mint/taler-mint-httpd_db.c b/src/mint/taler-mint-httpd_db.c index 5918607c..247a84e9 100644 --- a/src/mint/taler-mint-httpd_db.c +++ b/src/mint/taler-mint-httpd_db.c @@ -1265,6 +1265,11 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection, &melts[j])) { GNUNET_break (0); + for (i=0;ilibrary_name; +#if SKIP GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name, plugin)); +#endif GNUNET_free (lib_name); } diff --git a/src/mintdb/plugin_mintdb_common.c b/src/mintdb/plugin_mintdb_common.c index 99ff92ae..bbe104f6 100644 --- a/src/mintdb/plugin_mintdb_common.c +++ b/src/mintdb/plugin_mintdb_common.c @@ -127,19 +127,34 @@ common_free_melt_commitment (void *cls, unsigned int i; unsigned int k; - GNUNET_free (mc->melts); - for (i=0;inum_newcoins;i++) - GNUNET_CRYPTO_rsa_public_key_free (mc->denom_pubs[i].rsa_public_key); - GNUNET_free (mc->denom_pubs); - for (k=0;kmelts) + { + for (i=0;inum_oldcoins;i++) + { + GNUNET_CRYPTO_rsa_signature_free (mc->melts[i].coin.denom_sig.rsa_signature); + GNUNET_CRYPTO_rsa_public_key_free (mc->melts[i].coin.denom_pub.rsa_public_key); + } + GNUNET_free (mc->melts); + } + if (NULL != mc->denom_pubs) { for (i=0;inum_newcoins;i++) + if (NULL != mc->denom_pubs[i].rsa_public_key) + GNUNET_CRYPTO_rsa_public_key_free (mc->denom_pubs[i].rsa_public_key); + GNUNET_free (mc->denom_pubs); + } + for (k=0;kcommit_coins[k]) { - GNUNET_free (mc->commit_coins[k][i].refresh_link); - GNUNET_free (mc->commit_coins[k][i].coin_ev); + for (i=0;inum_newcoins;i++) + { + GNUNET_free (mc->commit_coins[k][i].refresh_link); + GNUNET_free (mc->commit_coins[k][i].coin_ev); + } + GNUNET_free (mc->commit_coins[k]); } - GNUNET_free (mc->commit_coins[k]); - GNUNET_free (mc->commit_links[k]); + GNUNET_free_non_null (mc->commit_links[k]); } GNUNET_free (mc); } diff --git a/src/mintdb/plugin_mintdb_postgres.c b/src/mintdb/plugin_mintdb_postgres.c index 62110802..1347875e 100644 --- a/src/mintdb/plugin_mintdb_postgres.c +++ b/src/mintdb/plugin_mintdb_postgres.c @@ -2273,12 +2273,15 @@ get_known_coin (void *cls, return GNUNET_YES; { struct TALER_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_rsa_public_key ("denom_pub", &coin_info->denom_pub.rsa_public_key), - TALER_PQ_result_spec_rsa_signature ("denom_sig", &coin_info->denom_sig.rsa_signature), + TALER_PQ_result_spec_rsa_public_key ("denom_pub", + &coin_info->denom_pub.rsa_public_key), + TALER_PQ_result_spec_rsa_signature ("denom_sig", + &coin_info->denom_sig.rsa_signature), TALER_PQ_result_spec_end }; - if (GNUNET_OK != TALER_PQ_extract_result (result, rs, 0)) + if (GNUNET_OK != + TALER_PQ_extract_result (result, rs, 0)) { PQclear (result); GNUNET_break (0); @@ -2428,7 +2431,11 @@ postgres_get_refresh_melt (void *cls, &coin)) return GNUNET_SYSERR; if (NULL == melt) + { + GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature); + GNUNET_CRYPTO_rsa_public_key_free (coin.denom_pub.rsa_public_key); return GNUNET_OK; + } melt->coin = coin; melt->coin_sig = coin_sig; melt->session_hash = *session_hash; @@ -2974,28 +2981,7 @@ postgres_get_melt_commitment (void *cls, return mc; cleanup: - GNUNET_free_non_null (mc->melts); - if (NULL != mc->denom_pubs) - { - for (i=0;i<(unsigned int) mc->num_newcoins;i++) - if (NULL != mc->denom_pubs[i].rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (mc->denom_pubs[i].rsa_public_key); - GNUNET_free (mc->denom_pubs); - } - for (cnc_index=0;cnc_indexcommit_coins[cnc_index]) - { - for (i=0;i<(unsigned int) mc->num_newcoins;i++) - { - GNUNET_free_non_null (mc->commit_coins[cnc_index][i].refresh_link); - GNUNET_free_non_null (mc->commit_coins[cnc_index][i].coin_ev); - } - GNUNET_free (mc->commit_coins[cnc_index]); - } - GNUNET_free_non_null (mc->commit_links[cnc_index]); - } - GNUNET_free (mc); + common_free_melt_commitment (cls, mc); return NULL; } -- cgit v1.2.3