fix #3818 and handle coins being melted into multiple sessions

This commit is contained in:
Christian Grothoff 2015-06-15 14:18:37 +02:00
parent 699f283ca7
commit 2ca543cd07
5 changed files with 272 additions and 135 deletions

View File

@ -576,6 +576,22 @@ struct TALER_MINTDB_MeltCommitment
struct TALER_MINTDB_Session;
/**
* Function called with the session hashes and transfer secret
* information for a given coin.
*
* @param cls closure
* @param session_hash a session the coin was melted in
* @param transfer_pub public transfer key for the session
* @param shared_secret_enc set to shared secret for the session
*/
typedef void
(*TALER_MINTDB_TransferDataCallback)(void *cls,
const struct GNUNET_HashCode *session_hash,
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_EncryptedLinkSecretP *shared_secret_enc);
/**
* @brief The plugin API, returned from the plugin's "init" function.
* The argument given to "init" is simply a configuration handle.
@ -1092,13 +1108,13 @@ struct TALER_MINTDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param sesssion database connection
* @param coin_pub public key to use to retrieve linkage data
* @return all known link data for the coin
* @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,
const struct TALER_CoinSpendPublicKeyP *coin_pub);
const struct GNUNET_HashCode *session_hash);
/**
@ -1122,8 +1138,8 @@ struct TALER_MINTDB_Plugin
* @param cls the @e cls of this struct with the plugin-specific state
* @param sesssion database connection
* @param coin_pub public key of the coin
* @param[out] transfer_pub public transfer key
* @param[out] shared_secret_enc set to shared secret
* @param tdc function to call for each session the coin was melted into
* @param tdc_cls closure for @a tdc
* @return #GNUNET_OK on success,
* #GNUNET_NO on failure (not found)
* #GNUNET_SYSERR on internal failure (database issue)
@ -1132,8 +1148,9 @@ struct TALER_MINTDB_Plugin
(*get_transfer) (void *cls,
struct TALER_MINTDB_Session *sesssion,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_TransferPublicKeyP *transfer_pub,
struct TALER_EncryptedLinkSecretP *shared_secret_enc);
TALER_MINTDB_TransferDataCallback tdc,
void *tdc_cls);
/**

View File

@ -1231,6 +1231,92 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
}
/**
* Closure for #handle_transfer_data().
*/
struct HTD_Context
{
/**
* Session link data we collect.
*/
struct TMH_RESPONSE_LinkSessionInfo *sessions;
/**
* Database session. Nothing to do with @a sessions.
*/
struct TALER_MINTDB_Session *session;
/**
* MHD connection, for queueing replies.
*/
struct MHD_Connection *connection;
/**
* Number of sessions the coin was melted into.
*/
unsigned int num_sessions;
/**
* How are we expected to proceed. #GNUNET_SYSERR if we
* failed to return an error (should return #MHD_NO).
* #GNUNET_NO if we succeeded in queueing an MHD error
* (should return #MHD_YES from #TMH_execute_refresh_link),
* #GNUNET_OK if we should call #TMH_RESPONSE_reply_refresh_link_success().
*/
int status;
};
/**
* Function called with the session hashes and transfer secret
* information for a given coin. Gets the linkage data and
* builds the reply for the client.
*
*
* @param cls closure, a `struct HTD_Context`
* @param session_hash a session the coin was melted in
* @param transfer_pub public transfer key for the session
* @param shared_secret_enc set to shared secret for the session
*/
static void
handle_transfer_data (void *cls,
const struct GNUNET_HashCode *session_hash,
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_EncryptedLinkSecretP *shared_secret_enc)
{
struct HTD_Context *ctx = cls;
struct TALER_MINTDB_LinkDataList *ldl;
struct TMH_RESPONSE_LinkSessionInfo *lsi;
if (GNUNET_OK != ctx->status)
return;
ldl = TMH_plugin->get_link_data_list (TMH_plugin->cls,
ctx->session,
session_hash);
if (NULL == ldl)
{
GNUNET_break (0);
ctx->status = GNUNET_NO;
if (MHD_NO ==
TMH_RESPONSE_reply_json_pack (ctx->connection,
MHD_HTTP_NOT_FOUND,
"{s:s}",
"error",
"link data not found (link)"))
ctx->status = GNUNET_SYSERR;
return;
}
GNUNET_array_grow (ctx->sessions,
ctx->num_sessions,
ctx->num_sessions + 1);
lsi = &ctx->sessions[ctx->num_sessions - 1];
lsi->transfer_pub = *transfer_pub;
lsi->shared_secret_enc = *shared_secret_enc;
lsi->ldl = ldl;
}
/**
* Execute a "/refresh/link". Returns the linkage information that
* will allow the owner of a coin to follow the refresh trail to
@ -1244,52 +1330,47 @@ int
TMH_DB_execute_refresh_link (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub)
{
struct HTD_Context ctx;
int res;
struct TALER_MINTDB_Session *session;
struct TALER_TransferPublicKeyP transfer_pub;
struct TALER_EncryptedLinkSecretP shared_secret_enc;
struct TALER_MINTDB_LinkDataList *ldl;
unsigned int i;
if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
if (NULL == (ctx.session = TMH_plugin->get_session (TMH_plugin->cls,
GNUNET_NO)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
ctx.connection = connection;
ctx.num_sessions = 0;
ctx.sessions = NULL;
ctx.status = GNUNET_OK;
res = TMH_plugin->get_transfer (TMH_plugin->cls,
session,
ctx.session,
coin_pub,
&transfer_pub,
&shared_secret_enc);
if (GNUNET_SYSERR == res)
&handle_transfer_data,
&ctx);
if (GNUNET_SYSERR == ctx.status)
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
res = MHD_NO;
goto cleanup;
}
if (GNUNET_NO == res)
if (GNUNET_NO == ctx.status)
{
res = MHD_YES;
goto cleanup;
}
GNUNET_assert (GNUNET_OK == ctx.status);
if (0 == ctx.num_sessions)
return TMH_RESPONSE_reply_arg_unknown (connection,
"coin_pub");
}
GNUNET_assert (GNUNET_OK == res);
ldl = TMH_plugin->get_link_data_list (TMH_plugin->cls,
session,
coin_pub);
if (NULL == ldl)
{
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s}",
"error",
"link data not found (link)");
}
res = TMH_RESPONSE_reply_refresh_link_success (connection,
&transfer_pub,
&shared_secret_enc,
ldl);
TMH_plugin->free_link_data_list (TMH_plugin->cls,
ldl);
ctx.num_sessions,
ctx.sessions);
cleanup:
for (i=0;i<ctx.num_sessions;i++)
TMH_plugin->free_link_data_list (TMH_plugin->cls,
ctx.sessions[i].ldl);
GNUNET_free (ctx.sessions);
return res;
}

View File

@ -908,58 +908,65 @@ TMH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
* Send a response for "/refresh/link".
*
* @param connection the connection to send the response to
* @param transfer_pub transfer public key
* @param shared_secret_enc encrypted shared secret
* @param ldl linked list with link data
* @param num_sessions number of sessions the coin was used in
* @param sessions array of @a num_session entries with
* information for each session
* @return a MHD result code
*/
int
TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_EncryptedLinkSecretP *shared_secret_enc,
const struct TALER_MINTDB_LinkDataList *ldl)
unsigned int num_sessions,
const struct TMH_RESPONSE_LinkSessionInfo *sessions)
{
const struct TALER_MINTDB_LinkDataList *pos;
json_t *root;
json_t *list;
json_t *mlist;
int res;
unsigned int i;
list = json_array ();
for (pos = ldl; NULL != pos; pos = pos->next)
mlist = json_array ();
for (i=0;i<num_sessions;i++)
{
json_t *obj;
const struct TALER_MINTDB_LinkDataList *pos;
json_t *list = json_array ();
obj = json_object ();
json_object_set_new (obj,
"link_enc",
TALER_json_from_data (ldl->link_data_enc->coin_priv_enc,
sizeof (struct TALER_CoinSpendPrivateKeyP) +
ldl->link_data_enc->blinding_key_enc_size));
json_object_set_new (obj,
"denom_pub",
TALER_json_from_rsa_public_key (ldl->denom_pub.rsa_public_key));
json_object_set_new (obj,
"ev_sig",
TALER_json_from_rsa_signature (ldl->ev_sig.rsa_signature));
json_array_append_new (list, obj);
for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
{
json_t *obj;
obj = json_object ();
json_object_set_new (obj,
"link_enc",
TALER_json_from_data (pos->link_data_enc->coin_priv_enc,
sizeof (struct TALER_CoinSpendPrivateKeyP) +
pos->link_data_enc->blinding_key_enc_size));
json_object_set_new (obj,
"denom_pub",
TALER_json_from_rsa_public_key (pos->denom_pub.rsa_public_key));
json_object_set_new (obj,
"ev_sig",
TALER_json_from_rsa_signature (pos->ev_sig.rsa_signature));
json_array_append_new (list,
obj);
}
root = json_object ();
json_object_set_new (root,
"new_coins",
list);
json_object_set_new (root,
"transfer_pub",
TALER_json_from_data (&sessions[i].transfer_pub,
sizeof (struct TALER_TransferPublicKeyP)));
json_object_set_new (root,
"secret_enc",
TALER_json_from_data (&sessions[i].shared_secret_enc,
sizeof (struct TALER_EncryptedLinkSecretP)));
json_array_append_new (mlist,
root);
}
root = json_object ();
json_object_set_new (root,
"new_coins",
list);
json_object_set_new (root,
"transfer_pub",
TALER_json_from_data (transfer_pub,
sizeof (struct TALER_TransferPublicKeyP)));
json_object_set_new (root,
"secret_enc",
TALER_json_from_data (shared_secret_enc,
sizeof (struct TALER_EncryptedLinkSecretP)));
res = TMH_RESPONSE_reply_json (connection,
root,
MHD_HTTP_OK);
json_decref (root);
mlist,
MHD_HTTP_OK);
json_decref (mlist);
return res;
}

