add idempotency checks for /refresh/reveal, fixing #4793
This commit is contained in:
parent
e140ca9dce
commit
7fd6be5cef
@ -1,3 +1,6 @@
|
|||||||
|
Thu Nov 17 16:37:22 CET 2016
|
||||||
|
Added missing idempotency checks for /refresh/reveal. -CG
|
||||||
|
|
||||||
Thu Nov 17 11:37:56 CET 2016
|
Thu Nov 17 11:37:56 CET 2016
|
||||||
Fixed a few cases of missing database rollbacks, causing the
|
Fixed a few cases of missing database rollbacks, causing the
|
||||||
exchange to be stuck. -CG
|
exchange to be stuck. -CG
|
||||||
|
@ -1710,6 +1710,9 @@ interpreter_run (void *cls)
|
|||||||
fail (is);
|
fail (is);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
|
"Running command `%s'\n",
|
||||||
|
cmd->label);
|
||||||
switch (cmd->oc)
|
switch (cmd->oc)
|
||||||
{
|
{
|
||||||
case OC_END:
|
case OC_END:
|
||||||
@ -2836,6 +2839,12 @@ run (void *cls)
|
|||||||
.expected_response_code = MHD_HTTP_OK,
|
.expected_response_code = MHD_HTTP_OK,
|
||||||
.details.refresh_reveal.melt_ref = "refresh-melt-1" },
|
.details.refresh_reveal.melt_ref = "refresh-melt-1" },
|
||||||
|
|
||||||
|
/* do it again to check idempotency */
|
||||||
|
{ .oc = OC_REFRESH_REVEAL,
|
||||||
|
.label = "refresh-reveal-1-idempotency",
|
||||||
|
.expected_response_code = MHD_HTTP_OK,
|
||||||
|
.details.refresh_reveal.melt_ref = "refresh-melt-1" },
|
||||||
|
|
||||||
/* Test that /refresh/link works */
|
/* Test that /refresh/link works */
|
||||||
{ .oc = OC_REFRESH_LINK,
|
{ .oc = OC_REFRESH_LINK,
|
||||||
.label = "refresh-link-1",
|
.label = "refresh-link-1",
|
||||||
@ -2849,7 +2858,7 @@ run (void *cls)
|
|||||||
.label = "refresh-deposit-refreshed-1a",
|
.label = "refresh-deposit-refreshed-1a",
|
||||||
.expected_response_code = MHD_HTTP_OK,
|
.expected_response_code = MHD_HTTP_OK,
|
||||||
.details.deposit.amount = "EUR:1",
|
.details.deposit.amount = "EUR:1",
|
||||||
.details.deposit.coin_ref = "refresh-reveal-1",
|
.details.deposit.coin_ref = "refresh-reveal-1-idempotency",
|
||||||
.details.deposit.coin_idx = 0,
|
.details.deposit.coin_idx = 0,
|
||||||
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost:8082/\", \"account_number\":42 }",
|
.details.deposit.wire_details = "{ \"type\":\"test\", \"bank_uri\":\"http://localhost:8082/\", \"account_number\":42 }",
|
||||||
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
|
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
|
||||||
@ -3072,12 +3081,6 @@ main (int argc,
|
|||||||
enum GNUNET_OS_ProcessStatusType type;
|
enum GNUNET_OS_ProcessStatusType type;
|
||||||
unsigned long code;
|
unsigned long code;
|
||||||
|
|
||||||
GNUNET_log_setup ("test-exchange-api",
|
|
||||||
"DEBUG",
|
|
||||||
"/tmp/logs");
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test log\n");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* These might get in the way... */
|
/* These might get in the way... */
|
||||||
unsetenv ("XDG_DATA_HOME");
|
unsetenv ("XDG_DATA_HOME");
|
||||||
unsetenv ("XDG_CONFIG_HOME");
|
unsetenv ("XDG_CONFIG_HOME");
|
||||||
|
@ -1280,6 +1280,18 @@ refresh_exchange_coin (struct MHD_Connection *connection,
|
|||||||
ev_sig.rsa_signature = NULL;
|
ev_sig.rsa_signature = NULL;
|
||||||
return ev_sig;
|
return ev_sig;
|
||||||
}
|
}
|
||||||
|
if (GNUNET_OK ==
|
||||||
|
TEH_plugin->get_refresh_out (TEH_plugin->cls,
|
||||||
|
session,
|
||||||
|
session_hash,
|
||||||
|
coin_off,
|
||||||
|
&ev_sig))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Returning cashed reply for /refresh/reveal signature\n");
|
||||||
|
return ev_sig;
|
||||||
|
}
|
||||||
|
|
||||||
ev_sig.rsa_signature
|
ev_sig.rsa_signature
|
||||||
= GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
|
= GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
|
||||||
commit_coin->coin_ev,
|
commit_coin->coin_ev,
|
||||||
@ -1289,7 +1301,7 @@ refresh_exchange_coin (struct MHD_Connection *connection,
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return ev_sig;
|
return ev_sig;
|
||||||
}
|
}
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_SYSERR ==
|
||||||
TEH_plugin->insert_refresh_out (TEH_plugin->cls,
|
TEH_plugin->insert_refresh_out (TEH_plugin->cls,
|
||||||
session,
|
session,
|
||||||
session_hash,
|
session_hash,
|
||||||
@ -1300,6 +1312,7 @@ refresh_exchange_coin (struct MHD_Connection *connection,
|
|||||||
GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
|
GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
|
||||||
ev_sig.rsa_signature = NULL;
|
ev_sig.rsa_signature = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ev_sig;
|
return ev_sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ TEH_RESPONSE_reply_internal_db_error (struct MHD_Connection *connection,
|
|||||||
{
|
{
|
||||||
return TEH_RESPONSE_reply_internal_error (connection,
|
return TEH_RESPONSE_reply_internal_error (connection,
|
||||||
ec,
|
ec,
|
||||||
"Failed to connect to database");
|
"Failure in database interaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1154,6 +1154,15 @@ postgres_prepare (PGconn *db_conn)
|
|||||||
"($1, $2, $3)",
|
"($1, $2, $3)",
|
||||||
3, NULL);
|
3, NULL);
|
||||||
|
|
||||||
|
/* Used in #postgres_get_refresh_out() to test if the
|
||||||
|
generated signature(s) already exists */
|
||||||
|
PREPARE ("get_refresh_out",
|
||||||
|
"SELECT ev_sig"
|
||||||
|
" FROM refresh_out"
|
||||||
|
" WHERE session_hash=$1"
|
||||||
|
" AND newcoin_index=$2",
|
||||||
|
2, NULL);
|
||||||
|
|
||||||
/* Used in #postgres_get_link_data_list(). We use the session_hash
|
/* Used in #postgres_get_link_data_list(). We use the session_hash
|
||||||
to obtain the "noreveal_index" for that session, and then select the
|
to obtain the "noreveal_index" for that session, and then select the
|
||||||
corresponding signatures (ev_sig) and the denomination keys from
|
corresponding signatures (ev_sig) and the denomination keys from
|
||||||
@ -3431,6 +3440,67 @@ postgres_get_refresh_transfer_public_key (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert signature of a new coin generated during refresh into
|
||||||
|
* the database indexed by the refresh session and the index
|
||||||
|
* of the coin. This data is later used should an old coin
|
||||||
|
* be used to try to obtain the private keys during "/refresh/link".
|
||||||
|
*
|
||||||
|
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
||||||
|
* @param session database connection
|
||||||
|
* @param session_hash hash to identify refresh session
|
||||||
|
* @param newcoin_index coin index
|
||||||
|
* @param ev_sig coin signature
|
||||||
|
* @return #GNUNET_OK on success, #GNUNET_NO if we have no such result
|
||||||
|
* #GNUNET_SYSERR on error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
postgres_get_refresh_out (void *cls,
|
||||||
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
|
const struct GNUNET_HashCode *session_hash,
|
||||||
|
uint16_t newcoin_index,
|
||||||
|
struct TALER_DenominationSignature *ev_sig)
|
||||||
|
{
|
||||||
|
PGresult *result;
|
||||||
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
|
GNUNET_PQ_query_param_auto_from_type (session_hash),
|
||||||
|
GNUNET_PQ_query_param_uint16 (&newcoin_index),
|
||||||
|
GNUNET_PQ_query_param_end
|
||||||
|
};
|
||||||
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
|
GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
|
||||||
|
&ev_sig->rsa_signature),
|
||||||
|
GNUNET_PQ_result_spec_end
|
||||||
|
};
|
||||||
|
|
||||||
|
result = GNUNET_PQ_exec_prepared (session->conn,
|
||||||
|
"get_refresh_out",
|
||||||
|
params);
|
||||||
|
if (PGRES_TUPLES_OK != PQresultStatus (result))
|
||||||
|
{
|
||||||
|
BREAK_DB_ERR (result);
|
||||||
|
PQclear (result);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
if (1 != PQntuples (result))
|
||||||
|
{
|
||||||
|
PQclear (result);
|
||||||
|
return GNUNET_NO;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_PQ_extract_result (result,
|
||||||
|
rs,
|
||||||
|
0))
|
||||||
|
{
|
||||||
|
PQclear (result);
|
||||||
|
GNUNET_break (0);
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
PQclear (result);
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert signature of a new coin generated during refresh into
|
* Insert signature of a new coin generated during refresh into
|
||||||
* the database indexed by the refresh session and the index
|
* the database indexed by the refresh session and the index
|
||||||
@ -3443,6 +3513,7 @@ postgres_get_refresh_transfer_public_key (void *cls,
|
|||||||
* @param newcoin_index coin index
|
* @param newcoin_index coin index
|
||||||
* @param ev_sig coin signature
|
* @param ev_sig coin signature
|
||||||
* @return #GNUNET_OK on success
|
* @return #GNUNET_OK on success
|
||||||
|
* #GNUNET_SYSERR on error
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
postgres_insert_refresh_out (void *cls,
|
postgres_insert_refresh_out (void *cls,
|
||||||
@ -5050,6 +5121,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
|
|||||||
plugin->free_refresh_commit_coins = &postgres_free_refresh_commit_coins;
|
plugin->free_refresh_commit_coins = &postgres_free_refresh_commit_coins;
|
||||||
plugin->insert_refresh_transfer_public_key = &postgres_insert_refresh_transfer_public_key;
|
plugin->insert_refresh_transfer_public_key = &postgres_insert_refresh_transfer_public_key;
|
||||||
plugin->get_refresh_transfer_public_key = &postgres_get_refresh_transfer_public_key;
|
plugin->get_refresh_transfer_public_key = &postgres_get_refresh_transfer_public_key;
|
||||||
|
plugin->get_refresh_out = &postgres_get_refresh_out;
|
||||||
plugin->insert_refresh_out = &postgres_insert_refresh_out;
|
plugin->insert_refresh_out = &postgres_insert_refresh_out;
|
||||||
plugin->get_link_data_list = &postgres_get_link_data_list;
|
plugin->get_link_data_list = &postgres_get_link_data_list;
|
||||||
plugin->free_link_data_list = &common_free_link_data_list;
|
plugin->free_link_data_list = &common_free_link_data_list;
|
||||||
|
@ -1359,6 +1359,27 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
struct TALER_TransferPublicKeyP *tp);
|
struct TALER_TransferPublicKeyP *tp);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get signature of a new coin generated during refresh into
|
||||||
|
* the database indexed by the refresh session and the index
|
||||||
|
* of the coin.
|
||||||
|
*
|
||||||
|
* @param cls the @e cls of this struct with the plugin-specific state
|
||||||
|
* @param session database connection
|
||||||
|
* @param session_hash hash to identify refresh session
|
||||||
|
* @param newcoin_index coin index
|
||||||
|
* @param[out] ev_sig coin signature
|
||||||
|
* @return #GNUNET_OK on success, #GNUNET_NO if we have no such entry,
|
||||||
|
* #GNUNET_SYSERR on error
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
(*get_refresh_out) (void *cls,
|
||||||
|
struct TALER_EXCHANGEDB_Session *session,
|
||||||
|
const struct GNUNET_HashCode *session_hash,
|
||||||
|
uint16_t newcoin_index,
|
||||||
|
struct TALER_DenominationSignature *ev_sig);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert signature of a new coin generated during refresh into
|
* Insert signature of a new coin generated during refresh into
|
||||||
* the database indexed by the refresh session and the index
|
* the database indexed by the refresh session and the index
|
||||||
@ -1371,6 +1392,7 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
* @param newcoin_index coin index
|
* @param newcoin_index coin index
|
||||||
* @param ev_sig coin signature
|
* @param ev_sig coin signature
|
||||||
* @return #GNUNET_OK on success
|
* @return #GNUNET_OK on success
|
||||||
|
* #GNUNET_SYSERR on error
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
(*insert_refresh_out) (void *cls,
|
(*insert_refresh_out) (void *cls,
|
||||||
|
Loading…
Reference in New Issue
Block a user