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/include/taler_mintdb_plugin.h | 67 +-------------------------------------- 1 file changed, 1 insertion(+), 66 deletions(-) (limited to 'src/include/taler_mintdb_plugin.h') diff --git a/src/include/taler_mintdb_plugin.h b/src/include/taler_mintdb_plugin.h index c5b9828d..14518d28 100644 --- a/src/include/taler_mintdb_plugin.h +++ b/src/include/taler_mintdb_plugin.h @@ -416,30 +416,6 @@ struct TALER_MINTDB_LinkDataList }; -/** - * @brief Specification for a /lock operation. - */ -struct TALER_MINTDB_LockOperation -{ - /** - * Information about the coin that is being locked. - */ - struct TALER_CoinPublicInfo coin; - - /** - * Signature over the locking operation. - */ - struct TALER_CoinSpendSignatureP coin_sig; - - /** - * How much value is being locked? - */ - struct TALER_Amount amount; - - // FIXME: more needed... -}; - - /** * @brief Enumeration to classify the different types of transactions * that can be done with a coin. @@ -454,12 +430,8 @@ enum TALER_MINTDB_TransactionType /** * /refresh/melt operation. */ - TALER_MINTDB_TT_REFRESH_MELT = 1, + TALER_MINTDB_TT_REFRESH_MELT = 1 - /** - * /lock operation. - */ - TALER_MINTDB_TT_LOCK = 2 }; @@ -495,11 +467,6 @@ struct TALER_MINTDB_TransactionList */ struct TALER_MINTDB_RefreshMelt *melt; - /** - * Details if transaction was a /lock operation. - */ - struct TALER_MINTDB_LockOperation *lock; - } details; }; @@ -1124,38 +1091,6 @@ struct TALER_MINTDB_Plugin void *tdc_cls); - - /** - * Test if the given /lock request is known to us. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection - * @param lock lock operation - * @return #GNUNET_YES if known, - * #GNUNET_NO if not, - * #GNUNET_SYSERR on internal error - */ - int - (*have_lock) (void *cls, - struct TALER_MINTDB_Session *sesssion, - const struct TALER_MINTDB_LockOperation *lock); - - - /** - * Store the given /lock request in the database. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param sesssion database connection - * @param lock lock operation - * @return #GNUNET_OK on success - * #GNUNET_SYSERR on internal error - */ - int - (*insert_lock) (void *cls, - struct TALER_MINTDB_Session *sesssion, - const struct TALER_MINTDB_LockOperation *lock); - - /** * Compile a list of all (historic) transactions performed * with the given coin (/refresh/melt and /deposit operations). -- 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/include/taler_mintdb_plugin.h') 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/include/taler_mintdb_plugin.h') 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 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/include/taler_mintdb_plugin.h') 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