View File

@ -334,20 +334,42 @@ TMH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
const char *missmatch_object);
/**
* Information for each session a coin was melted into.
*/
struct TMH_RESPONSE_LinkSessionInfo
{
/**
* Transfer public key of the coin.
*/
struct TALER_TransferPublicKeyP transfer_pub;
/**
* Encrypted shared secret for decrypting the transfer secrets.
*/
struct TALER_EncryptedLinkSecretP shared_secret_enc;
/**
* Linked data of coins being created in the session.
*/
struct TALER_MINTDB_LinkDataList *ldl;
};
/**
* Send a response for "/refresh/link".
*
* @param connection the connection to send the response to
* @param transfer_pub transfer public key
* @param shared_secret_enc encrypted shared secret
* @param ldl linked list with link data
* @param num_sessions number of sessions the coin was used in
* @param sessions array of @a num_session entries with
* information for each session
* @return a MHD result code
*/
int
TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
const struct TALER_TransferPublicKeyP *transfer_pub,
const struct TALER_EncryptedLinkSecretP *shared_secret_enc,
const struct TALER_MINTDB_LinkDataList *ldl);
unsigned int num_sessions,
const struct TMH_RESPONSE_LinkSessionInfo *sessions);
#endif

View File

@ -312,7 +312,7 @@ postgres_create_tables (void *cls,
*/
SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_melts "
"(coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub)"
",session BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
",session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash)"
",oldcoin_index INT2 NOT NULL"
",coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)"
",amount_with_fee_val INT8 NOT NULL"
@ -321,7 +321,7 @@ postgres_create_tables (void *cls,
",melt_fee_val INT8 NOT NULL"
",melt_fee_frac INT8 NOT NULL"
",melt_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
",PRIMARY KEY (session, oldcoin_index)" /* a coin can be used only
",PRIMARY KEY (session_hash, oldcoin_index)" /* a coin can be used only
once in a refresh session */
") ");
/* Table with information about the desired denominations to be created
@ -675,7 +675,7 @@ postgres_prepare (PGconn *db_conn)
PREPARE ("insert_refresh_melt",
"INSERT INTO refresh_melts "
"(coin_pub "
",session"
",session_hash"
",oldcoin_index "
",coin_sig "
",amount_with_fee_val "
@ -700,12 +700,12 @@ postgres_prepare (PGconn *db_conn)
",melt_fee_frac "
",melt_fee_curr "
" FROM refresh_melts"
" WHERE session=$1 AND oldcoin_index=$2",
" WHERE session_hash=$1 AND oldcoin_index=$2",
2, NULL);
/* Query the 'refresh_melts' by coin public key */
PREPARE ("get_refresh_melt_by_coin",
"SELECT"
" session"
" session_hash"
/* ",oldcoin_index" // not needed */
",coin_sig"
",amount_with_fee_val"
@ -845,43 +845,49 @@ postgres_prepare (PGconn *db_conn)
") VALUES "
"($1, $2, $3)",
3, NULL);
#if 0
/* FIXME: not complete yet -- #3818... */
/* Used in #postgres_get_link_data_list().
FIXME: document how this is supposed to work... */
/* Used in #postgres_get_link_data_list(). We use the session_hash
to obtain the "noreveal_index" for that session, and then select
the encrypted link vectors (link_vector_enc) and the
corresponding signatures (ev_sig) and the denomination keys from
the respective tables (namely refresh_melts and refresh_order)
using the session_hash as the primary filter (on join) and the
'noreveal_index' to constrain the selection on the commitment.
We also want to get the triplet for each of the newcoins, so we
have another constraint to ensure we get each triplet with
matching "newcoin_index" values. NOTE: This may return many
results, both for different sessions and for the different coins
being minted in the refresh ops. NOTE: There may be more
efficient ways to express the same query. */
PREPARE ("get_link",
"SELECT link_vector_enc,ro.denom_pub,ev_sig"
" FROM refresh_melt rm "
"SELECT link_vector_enc,ev_sig,ro.denom_pub"
" FROM refresh_melts rm "
" JOIN refresh_order ro USING (session_hash)"
" JOIN refresh_commit_coin rcc USING (session_hash)"
" JOIN refresh_sessions rs USING (session_hash)"
" JOIN refresh_out rc USING (session_hash)"
" WHERE rm.coin_pub=$1"
" WHERE ro.session_hash=$1"
" AND ro.newcoin_index=rcc.newcoin_index"
" AND ro.newcoin_index=rc.newcoin_index"
" AND rcc.cnc_index=rs.noreveal_index % ("
" SELECT count(*) FROM refresh_commit_coin rcc2"
" WHERE rcc2.newcoin_index=0"
" AND rcc2.session_hash=rs.session_hash"
" ) ",
" AND rcc.cnc_index=rs.noreveal_index",
1, NULL);
/* Used in #postgres_get_transfer().
FIXME: document how this is supposed to work... -- #3818 */
/* Used in #postgres_get_transfer(). Given the public key of a
melted coin, we obtain the corresponding encrypted link secret
and the transfer public key. This is done by first finding
the session_hash(es) of all sessions the coin was melted into,
and then constraining the result to the selected "noreveal_index"
and the transfer public key to the corresponding index of the
old coin.
NOTE: This may (in theory) return multiple results, one per session
that the old coin was melted into. */
PREPARE ("get_transfer",
"SELECT transfer_pub,link_secret_enc"
" FROM refresh_melt rm"
"SELECT transfer_pub,link_secret_enc,session_hash"
" FROM refresh_melts rm"
" JOIN refresh_commit_link rcl USING (session_hash)"
" JOIN refresh_sessions rs USING (session_hash)"
" WHERE rm.coin_pub=$1"
" AND rm.oldcoin_index = rcl.oldcoin_index"
" AND rcl.cnc_index=rs.noreveal_index % ("
" SELECT count(*) FROM refresh_commit_coin rcc2"
" WHERE newcoin_index=0"
" AND rcc2.session_hash=rm.session_hash"
" )",
" AND rcl.cnc_index=rs.noreveal_index",
1, NULL);
#endif
return GNUNET_OK;
#undef PREPARE
}
@ -2848,20 +2854,20 @@ postgres_insert_refresh_out (void *cls,
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
* @param coin_pub public key to use to retrieve linkage data
* @return all known link data for the coin
* @param session_hash refresh session to get linkage data for
* @return all known link data for the session
*/
static struct TALER_MINTDB_LinkDataList *
postgres_get_link_data_list (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub)
const struct GNUNET_HashCode *session_hash)
{
struct TALER_MINTDB_LinkDataList *ldl;
struct TALER_MINTDB_LinkDataList *pos;
int i;
int nrows;
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_auto_from_type (coin_pub),
TALER_PQ_query_param_auto_from_type (session_hash),
TALER_PQ_query_param_end
};
PGresult *result;
@ -2895,10 +2901,10 @@ postgres_get_link_data_list (void *cls,
TALER_PQ_result_spec_variable_size ("link_vector_enc",
&ld_buf,
&ld_buf_size),
TALER_PQ_result_spec_rsa_public_key ("denom_pub",
&denom_pub),
TALER_PQ_result_spec_rsa_signature ("ev_sig",
&sig),
TALER_PQ_result_spec_rsa_public_key ("denom_pub",
&denom_pub),
TALER_PQ_result_spec_end
};
@ -2942,8 +2948,8 @@ postgres_get_link_data_list (void *cls,
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session database connection
* @param coin_pub public key of the coin
* @param[out] transfer_pub public transfer key
* @param[out] shared_secret_enc set to shared secret
* @param tdc function to call for each session the coin was melted into
* @param tdc_cls closure for @a tdc
* @return #GNUNET_OK on success,
* #GNUNET_NO on failure (not found)
* #GNUNET_SYSERR on internal failure (database issue)
@ -2952,14 +2958,16 @@ static int
postgres_get_transfer (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
struct TALER_TransferPublicKeyP *transfer_pub,
struct TALER_EncryptedLinkSecretP *shared_secret_enc)
TALER_MINTDB_TransferDataCallback tdc,
void *tdc_cls)
{
struct TALER_PQ_QueryParam params[] = {
TALER_PQ_query_param_auto_from_type (coin_pub),
TALER_PQ_query_param_end
};
PGresult *result;
int nrows;
int i;
result = TALER_PQ_exec_prepared (session->conn,
"get_transfer",
@ -2971,23 +2979,21 @@ postgres_get_transfer (void *cls,
PQclear (result);
return GNUNET_SYSERR;
}
if (0 == PQntuples (result))
nrows = PQntuples (result);
if (0 == nrows)
{
PQclear (result);
return GNUNET_NO;
}
if (1 != PQntuples (result))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"got %d tuples from get_transfer\n",
PQntuples (result));
GNUNET_break (0);
return GNUNET_SYSERR;
}
for (i=0;i<nrows;i++)
{
struct GNUNET_HashCode session_hash;
struct TALER_TransferPublicKeyP transfer_pub;
struct TALER_EncryptedLinkSecretP shared_secret_enc;
struct TALER_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_auto_from_type ("transfer_pub", transfer_pub),
TALER_PQ_result_spec_auto_from_type ("link_secret_enc", shared_secret_enc),
TALER_PQ_result_spec_auto_from_type ("transfer_pub", &transfer_pub),
TALER_PQ_result_spec_auto_from_type ("link_secret_enc", &shared_secret_enc),
TALER_PQ_result_spec_auto_from_type ("session_hash", &session_hash),
TALER_PQ_result_spec_end
};
@ -2998,6 +3004,10 @@ postgres_get_transfer (void *cls,
GNUNET_break (0);
return GNUNET_SYSERR;
}
tdc (tdc_cls,
&session_hash,
&transfer_pub,
&shared_secret_enc);
}
PQclear (result);
return GNUNET_OK;
@ -3121,7 +3131,7 @@ postgres_get_coin_transactions (void *cls,
melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt);
{
struct TALER_PQ_ResultSpec rs[] = {
TALER_PQ_result_spec_auto_from_type ("session",
TALER_PQ_result_spec_auto_from_type ("session_hash",
&melt->session_hash),
/* oldcoin_index not needed */
TALER_PQ_result_spec_auto_from_type ("coin_sig",