diff options
Diffstat (limited to 'src')
26 files changed, 2391 insertions, 3722 deletions
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c index cd1ee0d27..41fd7f3cb 100644 --- a/src/auditor/taler-auditor.c +++ b/src/auditor/taler-auditor.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2016, 2017 Inria + Copyright (C) 2016, 2017 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero Public License as published by the Free Software @@ -275,6 +275,17 @@ static json_t *report_bad_sig_losses; */ static struct TALER_Amount total_bad_sig_loss; +/** + * Array of refresh transactions where the /refresh/reveal has not yet + * happened (and may of course never happen). + */ +static json_t *report_refreshs_hanging; + +/** + * Total amount lost by operations for which signatures were invalid. + */ +static struct TALER_Amount total_refresh_hanging; + /* ***************************** Report logic **************************** */ @@ -1893,7 +1904,7 @@ check_transaction_history (const struct TALER_CoinSpendPublicKeyP *coin_pub, } break; case TALER_EXCHANGEDB_TT_REFRESH_MELT: - amount_with_fee = &tl->details.melt->amount_with_fee; + amount_with_fee = &tl->details.melt->session.amount_with_fee; fee = &tl->details.melt->melt_fee; fee_dki = &dki->properties.fee_refresh; if (GNUNET_OK != @@ -2104,7 +2115,7 @@ wire_transfer_information_cb (void *cls, coin = &tl->details.deposit->coin; break; case TALER_EXCHANGEDB_TT_REFRESH_MELT: - coin = &tl->details.melt->coin; + coin = &tl->details.melt->session.coin; break; case TALER_EXCHANGEDB_TT_REFUND: coin = &tl->details.refund->coin; @@ -2968,6 +2979,54 @@ withdraw_cb (void *cls, /** + * Closure for #reveal_data_cb(). + */ +struct RevealContext +{ + + /** + * Denomination public keys of the new coins. + */ + struct TALER_DenominationPublicKey *new_dps; + + /** + * Size of the @a new_dp and @a new_dki arrays. + */ + unsigned int num_newcoins; +}; + + +/** + * Function called with information about a refresh order. + * + * @param cls closure + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs array of @e num_tprivs transfer private keys + * @param tp transfer public key information + */ +static void +reveal_data_cb (void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp) +{ + struct RevealContext *rctx = cls; + + rctx->num_newcoins = num_newcoins; + rctx->new_dps = GNUNET_new_array (num_newcoins, + struct TALER_DenominationPublicKey); + for (unsigned int i=0;i<num_newcoins;i++) + rctx->new_dps[i].rsa_public_key + = GNUNET_CRYPTO_rsa_public_key_dup (rrcs[i].denom_pub.rsa_public_key); +} + + +/** * Function called with details about coins that were melted, with the * goal of auditing the refresh's execution. Verifies the signature * and updates our information about coins outstanding (the old coin's @@ -2980,7 +3039,6 @@ withdraw_cb (void *cls, * @param coin_pub public key of the coin * @param coin_sig signature from the coin * @param amount_with_fee amount that was deposited including fee - * @param num_newcoins how many coins were issued * @param noreveal_index which index was picked by the exchange in cut-and-choose * @param session_hash what is the session hash * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop @@ -2992,9 +3050,8 @@ refresh_session_cb (void *cls, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, - uint16_t num_newcoins, - uint16_t noreveal_index, - const struct GNUNET_HashCode *session_hash) + uint32_t noreveal_index, + const struct TALER_RefreshCommitmentP *rc) { struct CoinContext *cc = cls; struct TALER_RefreshMeltCoinAffirmationPS rmc; @@ -3020,7 +3077,7 @@ refresh_session_cb (void *cls, /* verify melt signature */ rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); rmc.purpose.size = htonl (sizeof (rmc)); - rmc.session_hash = *session_hash; + rmc.rc = *rc; TALER_amount_hton (&rmc.amount_with_fee, amount_with_fee); rmc.melt_fee = dki->properties.fee_refresh; @@ -3050,161 +3107,189 @@ refresh_session_cb (void *cls, TALER_amount2s (amount_with_fee)); { - struct TALER_DenominationPublicKey new_dp[num_newcoins]; - const struct TALER_EXCHANGEDB_DenominationKeyInformationP *new_dki[num_newcoins]; + struct RevealContext reveal_ctx; struct TALER_Amount refresh_cost; int err; GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (amount_with_fee->currency, &refresh_cost)); - qs = edb->get_refresh_order (edb->cls, - esession, - session_hash, - num_newcoins, - new_dp); - if (0 >= qs) + memset (&reveal_ctx, + 0, + sizeof (reveal_ctx)); + qs = edb->get_refresh_reveal (edb->cls, + esession, + rc, + &reveal_data_cb, + &reveal_ctx); + if (0 > qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); cc->qs = GNUNET_DB_STATUS_HARD_ERROR; return GNUNET_SYSERR; } - /* Update outstanding amounts for all new coin's denominations, and check - that the resulting amounts are consistent with the value being refreshed. */ - err = GNUNET_NO; - for (unsigned int i=0;i<num_newcoins;i++) + if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || + (0 == reveal_ctx.num_newcoins) ) { - /* lookup new coin denomination key */ - qs = get_denomination_info (&new_dp[i], - &new_dki[i], - NULL); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - cc->qs = qs; - err = GNUNET_YES; - } - GNUNET_CRYPTO_rsa_public_key_free (new_dp[i].rsa_public_key); - new_dp[i].rsa_public_key = NULL; - } - if (err) - return GNUNET_SYSERR; - - /* calculate total refresh cost */ - for (unsigned int i=0;i<num_newcoins;i++) - { - /* update cost of refresh */ - struct TALER_Amount fee; - struct TALER_Amount value; - - TALER_amount_ntoh (&fee, - &new_dki[i]->properties.fee_withdraw); - TALER_amount_ntoh (&value, - &new_dki[i]->properties.value); - if ( (GNUNET_OK != - TALER_amount_add (&refresh_cost, - &refresh_cost, - &fee)) || - (GNUNET_OK != - TALER_amount_add (&refresh_cost, - &refresh_cost, - &value)) ) - { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; - } + /* This can happen if /refresh/reveal was not yet called or only + with invalid data, even if the exchange is correctly + operating. We still report it. */ + report (report_refreshs_hanging, + json_pack ("{s:I, s:o, s:o}", + "row", (json_int_t) rowid, + "amount", TALER_JSON_from_amount (amount_with_fee), + "coin_pub", GNUNET_JSON_from_data_auto (coin_pub))); + GNUNET_break (GNUNET_OK == + TALER_amount_add (&total_refresh_hanging, + &total_refresh_hanging, + amount_with_fee)); + return GNUNET_OK; } + // FIXME: free reveal_ctx.num_newcoins later! - /* compute contribution of old coin */ { - struct TALER_Amount melt_fee; + const struct TALER_EXCHANGEDB_DenominationKeyInformationP *new_dkis[reveal_ctx.num_newcoins]; - TALER_amount_ntoh (&melt_fee, - &dki->properties.fee_refresh); - if (GNUNET_OK != - TALER_amount_subtract (&amount_without_fee, - amount_with_fee, - &melt_fee)) + /* Update outstanding amounts for all new coin's denominations, and check + that the resulting amounts are consistent with the value being refreshed. */ + err = GNUNET_NO; + for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++) { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; + /* lookup new coin denomination key */ + qs = get_denomination_info (&reveal_ctx.new_dps[i], + &new_dkis[i], + NULL); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); + cc->qs = qs; + err = GNUNET_YES; + } + GNUNET_CRYPTO_rsa_public_key_free (reveal_ctx.new_dps[i].rsa_public_key); + reveal_ctx.new_dps[i].rsa_public_key = NULL; } - } - - /* check old coin covers complete expenses */ - if (1 == TALER_amount_cmp (&refresh_cost, - &amount_without_fee)) - { - /* refresh_cost > amount_without_fee */ - report_amount_arithmetic_inconsistency ("melt (fee)", - rowid, - &amount_without_fee, - &refresh_cost, - -1); - return GNUNET_OK; - } + GNUNET_free (reveal_ctx.new_dps); + reveal_ctx.new_dps = NULL; - /* update outstanding denomination amounts */ - for (unsigned int i=0;i<num_newcoins;i++) - { - struct DenominationSummary *dsi; - struct TALER_Amount value; + if (err) + return GNUNET_SYSERR; - dsi = get_denomination_summary (cc, - new_dki[i], - &new_dki[i]->properties.denom_hash); - if (NULL == dsi) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_amount_ntoh (&value, - &new_dki[i]->properties.value); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Created fresh coin in denomination `%s' of value %s\n", - GNUNET_h2s (&new_dki[i]->properties.denom_hash), - TALER_amount2s (&value)); - if (GNUNET_OK != - TALER_amount_add (&dsi->denom_balance, - &dsi->denom_balance, - &value)) + /* calculate total refresh cost */ + for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++) { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; + /* update cost of refresh */ + struct TALER_Amount fee; + struct TALER_Amount value; + + TALER_amount_ntoh (&fee, + &new_dkis[i]->properties.fee_withdraw); + TALER_amount_ntoh (&value, + &new_dkis[i]->properties.value); + if ( (GNUNET_OK != + TALER_amount_add (&refresh_cost, + &refresh_cost, + &fee)) || + (GNUNET_OK != + TALER_amount_add (&refresh_cost, + &refresh_cost, + &value)) ) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } } - if (GNUNET_OK != - TALER_amount_add (&dsi->denom_risk, - &dsi->denom_risk, - &value)) + + /* compute contribution of old coin */ { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; + struct TALER_Amount melt_fee; + + TALER_amount_ntoh (&melt_fee, + &dki->properties.fee_refresh); + if (GNUNET_OK != + TALER_amount_subtract (&amount_without_fee, + amount_with_fee, + &melt_fee)) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "New balance of denomination `%s' is %s\n", - GNUNET_h2s (&new_dki[i]->properties.denom_hash), - TALER_amount2s (&dsi->denom_balance)); - if (GNUNET_OK != - TALER_amount_add (&total_escrow_balance, - &total_escrow_balance, - &value)) + + /* check old coin covers complete expenses */ + if (1 == TALER_amount_cmp (&refresh_cost, + &amount_without_fee)) { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; + /* refresh_cost > amount_without_fee */ + report_amount_arithmetic_inconsistency ("melt (fee)", + rowid, + &amount_without_fee, + &refresh_cost, + -1); + return GNUNET_OK; } - if (GNUNET_OK != - TALER_amount_add (&total_risk, - &total_risk, - &value)) + + /* update outstanding denomination amounts */ + for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++) { - GNUNET_break (0); - cc->qs = GNUNET_DB_STATUS_HARD_ERROR; - return GNUNET_SYSERR; + struct DenominationSummary *dsi; + struct TALER_Amount value; + + dsi = get_denomination_summary (cc, + new_dkis[i], + &new_dkis[i]->properties.denom_hash); + if (NULL == dsi) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + TALER_amount_ntoh (&value, + &new_dkis[i]->properties.value); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Created fresh coin in denomination `%s' of value %s\n", + GNUNET_h2s (&new_dkis[i]->properties.denom_hash), + TALER_amount2s (&value)); + if (GNUNET_OK != + TALER_amount_add (&dsi->denom_balance, + &dsi->denom_balance, + &value)) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_amount_add (&dsi->denom_risk, + &dsi->denom_risk, + &value)) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "New balance of denomination `%s' is %s\n", + GNUNET_h2s (&new_dkis[i]->properties.denom_hash), + TALER_amount2s (&dsi->denom_balance)); + if (GNUNET_OK != + TALER_amount_add (&total_escrow_balance, + &total_escrow_balance, + &value)) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } + if (GNUNET_OK != + TALER_amount_add (&total_risk, + &total_risk, + &value)) + { + GNUNET_break (0); + cc->qs = GNUNET_DB_STATUS_HARD_ERROR; + return GNUNET_SYSERR; + } } } } @@ -4081,6 +4166,9 @@ run (void *cls, GNUNET_assert (GNUNET_OK == TALER_amount_get_zero (currency, &total_bad_sig_loss)); + GNUNET_assert (GNUNET_OK == + TALER_amount_get_zero (currency, + &total_refresh_hanging)); GNUNET_assert (NULL != (report_emergencies = json_array ())); GNUNET_assert (NULL != @@ -4104,6 +4192,8 @@ run (void *cls, GNUNET_assert (NULL != (report_bad_sig_losses = json_array ())); GNUNET_assert (NULL != + (report_refreshs_hanging = json_array ())); + GNUNET_assert (NULL != (report_fee_time_inconsistencies = json_array ())); setup_sessions_and_run (); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -4129,7 +4219,7 @@ run (void *cls, " s:o, s:o, s:o, s:o, s:o," " s:o, s:o, s:o, s:o, s:o," " s:o, s:o, s:o, s:o, s:o," - " s:o }", + " s:o, s:o, s:o }", /* blocks of 5 for easier counting/matching to format string */ /* block */ "reserve_balance_insufficient_inconsistencies", @@ -4199,7 +4289,11 @@ run (void *cls, TALER_JSON_from_amount (&total_aggregation_fee_income), /* block */ "wire_fee_time_inconsistencies", - report_fee_time_inconsistencies); + report_fee_time_inconsistencies, + "total_refresh_hanging", + TALER_JSON_from_amount (&total_refresh_hanging), + "refresh_hanging", + report_refreshs_hanging); GNUNET_break (NULL != report); json_dumpf (report, stdout, diff --git a/src/benchmark/taler-exchange-benchmark.c b/src/benchmark/taler-exchange-benchmark.c index a90773ba0..a2b8d90a4 100644 --- a/src/benchmark/taler-exchange-benchmark.c +++ b/src/benchmark/taler-exchange-benchmark.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA + Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -623,7 +623,7 @@ static void melt_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - uint16_t noreveal_index, + uint32_t noreveal_index, const struct TALER_ExchangePublicKeyP *exchange_pub, const json_t *full_response) { diff --git a/src/exchange-lib/exchange_api_common.c b/src/exchange-lib/exchange_api_common.c index 9d2a827f2..de05348f3 100644 --- a/src/exchange-lib/exchange_api_common.c +++ b/src/exchange-lib/exchange_api_common.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015-2017 Inria & GNUnet e.V. + Copyright (C) 2015-2017 Taler Systems SA 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 @@ -143,8 +143,8 @@ TALER_EXCHANGE_verify_coin_history (const char *currency, struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("coin_sig", &sig), - GNUNET_JSON_spec_fixed_auto ("session_hash", - &rm.session_hash), + GNUNET_JSON_spec_fixed_auto ("rc", + &rm.rc), TALER_JSON_spec_amount_nbo ("melt_fee", &rm.melt_fee), GNUNET_JSON_spec_end() diff --git a/src/exchange-lib/exchange_api_refresh.c b/src/exchange-lib/exchange_api_refresh.c index 305747f63..1d0f406e2 100644 --- a/src/exchange-lib/exchange_api_refresh.c +++ b/src/exchange-lib/exchange_api_refresh.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2015, 2016 GNUnet e.V. + Copyright (C) 2015, 2016, 2017 Taler Systems SA 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 @@ -101,7 +101,7 @@ struct MeltDataP /** * Hash over the melting session. */ - struct GNUNET_HashCode melt_session_hash; + struct TALER_RefreshCommitmentP rc; /** * Number of coins we are melting, in NBO @@ -180,9 +180,9 @@ struct MeltData { /** - * Hash over the melting session. + * Hash over the committed data during refresh operation. */ - struct GNUNET_HashCode melt_session_hash; + struct TALER_RefreshCommitmentP rc; /** * Number of coins we are creating @@ -466,7 +466,6 @@ deserialize_denomination_key (struct TALER_DenominationPublicKey *dk, dk->rsa_public_key = GNUNET_CRYPTO_rsa_public_key_decode (&buf[sizeof (uint32_t)], pbuf_size); - if (NULL == dk->rsa_public_key) { GNUNET_break (0); @@ -542,8 +541,6 @@ serialize_melt_data (const struct MeltData *md, size_t size; size_t asize; char *buf; - unsigned int i; - unsigned int j; size = 0; asize = (size_t) -1; /* make the compiler happy */ @@ -563,18 +560,18 @@ serialize_melt_data (const struct MeltData *md, asize = size; /* just for invariant check later */ size = sizeof (struct MeltDataP); mdp = (struct MeltDataP *) buf; - mdp->melt_session_hash = md->melt_session_hash; + mdp->rc = md->rc; mdp->num_fresh_coins = htons (md->num_fresh_coins); } size += serialize_melted_coin (&md->melted_coin, buf, size); - for (i=0;i<md->num_fresh_coins;i++) + for (unsigned int i=0;i<md->num_fresh_coins;i++) size += serialize_denomination_key (&md->fresh_pks[i], buf, size); - for (i=0;i<TALER_CNC_KAPPA;i++) - for(j=0;j<md->num_fresh_coins;j++) + for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) + for(unsigned int j=0;j<md->num_fresh_coins;j++) size += serialize_fresh_coin (&md->fresh_coins[i][j], buf, size); @@ -607,7 +604,7 @@ deserialize_melt_data (const char *buf, buf, sizeof (struct MeltDataP)); md = GNUNET_new (struct MeltData); - md->melt_session_hash = mdp.melt_session_hash; + md->rc = mdp.rc; md->num_fresh_coins = ntohs (mdp.num_fresh_coins); md->fresh_pks = GNUNET_new_array (md->num_fresh_coins, struct TALER_DenominationPublicKey); @@ -700,34 +697,14 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr { struct MeltData md; char *buf; - struct GNUNET_HashContext *hash_context; struct TALER_Amount total; struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA]; + struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA]; GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv, &coin_pub.eddsa_pub); - hash_context = GNUNET_CRYPTO_hash_context_start (); /* build up melt data structure */ - for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) - { - struct GNUNET_CRYPTO_EcdhePrivateKey *tpk; - struct TALER_TransferPublicKeyP tp; - - tpk = GNUNET_CRYPTO_ecdhe_key_create (); - md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk; - GNUNET_free (tpk); - - GNUNET_CRYPTO_ecdhe_key_get_public (&md.melted_coin.transfer_priv[i].ecdhe_priv, - &tp.ecdhe_pub); - GNUNET_CRYPTO_hash_context_read (hash_context, - &tp, - sizeof (struct TALER_TransferPublicKeyP)); - /* DH */ - TALER_link_derive_transfer_secret (melt_priv, - &md.melted_coin.transfer_priv[i], - &trans_sec[i]); - } md.num_fresh_coins = fresh_pks_len; md.melted_coin.coin_priv = *melt_priv; md.melted_coin.melt_amount_with_fee = *melt_amount; @@ -735,6 +712,9 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr md.melted_coin.original_value = melt_pk->value; md.melted_coin.expire_deposit = melt_pk->expire_deposit; + GNUNET_assert (GNUNET_OK == + TALER_amount_get_zero (melt_amount->currency, + &total)); md.melted_coin.pub_key.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (melt_pk->key.rsa_public_key); md.melted_coin.sig.rsa_signature @@ -742,40 +722,24 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr md.fresh_pks = GNUNET_new_array (fresh_pks_len, struct TALER_DenominationPublicKey); for (unsigned int i=0;i<fresh_pks_len;i++) + { md.fresh_pks[i].rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pks[i].key.rsa_public_key); - for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) - { - md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len, - struct TALER_PlanchetSecretsP); - for (unsigned int j=0;j<fresh_pks_len;j++) - { - TALER_planchet_setup_refresh (&trans_sec[i], - j, - &md.fresh_coins[i][j]); - } - } - - /* verify that melt_amount is above total cost */ - GNUNET_assert (GNUNET_OK == - TALER_amount_get_zero (melt_amount->currency, - &total)); - for (unsigned int j=0;j<fresh_pks_len;j++) - { if ( (GNUNET_OK != TALER_amount_add (&total, &total, - &fresh_pks[j].value)) || + &fresh_pks[i].value)) || (GNUNET_OK != TALER_amount_add (&total, &total, - &fresh_pks[j].fee_withdraw)) ) + &fresh_pks[i].fee_withdraw)) ) { GNUNET_break (0); free_melt_data (&md); return NULL; } } + /* verify that melt_amount is above total cost */ if (1 == TALER_amount_cmp (&total, melt_amount) ) @@ -787,63 +751,64 @@ TALER_EXCHANGE_refresh_prepare (const struct TALER_CoinSpendPrivateKeyP *melt_pr return NULL; } - /* next, add all of the hashes from the denomination keys to the - hash_context */ - for (unsigned int i=0;i<fresh_pks_len;i++) - { - char *buf; - size_t buf_size; - - buf_size = GNUNET_CRYPTO_rsa_public_key_encode (fresh_pks[i].key.rsa_public_key, - &buf); - GNUNET_CRYPTO_hash_context_read (hash_context, - buf, - buf_size); - GNUNET_free (buf); - } + /* build up coins */ + for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) { - struct TALER_AmountNBO melt_amountn; + struct GNUNET_CRYPTO_EcdhePrivateKey *tpk; - GNUNET_CRYPTO_hash_context_read (hash_context, - &coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP)); - TALER_amount_hton (&melt_amountn, - melt_amount); - GNUNET_CRYPTO_hash_context_read (hash_context, - &melt_amountn, - sizeof (struct TALER_AmountNBO)); + tpk = GNUNET_CRYPTO_ecdhe_key_create (); + md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk; + GNUNET_free (tpk); - } - for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) - { - for (unsigned int j = 0; j < fresh_pks_len; j++) + GNUNET_CRYPTO_ecdhe_key_get_public (&md.melted_coin.transfer_priv[i].ecdhe_priv, + &rce[i].transfer_pub.ecdhe_pub); + TALER_link_derive_transfer_secret (melt_priv, + &md.melted_coin.transfer_priv[i], + &trans_sec[i]); + md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len, + struct TALER_PlanchetSecretsP); + rce[i].new_coins = GNUNET_new_array (fresh_pks_len, + struct TALER_RefreshCoinData); + for (unsigned int j=0;j<fresh_pks_len;j++) { - const struct TALER_PlanchetSecretsP *fc; /* coin this is about */ + struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j]; + struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j]; struct TALER_PlanchetDetail pd; - fc = &md.fresh_coins[i][j]; + TALER_planchet_setup_refresh (&trans_sec[i], + j, + fc); if (GNUNET_OK != TALER_planchet_prepare (&md.fresh_pks[j], fc, &pd)) { GNUNET_break_op (0); - GNUNET_CRYPTO_hash_context_abort (hash_context); free_melt_data (&md); return NULL; } - GNUNET_CRYPTO_hash_context_read (hash_context, - pd.coin_ev, - pd.coin_ev_size); - GNUNET_free (pd.coin_ev); + rcd->dk = &md.fresh_pks[j]; + rcd->coin_ev = pd.coin_ev; + rcd->coin_ev_size = pd.coin_ev_size; } } - GNUNET_CRYPTO_hash_context_finish (hash_context, - &md.melt_session_hash); + /* Compute refresh commitment */ + TALER_refresh_get_commitment (&md.rc, + TALER_CNC_KAPPA, + fresh_pks_len, + rce, + &coin_pub, + melt_amount); /* finally, serialize everything */ buf = serialize_melt_data (&md, res_size); + for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) + { + for (unsigned int j = 0; j < fresh_pks_len; j++) + GNUNET_free_non_null (rce[i].new_coins[j].coin_ev); + GNUNET_free_non_null (rce[i].new_coins); + } free_melt_data (&md); return buf; } @@ -909,14 +874,14 @@ static int verify_refresh_melt_signature_ok (struct TALER_EXCHANGE_RefreshMeltHandle *rmh, const json_t *json, struct TALER_ExchangePublicKeyP *exchange_pub, - uint16_t *noreveal_index) + uint32_t *noreveal_index) { struct TALER_ExchangeSignatureP exchange_sig; const struct TALER_EXCHANGE_Keys *key_state; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig), GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub), - GNUNET_JSON_spec_uint16 ("noreveal_index", noreveal_index), + GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index), GNUNET_JSON_spec_end() }; struct TALER_RefreshMeltConfirmationPS confirm; @@ -950,9 +915,8 @@ verify_refresh_melt_signature_ok (struct TALER_EXCHANGE_RefreshMeltHandle *rmh, /* verify signature by exchange */ confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT); confirm.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS)); - confirm.session_hash = rmh->md->melt_session_hash; - confirm.noreveal_index = htons (*noreveal_index); - confirm.reserved = htons (0); + confirm.rc = rmh->md->rc; + confirm.noreveal_index = htonl (*noreveal_index); if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT, &confirm.purpose, @@ -1076,7 +1040,7 @@ handle_refresh_melt_finished (void *cls, const json_t *json) { struct TALER_EXCHANGE_RefreshMeltHandle *rmh = cls; - uint16_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */ + uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */ struct TALER_ExchangePublicKeyP exchange_pub; rmh->job = NULL; @@ -1145,7 +1109,7 @@ handle_refresh_melt_finished (void *cls, rmh->melt_cb (rmh->melt_cb_cls, response_code, TALER_JSON_get_error_code (json), - UINT16_MAX, + UINT32_MAX, NULL, json); TALER_EXCHANGE_refresh_melt_cancel (rmh); @@ -1153,46 +1117,6 @@ handle_refresh_melt_finished (void *cls, /** - * Convert a coin to be melted to the respective JSON encoding. - * - * @param melt_session_hash session hash to use - * @param mc coin to be melted - * @return JSON encoding of the melting request - */ -static json_t * -melted_coin_to_json (const struct GNUNET_HashCode *melt_session_hash, - const struct MeltedCoin *mc) -{ - struct TALER_CoinSpendSignatureP confirm_sig; - struct TALER_RefreshMeltCoinAffirmationPS melt; - - melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); - melt.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); - melt.session_hash = *melt_session_hash; - TALER_amount_hton (&melt.amount_with_fee, - &mc->melt_amount_with_fee); - TALER_amount_hton (&melt.melt_fee, - &mc->fee_melt); - GNUNET_CRYPTO_eddsa_key_get_public (&mc->coin_priv.eddsa_priv, - &melt.coin_pub.eddsa_pub); - GNUNET_CRYPTO_eddsa_sign (&mc->coin_priv.eddsa_priv, - &melt.purpose, - &confirm_sig.eddsa_signature); - return json_pack ("{s:o, s:o, s:o, s:o, s:o}", - "coin_pub", - GNUNET_JSON_from_data_auto (&melt.coin_pub), - "denom_pub", - GNUNET_JSON_from_rsa_public_key (mc->pub_key.rsa_public_key), - "denom_sig", - GNUNET_JSON_from_rsa_signature (mc->sig.rsa_signature), - "confirm_sig", - GNUNET_JSON_from_data_auto (&confirm_sig), - "value_with_fee", - TALER_JSON_from_amount (&mc->melt_amount_with_fee)); -} - - -/** * Submit a melt request to the exchange and get the exchange's * response. * @@ -1220,17 +1144,12 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange, void *melt_cb_cls) { json_t *melt_obj; - json_t *new_denoms; - json_t *melt_coin; - json_t *coin_evs; - json_t *transfer_pubs; - json_t *tmp; struct TALER_EXCHANGE_RefreshMeltHandle *rmh; CURL *eh; struct GNUNET_CURL_Context *ctx; struct MeltData *md; - unsigned int i; - unsigned int j; + struct TALER_CoinSpendSignatureP confirm_sig; + struct TALER_RefreshMeltCoinAffirmationPS melt; GNUNET_assert (GNUNET_YES == MAH_handle_is_ready (exchange)); @@ -1242,78 +1161,35 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange, return NULL; } - /* build JSON request, each of the 4 arrays first */ - new_denoms = json_array (); - melt_coin = melted_coin_to_json (&md->melt_session_hash, - &md->melted_coin); - coin_evs = json_array (); - transfer_pubs = json_array (); - - /* now transfer_pubs */ - for (j=0;j<TALER_CNC_KAPPA;j++) - { - const struct MeltedCoin *mc = &md->melted_coin; - struct TALER_TransferPublicKeyP transfer_pub; - - GNUNET_CRYPTO_ecdhe_key_get_public (&mc->transfer_priv[j].ecdhe_priv, - &transfer_pub.ecdhe_pub); - GNUNET_assert (0 == - json_array_append_new (transfer_pubs, - GNUNET_JSON_from_data_auto (&transfer_pub))); - } - - /* now new_denoms */ - for (i=0;i<md->num_fresh_coins;i++) - { - GNUNET_assert (0 == - json_array_append_new (new_denoms, - GNUNET_JSON_from_rsa_public_key - (md->fresh_pks[i].rsa_public_key))); - } - - /* now coin_evs */ - for (j=0;j<TALER_CNC_KAPPA;j++) - { - tmp = json_array (); - for (i=0;i<md->num_fresh_coins;i++) - { - const struct TALER_PlanchetSecretsP *fc = &md->fresh_coins[j][i]; - struct TALER_PlanchetDetail pd; - - if (GNUNET_OK != - TALER_planchet_prepare (&md->fresh_pks[i], - fc, - &pd)) - { - /* This should have been noticed during the preparation stage. */ - GNUNET_break (0); - json_decref (new_denoms); - json_decref (tmp); - json_decref (coin_evs); - json_decref (melt_coin); - json_decref (transfer_pubs); - return NULL; - } - GNUNET_assert (0 == - json_array_append_new (tmp, - GNUNET_JSON_from_data (pd.coin_ev, - pd.coin_ev_size))); - GNUNET_free (pd.coin_ev); - } - GNUNET_assert (0 == - json_array_append_new (coin_evs, - tmp)); - } - - /* finally, assemble main JSON request from constitutent arrays */ - melt_obj = json_pack ("{s:o, s:o, s:o, s:o}", - "new_denoms", new_denoms, - "melt_coin", melt_coin, - "coin_evs", coin_evs, - "transfer_pubs", transfer_pubs); + melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); + melt.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); + melt.rc = md->rc; + TALER_amount_hton (&melt.amount_with_fee, + &md->melted_coin.melt_amount_with_fee); + TALER_amount_hton (&melt.melt_fee, + &md->melted_coin.fee_melt); + GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv, + &melt.coin_pub.eddsa_pub); + GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv, + &melt.purpose, + &confirm_sig.eddsa_signature); + melt_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}", + "coin_pub", + GNUNET_JSON_from_data_auto (&melt.coin_pub), + "denom_pub", + GNUNET_JSON_from_rsa_public_key (md->melted_coin.pub_key.rsa_public_key), + "denom_sig", + GNUNET_JSON_from_rsa_signature (md->melted_coin.sig.rsa_signature), + "confirm_sig", + GNUNET_JSON_from_data_auto (&confirm_sig), + "value_with_fee", + TALER_JSON_from_amount (&md->melted_coin.melt_amount_with_fee), + "rc", + GNUNET_JSON_from_data_auto (&melt.rc)); if (NULL == melt_obj) { GNUNET_break (0); + free_melt_data (md); return NULL; } @@ -1325,7 +1201,6 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange, rmh->md = md; rmh->url = MAH_path_to_url (exchange, "/refresh/melt"); - eh = curl_easy_init (); GNUNET_assert (NULL != (rmh->json_enc = json_dumps (melt_obj, @@ -1449,7 +1324,6 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh, struct TALER_CoinSpendPrivateKeyP *coin_privs, struct TALER_DenominationSignature *sigs) { - unsigned int i; json_t *jsona; struct GNUNET_JSON_Specification outer_spec[] = { GNUNET_JSON_spec_json ("ev_sigs", &jsona), @@ -1478,7 +1352,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh, GNUNET_JSON_parse_free (outer_spec); return GNUNET_SYSERR; } - for (i=0;i<rrh->md->num_fresh_coins;i++) + for (unsigned int i=0;i<rrh->md->num_fresh_coins;i++) { const struct TALER_PlanchetSecretsP *fc; struct TALER_DenominationPublicKey *pk; @@ -1559,7 +1433,6 @@ handle_refresh_reveal_finished (void *cls, { struct TALER_CoinSpendPrivateKeyP coin_privs[rrh->md->num_fresh_coins]; struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins]; - unsigned int i; int ret; memset (sigs, 0, sizeof (sigs)); @@ -1582,7 +1455,7 @@ handle_refresh_reveal_finished (void *cls, json); rrh->reveal_cb = NULL; } - for (i=0;i<rrh->md->num_fresh_coins;i++) + for (unsigned int i=0;i<rrh->md->num_fresh_coins;i++) if (NULL != sigs[i].rsa_signature) GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature); } @@ -1647,17 +1520,19 @@ struct TALER_EXCHANGE_RefreshRevealHandle * TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange, size_t refresh_data_length, const char *refresh_data, - uint16_t noreveal_index, + uint32_t noreveal_index, TALER_EXCHANGE_RefreshRevealCallback reveal_cb, void *reveal_cb_cls) { struct TALER_EXCHANGE_RefreshRevealHandle *rrh; json_t *transfer_privs; + json_t *new_denoms_h; + json_t *coin_evs; json_t *reveal_obj; CURL *eh; struct GNUNET_CURL_Context *ctx; struct MeltData *md; - unsigned int j; + struct TALER_TransferPublicKeyP transfer_pub; GNUNET_assert (GNUNET_YES == MAH_handle_is_ready (exchange)); @@ -1678,9 +1553,45 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange, return NULL; } + /* now transfer_pub */ + GNUNET_CRYPTO_ecdhe_key_get_public (&md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv, + &transfer_pub.ecdhe_pub); + + /* now new_denoms */ + GNUNET_assert (NULL != (new_denoms_h = json_array ())); + GNUNET_assert (NULL != (coin_evs = json_array ())); + for (unsigned int i=0;i<md->num_fresh_coins;i++) + { + struct GNUNET_HashCode denom_hash; + struct TALER_PlanchetDetail pd; + + GNUNET_CRYPTO_rsa_public_key_hash (md->fresh_pks[i].rsa_public_key, + &denom_hash); + GNUNET_assert (0 == + json_array_append_new (new_denoms_h, + GNUNET_JSON_from_data_auto (&denom_hash))); + + if (GNUNET_OK != + TALER_planchet_prepare (&md->fresh_pks[i], + &md->fresh_coins[noreveal_index][i], + &pd)) + { + /* This should have been noticed during the preparation stage. */ + GNUNET_break (0); + json_decref (new_denoms_h); + json_decref (coin_evs); + return NULL; + } + GNUNET_assert (0 == + json_array_append_new (coin_evs, + GNUNET_JSON_from_data (pd.coin_ev, + pd.coin_ev_size))); + GNUNET_free (pd.coin_ev); + } + /* build array of transfer private keys */ - transfer_privs = json_array (); - for (j=0;j<TALER_CNC_KAPPA;j++) + GNUNET_assert (NULL != (transfer_privs = json_array ())); + for (unsigned int j=0;j<TALER_CNC_KAPPA;j++) { if (j == noreveal_index) { @@ -1694,11 +1605,17 @@ TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange, } /* build main JSON request */ - reveal_obj = json_pack ("{s:o, s:o}", - "session_hash", - GNUNET_JSON_from_data_auto (&md->melt_session_hash), + reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}", + "rc", + GNUNET_JSON_from_data_auto (&md->rc), + "transfer_pub", + GNUNET_JSON_from_data_auto (&transfer_pub), "transfer_privs", - transfer_privs); + transfer_privs, + "new_denoms_h", + new_denoms_h, + "coin_evs", + coin_evs); if (NULL == reveal_obj) { GNUNET_break (0); diff --git a/src/exchange-lib/test_exchange_api.c b/src/exchange-lib/test_exchange_api.c index 7c0dfa982..5de9318ff 100644 --- a/src/exchange-lib/test_exchange_api.c +++ b/src/exchange-lib/test_exchange_api.c @@ -1199,7 +1199,7 @@ static void melt_cb (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - uint16_t noreveal_index, + uint32_t noreveal_index, const struct TALER_ExchangePublicKeyP *exchange_pub, const json_t *full_response) { @@ -1312,8 +1312,6 @@ link_cb (void *cls, struct InterpreterState *is = cls; struct Command *cmd = &is->commands[is->ip]; const struct Command *ref; - unsigned int i; - unsigned int j; unsigned int found; cmd->details.refresh_link.rlh = NULL; @@ -1341,16 +1339,16 @@ link_cb (void *cls, return; } /* check that the coins match */ - for (i=0;i<num_coins;i++) - for (j=i+1;j<num_coins;j++) + for (unsigned int i=0;i<num_coins;i++) + for (unsigned int j=i+1;j<num_coins;j++) if (0 == memcmp (&coin_privs[i], &coin_privs[j], sizeof (struct TALER_CoinSpendPrivateKeyP))) GNUNET_break (0); /* Note: coins might be legitimately permutated in here... */ found = 0; - for (i=0;i<num_coins;i++) - for (j=0;j<num_coins;j++) + for (unsigned int i=0;i<num_coins;i++) + for (unsigned int j=0;j<num_coins;j++) { const struct FreshCoin *fc; diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 30bc33e17..bd7777ce3 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -155,7 +155,7 @@ TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionLis if (GNUNET_OK != TALER_amount_add (&spent, &spent, - &pos->details.melt->amount_with_fee)) + &pos->details.melt->session.amount_with_fee)) { GNUNET_break (0); return GNUNET_SYSERR; diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c index be87f6d49..801d6fee6 100644 --- a/src/exchange/taler-exchange-httpd_keystate.c +++ b/src/exchange/taler-exchange-httpd_keystate.c @@ -39,7 +39,7 @@ * release version, and the format is NOT the same that semantic * versioning uses either. */ -#define TALER_PROTOCOL_VERSION "1:0:1" +#define TALER_PROTOCOL_VERSION "2:0:0" /** @@ -1499,7 +1499,7 @@ make_fresh_key_state () GNUNET_h2s (&dke->denom_key_hash), dke); } - + /* Determine size of `krd_array` by counting number of discrete denomination key starting times. */ last = GNUNET_TIME_UNIT_ZERO_ABS; @@ -1662,15 +1662,37 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, enum TEH_KS_DenominationKeyUse use) { struct GNUNET_HashCode hc; + + GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key, + &hc); + return TEH_KS_denomination_key_lookup_by_hash (key_state, + &hc, + use); +} + + +/** + * Look up the issue for a denom public key. Note that the result + * is only valid while the @a key_state is not released! + * + * @param key_state state to look in + * @param denom_pub_hash hash of denomination public key + * @param use purpose for which the key is being located + * @return the denomination key issue, + * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) + */ +struct TALER_EXCHANGEDB_DenominationKeyIssueInformation * +TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state, + const struct GNUNET_HashCode *denom_pub_hash, + enum TEH_KS_DenominationKeyUse use) +{ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; struct GNUNET_TIME_Absolute now; const struct GNUNET_CONTAINER_MultiHashMap *map; - GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key, - &hc); map = (TEH_KS_DKU_PAYBACK == use) ? key_state->revoked_map : key_state->denomkey_map; dki = GNUNET_CONTAINER_multihashmap_get (map, - &hc); + denom_pub_hash); if (NULL == dki) return NULL; now = GNUNET_TIME_absolute_get (); @@ -1679,7 +1701,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as start time is in the future\n", - GNUNET_h2s (&hc)); + GNUNET_h2s (denom_pub_hash)); return NULL; } now = GNUNET_TIME_absolute_get (); @@ -1691,7 +1713,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to create coins has passed\n", - GNUNET_h2s (&hc)); + GNUNET_h2s (denom_pub_hash)); return NULL; } break; @@ -1701,7 +1723,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to spend coin has passed\n", - GNUNET_h2s (&hc)); + GNUNET_h2s (denom_pub_hash)); return NULL; } break; @@ -1711,7 +1733,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Not returning DKI for %s, as time to payback coin has passed\n", - GNUNET_h2s (&hc)); + GNUNET_h2s (denom_pub_hash)); return NULL; } break; diff --git a/src/exchange/taler-exchange-httpd_keystate.h b/src/exchange/taler-exchange-httpd_keystate.h index b956c6308..c332182e0 100644 --- a/src/exchange/taler-exchange-httpd_keystate.h +++ b/src/exchange/taler-exchange-httpd_keystate.h @@ -121,6 +121,22 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state, /** + * Look up the issue for a denom public key. Note that the result + * is only valid while the @a key_state is not released! + * + * @param key_state state to look in + * @param denom_pub_hash hash of denomination public key + * @param use purpose for which the key is being located + * @return the denomination key issue, + * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use) + */ +struct TALER_EXCHANGEDB_DenominationKeyIssueInformation * +TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state, + const struct GNUNET_HashCode *denom_pub_hash, + enum TEH_KS_DenominationKeyUse use); + + +/** * Read signals from a pipe in a loop, and reload keys from disk if * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and * restart if SIGHUP is received. diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c b/src/exchange/taler-exchange-httpd_refresh_link.c index 0be699840..aee23369c 100644 --- a/src/exchange/taler-exchange-httpd_refresh_link.c +++ b/src/exchange/taler-exchange-httpd_refresh_link.c @@ -32,183 +32,88 @@ /** - * @brief Information for each session a coin was melted into. - */ -struct TEH_RESPONSE_LinkSessionInfo -{ - /** - * Transfer public key of the coin. - */ - struct TALER_TransferPublicKeyP transfer_pub; - - /** - * Linked data of coins being created in the session. - */ - struct TALER_EXCHANGEDB_LinkDataList *ldl; - -}; - - -/** * Closure for #handle_transfer_data(). */ struct HTD_Context { /** - * Public key of the coin that we are tracing. + * Public key of the coin for which we are running /refresh/link. */ struct TALER_CoinSpendPublicKeyP coin_pub; /** - * Session link data we collect. - */ - struct TEH_RESPONSE_LinkSessionInfo *sessions; - - /** - * Database session. Nothing to do with @a sessions. + * Json array with transfer data we collect. */ - struct TALER_EXCHANGEDB_Session *session; - - /** - * MHD connection, for queueing replies. - */ - struct MHD_Connection *connection; - - /** - * Number of sessions the coin was melted into. - */ - unsigned int num_sessions; + json_t *mlist; /** - * 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 #TEH_execute_refresh_link), - * #GNUNET_OK if we should call #reply_refresh_link_success(). + * Taler error code. */ - int status; + enum TALER_ErrorCode ec; }; /** - * Send a response for "/refresh/link". - * - * @param connection the connection to send the response to - * @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 - */ -static int -reply_refresh_link_success (struct MHD_Connection *connection, - unsigned int num_sessions, - const struct TEH_RESPONSE_LinkSessionInfo *sessions) -{ - json_t *mlist; - int res; - - mlist = json_array (); - for (unsigned int i=0;i<num_sessions;i++) - { - json_t *list = json_array (); - json_t *root; - - for (const struct TALER_EXCHANGEDB_LinkDataList *pos = sessions[i].ldl; - NULL != pos; - pos = pos->next) - { - json_t *obj; - - obj = json_object (); - json_object_set_new (obj, - "denom_pub", - GNUNET_JSON_from_rsa_public_key (pos->denom_pub.rsa_public_key)); - json_object_set_new (obj, - "ev_sig", - GNUNET_JSON_from_rsa_signature (pos->ev_sig.rsa_signature)); - GNUNET_assert (0 == - json_array_append_new (list, - obj)); - } - root = json_object (); - json_object_set_new (root, - "new_coins", - list); - json_object_set_new (root, - "transfer_pub", - GNUNET_JSON_from_data_auto (&sessions[i].transfer_pub)); - GNUNET_assert (0 == - json_array_append_new (mlist, - root)); - } - res = TEH_RESPONSE_reply_json (connection, - mlist, - MHD_HTTP_OK); - json_decref (mlist); - return res; -} - - -/** * 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 ldl link data related to @a transfer_pub */ static void -handle_transfer_data (void *cls, - const struct GNUNET_HashCode *session_hash, - const struct TALER_TransferPublicKeyP *transfer_pub) +handle_link_data (void *cls, + const struct TALER_TransferPublicKeyP *transfer_pub, + const struct TALER_EXCHANGEDB_LinkDataList *ldl) { struct HTD_Context *ctx = cls; - struct TALER_EXCHANGEDB_LinkDataList *ldl; - struct TEH_RESPONSE_LinkSessionInfo *lsi; - enum GNUNET_DB_QueryStatus qs; + json_t *list; + json_t *root; - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx->status) + if (NULL == ctx->mlist) return; - ldl = NULL; - qs = TEH_plugin->get_link_data_list (TEH_plugin->cls, - ctx->session, - session_hash, - &ldl); - if (qs <= 0) + if (NULL == (list = json_array ())) + goto fail; + + for (const struct TALER_EXCHANGEDB_LinkDataList *pos = ldl; + NULL != pos; + pos = pos->next) { - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - ctx->status = GNUNET_DB_STATUS_HARD_ERROR; - else - ctx->status = qs; - return; + json_t *obj; + + if (NULL == (obj = json_object ())) + goto fail; + json_object_set_new (obj, + "denom_pub", + GNUNET_JSON_from_rsa_public_key (pos->denom_pub.rsa_public_key)); + json_object_set_new (obj, + "ev_sig", + GNUNET_JSON_from_rsa_signature (pos->ev_sig.rsa_signature)); + if (0 != + json_array_append_new (list, + obj)) + goto fail; } - GNUNET_assert (NULL != ldl); - 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->ldl = ldl; -} - - -/** - * Free session data kept in @a ctx - * - * @param ctx context to clean up - */ -static void -purge_context (struct HTD_Context *ctx) -{ - for (unsigned int i=0;i<ctx->num_sessions;i++) - TEH_plugin->free_link_data_list (TEH_plugin->cls, - ctx->sessions[i].ldl); - GNUNET_free_non_null (ctx->sessions); - ctx->sessions = NULL; - ctx->num_sessions = 0; + if (NULL == (root = json_object ())) + goto fail; + json_object_set_new (root, + "new_coins", + list); + json_object_set_new (root, + "transfer_pub", + GNUNET_JSON_from_data_auto (transfer_pub)); + if (0 != + json_array_append_new (ctx->mlist, + root)) + goto fail; + return; + fail: + ctx->ec = TALER_EC_JSON_ALLOCATION_FAILURE; + json_decref (ctx->mlist); + ctx->mlist = NULL; } @@ -239,14 +144,18 @@ refresh_link_transaction (void *cls, struct HTD_Context *ctx = cls; enum GNUNET_DB_QueryStatus qs; - ctx->session = session; - ctx->status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; - qs = TEH_plugin->get_transfer (TEH_plugin->cls, - session, - &ctx->coin_pub, - &handle_transfer_data, - ctx); - ctx->session = NULL; + qs = TEH_plugin->get_link_data (TEH_plugin->cls, + session, + &ctx->coin_pub, + &handle_link_data, + ctx); + if (NULL == ctx->mlist) + { + *mhd_ret = TEH_RESPONSE_reply_internal_error (connection, + ctx->ec, + "coin_pub"); + return GNUNET_DB_STATUS_HARD_ERROR; + } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection, @@ -254,21 +163,6 @@ refresh_link_transaction (void *cls, "coin_pub"); return GNUNET_DB_STATUS_HARD_ERROR; } - if (0 < qs) - { - qs = ctx->status; - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) - { - *mhd_ret = TEH_RESPONSE_reply_json_pack (ctx->connection, - MHD_HTTP_NOT_FOUND, - "{s:s}", - "error", - "link data not found (link)"); - return GNUNET_DB_STATUS_HARD_ERROR; - } - return qs; - } - purge_context (ctx); return qs; } @@ -306,19 +200,21 @@ TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh, return MHD_NO; if (GNUNET_OK != res) return MHD_YES; + ctx.mlist = json_array (); if (GNUNET_OK != TEH_DB_run_transaction (connection, &mhd_ret, &refresh_link_transaction, &ctx)) { - purge_context (&ctx); + if (NULL != ctx.mlist) + json_decref (ctx.mlist); return mhd_ret; } - mhd_ret = reply_refresh_link_success (connection, - ctx.num_sessions, - ctx.sessions); - purge_context (&ctx); + mhd_ret = TEH_RESPONSE_reply_json (connection, + ctx.mlist, + MHD_HTTP_OK); + json_decref (ctx.mlist); return mhd_ret; } diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c index 320ec9d8b..400d2bb8c 100644 --- a/src/exchange/taler-exchange-httpd_refresh_melt.c +++ b/src/exchange/taler-exchange-httpd_refresh_melt.c @@ -32,38 +32,6 @@ /** - * @brief Details about a melt operation of an individual coin. - */ -struct TEH_DB_MeltDetails -{ - - /** - * Information about the coin being melted. - */ - struct TALER_CoinPublicInfo coin_info; - - /** - * Signature allowing the melt (using - * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign over. - */ - struct TALER_CoinSpendSignatureP melt_sig; - - /** - * How much of the coin's value did the client allow to be melted? - * This amount includes the fees, so the final amount contributed - * to the melt is this value minus the fee for melting the coin. - */ - struct TALER_Amount melt_amount_with_fee; - - /** - * What fee is earned by the exchange? Set delayed during - * #verify_coin_public_info(). - */ - struct TALER_Amount melt_fee; -}; - - -/** * Send a response for a failed "/refresh/melt" request. The * transaction history of the given coin demonstrates that the * @a residual value of the coin is below the @a requested @@ -116,14 +84,14 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection, * Send a response to a "/refresh/melt" request. * * @param connection the connection to send the response to - * @param session_hash hash of the refresh session + * @param rc value the client commited to * @param noreveal_index which index will the client not have to reveal * @return a MHD status code */ static int reply_refresh_melt_success (struct MHD_Connection *connection, - const struct GNUNET_HashCode *session_hash, - uint16_t noreveal_index) + const struct TALER_RefreshCommitmentP *rc, + uint32_t noreveal_index) { struct TALER_RefreshMeltConfirmationPS body; struct TALER_ExchangePublicKeyP pub; @@ -132,9 +100,8 @@ reply_refresh_melt_success (struct MHD_Connection *connection, body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS)); body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT); - body.session_hash = *session_hash; - body.noreveal_index = htons (noreveal_index); - body.reserved = htons (0); + body.rc = *rc; + body.noreveal_index = htonl (noreveal_index); if (GNUNET_OK != TEH_KS_sign (&body.purpose, &pub, @@ -162,63 +129,22 @@ struct RefreshMeltContext { /** - * Key state that can be used to lookup keys. + * noreveal_index is only initialized during + * #refresh_melt_transaction(). */ - struct TEH_KS_StateHandle *key_state; + struct TALER_EXCHANGEDB_RefreshSession refresh_session; /** - * Information about the denomination key of the coin being - * melted. + * Information about the @e coin's denomination. */ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; - /** - * Array of denominations of the fresh coins. - */ - struct TALER_DenominationPublicKey *denom_pubs; - - /** - * Number of new coins to be generated in the melt. - * Size of the @e denom_pubs array. - */ - unsigned int num_newcoins; - - /** - * Details about the coin to be melted. - */ - struct TEH_DB_MeltDetails coin_melt_details; - - /** - * Set to the session hash once the @e hash_context has finished. - */ - struct GNUNET_HashCode session_hash; - - /** - * Hash operation used to calculate the session hash. - */ - struct GNUNET_HashContext *hash_context; - - /** - * Committments to the blinded envelopes for the fresh coins. - */ - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA]; - - /** - * Commmittments to the transfer public keys. - */ - struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA]; - - /** - * Initialized during #refresh_melt_transaction(). - */ - struct TALER_EXCHANGEDB_RefreshSession refresh_session; - }; /** - * Parse coin melt requests from a JSON object and write them to - * the database. + * Check that the coin has sufficient funds left for the selected + * melt operation. * * @param connection the connection to send errors to * @param session the database connection @@ -233,20 +159,19 @@ refresh_check_melt (struct MHD_Connection *connection, int *mhd_ret) { struct TALER_EXCHANGEDB_TransactionList *tl; - struct TALER_EXCHANGEDB_RefreshMelt *meltp = &rmc->refresh_session.melt; struct TALER_Amount coin_value; - struct TALER_Amount coin_residual; struct TALER_Amount spent; enum GNUNET_DB_QueryStatus qs; TALER_amount_ntoh (&coin_value, &rmc->dki->issue.properties.value); - /* fee for THIS transaction; the melt amount includes the fee! */ - spent = rmc->coin_melt_details.melt_amount_with_fee; + /* Start with cost of this melt transaction */ + spent = rmc->refresh_session.amount_with_fee; + /* add historic transaction costs of this coin */ qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls, session, - &rmc->coin_melt_details.coin_info.coin_pub, + &rmc->refresh_session.coin.coin_pub, &tl); if (0 > qs) { @@ -267,33 +192,32 @@ refresh_check_melt (struct MHD_Connection *connection, TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED); return GNUNET_DB_STATUS_HARD_ERROR; } + /* Refuse to refresh when the coin's value is insufficient for the cost of all transactions. */ if (TALER_amount_cmp (&coin_value, &spent) < 0) { + struct TALER_Amount coin_residual; + GNUNET_assert (GNUNET_SYSERR != TALER_amount_subtract (&coin_residual, &spent, - &rmc->coin_melt_details.melt_amount_with_fee)); + &rmc->refresh_session.amount_with_fee)); *mhd_ret = reply_refresh_melt_insufficient_funds (connection, - &rmc->coin_melt_details.coin_info.coin_pub, + &rmc->refresh_session.coin.coin_pub, coin_value, tl, - &rmc->coin_melt_details.melt_amount_with_fee, + &rmc->refresh_session.amount_with_fee, &coin_residual); TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, tl); return GNUNET_DB_STATUS_HARD_ERROR; } + + /* we're good, coin has sufficient funds to be melted */ TEH_plugin->free_coin_transaction_list (TEH_plugin->cls, tl); - - meltp->coin = rmc->coin_melt_details.coin_info; - meltp->coin_sig = rmc->coin_melt_details.melt_sig; - meltp->session_hash = rmc->session_hash; - meltp->amount_with_fee = rmc->coin_melt_details.melt_amount_with_fee; - meltp->melt_fee = rmc->coin_melt_details.melt_fee; return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } @@ -325,17 +249,19 @@ refresh_melt_transaction (void *cls, int *mhd_ret) { struct RefreshMeltContext *rmc = cls; + struct TALER_EXCHANGEDB_RefreshMelt rm; enum GNUNET_DB_QueryStatus qs; - qs = TEH_plugin->get_refresh_session (TEH_plugin->cls, - session, - &rmc->session_hash, - &rmc->refresh_session); + /* Check if we already created such a session */ + qs = TEH_plugin->get_melt (TEH_plugin->cls, + session, + &rmc->refresh_session.rc, + &rm); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { *mhd_ret = reply_refresh_melt_success (connection, - &rmc->session_hash, - rmc->refresh_session.noreveal_index); + &rmc->refresh_session.rc, + rm.session.noreveal_index); return GNUNET_DB_STATUS_HARD_ERROR; } if (0 > qs) @@ -346,12 +272,7 @@ refresh_melt_transaction (void *cls, return qs; } - /* store 'global' session data */ - rmc->refresh_session.num_newcoins = rmc->num_newcoins; - rmc->refresh_session.noreveal_index - = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, - TALER_CNC_KAPPA); - + /* check coin has enough funds remaining on it to cover melt cost */ qs = refresh_check_melt (connection, session, rmc, @@ -359,28 +280,15 @@ refresh_melt_transaction (void *cls, if (0 > qs) return qs; - if ( (0 >= - (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls, - session, - &rmc->session_hash, - &rmc->refresh_session))) || - (0 >= - (qs = TEH_plugin->insert_refresh_order (TEH_plugin->cls, - session, - &rmc->session_hash, - rmc->num_newcoins, - rmc->denom_pubs))) || - (0 >= - (qs = TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls, - session, - &rmc->session_hash, - rmc->num_newcoins, - rmc->commit_coin[rmc->refresh_session.noreveal_index]))) || - (0 >= - (qs = TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls, - session, - &rmc->session_hash, - &rmc->transfer_pub[rmc->refresh_session.noreveal_index]))) ) + /* pick challenge and persist it */ + rmc->refresh_session.noreveal_index + = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG, + TALER_CNC_KAPPA); + + if (0 >= + (qs = TEH_plugin->insert_melt (TEH_plugin->cls, + session, + &rmc->refresh_session))) { if (GNUNET_DB_STATUS_SOFT_ERROR != qs) { @@ -395,233 +303,6 @@ refresh_melt_transaction (void *cls, /** - * Handle a "/refresh/melt" request after the main JSON parsing has - * happened. We now need to validate the coins being melted and the - * session signature and then hand things of to execute the melt - * operation. - * - * @param connection the MHD connection to handle - * @param[out] mhd_ret set on failure to return value for MHD - * @param rmc information about the melt to process - * @return MHD result code - */ -static int -refresh_melt_prepare (struct MHD_Connection *connection, - int *mhd_ret, - struct RefreshMeltContext *rmc) -{ - struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk; - struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki; - struct TALER_Amount cost; - struct TALER_Amount total_cost; - struct TALER_Amount value; - struct TALER_Amount fee_withdraw; - struct TALER_Amount fee_melt; - struct TALER_Amount total_melt; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "/refresh/melt request for session %s\n", - GNUNET_h2s (&rmc->session_hash)); - - GNUNET_assert (GNUNET_OK == - TALER_amount_get_zero (TEH_exchange_currency_string, - &total_cost)); - for (unsigned int i=0;i<rmc->num_newcoins;i++) - { - dk = TEH_KS_denomination_key_lookup (rmc->key_state, - &rmc->denom_pubs[i], - TEH_KS_DKU_WITHDRAW); - if (NULL == dk) - { - GNUNET_break_op (0); - *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection, - TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND, - "new_denoms"); - return GNUNET_SYSERR; - } - dki = &dk->issue; - TALER_amount_ntoh (&value, - &dki->properties.value); - TALER_amount_ntoh (&fee_withdraw, - &dki->properties.fee_withdraw); - if ( (GNUNET_OK != - TALER_amount_add (&cost, - &value, - &fee_withdraw)) || - (GNUNET_OK != - TALER_amount_add (&total_cost, - &cost, - &total_cost)) ) - { - GNUNET_break_op (0); - *mhd_ret = TEH_RESPONSE_reply_internal_error (connection, - TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW, - "cost calculation failure"); - return GNUNET_SYSERR; - } - } - - dki = &rmc->dki->issue; - TALER_amount_ntoh (&fee_melt, - &dki->properties.fee_refresh); - if (GNUNET_OK != - TALER_amount_subtract (&total_melt, - &rmc->coin_melt_details.melt_amount_with_fee, - &fee_melt)) - { - GNUNET_break_op (0); - *mhd_ret = TEH_RESPONSE_reply_external_error (connection, - TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION, - "Melt contribution below melting fee"); - return GNUNET_SYSERR; - } - if (0 != - TALER_amount_cmp (&total_cost, - &total_melt)) - { - GNUNET_break_op (0); - /* We require total value of coins being melted and - total value of coins being generated to match! */ - *mhd_ret = TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_BAD_REQUEST, - "{s:s, s:I}", - "error", "value mismatch", - "code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH); - return GNUNET_SYSERR; - } - return TEH_DB_run_transaction (connection, - mhd_ret, - &refresh_melt_transaction, - rmc); -} - - -/** - * Extract public coin information from a JSON object. - * - * @param connection the connection to send error responses to - * @param coin_info the JSON object to extract the coin info from - * @param[out] r_melt_detail set to details about the coin's melting permission (if valid) - * @return #GNUNET_YES if coin public info in JSON was valid - * #GNUNET_NO JSON was invalid, response was generated - * #GNUNET_SYSERR on internal error - */ -static int -get_coin_public_info (struct MHD_Connection *connection, - const json_t *coin_info, - struct TEH_DB_MeltDetails *r_melt_detail) -{ - int ret; - struct TALER_CoinSpendSignatureP melt_sig; - struct TALER_DenominationSignature sig; - struct TALER_DenominationPublicKey pk; - struct TALER_Amount amount; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("coin_pub", &r_melt_detail->coin_info.coin_pub), - TALER_JSON_spec_denomination_signature ("denom_sig", &sig), - TALER_JSON_spec_denomination_public_key ("denom_pub", &pk), - GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig), - TALER_JSON_spec_amount ("value_with_fee", &amount), - GNUNET_JSON_spec_end () - }; - - ret = TEH_PARSE_json_data (connection, - coin_info, - spec); - if (GNUNET_OK != ret) - { - GNUNET_break_op (0); - return ret; - } - /* check exchange signature on the coin */ - r_melt_detail->coin_info.denom_sig = sig; - r_melt_detail->coin_info.denom_pub = pk; - if (GNUNET_OK != - TALER_test_coin_valid (&r_melt_detail->coin_info)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - r_melt_detail->coin_info.denom_sig.rsa_signature = NULL; - r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL; - return (MHD_YES == - TEH_RESPONSE_reply_signature_invalid (connection, - TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID, - "denom_sig")) - ? GNUNET_NO : GNUNET_SYSERR; - } - r_melt_detail->melt_sig = melt_sig; - r_melt_detail->melt_amount_with_fee = amount; - return GNUNET_OK; -} - - -/** - * Release memory from the @a commit_coin array. - * - * @param commit_coin array to release - * @param kappa size of 1st dimension - * @param num_new_coins size of 2nd dimension - */ -static void -free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin, - unsigned int kappa, - unsigned int num_new_coins) -{ - for (unsigned int i=0;i<kappa;i++) - { - if (NULL == commit_coin[i]) - break; - for (unsigned int j=0;j<num_new_coins;j++) - GNUNET_free_non_null (commit_coin[i][j].coin_ev); - GNUNET_free (commit_coin[i]); - commit_coin[i] = NULL; - } -} - - -/** - * Cleanup state kept in the @a rmc. - * - * @param rmc state to clean up; does not free @a rmc itself - */ -static void -cleanup_rmc (struct RefreshMeltContext *rmc) -{ - free_commit_coins (rmc->commit_coin, - TALER_CNC_KAPPA, - rmc->num_newcoins); - if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key) - { - GNUNET_CRYPTO_rsa_public_key_free (rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key); - rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key = NULL; - } - if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (rmc->coin_melt_details.coin_info.denom_sig.rsa_signature); - rmc->coin_melt_details.coin_info.denom_sig.rsa_signature = NULL; - } - if (NULL != rmc->denom_pubs) - { - for (unsigned int j=0;j<rmc->num_newcoins;j++) - if (NULL != rmc->denom_pubs[j].rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (rmc->denom_pubs[j].rsa_public_key); - GNUNET_free (rmc->denom_pubs); - rmc->denom_pubs = NULL; - } - if (NULL != rmc->hash_context) - { - GNUNET_CRYPTO_hash_context_abort (rmc->hash_context); - rmc->hash_context = NULL; - } - if (NULL != rmc->key_state) - { - TEH_KS_release (rmc->key_state); - rmc->key_state = NULL; - } -} - - -/** * Handle a "/refresh/melt" request after the first parsing has * happened. We now need to validate the coins being melted and the * session signature and then hand things of to execute the melt @@ -629,223 +310,70 @@ cleanup_rmc (struct RefreshMeltContext *rmc) * processing on to #handle_refresh_melt_binary(). * * @param connection the MHD connection to handle - * @param new_denoms array of denomination keys - * @param melt_coin coin to melt - * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys - * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign + * @param[in,out] rmc details about the melt request * @return MHD result code */ static int -handle_refresh_melt_json (struct MHD_Connection *connection, - const json_t *new_denoms, - const json_t *melt_coin, - const json_t *transfer_pubs, - const json_t *coin_evs) +handle_refresh_melt (struct MHD_Connection *connection, + struct RefreshMeltContext *rmc) { - int res; - int mhd_ret; - struct RefreshMeltContext rmc; - - memset (&rmc, - 0, - sizeof (rmc)); - /* For the signature check, we hash most of the inputs together - (except for the signatures on the coins). */ - rmc.hash_context = GNUNET_CRYPTO_hash_context_start (); - for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) - { - struct GNUNET_JSON_Specification trans_spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, &rmc.transfer_pub[i]), - GNUNET_JSON_spec_end () - }; - - res = TEH_PARSE_json_array (connection, - transfer_pubs, - trans_spec, - i, -1); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - cleanup_rmc (&rmc); - return mhd_ret; - } - GNUNET_CRYPTO_hash_context_read (rmc.hash_context, - &rmc.transfer_pub[i], - sizeof (struct TALER_TransferPublicKeyP)); - } - - rmc.num_newcoins = json_array_size (new_denoms); - rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins, - struct TALER_DenominationPublicKey); - for (unsigned int i=0;i<rmc.num_newcoins;i++) - { - char *buf; - size_t buf_size; - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_denomination_public_key (NULL, - &rmc.denom_pubs[i]), - GNUNET_JSON_spec_end () - }; - - res = TEH_PARSE_json_array (connection, - new_denoms, - spec, - i, - -1); - if (GNUNET_OK != res) - { - mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO; - cleanup_rmc (&rmc); - return mhd_ret; - } - buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rmc.denom_pubs[i].rsa_public_key, - &buf); - GNUNET_CRYPTO_hash_context_read (rmc.hash_context, - buf, - buf_size); - GNUNET_free (buf); - } - - /* decode JSON data on coin to melt and check that this is a - valid coin */ + /* sanity-check that "total melt amount > melt fee" */ { - struct TALER_AmountNBO melt_amount; + struct TALER_Amount fee_refresh; - res = get_coin_public_info (connection, - melt_coin, - &rmc.coin_melt_details); - if (GNUNET_OK != res) + TALER_amount_ntoh (&fee_refresh, + &rmc->dki->issue.properties.fee_refresh); + if (TALER_amount_cmp (&fee_refresh, + &rmc->refresh_session.amount_with_fee) > 0) { GNUNET_break_op (0); - mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO; - cleanup_rmc (&rmc); - return mhd_ret; - } - TALER_amount_hton (&melt_amount, - &rmc.coin_melt_details.melt_amount_with_fee); - GNUNET_CRYPTO_hash_context_read (rmc.hash_context, - &rmc.coin_melt_details.coin_info.coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP)); - GNUNET_CRYPTO_hash_context_read (rmc.hash_context, - &melt_amount, - sizeof (struct TALER_AmountNBO)); - } - - /* parse JSON arrays into binary arrays and hash everything - together for the signature check */ - for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++) - { - rmc.commit_coin[i] = GNUNET_new_array (rmc.num_newcoins, - struct TALER_EXCHANGEDB_RefreshCommitCoin); - for (unsigned int j = 0; j < rmc.num_newcoins; j++) - { - struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &rmc.commit_coin[i][j]; - struct GNUNET_JSON_Specification coin_spec[] = { - GNUNET_JSON_spec_varsize (NULL, - (void **) &rcc->coin_ev, - &rcc->coin_ev_size), - GNUNET_JSON_spec_end () - }; - - res = TEH_PARSE_json_array (connection, - coin_evs, - coin_spec, - i, - j, - -1); - if (GNUNET_OK != res) - { - GNUNET_break_op (0); - mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - cleanup_rmc (&rmc); - return mhd_ret; - } - - GNUNET_CRYPTO_hash_context_read (rmc.hash_context, - rcc->coin_ev, - rcc->coin_ev_size); + return TEH_RESPONSE_reply_external_error (connection, + TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION, + "melt amount smaller than melting fee"); } } - GNUNET_CRYPTO_hash_context_finish (rmc.hash_context, - &rmc.session_hash); - rmc.hash_context = NULL; - - rmc.key_state = TEH_KS_acquire (); - if (NULL == rmc.key_state) - { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - return TEH_RESPONSE_reply_internal_error (connection, - TALER_EC_EXCHANGE_BAD_CONFIGURATION, - "no keys"); - } - rmc.dki = TEH_KS_denomination_key_lookup (rmc.key_state, - &rmc.coin_melt_details.coin_info.denom_pub, - TEH_KS_DKU_DEPOSIT); - if (NULL == rmc.dki) - { - TEH_KS_release (rmc.key_state); - TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); - return TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND, - "denom_pub"); - } - /* verify signature of coin for melt operation */ { struct TALER_RefreshMeltCoinAffirmationPS body; - struct TALER_Amount fee_refresh; - TALER_amount_ntoh (&fee_refresh, - &rmc.dki->issue.properties.fee_refresh); - rmc.coin_melt_details.melt_fee = fee_refresh; body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); - body.session_hash = rmc.session_hash; + body.rc = rmc->refresh_session.rc; TALER_amount_hton (&body.amount_with_fee, - &rmc.coin_melt_details.melt_amount_with_fee); - TALER_amount_hton (&body.melt_fee, - &fee_refresh); - body.coin_pub = rmc.coin_melt_details.coin_info.coin_pub; - if (TALER_amount_cmp (&fee_refresh, - &rmc.coin_melt_details.melt_amount_with_fee) > 0) - { - GNUNET_break_op (0); - cleanup_rmc (&rmc); - return TEH_RESPONSE_reply_external_error (connection, - TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT, - "melt amount smaller than melting fee"); - } + &rmc->refresh_session.amount_with_fee); + body.melt_fee = rmc->dki->issue.properties.fee_refresh; + body.coin_pub = rmc->refresh_session.coin.coin_pub; if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, &body.purpose, - &rmc.coin_melt_details.melt_sig.eddsa_signature, - &rmc.coin_melt_details.coin_info.coin_pub.eddsa_pub)) + &rmc->refresh_session.coin_sig.eddsa_signature, + &rmc->refresh_session.coin.coin_pub.eddsa_pub)) { GNUNET_break_op (0); - cleanup_rmc (&rmc); return TEH_RESPONSE_reply_signature_invalid (connection, TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID, "confirm_sig"); } } - /* prepare commit */ - if (GNUNET_OK != - refresh_melt_prepare (connection, - &mhd_ret, - &rmc)) + /* run transaction */ { - cleanup_rmc (&rmc); - return mhd_ret; + int mhd_ret; + + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + &mhd_ret, + &refresh_melt_transaction, + rmc)) + return mhd_ret; } - mhd_ret = reply_refresh_melt_success (connection, - &rmc.session_hash, - rmc.refresh_session.noreveal_index); - cleanup_rmc (&rmc); - return mhd_ret; + + /* generate ordinary response */ + return reply_refresh_melt_success (connection, + &rmc->refresh_session.rc, + rmc->refresh_session.noreveal_index); } @@ -870,16 +398,22 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, size_t *upload_data_size) { json_t *root; - json_t *new_denoms; - json_t *melt_coin; - json_t *coin_evs; - json_t *transfer_pubs; + struct RefreshMeltContext rmc; int res; + struct TEH_KS_StateHandle *key_state; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("new_denoms", &new_denoms), - GNUNET_JSON_spec_json ("melt_coin", &melt_coin), - GNUNET_JSON_spec_json ("coin_evs", &coin_evs), - GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs), + GNUNET_JSON_spec_fixed_auto ("coin_pub", + &rmc.refresh_session.coin.coin_pub), + TALER_JSON_spec_denomination_signature ("denom_sig", + &rmc.refresh_session.coin.denom_sig), + TALER_JSON_spec_denomination_public_key ("denom_pub", + &rmc.refresh_session.coin.denom_pub), + GNUNET_JSON_spec_fixed_auto ("confirm_sig", + &rmc.refresh_session.coin_sig), + TALER_JSON_spec_amount ("value_with_fee", + &rmc.refresh_session.amount_with_fee), + GNUNET_JSON_spec_fixed_auto ("rc", + &rmc.refresh_session.rc), GNUNET_JSON_spec_end () }; @@ -894,6 +428,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, (NULL == root) ) return MHD_YES; + memset (&rmc, + 0, + sizeof (rmc)); res = TEH_PARSE_json_data (connection, root, spec); @@ -901,29 +438,60 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh, if (GNUNET_OK != res) return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; - /* Determine dimensionality of the request (kappa, #old and #new coins) */ - if (TALER_CNC_KAPPA != json_array_size (coin_evs)) + if (GNUNET_OK != + TALER_test_coin_valid (&rmc.refresh_session.coin)) { GNUNET_break_op (0); GNUNET_JSON_parse_free (spec); - return TEH_RESPONSE_reply_arg_invalid (connection, - TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID, - "coin_evs"); + return TEH_RESPONSE_reply_signature_invalid (connection, + TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID, + "denom_sig"); } - if (TALER_CNC_KAPPA != json_array_size (transfer_pubs)) + + /* run actual logic, now that the request was parsed */ + key_state = TEH_KS_acquire (); + if (NULL == key_state) { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TEH_RESPONSE_reply_arg_invalid (connection, - TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID, - "transfer_pubs"); + TALER_LOG_ERROR ("Lacking keys to operate\n"); + res = TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_EXCHANGE_BAD_CONFIGURATION, + "no keys"); + goto cleanup; + } + rmc.dki = TEH_KS_denomination_key_lookup (key_state, + &rmc.refresh_session.coin.denom_pub, + TEH_KS_DKU_DEPOSIT); + if (NULL == rmc.dki) + { + TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n"); + res = TEH_RESPONSE_reply_arg_unknown (connection, + TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND, + "denom_pub"); + goto cleanup; + } + + res = handle_refresh_melt (connection, + &rmc); + + + cleanup: + if (NULL != key_state) + { + TEH_KS_release (key_state); + key_state = NULL; + } + if (NULL != rmc.refresh_session.coin.denom_pub.rsa_public_key) + { + GNUNET_CRYPTO_rsa_public_key_free (rmc.refresh_session.coin.denom_pub.rsa_public_key); + rmc.refresh_session.coin.denom_pub.rsa_public_key = NULL; + } + if (NULL != rmc.refresh_session.coin.denom_sig.rsa_signature) + { + GNUNET_CRYPTO_rsa_signature_free (rmc.refresh_session.coin.denom_sig.rsa_signature); + rmc.refresh_session.coin.denom_sig.rsa_signature = NULL; } - res = handle_refresh_melt_json (connection, - new_denoms, - melt_coin, - transfer_pubs, - coin_evs); GNUNET_JSON_parse_free (spec); + return res; } diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c index a64ec1cee..4a7cd33db 100644 --- a/src/exchange/taler-exchange-httpd_refresh_reveal.c +++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c @@ -32,6 +32,12 @@ /** + * Maximum number of fresh coins we allow per refresh operation. + */ +#define MAX_FRESH_COINS 256 + + +/** * Send a response for "/refresh/reveal". * * @param connection the connection to send the response to @@ -79,133 +85,19 @@ reply_refresh_reveal_success (struct MHD_Connection *connection, * revealed value(s) do not match the original commitment. * * @param connection the connection to send the response to - * @param session info about session - * @param commit_coins array of @a num_newcoins committed envelopes at offset @a gamma - * @param denom_pubs array of @a num_newcoins denomination keys for the new coins - * @param gamma_tp transfer public key at offset @a gamma + * @param rc commitment computed by the exchange * @return a MHD result code */ static int reply_refresh_reveal_missmatch (struct MHD_Connection *connection, - const struct TALER_EXCHANGEDB_RefreshSession *session, - const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins, - const struct TALER_DenominationPublicKey *denom_pubs, - const struct TALER_TransferPublicKeyP *gamma_tp) + const struct TALER_RefreshCommitmentP *rc) { - json_t *info_new; - json_t *info_commit_k; - - info_new = json_array (); - info_commit_k = json_array (); - for (unsigned int i=0;i<session->num_newcoins;i++) - { - const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc; - json_t *cc_json; - - GNUNET_assert (0 == - json_array_append_new (info_new, - GNUNET_JSON_from_rsa_public_key (denom_pubs[i].rsa_public_key))); - - cc = &commit_coins[i]; - cc_json = json_pack ("{s:o}", - "coin_ev", - GNUNET_JSON_from_data (cc->coin_ev, - cc->coin_ev_size)); - GNUNET_assert (0 == - json_array_append_new (info_commit_k, - cc_json)); - } return TEH_RESPONSE_reply_json_pack (connection, MHD_HTTP_CONFLICT, - "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:i}", + "{s:s, s:I, s:o}", "error", "commitment violation", "code", (json_int_t) TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION, - "coin_sig", GNUNET_JSON_from_data_auto (&session->melt.coin_sig), - "coin_pub", GNUNET_JSON_from_data_auto (&session->melt.coin.coin_pub), - "melt_amount_with_fee", TALER_JSON_from_amount (&session->melt.amount_with_fee), - "melt_fee", TALER_JSON_from_amount (&session->melt.melt_fee), - "newcoin_infos", info_new, - "commit_infos", info_commit_k, - "gamma_tp", GNUNET_JSON_from_data_auto (gamma_tp), - "gamma", (int) session->noreveal_index); -} - - -/** - * Check if the given @a transfer_privs correspond to an honest - * commitment for the given session. - * Checks that the transfer private keys match their commitments. - * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that they match. - * - * @param connection the MHD connection to handle - * @param session database connection to use - * @param session_hash hash of session to query - * @param off commitment offset to check - * @param transfer_priv private transfer key - * @param melt information about the melted coin - * @param num_newcoins number of newcoins being generated - * @param denom_pubs array of @a num_newcoins keys for the new coins - * @param hash_context hash context to update by hashing in the data - * from this offset - * @return #GNUNET_OK if the committment was honest, - * #GNUNET_NO if there was a problem and we generated an error message - * #GNUNET_SYSERR if we could not even generate an error message - */ -static int -check_commitment (struct MHD_Connection *connection, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - unsigned int off, - const struct TALER_TransferPrivateKeyP *transfer_priv, - const struct TALER_EXCHANGEDB_RefreshMelt *melt, - unsigned int num_newcoins, - const struct TALER_DenominationPublicKey *denom_pubs, - struct GNUNET_HashContext *hash_context) -{ - struct TALER_TransferSecretP transfer_secret; - - TALER_link_reveal_transfer_secret (transfer_priv, - &melt->coin.coin_pub, - &transfer_secret); - - /* Check that the commitments for all new coins were correct */ - for (unsigned int j = 0; j < num_newcoins; j++) - { - struct TALER_PlanchetSecretsP fc; - struct TALER_CoinSpendPublicKeyP coin_pub; - struct GNUNET_HashCode h_msg; - char *buf; - size_t buf_len; - - TALER_planchet_setup_refresh (&transfer_secret, - j, - &fc); - GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv, - &coin_pub.eddsa_pub); - GNUNET_CRYPTO_hash (&coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP), - &h_msg); - if (GNUNET_YES != - GNUNET_CRYPTO_rsa_blind (&h_msg, - &fc.blinding_key.bks, - denom_pubs[j].rsa_public_key, - &buf, - &buf_len)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Blind failed (bad denomination key!?)\n"); - return (MHD_YES == - TEH_RESPONSE_reply_internal_error (connection, - TALER_EC_REFRESH_REVEAL_BLINDING_ERROR, - "Blinding error")) - ? GNUNET_NO : GNUNET_SYSERR; - } - GNUNET_CRYPTO_hash_context_read (hash_context, - buf, - buf_len); - GNUNET_free (buf); - } - return GNUNET_OK; + "rc_expected", GNUNET_JSON_from_data_auto (rc)); } @@ -216,169 +108,83 @@ struct RevealContext { /** - * Hash of the refresh session. + * Commitment of the refresh operaton. */ - const struct GNUNET_HashCode *session_hash; + struct TALER_RefreshCommitmentP rc; /** - * Database session used to execute the transaction. + * Transfer public key at gamma. */ - struct TALER_EXCHANGEDB_Session *session; - - /** - * Session state from the database. - */ - struct TALER_EXCHANGEDB_RefreshSession refresh_session; + struct TALER_TransferPublicKeyP gamma_tp; /** - * Array of denomination public keys used for the refresh. + * Transfer private keys revealed to us. */ - struct TALER_DenominationPublicKey *denom_pubs; + struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1]; /** - * Envelopes with the signatures to be returned. + * Denominations being requested. */ - struct TALER_DenominationSignature *ev_sigs; + const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation **dkis; /** - * Commitment data from the DB giving data about original - * commitments, in particular the blinded envelopes (for - * index gamma). + * Envelopes to be signed. */ - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins; + const struct TALER_RefreshCoinData *rcds; /** - * Transfer public key associated with the gamma value - * selected by the exchange. + * Envelopes with the signatures to be returned. Initially NULL. */ - struct TALER_TransferPublicKeyP gamma_tp; + struct TALER_DenominationSignature *ev_sigs; /** - * Transfer private keys revealed to us. + * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL). */ - struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1]; + unsigned int num_fresh_coins; }; /** - * Exchange a coin as part of a refresh operation. Obtains the - * envelope from the database and performs the signing operation. + * Function called with information about a refresh order we already + * persisted. Stores the result in @a cls so we don't do the calculation + * again. * - * @param connection the MHD connection to handle - * @param session database connection to use - * @param session_hash hash of session to query - * @param key_state key state to lookup denomination pubs - * @param denom_pub denomination key for the coin to create - * @param commit_coin the coin that was committed - * @param coin_off number of the coin - * @param[out] ev_sig set to signature over the coin upon success - * @return database transaction status + * @param cls closure with a `struct RevealContext` + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs array of @e num_tprivs transfer private keys + * @param tp transfer public key information */ -static enum GNUNET_DB_QueryStatus -refresh_exchange_coin (struct MHD_Connection *connection, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TEH_KS_StateHandle *key_state, - const struct TALER_DenominationPublicKey *denom_pub, - const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin, - unsigned int coin_off, - struct TALER_DenominationSignature *ev_sig) +static void +check_exists_cb (void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp) { - struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; - enum GNUNET_DB_QueryStatus qs; + struct RevealContext *rctx = cls; - dki = TEH_KS_denomination_key_lookup (key_state, - denom_pub, - TEH_KS_DKU_WITHDRAW); - if (NULL == dki) + if (0 == num_newcoins) { GNUNET_break (0); - ev_sig->rsa_signature = NULL; - return GNUNET_DB_STATUS_HARD_ERROR; - } - qs = TEH_plugin->get_refresh_out (TEH_plugin->cls, - session, - session_hash, - coin_off, - ev_sig); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Returning cached reply for /refresh/reveal signature\n"); - return qs; - } - if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) - return qs; - - ev_sig->rsa_signature - = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key, - commit_coin->coin_ev, - commit_coin->coin_ev_size); - if (NULL == ev_sig->rsa_signature) - { - GNUNET_break (0); - return GNUNET_DB_STATUS_HARD_ERROR; - } - qs = TEH_plugin->insert_refresh_out (TEH_plugin->cls, - session, - session_hash, - coin_off, - ev_sig); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - if (NULL != ev_sig->rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (ev_sig->rsa_signature); - ev_sig->rsa_signature = NULL; - } - } - return qs; -} - - -/** - * Cleanup state of the transaction stored in @a rc. - * - * @param rc context to clean up - */ -static void -cleanup_rc (struct RevealContext *rc) -{ - if (NULL != rc->denom_pubs) - { - for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++) - if (NULL != rc->denom_pubs[i].rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (rc->denom_pubs[i].rsa_public_key); - GNUNET_free (rc->denom_pubs); - rc->denom_pubs = NULL; - } - if (NULL != rc->commit_coins) - { - for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++) - GNUNET_free_non_null (rc->commit_coins[j].coin_ev); - GNUNET_free (rc->commit_coins); - rc->commit_coins = NULL; - } - if (NULL != rc->ev_sigs) - { - for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++) - if (NULL != rc->ev_sigs[j].rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (rc->ev_sigs[j].rsa_signature); - GNUNET_free (rc->ev_sigs); - rc->ev_sigs = NULL; - } - if (NULL != rc->refresh_session.melt.coin.denom_sig.rsa_signature) - { - GNUNET_CRYPTO_rsa_signature_free (rc->refresh_session.melt.coin.denom_sig.rsa_signature); - rc->refresh_session.melt.coin.denom_sig.rsa_signature = NULL; - } - if (NULL != rc->refresh_session.melt.coin.denom_pub.rsa_public_key) - { - GNUNET_CRYPTO_rsa_public_key_free (rc->refresh_session.melt.coin.denom_pub.rsa_public_key); - rc->refresh_session.melt.coin.denom_pub.rsa_public_key = NULL; + return; } + GNUNET_break (TALER_CNC_KAPPA - 1 == num_tprivs); + GNUNET_break_op (0 == memcmp (tp, + &rctx->gamma_tp, + sizeof (struct TALER_TransferPublicKeyP))); + GNUNET_break_op (0 == memcmp (tprivs, + &rctx->transfer_privs, + sizeof (struct TALER_TransferPrivateKeyP) * num_tprivs)); + rctx->ev_sigs = GNUNET_new_array (num_newcoins, + struct TALER_DenominationSignature); + for (unsigned int i=0;i<num_newcoins;i++) + rctx->ev_sigs[i].rsa_signature + = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature); } @@ -408,233 +214,230 @@ refresh_reveal_transaction (void *cls, struct TALER_EXCHANGEDB_Session *session, int *mhd_ret) { - struct RevealContext *rc = cls; - unsigned int off; - struct GNUNET_HashContext *hash_context; - struct GNUNET_HashCode sh_check; + struct RevealContext *rctx = cls; + struct TALER_EXCHANGEDB_RefreshMelt refresh_melt; enum GNUNET_DB_QueryStatus qs; - rc->session = session; - qs = TEH_plugin->get_refresh_session (TEH_plugin->cls, - session, - rc->session_hash, - &rc->refresh_session); + /* Try to see if we already have given an answer before. */ + qs = TEH_plugin->get_refresh_reveal (TEH_plugin->cls, + session, + &rctx->rc, + &check_exists_cb, + rctx); + switch (qs) { + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + /* continue normal execution */ + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + return qs; + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (qs); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR); + return GNUNET_DB_STATUS_HARD_ERROR; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + default: + /* Hossa, already found our reply! */ + GNUNET_assert (NULL != rctx->ev_sigs); + return qs; + } + + /* Obtain basic information about the refresh operation and what + gamma we committed to. */ + qs = TEH_plugin->get_melt (TEH_plugin->cls, + session, + &rctx->rc, + &refresh_melt); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection, TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN, - "session_hash"); + "rc"); return GNUNET_DB_STATUS_HARD_ERROR; } if (GNUNET_DB_STATUS_SOFT_ERROR == qs) return qs; if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) || - (rc->refresh_session.noreveal_index >= TALER_CNC_KAPPA) ) + (refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) ) { GNUNET_break (0); - cleanup_rc (rc); *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR); return GNUNET_DB_STATUS_HARD_ERROR; } - rc->denom_pubs = GNUNET_new_array (rc->refresh_session.num_newcoins, - struct TALER_DenominationPublicKey); - qs = TEH_plugin->get_refresh_order (TEH_plugin->cls, - session, - rc->session_hash, - rc->refresh_session.num_newcoins, - rc->denom_pubs); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - cleanup_rc (rc); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR); - return GNUNET_DB_STATUS_HARD_ERROR; - } - hash_context = GNUNET_CRYPTO_hash_context_start (); - /* first, iterate over transfer public keys for hash_context */ - off = 0; - for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) + /* Verify commitment */ { - if (i == rc->refresh_session.noreveal_index) + /* Note that the contents of rcs[refresh_melt.session.noreveal_index] + will be aliased and are *not* allocated (or deallocated) in + this function -- in contrast to the other offsets! */ + struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA]; + struct TALER_RefreshCommitmentP rc_expected; + unsigned int off; + + off = 0; /* did we pass session.noreveal_index yet? */ + for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) { - off = 1; - /* obtain gamma_tp from db */ - qs = TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls, - session, - rc->session_hash, - &rc->gamma_tp); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + struct TALER_RefreshCommitmentEntry *rce = &rcs[i]; + + if (i == refresh_melt.session.noreveal_index) + { + /* Take these coin envelopes from the client */ + rce->transfer_pub = rctx->gamma_tp; + rce->new_coins = (struct TALER_RefreshCoinData *) rctx->rcds; + off = 1; + } + else { - GNUNET_CRYPTO_hash_context_abort (hash_context); - cleanup_rc (rc); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR); - return GNUNET_DB_STATUS_HARD_ERROR; + /* Reconstruct coin envelopes from transfer private key */ + struct TALER_TransferPrivateKeyP *tpriv = &rctx->transfer_privs[i - off]; + struct TALER_TransferSecretP ts; + + GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv, + &rce->transfer_pub.ecdhe_pub); + TALER_link_reveal_transfer_secret (tpriv, + &refresh_melt.session.coin.coin_pub, + &ts); + rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins, + struct TALER_RefreshCoinData); + for (unsigned int j=0;j<rctx->num_fresh_coins;j++) + { + struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; + struct TALER_PlanchetSecretsP ps; + struct TALER_PlanchetDetail pd; + + rcd->dk = &rctx->dkis[j]->denom_pub; + TALER_planchet_setup_refresh (&ts, + j, + &ps); + TALER_planchet_prepare (rcd->dk, + &ps, + &pd); + rcd->coin_ev = pd.coin_ev; + rcd->coin_ev_size = pd.coin_ev_size; + } } - GNUNET_CRYPTO_hash_context_read (hash_context, - &rc->gamma_tp, - sizeof (struct TALER_TransferPublicKeyP)); } - else + TALER_refresh_get_commitment (&rc_expected, + TALER_CNC_KAPPA, + rctx->num_fresh_coins, + rcs, + &refresh_melt.session.coin.coin_pub, + &refresh_melt.session.amount_with_fee); + + /* Free resources allocated above */ + for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) { - /* compute tp from private key */ - struct TALER_TransferPublicKeyP tp; - - GNUNET_CRYPTO_ecdhe_key_get_public (&rc->transfer_privs[i - off].ecdhe_priv, - &tp.ecdhe_pub); - GNUNET_CRYPTO_hash_context_read (hash_context, - &tp, - sizeof (struct TALER_TransferPublicKeyP)); - } - } + struct TALER_RefreshCommitmentEntry *rce = &rcs[i]; - /* next, add all of the hashes from the denomination keys to the - hash_context */ - for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++) - { - char *buf; - size_t buf_size; - - buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rc->denom_pubs[i].rsa_public_key, - &buf); - GNUNET_CRYPTO_hash_context_read (hash_context, - buf, - buf_size); - GNUNET_free (buf); - } + if (i == refresh_melt.session.noreveal_index) + continue; /* This offset is special... */ + for (unsigned int j=0;j<rctx->num_fresh_coins;j++) + { + struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; - /* next, add public key of coin and amount being refreshed */ - { - struct TALER_AmountNBO melt_amountn; - - GNUNET_CRYPTO_hash_context_read (hash_context, - &rc->refresh_session.melt.coin.coin_pub, - sizeof (struct TALER_CoinSpendPublicKeyP)); - TALER_amount_hton (&melt_amountn, - &rc->refresh_session.melt.amount_with_fee); - GNUNET_CRYPTO_hash_context_read (hash_context, - &melt_amountn, - sizeof (struct TALER_AmountNBO)); - } + GNUNET_free (rcd->coin_ev); + } + GNUNET_free (rce->new_coins); + } + + /* Verify rc_expected matches rc */ + if (0 != memcmp (&rctx->rc, + &rc_expected, + sizeof (struct TALER_RefreshCommitmentP))) + { + GNUNET_break_op (0); + *mhd_ret = reply_refresh_reveal_missmatch (connection, + &rc_expected); + return GNUNET_DB_STATUS_HARD_ERROR; + } + } /* end of checking "rc_expected" */ - rc->commit_coins = GNUNET_new_array (rc->refresh_session.num_newcoins, - struct TALER_EXCHANGEDB_RefreshCommitCoin); - off = 0; - for (unsigned int i=0;i<TALER_CNC_KAPPA;i++) + /* check amounts add up! */ { - int res; + struct TALER_Amount refresh_cost; - if (i == rc->refresh_session.noreveal_index) + refresh_cost = refresh_melt.melt_fee; + for (unsigned int i=0;i<rctx->num_fresh_coins;i++) { - off = 1; - /* obtain commit_coins for the selected gamma value from DB */ - qs = TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls, - session, - rc->session_hash, - rc->refresh_session.num_newcoins, - rc->commit_coins); - if (0 >= qs) - { - cleanup_rc (rc); - GNUNET_CRYPTO_hash_context_abort (hash_context); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - return qs; - GNUNET_break (0); - *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR); - return GNUNET_DB_STATUS_HARD_ERROR; - } - /* add envelopes to hash_context */ - for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++) + struct TALER_Amount fee_withdraw; + struct TALER_Amount value; + struct TALER_Amount total; + + TALER_amount_ntoh (&fee_withdraw, + &rctx->dkis[i]->issue.properties.fee_withdraw); + TALER_amount_ntoh (&value, + &rctx->dkis[i]->issue.properties.value); + if ( (GNUNET_OK != + TALER_amount_add (&total, + &fee_withdraw, + &value)) || + (GNUNET_OK != + TALER_amount_add (&refresh_cost, + &refresh_cost, + &total)) ) { - GNUNET_CRYPTO_hash_context_read (hash_context, - rc->commit_coins[j].coin_ev, - rc->commit_coins[j].coin_ev_size); + GNUNET_break_op (0); + *mhd_ret = TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW, + "failed to add up refresh costs"); + return GNUNET_DB_STATUS_HARD_ERROR; } - continue; } - if (GNUNET_OK != - (res = check_commitment (connection, - session, - rc->session_hash, - i, - &rc->transfer_privs[i - off], - &rc->refresh_session.melt, - rc->refresh_session.num_newcoins, - rc->denom_pubs, - hash_context))) + if (0 < TALER_amount_cmp (&refresh_cost, + &refresh_melt.session.amount_with_fee)) { GNUNET_break_op (0); - cleanup_rc (rc); - GNUNET_CRYPTO_hash_context_abort (hash_context); - *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO; + *mhd_ret = TEH_RESPONSE_reply_external_error (connection, + TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT, + "melted coin value is insufficient to cover cost of operation"); return GNUNET_DB_STATUS_HARD_ERROR; } } - /* Check session hash matches */ - GNUNET_CRYPTO_hash_context_finish (hash_context, - &sh_check); - if (0 != memcmp (&sh_check, - rc->session_hash, - sizeof (struct GNUNET_HashCode))) - { - GNUNET_break_op (0); - *mhd_ret = reply_refresh_reveal_missmatch (connection, - &rc->refresh_session, - rc->commit_coins, - rc->denom_pubs, - &rc->gamma_tp); - cleanup_rc (rc); - return GNUNET_DB_STATUS_HARD_ERROR; - } - /* Client request OK, sign coins */ - rc->ev_sigs = GNUNET_new_array (rc->refresh_session.num_newcoins, - struct TALER_DenominationSignature); + rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins, + struct TALER_DenominationSignature); + for (unsigned int i=0;i<rctx->num_fresh_coins;i++) { - struct TEH_KS_StateHandle *key_state; - - key_state = TEH_KS_acquire (); - if (NULL == key_state) + rctx->ev_sigs[i].rsa_signature + = GNUNET_CRYPTO_rsa_sign_blinded (rctx->dkis[i]->denom_priv.rsa_private_key, + rctx->rcds[i].coin_ev, + rctx->rcds[i].coin_ev_size); + if (NULL == rctx->ev_sigs[i].rsa_signature) { - TALER_LOG_ERROR ("Lacking keys to operate\n"); - cleanup_rc (rc); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_REFRESH_REVEAL_SIGNING_ERROR); return GNUNET_DB_STATUS_HARD_ERROR; } - for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++) + } + + /* Persist operation result in DB */ + { + struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[rctx->num_fresh_coins]; + + for (unsigned int i=0;i<rctx->num_fresh_coins;i++) { - qs = refresh_exchange_coin (connection, - session, - rc->session_hash, - key_state, - &rc->denom_pubs[j], - &rc->commit_coins[j], - j, - &rc->ev_sigs[j]); - if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) || - (NULL == rc->ev_sigs[j].rsa_signature) ) - { - *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_REFRESH_REVEAL_SIGNING_ERROR); - qs = GNUNET_DB_STATUS_HARD_ERROR; - break; - } + struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; + + rrc->denom_pub = rctx->dkis[i]->denom_pub; + rrc->coin_ev = rctx->rcds[i].coin_ev; + rrc->coin_ev_size = rctx->rcds[i].coin_ev_size; + rrc->coin_sig = rctx->ev_sigs[i]; } - TEH_KS_release (key_state); + qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls, + session, + &rctx->rc, + rctx->num_fresh_coins, + rrcs, + TALER_CNC_KAPPA - 1, + rctx->transfer_privs, + &rctx->gamma_tp); } - if (0 >= qs) + if (GNUNET_DB_STATUS_HARD_ERROR == qs) { - cleanup_rc (rc); - return qs; + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR); } return qs; } @@ -648,59 +451,171 @@ refresh_reveal_transaction (void *cls, * coins. * * @param connection the MHD connection to handle - * @param session_hash hash identifying the melting session + * @param rctx context for the operation, partially built at this time + * @param transfer_pub transfer public key * @param tp_json private transfer keys in JSON format + * @param new_denoms_h_json requests for fresh coins to be created + * @param coin_evs envelopes of gamma-selected coins to be signed * @return MHD result code - */ + */ static int handle_refresh_reveal_json (struct MHD_Connection *connection, - const struct GNUNET_HashCode *session_hash, - const json_t *tp_json) + struct RevealContext *rctx, + const json_t *tp_json, + const json_t *new_denoms_h_json, + const json_t *coin_evs) { - struct RevealContext rc; - int mhd_ret; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "reveal request for session %s\n", - GNUNET_h2s (session_hash)); - memset (&rc, - 0, - sizeof (rc)); - rc.session_hash = session_hash; - for (unsigned int i = 0; i < TALER_CNC_KAPPA - 1; i++) + unsigned int num_fresh_coins = json_array_size (new_denoms_h_json); + unsigned int num_tprivs = json_array_size (tp_json); + struct TEH_KS_StateHandle *key_state; + + GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1); + if ( (num_fresh_coins >= MAX_FRESH_COINS) || + (0 == num_fresh_coins) ) + { + GNUNET_break_op (0); + return TEH_RESPONSE_reply_arg_invalid (connection, + TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE, + "new_denoms"); + + } + if (json_array_size (new_denoms_h_json) != + json_array_size (coin_evs)) + { + GNUNET_break_op (0); + return TEH_RESPONSE_reply_arg_invalid (connection, + TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH, + "new_denoms/coin_evs"); + } + + /* Parse transfer private keys array */ + for (unsigned int i=0;i<num_tprivs;i++) { - struct GNUNET_JSON_Specification tp_spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]), + struct GNUNET_JSON_Specification trans_spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &rctx->transfer_privs[i]), GNUNET_JSON_spec_end () }; int res; res = TEH_PARSE_json_array (connection, tp_json, - tp_spec, + trans_spec, i, - -1); - GNUNET_break_op (GNUNET_OK == res); + -1); if (GNUNET_OK != res) - return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } - if (GNUNET_OK != - TEH_DB_run_transaction (connection, - &mhd_ret, - &refresh_reveal_transaction, - &rc)) + + /* Resolve denomination hashes */ { - cleanup_rc (&rc); - return mhd_ret; + const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins]; + struct TALER_RefreshCoinData rcds[num_fresh_coins]; + int res; + + /* Resolve denomination hashes */ + key_state = TEH_KS_acquire (); + if (NULL == key_state) + { + TALER_LOG_ERROR ("Lacking keys to operate\n"); + /* FIXME: use correct EC code! */ + return TEH_RESPONSE_reply_internal_error (connection, + TALER_EC_REFRESH_REVEAL_SIGNING_ERROR, + "exchange lacks keys"); + } + + /* Parse denomination key hashes */ + for (unsigned int i=0;i<num_fresh_coins;i++) + { + struct GNUNET_HashCode dpk_h; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, + &dpk_h), + GNUNET_JSON_spec_end () + }; + + res = TEH_PARSE_json_array (connection, + new_denoms_h_json, + spec, + i, + -1); + if (GNUNET_OK != res) + { + TEH_KS_release (key_state); + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + } + dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state, + &dpk_h, + TEH_KS_DKU_WITHDRAW); + if (NULL == dkis[i]) + { + TEH_KS_release (key_state); + return TEH_RESPONSE_reply_arg_invalid (connection, + TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND, + "new_denoms"); + } + } + + /* Parse coin envelopes */ + for (unsigned int i=0;i<num_fresh_coins;i++) + { + struct TALER_RefreshCoinData *rcd = &rcds[i]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_varsize (NULL, + (void **) &rcd->coin_ev, + &rcd->coin_ev_size), + GNUNET_JSON_spec_end () + }; + + res = TEH_PARSE_json_array (connection, + coin_evs, + spec, + i, + -1); + if (GNUNET_OK != res) + { + for (unsigned int j=0;j<i;j++) + GNUNET_free_non_null (rcds[j].coin_ev); + TEH_KS_release (key_state); + return (GNUNET_NO == res) ? MHD_YES : MHD_NO; + } + rcd->dk = &dkis[i]->denom_pub; + } + + rctx->num_fresh_coins = num_fresh_coins; + rctx->rcds = rcds; + rctx->dkis = dkis; + /* do transactional work */ + if (GNUNET_OK == + TEH_DB_run_transaction (connection, + &res, + &refresh_reveal_transaction, + rctx)) + { + /* Generate final (positive) response */ + GNUNET_assert (NULL != rctx->ev_sigs); + res = reply_refresh_reveal_success (connection, + num_fresh_coins, + rctx->ev_sigs); + + } + + /* free resources */ + if (NULL != rctx->ev_sigs) + { + for (unsigned int i=0;i<num_fresh_coins;i++) + if (NULL != rctx->ev_sigs[i].rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature); + GNUNET_free (rctx->ev_sigs); + } + for (unsigned int i=0;i<num_fresh_coins;i++) + GNUNET_free_non_null (rcds[i].coin_ev); + TEH_KS_release (key_state); + return res; } - mhd_ret = reply_refresh_reveal_success (connection, - rc.refresh_session.num_newcoins, - rc.ev_sigs); - cleanup_rc (&rc); - return mhd_ret; } + /** * Handle a "/refresh/reveal" request. This time, the client reveals * the private transfer keys except for the cut-and-choose value @@ -724,13 +639,18 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - struct GNUNET_HashCode session_hash; int res; json_t *root; + json_t *coin_evs; json_t *transfer_privs; + json_t *new_denoms_h; + struct RevealContext rctx; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash), + GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc), + GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp), GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs), + GNUNET_JSON_spec_json ("coin_evs", &coin_evs), + GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h), GNUNET_JSON_spec_end () }; @@ -745,6 +665,9 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh, (NULL == root) ) return MHD_YES; + memset (&rctx, + 0, + sizeof (rctx)); res = TEH_PARSE_json_data (connection, root, spec); @@ -754,7 +677,8 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh, GNUNET_break_op (0); return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } - /* Determine dimensionality of the request (kappa and #old coins) */ + + /* Check we got enough transfer private keys */ /* Note we do +1 as 1 row (cut-and-choose!) is missing! */ if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1) { @@ -765,8 +689,10 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh, "transfer_privs"); } res = handle_refresh_reveal_json (connection, - &session_hash, - transfer_privs); + &rctx, + transfer_privs, + new_denoms_h, + coin_evs); GNUNET_JSON_parse_free (spec); return res; } diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c index ac86416f3..64e7a9367 100644 --- a/src/exchange/taler-exchange-httpd_responses.c +++ b/src/exchange/taler-exchange-httpd_responses.c @@ -541,18 +541,18 @@ TEH_RESPONSE_compile_transaction_history (const struct TALER_EXCHANGEDB_Transact ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT); ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS)); - ms.session_hash = melt->session_hash; + ms.rc = melt->session.rc; TALER_amount_hton (&ms.amount_with_fee, - &melt->amount_with_fee); + &melt->session.amount_with_fee); TALER_amount_hton (&ms.melt_fee, &melt->melt_fee); - ms.coin_pub = melt->coin.coin_pub; + ms.coin_pub = melt->session.coin.coin_pub; /* internal sanity check before we hand out a bogus sig... */ if (GNUNET_OK != GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT, &ms.purpose, - &melt->coin_sig.eddsa_signature, - &melt->coin.coin_pub.eddsa_pub)) + &melt->session.coin_sig.eddsa_signature, + &melt->session.coin.coin_pub.eddsa_pub)) { GNUNET_break (0); json_decref (history); @@ -563,10 +563,10 @@ TEH_RESPONSE_compile_transaction_history (const struct TALER_EXCHANGEDB_Transact json_array_append_new (history, json_pack ("{s:s, s:o, s:o, s:o, s:o}", "type", "MELT", - "amount", TALER_JSON_from_amount (&melt->amount_with_fee), + "amount", TALER_JSON_from_amount (&melt->session.amount_with_fee), "melt_fee", TALER_JSON_from_amount (&melt->melt_fee), - "session_hash", GNUNET_JSON_from_data_auto (&melt->session_hash), - "coin_sig", GNUNET_JSON_from_data_auto (&melt->coin_sig)))); + "rc", GNUNET_JSON_from_data_auto (&melt->session.rc), + "coin_sig", GNUNET_JSON_from_data_auto (&melt->session.coin_sig)))); } break; case TALER_EXCHANGEDB_TT_REFUND: @@ -812,7 +812,7 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto return NULL; } - GNUNET_assert (0 == + GNUNET_assert (0 == json_array_append_new (json_history, json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}", "type", "PAYBACK", @@ -847,7 +847,7 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto } } ret |= 2; - rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); + rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED); rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS)); rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.closing->execution_date); TALER_amount_hton (&rcc.closing_amount, diff --git a/src/exchangedb/perf_taler_exchangedb.c b/src/exchangedb/perf_taler_exchangedb.c index dcd8a4e2b..e2591c88b 100644 --- a/src/exchangedb/perf_taler_exchangedb.c +++ b/src/exchangedb/perf_taler_exchangedb.c @@ -74,7 +74,7 @@ main (int argc, char ** argv) /* End of initialization */ /* Reserve initialization */ PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("02 - init reserve loop", - NB_RESERVE_INIT), + NB_RESERVE_INIT), PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_RESERVE ("02 - reserve"), PERF_TALER_EXCHANGEDB_INIT_CMD_INSERT_RESERVE ("02 - insert", "02 - reserve"), @@ -131,14 +131,24 @@ main (int argc, char ** argv) PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("05 - refresh session init loop", NB_REFRESH_INIT), PERF_TALER_EXCHANGEDB_INIT_CMD_START_TRANSACTION (""), - PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("05 - refresh session"), + PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("05 - denomination load", + "05 - refresh session init loop", + "01 - save denomination"), + PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("05 - reserve load", + "05 - refresh session init loop", + "02 - save reserve"), + PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_WITHDRAW ("05 - withdraw", + "05 - denomination load", + "05 - reserve load"), + PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("05 - refresh session", + "05 - withdraw"), PERF_TALER_EXCHANGEDB_INIT_CMD_SAVE_ARRAY ("05 - session array", - "05 - refresh session init loop", - "05 - refresh session", - NB_RESERVE_SAVE), + "05 - refresh session init loop", + "05 - refresh session", + NB_RESERVE_SAVE), PERF_TALER_EXCHANGEDB_INIT_CMD_COMMIT_TRANSACTION (""), PERF_TALER_EXCHANGEDB_INIT_CMD_END_LOOP ("05 - end", - "05 - refresh session init loop"), + "05 - refresh session init loop"), /* End of refresh session initialization */ /* Refresh melt initialization */ PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("06 - refresh melt init loop", @@ -328,9 +338,23 @@ main (int argc, char ** argv) NB_DEPOSIT_SAVE), PERF_TALER_EXCHANGEDB_INIT_CMD_GET_TIME ("29 - start"), PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("29 - insert refresh session", - NB_REFRESH_SAVE), + NB_REFRESH_SAVE), PERF_TALER_EXCHANGEDB_INIT_CMD_START_TRANSACTION (""), - PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION (""), + PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("29 - denomination load", + "29 - insert refresh session", + "01 - save denomination"), + PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("29 - reserve load", + "29 - insert refresh session", + "02 - save reserve"), + PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_WITHDRAW ("29 - withdraw", + "29 - denomination load", + "29 - reserve load"), + PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("29 - refresh session", + "29 - withdraw"), + PERF_TALER_EXCHANGEDB_INIT_CMD_SAVE_ARRAY ("29 - session array", + "29 - insert refresh session", + "29 - refresh session", + NB_RESERVE_SAVE), PERF_TALER_EXCHANGEDB_INIT_CMD_COMMIT_TRANSACTION (""), PERF_TALER_EXCHANGEDB_INIT_CMD_END_LOOP ("", "29 - insert refresh session"), diff --git a/src/exchangedb/perf_taler_exchangedb_init.c b/src/exchangedb/perf_taler_exchangedb_init.c index c46bea22d..412853980 100644 --- a/src/exchangedb/perf_taler_exchangedb_init.c +++ b/src/exchangedb/perf_taler_exchangedb_init.c @@ -435,56 +435,14 @@ PERF_TALER_EXCHANGEDB_coin_free (struct PERF_TALER_EXCHANGEDB_Coin *coin) /** - * @return a randomly generated refresh session - */ -struct TALER_EXCHANGEDB_RefreshSession * -PERF_TALER_EXCHANGEDB_refresh_session_init () -{ - struct TALER_EXCHANGEDB_RefreshSession *refresh_session; - - GNUNET_assert (NULL != - (refresh_session = GNUNET_new (struct TALER_EXCHANGEDB_RefreshSession))); - refresh_session->noreveal_index = 1; - refresh_session->num_newcoins = 1; - - return refresh_session; -} - - -/** - * @return #GNUNET_OK if the copy was successful, #GNUNET_SYSERR if it wasn't - */ -int -PERF_TALER_EXCHANGEDB_refresh_session_copy (struct TALER_EXCHANGEDB_RefreshSession *session, - struct TALER_EXCHANGEDB_RefreshSession *copy) -{ - *copy = *session; - return GNUNET_OK; -} - - -/** - * Free a refresh session - */ -int -PERF_TALER_EXCHANGEDB_refresh_session_free (struct TALER_EXCHANGEDB_RefreshSession *refresh_session) -{ - if (NULL == refresh_session) - return GNUNET_OK; - GNUNET_free (refresh_session); - return GNUNET_OK; -} - - -/** * Create a melt operation * - * @param session the refresh session + * @param rc the commitment of the refresh session * @param dki the denomination the melted coin uses * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt */ struct TALER_EXCHANGEDB_RefreshMelt * -PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session, +PERF_TALER_EXCHANGEDB_refresh_melt_init (struct TALER_RefreshCommitmentP *rc, struct PERF_TALER_EXCHANGEDB_Coin *coin) { struct TALER_EXCHANGEDB_RefreshMelt *melt; @@ -496,12 +454,12 @@ PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session, struct { struct GNUNET_CRYPTO_EccSignaturePurpose purpose; - struct GNUNET_HashCode session; + struct TALER_RefreshCommitmentP rc; } to_sign; to_sign.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_TEST; to_sign.purpose.size = htonl (sizeof (to_sign)); - to_sign.session = *session; + to_sign.rc = *rc; GNUNET_CRYPTO_eddsa_sign (&coin->priv, &to_sign.purpose, &coin_sig.eddsa_signature); @@ -513,16 +471,16 @@ PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session, TALER_string_to_amount (CURRENCY ":0.1", &amount_with_fee)); melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt); - melt->coin.coin_pub = coin->public_info.coin_pub; - melt->coin.denom_sig.rsa_signature = + melt->session.coin.coin_pub = coin->public_info.coin_pub; + melt->session.coin.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (coin->public_info.denom_sig.rsa_signature); - melt->coin.denom_pub.rsa_public_key = + melt->session.coin.denom_pub.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (coin->public_info.denom_pub.rsa_public_key); - GNUNET_assert (NULL != melt->coin.denom_pub.rsa_public_key); - GNUNET_assert (NULL != melt->coin.denom_sig.rsa_signature); - melt->coin_sig = coin_sig; - melt->session_hash = *session; - melt->amount_with_fee = amount; + GNUNET_assert (NULL != melt->session.coin.denom_pub.rsa_public_key); + GNUNET_assert (NULL != melt->session.coin.denom_sig.rsa_signature); + melt->session.coin_sig = coin_sig; + melt->session.rc = *rc; + melt->session.amount_with_fee = amount; melt->melt_fee = amount_with_fee; return melt; } @@ -541,9 +499,9 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct TALER_EXCHANGEDB_RefreshMe copy = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt); *copy = *melt; - copy->coin.denom_sig.rsa_signature = - GNUNET_CRYPTO_rsa_signature_dup (melt->coin.denom_sig.rsa_signature); - GNUNET_assert (NULL != copy->coin.denom_sig.rsa_signature); + copy->session.coin.denom_sig.rsa_signature = + GNUNET_CRYPTO_rsa_signature_dup (melt->session.coin.denom_sig.rsa_signature); + GNUNET_assert (NULL != copy->session.coin.denom_sig.rsa_signature); return copy; } @@ -558,51 +516,7 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct TALER_EXCHANGEDB_RefreshMe int PERF_TALER_EXCHANGEDB_refresh_melt_free (struct TALER_EXCHANGEDB_RefreshMelt *melt) { - GNUNET_CRYPTO_rsa_signature_free (melt->coin.denom_sig.rsa_signature); + GNUNET_CRYPTO_rsa_signature_free (melt->session.coin.denom_sig.rsa_signature); GNUNET_free (melt); return GNUNET_OK; } - - -/** - * Create a #TALER_EXCHANGEDB_RefreshCommitCoin - */ -struct TALER_EXCHANGEDB_RefreshCommitCoin * -PERF_TALER_EXCHANGEDB_refresh_commit_coin_init () -{ - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin; - - commit_coin = GNUNET_new (struct TALER_EXCHANGEDB_RefreshCommitCoin); - commit_coin->coin_ev = "coin_ev"; - commit_coin->coin_ev_size = 8; - return commit_coin; -} - - -/** - * Copies a #TALER_EXCHANGEDB_RefreshCommitCoin - * - * @param commit_coin the commit to copy - * @return a copy of @a commit_coin - */ -struct TALER_EXCHANGEDB_RefreshCommitCoin * -PERF_TALER_EXCHANGEDB_refresh_commit_coin_copy (struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin) -{ - struct TALER_EXCHANGEDB_RefreshCommitCoin *copy; - - copy = GNUNET_new (struct TALER_EXCHANGEDB_RefreshCommitCoin); - *copy = *commit_coin; - return copy; -} - - -/** - * Free a #TALER_EXCHANGEDB_RefreshCommitCoin - * - * @param commit_coin the coin to free - */ -void -PERF_TALER_EXCHANGEDB_refresh_commit_coin_free (struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin) -{ - GNUNET_free (commit_coin); -} diff --git a/src/exchangedb/perf_taler_exchangedb_init.h b/src/exchangedb/perf_taler_exchangedb_init.h index 11b2f1661..a1f2559e2 100644 --- a/src/exchangedb/perf_taler_exchangedb_init.h +++ b/src/exchangedb/perf_taler_exchangedb_init.h @@ -44,7 +44,7 @@ struct PERF_TALER_EXCHANGEDB_Reserve /** - * All informations about a coin + * All informations about a coin */ struct PERF_TALER_EXCHANGEDB_Coin { @@ -169,42 +169,20 @@ PERF_TALER_EXCHANGEDB_coin_free (struct PERF_TALER_EXCHANGEDB_Coin *coin); /** - * @return a randomly generated refresh session - */ -struct TALER_EXCHANGEDB_RefreshSession * -PERF_TALER_EXCHANGEDB_refresh_session_init (void); - - -/** - * @return #GNUNET_OK if the copy was successful, #GNUNET_SYSERR if it wasn't - */ -int -PERF_TALER_EXCHANGEDB_refresh_session_copy (struct TALER_EXCHANGEDB_RefreshSession *session, - struct TALER_EXCHANGEDB_RefreshSession *copy); - - -/** - * Frees memory of a refresh_session - */ -int -PERF_TALER_EXCHANGEDB_refresh_session_free (struct TALER_EXCHANGEDB_RefreshSession *refresh_session); - - -/** * Create a melt operation * - * @param session the refresh session + * @param rc the commitment of the refresh session * @param dki the denomination the melted coin uses - * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt + * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt */ struct TALER_EXCHANGEDB_RefreshMelt * -PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session, - struct PERF_TALER_EXCHANGEDB_Coin *coin); +PERF_TALER_EXCHANGEDB_refresh_melt_init (struct TALER_RefreshCommitmentP *rc, + struct PERF_TALER_EXCHANGEDB_Coin *coin); /** * Copies the internals of a #TALER_EXCHANGEDB_RefreshMelt - * + * * @param melt the refresh melt to copy * @return an copy of @ melt */ @@ -221,30 +199,4 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct TALER_EXCHANGEDB_RefreshMe int PERF_TALER_EXCHANGEDB_refresh_melt_free (struct TALER_EXCHANGEDB_RefreshMelt *melt); - -/** - * Create a #TALER_EXCHANGEDB_RefreshCommitCoin - */ -struct TALER_EXCHANGEDB_RefreshCommitCoin * -PERF_TALER_EXCHANGEDB_refresh_commit_coin_init (void); - - -/** - * Copies a #TALER_EXCHANGEDB_RefreshCommitCoin - * - * @param commit_coin the commit to copy - * @return a copy of @a commit_coin - */ -struct TALER_EXCHANGEDB_RefreshCommitCoin * -PERF_TALER_EXCHANGEDB_refresh_commit_coin_copy (struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin); - - -/** - * Free a #TALER_EXCHANGEDB_RefreshCommitCoin - * - * @param commit_coin the coin to free - */ -void -PERF_TALER_EXCHANGEDB_refresh_commit_coin_free (struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin); - #endif diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c b/src/exchangedb/perf_taler_exchangedb_interpreter.c index 7ec958c48..eca519527 100644 --- a/src/exchangedb/perf_taler_exchangedb_interpreter.c +++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c @@ -59,50 +59,40 @@ data_free (struct PERF_TALER_EXCHANGEDB_Data *data) { switch (data->type) { - case PERF_TALER_EXCHANGEDB_TIME: - if (NULL == data->data.time) - break; - GNUNET_free (data->data.time); - data->data.time = NULL; - break; - - case PERF_TALER_EXCHANGEDB_DEPOSIT: - if (NULL == data->data.deposit) - break; - PERF_TALER_EXCHANGEDB_deposit_free (data->data.deposit); - data->data.deposit = NULL; - break; - - case PERF_TALER_EXCHANGEDB_COIN: - if (NULL == data->data.coin) - break; - PERF_TALER_EXCHANGEDB_coin_free (data->data.coin); - data->data.coin = NULL; + case PERF_TALER_EXCHANGEDB_TIME: + if (NULL == data->data.time) break; - - case PERF_TALER_EXCHANGEDB_RESERVE: - if (NULL == data->data.reserve) - break; - PERF_TALER_EXCHANGEDB_reserve_free (data->data.reserve); - data->data.reserve = NULL; + GNUNET_free (data->data.time); + data->data.time = NULL; + break; + case PERF_TALER_EXCHANGEDB_DEPOSIT: + if (NULL == data->data.deposit) break; - - case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO: - if (NULL == data->data.dki) - break; - PERF_TALER_EXCHANGEDB_denomination_free (data->data.dki); - data->data.dki = NULL; + PERF_TALER_EXCHANGEDB_deposit_free (data->data.deposit); + data->data.deposit = NULL; + break; + case PERF_TALER_EXCHANGEDB_COIN: + if (NULL == data->data.coin) break; - - case PERF_TALER_EXCHANGEDB_REFRESH_HASH: - if (NULL == data->data.session_hash) - break; - GNUNET_free (data->data.session_hash); - data->data.session_hash = NULL; + GNUNET_free (data->data.coin); + data->data.coin = NULL; + break; + case PERF_TALER_EXCHANGEDB_RESERVE: + if (NULL == data->data.reserve) break; - - case PERF_TALER_EXCHANGEDB_NONE: + PERF_TALER_EXCHANGEDB_reserve_free (data->data.reserve); + data->data.reserve = NULL; + break; + case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO: + if (NULL == data->data.dki) break; + PERF_TALER_EXCHANGEDB_denomination_free (data->data.dki); + data->data.dki = NULL; + break; + case PERF_TALER_EXCHANGEDB_REFRESH_HASH: + break; + case PERF_TALER_EXCHANGEDB_NONE: + break; } } @@ -124,33 +114,25 @@ data_copy (const struct PERF_TALER_EXCHANGEDB_Data *data, copy->data.time = GNUNET_new (struct GNUNET_TIME_Absolute); *copy->data.time = *data->data.time; return; - case PERF_TALER_EXCHANGEDB_DEPOSIT: copy->data.deposit = PERF_TALER_EXCHANGEDB_deposit_copy (data->data.deposit); return; - case PERF_TALER_EXCHANGEDB_COIN: copy->data.coin = PERF_TALER_EXCHANGEDB_coin_copy (data->data.coin); return; - case PERF_TALER_EXCHANGEDB_RESERVE: copy->data.reserve = PERF_TALER_EXCHANGEDB_reserve_copy (data->data.reserve); return; - case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO: copy->data.dki = PERF_TALER_EXCHANGEDB_denomination_copy (data->data.dki); return; - case PERF_TALER_EXCHANGEDB_REFRESH_HASH: - copy-> data.session_hash = GNUNET_new (struct GNUNET_HashCode); - *copy->data.session_hash - = *data->data.session_hash; + copy->data.rc = data->data.rc; break; - case PERF_TALER_EXCHANGEDB_NONE: break; } @@ -210,9 +192,10 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) if (PERF_TALER_EXCHANGEDB_CMD_LOOP != cmd[ret].command) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.end_loop.label_loop); + cmd[i].details.end_loop.label_loop, + cmd[i].label); return GNUNET_SYSERR; } cmd[i].details.end_loop.index_loop = ret; @@ -228,17 +211,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", + "%d:Undefined reference to %s at %s\n", i, - cmd[i].details.save_array.label_save); + cmd[i].details.save_array.label_save, + cmd[i].label); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_NONE == cmd[ret].exposed.type) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.save_array.label_save); + cmd[i].details.save_array.label_save, + cmd[i].label); return GNUNET_SYSERR; } cmd[i].details.save_array.index_save = ret; @@ -248,17 +233,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", + "%d:Undefined reference to %s at %s\n", i, - cmd[i].details.save_array.label_loop); + cmd[i].details.save_array.label_loop, + cmd[i].label); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_CMD_LOOP != cmd[ret].command) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.save_array.label_loop); + cmd[i].details.save_array.label_loop, + cmd[i].label); return GNUNET_SYSERR; } cmd[i].details.save_array.index_loop = ret; @@ -281,17 +268,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", + "%d:Undefined reference to %s at %s\n", i, - cmd[i].details.load_array.label_save); + cmd[i].details.load_array.label_save, + cmd[i].label); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_CMD_SAVE_ARRAY != cmd[ret].command) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.load_array.label_save); + cmd[i].details.load_array.label_save, + cmd[i].label); return GNUNET_SYSERR; } cmd[i].details.load_array.index_save = ret; @@ -732,188 +721,73 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION: - { - int ret; - - ret = cmd_find (cmd, - cmd[i].details.get_refresh_session.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.get_refresh_session.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.get_refresh_session.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.get_refresh_session.index_hash = ret; - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER: + case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION: { int ret; - ret = cmd_find (cmd, - cmd[i].details.insert_refresh_order.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.insert_refresh_order.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.insert_refresh_order.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.insert_refresh_order.index_hash = ret; ret = cmd_find (cmd, - cmd[i].details.insert_refresh_order.label_denom); + cmd[i].details.create_refresh_session.label_coin); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", + "%d:Undefined reference to %s at %s\n", i, - cmd[i].details.insert_refresh_order.label_denom); + cmd[i].details.create_refresh_session.label_coin, + cmd[i].label); return GNUNET_SYSERR; } - if (PERF_TALER_EXCHANGEDB_DENOMINATION_INFO != cmd[ret].exposed.type) + if (PERF_TALER_EXCHANGEDB_COIN != cmd[ret].exposed.type) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.insert_refresh_order.label_denom); + cmd[i].details.create_refresh_session.label_coin, + cmd[i].label); return GNUNET_SYSERR; } - cmd[i].details.insert_refresh_order.index_denom = ret; + cmd[i].details.create_refresh_session.index_coin = ret; } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER: + case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION: { int ret; - ret = cmd_find (cmd, - cmd[i].details.get_refresh_order.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.get_refresh_order.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.get_refresh_order.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.get_refresh_order.index_hash = ret; - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN: - { - int ret; - ret = cmd_find (cmd, - cmd[i].details.insert_refresh_commit_coin.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.insert_refresh_commit_coin.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.insert_refresh_commit_coin.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.insert_refresh_commit_coin.index_hash = ret; - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN: - { - int ret; - ret = cmd_find (cmd, - cmd[i].details.get_refresh_commit_coin.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.get_refresh_commit_coin.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.get_refresh_commit_coin.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.get_refresh_commit_coin.index_hash = ret; - } - break; - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK: - { - int ret; ret = cmd_find (cmd, - cmd[i].details.insert_refresh_commit_link.label_hash); + cmd[i].details.get_refresh_session.label_hash); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", + "%d:Undefined reference to %s at %s\n", i, - cmd[i].details.insert_refresh_commit_link.label_hash); + cmd[i].details.get_refresh_session.label_hash, + cmd[i].label); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", + "%d:Wrong type reference to %s at %s\n", i, - cmd[i].details.insert_refresh_commit_link.label_hash); + cmd[i].details.get_refresh_session.label_hash, + cmd[i].label); return GNUNET_SYSERR; } - cmd[i].details.insert_refresh_commit_link.index_hash = ret; + cmd[i].details.get_refresh_session.index_hash = ret; } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK: - { + case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL: + { int ret; ret = cmd_find (cmd, - cmd[i].details.get_refresh_commit_link.label_hash); + cmd[i].details.insert_refresh_reveal.label_hash); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Undefined reference to %s\n", i, - cmd[i].details.get_refresh_commit_link.label_hash); + cmd[i].details.insert_refresh_reveal.label_hash); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) @@ -921,49 +795,44 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Wrong type reference to %s\n", i, - cmd[i].details.get_refresh_commit_link.label_hash); + cmd[i].details.insert_refresh_reveal.label_hash); return GNUNET_SYSERR; } - cmd[i].details.get_refresh_commit_link.index_hash = ret; - } - break; + cmd[i].details.insert_refresh_reveal.index_hash = ret; - case PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT: - { - int ret; ret = cmd_find (cmd, - cmd[i].details.get_melt_commitment.label_hash); + cmd[i].details.insert_refresh_reveal.label_denom); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Undefined reference to %s\n", i, - cmd[i].details.get_melt_commitment.label_hash); + cmd[i].details.insert_refresh_reveal.label_denom); return GNUNET_SYSERR; } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) + if (PERF_TALER_EXCHANGEDB_DENOMINATION_INFO != cmd[ret].exposed.type) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Wrong type reference to %s\n", i, - cmd[i].details.get_melt_commitment.label_hash); + cmd[i].details.insert_refresh_reveal.label_denom); return GNUNET_SYSERR; } - cmd[i].details.get_melt_commitment.index_hash = ret; + cmd[i].details.insert_refresh_reveal.index_denom = ret; } break; - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT: - { + case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL: + { int ret; ret = cmd_find (cmd, - cmd[i].details.insert_refresh_out.label_hash); + cmd[i].details.get_refresh_reveal.label_hash); if (GNUNET_SYSERR == ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Undefined reference to %s\n", i, - cmd[i].details.insert_refresh_out.label_hash); + cmd[i].details.get_refresh_reveal.label_hash); return GNUNET_SYSERR; } if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) @@ -971,14 +840,14 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "%d:Wrong type reference to %s\n", i, - cmd[i].details.insert_refresh_out.label_hash); + cmd[i].details.get_refresh_reveal.label_hash); return GNUNET_SYSERR; } - cmd[i].details.insert_refresh_out.index_hash = ret; + cmd[i].details.get_refresh_reveal.index_hash = ret; } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST: + case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA: { int ret; ret = cmd_find (cmd, @@ -1003,31 +872,6 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER: - { - int ret; - ret = cmd_find (cmd, - cmd[i].details.get_transfer.label_hash); - if (GNUNET_SYSERR == ret) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Undefined reference to %s\n", - i, - cmd[i].details.get_transfer.label_hash); - return GNUNET_SYSERR; - } - if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "%d:Wrong type reference to %s\n", - i, - cmd[i].details.get_transfer.label_hash); - return GNUNET_SYSERR; - } - cmd[i].details.get_transfer.index_hash = ret; - } - break; - case PERF_TALER_EXCHANGEDB_CMD_END: case PERF_TALER_EXCHANGEDB_CMD_DEBUG: case PERF_TALER_EXCHANGEDB_CMD_LOOP: @@ -1038,7 +882,6 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) case PERF_TALER_EXCHANGEDB_CMD_GET_TIME: case PERF_TALER_EXCHANGEDB_CMD_CREATE_DENOMINATION: case PERF_TALER_EXCHANGEDB_CMD_CREATE_RESERVE: - case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION: break; } } @@ -1092,12 +935,11 @@ cmd_clean (struct PERF_TALER_EXCHANGEDB_Cmd cmd[]) static void interpret_end_loop (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) { - unsigned int i; int jump; jump = state->cmd[state->i].details.end_loop.index_loop; // Cleaning up the memory in the loop - for (i = jump; i < state->i; i++) + for (unsigned int i = jump; i < state->i; i++) data_free (&state->cmd[i].exposed); state->cmd[jump].details.loop.curr_iteration++; @@ -1230,6 +1072,29 @@ interprete_load_random (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) /** + * Function called with information about a refresh order. + * + * @param cls closure + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs array of @e num_tprivs transfer private keys + * @param tp transfer public key information + */ +static void +refresh_reveal_cb (void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp) +{ + /* intentionally empty */ +} + + +/** * Iterate over the commands, acting accordingly at each step * * @param state the current state of the interpreter @@ -1340,10 +1205,11 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) deposit_index = state->cmd[state->i].details.insert_deposit.index_deposit; deposit = state->cmd[deposit_index].exposed.data.deposit; qs = state->plugin->insert_deposit (state->plugin->cls, - state->session, - deposit); + state->session, + deposit); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); - state->cmd[state->i].exposed.data.deposit = deposit; + state->cmd[state->i].exposed.data.deposit + = PERF_TALER_EXCHANGEDB_deposit_copy (deposit); } break; @@ -1502,8 +1368,8 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) coin_index = state->cmd[state->i].details.insert_withdraw.index_coin; coin = state->cmd[coin_index].exposed.data.coin; qs = state->plugin->insert_withdraw_info (state->plugin->cls, - state->session, - &coin->blind); + state->session, + &coin->blind); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); } break; @@ -1546,143 +1412,100 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION: { - struct GNUNET_HashCode *hash; - struct TALER_EXCHANGEDB_RefreshSession *refresh_session; + struct TALER_EXCHANGEDB_RefreshSession refresh_session; + unsigned int coin_index; + struct PERF_TALER_EXCHANGEDB_Coin *coin; - hash = GNUNET_new (struct GNUNET_HashCode); - refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init (); + coin_index = state->cmd[state->i].details.create_refresh_session.index_coin; + coin = state->cmd[coin_index].exposed.data.coin; + + refresh_session.coin = coin->public_info; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &refresh_session.coin_sig, + sizeof (refresh_session.coin_sig)); GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, - hash); + &refresh_session.rc.session_hash); + GNUNET_assert (GNUNET_OK == + TALER_string_to_amount (CURRENCY ":1.1", + &refresh_session.amount_with_fee)); + refresh_session.noreveal_index = 1; GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == - state->plugin->create_refresh_session (state->session, - state->session, - hash, - refresh_session)); - state->cmd[state->i].exposed.data.session_hash = hash; - PERF_TALER_EXCHANGEDB_refresh_session_free (refresh_session); - GNUNET_free (refresh_session); + state->plugin->insert_melt (state->session, + state->session, + &refresh_session)); + state->cmd[state->i].exposed.data.rc = refresh_session.rc; } break; case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION: { unsigned int hash_index; - struct GNUNET_HashCode *hash; - struct TALER_EXCHANGEDB_RefreshSession refresh; + const struct TALER_RefreshCommitmentP *rc; + struct TALER_EXCHANGEDB_RefreshMelt refresh; hash_index = state->cmd[state->i].details.get_refresh_session.index_hash; - hash = state->cmd[hash_index].exposed.data.session_hash; - state->plugin->get_refresh_session (state->session, - state->session, - hash, - &refresh); + rc = &state->cmd[hash_index].exposed.data.rc; + state->plugin->get_melt (state->session, + state->session, + rc, + &refresh); } break; - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER: + case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL: { unsigned int hash_index; unsigned int denom_index; - struct GNUNET_HashCode *session_hash; + const struct TALER_RefreshCommitmentP *rc; struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *denom; + struct TALER_TransferPublicKeyP tpub; + struct TALER_TransferPrivateKeyP tprivs[2]; + struct TALER_EXCHANGEDB_RefreshRevealedCoin rrc; - hash_index = state->cmd[state->i].details.insert_refresh_order.index_hash; - denom_index = state->cmd[state->i].details.insert_refresh_order.index_denom; - session_hash = state->cmd[hash_index].exposed.data.session_hash; + hash_index = state->cmd[state->i].details.insert_refresh_reveal.index_hash; + denom_index = state->cmd[state->i].details.insert_refresh_reveal.index_denom; + rc = &state->cmd[hash_index].exposed.data.rc; denom = state->cmd[denom_index].exposed.data.dki; + rrc.denom_pub = denom->denom_pub; + rrc.coin_ev = "coin_ev"; + rrc.coin_ev_size = strlen (rrc.coin_ev) + 1; + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &rrc.coin_sig, + sizeof (struct TALER_CoinSpendSignatureP)); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + tprivs, + sizeof (struct TALER_TransferPrivateKeyP) * 2); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + &tpub, + sizeof (struct TALER_TransferPublicKeyP)); GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == - state->plugin->insert_refresh_order (state->plugin->cls, - state->session, - session_hash, - 1, - &denom->denom_pub)); - + state->plugin->insert_refresh_reveal (state->plugin->cls, + state->session, + rc, + 1, + &rrc, + 2, + tprivs, + &tpub)); } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER: + case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL: { int hash_index; - struct GNUNET_HashCode *hash; - struct TALER_DenominationPublicKey denom_pub; - - hash_index = state->cmd[state->i].details.get_refresh_order.index_hash; - hash = state->cmd[hash_index].exposed.data.session_hash; - state->plugin->get_refresh_order (state->plugin->cls, - state->session, - hash, - 1, - &denom_pub); - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN: - { - enum GNUNET_DB_QueryStatus qs; - unsigned int hash_index; - struct TALER_EXCHANGEDB_RefreshCommitCoin *refresh_commit; - - hash_index = state->cmd[state->i].details.insert_refresh_commit_coin.index_hash; - refresh_commit = PERF_TALER_EXCHANGEDB_refresh_commit_coin_init (); - qs = state->plugin->insert_refresh_commit_coins (state->plugin->cls, - state->session, - state->cmd[hash_index].exposed.data.session_hash, - 1, - refresh_commit); - GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs); - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN: - { - unsigned int hash_index; - struct TALER_EXCHANGEDB_RefreshCommitCoin refresh_commit; - - hash_index = state->cmd[state->i].details.insert_refresh_commit_coin.index_hash; - state->plugin->get_refresh_commit_coins (state->plugin->cls, - state->session, - state->cmd[hash_index].exposed.data.session_hash, - 1, - &refresh_commit); - - } - break; - - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK: - { -// unsigned int hash_index; -// -// hash_index = state->cmd[state->i].details.insert_refresh_commit_link.index_hash; - } - break; + const struct TALER_RefreshCommitmentP *rc; - case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK: - { - int ret; - unsigned int hash_index; - struct TALER_EXCHANGEDB_RefreshCommitCoin commit_coin; - - // FIXME: this should go after the public key! - hash_index = state->cmd[state->i].details.get_refresh_commit_link.index_hash; - ret = state->plugin->get_refresh_commit_coins(state->plugin->cls, - state->session, - state->cmd[hash_index].exposed.data.session_hash, - 1, - &commit_coin); - GNUNET_assert (GNUNET_SYSERR != ret); + hash_index = state->cmd[state->i].details.get_refresh_reveal.index_hash; + rc = &state->cmd[hash_index].exposed.data.rc; + state->plugin->get_refresh_reveal (state->plugin->cls, + state->session, + rc, + &refresh_reveal_cb, + state); } break; - case PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT: - break; - - case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT: - break; - - case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST: - break; - - case PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER: + case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA: break; } diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.h b/src/exchangedb/perf_taler_exchangedb_interpreter.h index 169811bdd..60e676069 100644 --- a/src/exchangedb/perf_taler_exchangedb_interpreter.h +++ b/src/exchangedb/perf_taler_exchangedb_interpreter.h @@ -331,7 +331,7 @@ } /** - * Inserts informations about a withdrawal in the database + * Inserts information about a withdrawal into the database * * @exposes #PERF_TALER_EXCHANGEDB_COIN * @@ -452,16 +452,18 @@ _label_coin), \ PERF_TALER_EXCHANGEDB_INIT_CMD_INSERT_DEPOSIT (_label "insert", \ _label "deposit") + /** * Insert informations about a refresh session * melts one coin into another * * @param _label the label of the command */ -#define PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION(_label) \ +#define PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION(_label, _label_coin) \ { \ .command = PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION, \ .label = _label, \ + .details.create_refresh_session.label_coin = _label_coin, \ .exposed.type = PERF_TALER_EXCHANGEDB_REFRESH_HASH \ } @@ -519,7 +521,7 @@ struct PERF_TALER_EXCHANGEDB_Data /** #PERF_TALER_EXCHANGEDB_DENOMINATION_INFO */ struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki; /** #PERF_TALER_EXCHANGEDB_REFRESH_HASH */ - struct GNUNET_HashCode *session_hash; + struct TALER_RefreshCommitmentP rc; } data; }; @@ -679,54 +681,19 @@ enum PERF_TALER_EXCHANGEDB_CMD_Name PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION, /** - * Insert a melt refresh order - */ - PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER, - - /** - * Get informations about a refresh order - */ - PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER, - - /** - * Insert refresh commit coin - */ - PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN, - - /** - * Get refresh commit coin - */ - PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN, - - /** - * Insert refresh commit link - */ - PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK, - - /** - * Get refresh commit link - */ - PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK, - - /** - * Get information avout the melt commit + * Insert a melt refresh reveal data */ - PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT, + PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL, /** - * Insert a new coin into the database after a melt operation + * Get informations about a refresh reveal data */ - PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT, + PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL, /** * Get the link data list of a coin */ - PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST, - - /** - * Get the shared secret and the transfere public key - */ - PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER + PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA }; @@ -1029,6 +996,18 @@ union PERF_TALER_EXCHANGEDB_CMD_Details } get_deposit; /** + * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION command + */ + struct PERF_TALER_EXCHANGEDB_CMD_createRefreshSessionDetails + { + /** + * label of the source of the hash of the session + */ + const char *label_coin; + unsigned int index_coin; + } create_refresh_session; + + /** * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION command */ struct PERF_TALER_EXCHANGEDB_CMD_getRefreshSessionDetails @@ -1041,9 +1020,9 @@ union PERF_TALER_EXCHANGEDB_CMD_Details } get_refresh_session; /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER command + * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL command */ - struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshOrderDetails + struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshRevealDetails { /** * The refresh session hash @@ -1056,12 +1035,12 @@ union PERF_TALER_EXCHANGEDB_CMD_Details */ const char *label_denom; unsigned int index_denom; - } insert_refresh_order; + } insert_refresh_reveal; /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER command + * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL command */ - struct PERF_TALER_EXCHANGEDB_CMD_getRefreshOrderDetails + struct PERF_TALER_EXCHANGEDB_CMD_getRefreshRevealDetails { /** * The session hash @@ -1069,82 +1048,7 @@ union PERF_TALER_EXCHANGEDB_CMD_Details const char *label_hash; unsigned int index_hash; - } get_refresh_order; - - /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN command - */ - struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshCommitCoinDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - - } insert_refresh_commit_coin; - - /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN command - */ - struct PERF_TALER_EXCHANGEDB_CMD_getRefreshCommitCoinDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - - } get_refresh_commit_coin; - - /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK command - */ - struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshCommitLinkDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - - } insert_refresh_commit_link; - - /** - * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK command - */ - struct PERF_TALER_EXCHANGEDB_CMD_getRefreshCommitLinkDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - } get_refresh_commit_link; - - /** - * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT command - */ - struct PERF_TALER_EXCHANGEDB_CMD_getMeltCommitmentDaetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - } get_melt_commitment; - - /** - * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT command - */ - struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshOutDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - } insert_refresh_out; + } get_refresh_reveal; /** * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST command @@ -1158,17 +1062,6 @@ union PERF_TALER_EXCHANGEDB_CMD_Details unsigned int index_hash; } get_link_data_list; - /** - * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER command - */ - struct PERF_TALER_EXCHANGEDB_CMD_getTransferDetails - { - /** - * The refresh session hash - */ - const char *label_hash; - unsigned int index_hash; - } get_transfer; }; diff --git a/src/exchangedb/plugin_exchangedb_common.c b/src/exchangedb/plugin_exchangedb_common.c index 61c00c14d..e4b832491 100644 --- a/src/exchangedb/plugin_exchangedb_common.c +++ b/src/exchangedb/plugin_exchangedb_common.c @@ -74,31 +74,6 @@ common_free_reserve_history (void *cls, /** - * Free memory of the link data list. - * - * @param cls the @e cls of this struct with the plugin-specific state (unused) - * @param ldl link data list to release - */ -static void -common_free_link_data_list (void *cls, - struct TALER_EXCHANGEDB_LinkDataList *ldl) -{ - struct TALER_EXCHANGEDB_LinkDataList *next; - - while (NULL != ldl) - { - next = ldl->next; - if (NULL != ldl->denom_pub.rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (ldl->denom_pub.rsa_public_key); - if (NULL != ldl->ev_sig.rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (ldl->ev_sig.rsa_signature); - GNUNET_free (ldl); - ldl = next; - } -} - - -/** * Free linked list of transactions. * * @param cls the @e cls of this struct with the plugin-specific state (unused) @@ -125,10 +100,10 @@ common_free_coin_transaction_list (void *cls, GNUNET_free (list->details.deposit); break; case TALER_EXCHANGEDB_TT_REFRESH_MELT: - if (NULL != list->details.melt->coin.denom_pub.rsa_public_key) - GNUNET_CRYPTO_rsa_public_key_free (list->details.melt->coin.denom_pub.rsa_public_key); - if (NULL != list->details.melt->coin.denom_sig.rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (list->details.melt->coin.denom_sig.rsa_signature); + if (NULL != list->details.melt->session.coin.denom_pub.rsa_public_key) + GNUNET_CRYPTO_rsa_public_key_free (list->details.melt->session.coin.denom_pub.rsa_public_key); + if (NULL != list->details.melt->session.coin.denom_sig.rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (list->details.melt->session.coin.denom_sig.rsa_signature); GNUNET_free (list->details.melt); break; case TALER_EXCHANGEDB_TT_REFUND: diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 93a8c1a8b..b6e862ab9 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -23,6 +23,7 @@ * @author Marcello Stanisci */ #include "platform.h" +#include "taler_error_codes.h" #include "taler_pq_lib.h" #include "taler_exchangedb_plugin.h" #include <pthread.h> @@ -105,12 +106,10 @@ postgres_drop_tables (void *cls) GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_out CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_fee CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS deposits CASCADE;"), - GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_out CASCADE;"), - GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_commit_coin CASCADE;"), - GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_transfer_public_key CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refunds CASCADE;"), - GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_order CASCADE;"), - GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_sessions CASCADE;"), + GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_commitments CASCADE;"), + GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_revealed_coins CASCADE;"), + GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_transfer_keys CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS known_coins CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_close CASCADE;"), GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_out CASCADE;"), @@ -266,67 +265,46 @@ postgres_create_tables (void *cls) ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE" ",denom_sig BYTEA NOT NULL" ");"), - /** - * The DB will show negative values for some values of the following fields as - * we use them as 16 bit unsigned integers - * @a num_newcoins - * @a noreveal_index - * Do not do arithmetic in SQL on these fields. - * NOTE: maybe we should instead forbid values >= 2^15 categorically? - */ - GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_sessions " + + /* Table with the commitments made when melting a coin. */ + GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commitments " "(melt_serial_id BIGSERIAL UNIQUE" - ",session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)" + ",rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=64)" ",old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE" ",old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)" ",amount_with_fee_val INT8 NOT NULL" ",amount_with_fee_frac INT4 NOT NULL" ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" - ",num_newcoins INT2 NOT NULL" - ",noreveal_index INT2 NOT NULL" - ");"), - GNUNET_PQ_make_try_execute ("CREATE INDEX refresh_sessions_old_coin_pub_index ON " - "refresh_sessions (old_coin_pub);"), - - /* Table with information about the desired denominations to be created - during a refresh operation; contains the denomination key for each - of the coins (for a given refresh session) */ - GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_order " - "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE" - ",newcoin_index INT2 NOT NULL " - ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE" - ",PRIMARY KEY (session_hash, newcoin_index)" + ",noreveal_index INT4 NOT NULL" ");"), - /* Table with the commitments for a refresh operation; includes - the session_hash for which this is the link information, the - oldcoin index and the cut-and-choose index (from 0 to #TALER_CNC_KAPPA-1), - as well as the actual link data (the transfer public key and the encrypted - link secret) */ - GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_transfer_public_key " - "(session_hash BYTEA NOT NULL PRIMARY KEY REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE" - ",transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)" - ");"), - /* Table with the commitments for the new coins that are to be created + GNUNET_PQ_make_try_execute ("CREATE INDEX refresh_commitments_old_coin_pub_index ON " + "refresh_commitments (old_coin_pub);"), + + /* Table with the revelations about the new coins that are to be created during a melting session. Includes the session, the cut-and-choose index and the index of the new coin, and the envelope of the new coin to be signed, as well as the encrypted information about the private key and the blinding factor for the coin (for verification in case this newcoin_index is chosen to be revealed) */ - GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commit_coin " - "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE" - ",newcoin_index INT2 NOT NULL" + GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_revealed_coins " + "(rc BYTEA NOT NULL REFERENCES refresh_commitments (rc) ON DELETE CASCADE" + ",newcoin_index INT4 NOT NULL" + ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations (denom_pub_hash) ON DELETE CASCADE" ",coin_ev BYTEA NOT NULL" - ",PRIMARY KEY (session_hash, newcoin_index)" - ");"), - /* Table with the signatures over coins generated during a refresh - operation. Needed to answer /refresh/link queries later. Stores - the coin signatures under the respective session hash and index. */ - GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_out " - "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE" - ",newcoin_index INT2 NOT NULL" ",ev_sig BYTEA NOT NULL" - ",PRIMARY KEY (session_hash, newcoin_index)" + ",PRIMARY KEY (rc, newcoin_index)" + ");"), + + /* Table with the transfer keys of a refresh operation; includes + the rc for which this is the link information, the + transfer public key (for gamma) and the revealed transfer private + keys (array of TALER_CNC_KAPPA - 1 entries, with gamma being skipped) */ + GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_transfer_keys " + "(rc BYTEA NOT NULL PRIMARY KEY REFERENCES refresh_commitments (rc) ON DELETE CASCADE" + ",transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)" + ",transfer_privs BYTEA NOT NULL" ");"), + /* This table contains the wire transfers the exchange is supposed to execute to transmit funds to the merchants (and manage refunds). */ GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS deposits " @@ -726,64 +704,7 @@ postgres_prepare (PGconn *db_conn) " WHERE reserve_out_serial_id>=$1" " ORDER BY reserve_out_serial_id ASC;", 1), - /* Used in #postgres_get_refresh_session() to fetch - high-level information about a refresh session */ - GNUNET_PQ_make_prepare ("get_refresh_session", - "SELECT" - " old_coin_pub" - ",old_coin_sig" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",amount_with_fee_curr" - ",denom.fee_refresh_val " - ",denom.fee_refresh_frac " - ",denom.fee_refresh_curr " - ",num_newcoins" - ",noreveal_index" - " FROM refresh_sessions" - " JOIN known_coins" - " ON (refresh_sessions.old_coin_pub = known_coins.coin_pub)" - " JOIN denominations denom" - " USING (denom_pub_hash)" - " WHERE session_hash=$1;", - 1), - /* Used in #postgres_select_refreshs_above_serial_id() to fetch - refresh session with id '\geq' the given parameter */ - GNUNET_PQ_make_prepare ("audit_get_refresh_sessions_incr", - "SELECT" - " denom.denom_pub" - ",old_coin_pub" - ",old_coin_sig" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",amount_with_fee_curr" - ",num_newcoins" - ",noreveal_index" - ",melt_serial_id" - ",session_hash" - " FROM refresh_sessions" - " JOIN known_coins kc" - " ON (refresh_sessions.old_coin_pub = kc.coin_pub)" - " JOIN denominations denom" - " ON (kc.denom_pub_hash = denom.denom_pub_hash)" - " WHERE melt_serial_id>=$1" - " ORDER BY melt_serial_id ASC;", - 1), - /* Used in #postgres_create_refresh_session() to store - high-level information about a refresh session */ - GNUNET_PQ_make_prepare ("insert_refresh_session", - "INSERT INTO refresh_sessions " - "(session_hash " - ",old_coin_pub " - ",old_coin_sig " - ",amount_with_fee_val " - ",amount_with_fee_frac " - ",amount_with_fee_curr " - ",num_newcoins " - ",noreveal_index " - ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8);", - 8), + /* Used in #postgres_get_known_coin() to fetch the denomination public key and signature for a coin known to the exchange. */ @@ -807,30 +728,68 @@ postgres_prepare (PGconn *db_conn) ") VALUES " "($1,$2,$3);", 3), - /* Store information about the desired denominations for a - refresh operation, used in #postgres_insert_refresh_order() */ - GNUNET_PQ_make_prepare ("insert_refresh_order", - "INSERT INTO refresh_order " - "(newcoin_index " - ",session_hash " - ",denom_pub_hash " + + /* Used in #postgres_insert_melt() to store + high-level information about a melt operation */ + GNUNET_PQ_make_prepare ("insert_melt", + "INSERT INTO refresh_commitments " + "(rc " + ",old_coin_pub " + ",old_coin_sig " + ",amount_with_fee_val " + ",amount_with_fee_frac " + ",amount_with_fee_curr " + ",noreveal_index " ") VALUES " - "($1, $2, $3);", - 3), - /* Obtain information about the desired denominations for a - refresh operation, used in #postgres_get_refresh_order() */ - GNUNET_PQ_make_prepare ("get_refresh_order", - "SELECT denom_pub" - " FROM refresh_order" - " JOIN denominations denom " - " USING (denom_pub_hash)" - " WHERE session_hash=$1" - " AND newcoin_index=$2;", - 2), - /* Query the 'refresh_sessions' by coin public key */ + "($1, $2, $3, $4, $5, $6, $7);", + 7), + /* Used in #postgres_get_melt() to fetch + high-level information about a melt operation */ + GNUNET_PQ_make_prepare ("get_melt", + "SELECT" + " denom.denom_pub" + ",denom.fee_refresh_val" + ",denom.fee_refresh_frac" + ",denom.fee_refresh_curr" + ",kc.denom_sig" + ",old_coin_pub" + ",old_coin_sig" + ",amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",noreveal_index" + " FROM refresh_commitments" + " JOIN known_coins kc" + " ON (refresh_commitments.old_coin_pub = kc.coin_pub)" + " JOIN denominations denom" + " ON (kc.denom_pub_hash = denom.denom_pub_hash)" + " WHERE rc=$1;", + 1), + /* Used in #postgres_select_refreshs_above_serial_id() to fetch + refresh session with id '\geq' the given parameter */ + GNUNET_PQ_make_prepare ("audit_get_refresh_commitments_incr", + "SELECT" + " denom.denom_pub" + ",old_coin_pub" + ",old_coin_sig" + ",amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",noreveal_index" + ",melt_serial_id" + ",rc" + " FROM refresh_commitments" + " JOIN known_coins kc" + " ON (refresh_commitments.old_coin_pub = kc.coin_pub)" + " JOIN denominations denom" + " ON (kc.denom_pub_hash = denom.denom_pub_hash)" + " WHERE melt_serial_id>=$1" + " ORDER BY melt_serial_id ASC;", + 1), + /* Query the 'refresh_commitments' by coin public key */ GNUNET_PQ_make_prepare ("get_refresh_session_by_coin", "SELECT" - " session_hash" + " rc" ",old_coin_sig" ",amount_with_fee_val" ",amount_with_fee_frac" @@ -838,31 +797,75 @@ postgres_prepare (PGconn *db_conn) ",denom.fee_refresh_val " ",denom.fee_refresh_frac " ",denom.fee_refresh_curr " - " FROM refresh_sessions" + " FROM refresh_commitments" " JOIN known_coins " - " ON (refresh_sessions.old_coin_pub = known_coins.coin_pub)" + " ON (refresh_commitments.old_coin_pub = known_coins.coin_pub)" " JOIN denominations denom USING (denom_pub_hash)" " WHERE old_coin_pub=$1;", 1), - /* Fetch refunds with rowid '\geq' the given parameter */ - GNUNET_PQ_make_prepare ("audit_get_refunds_incr", - "SELECT" - " merchant_pub" - ",merchant_sig" - ",h_contract_terms" - ",rtransaction_id" + + /* Store information about the desired denominations for a + refresh operation, used in #postgres_insert_refresh_reveal() */ + GNUNET_PQ_make_prepare ("insert_refresh_revealed_coin", + "INSERT INTO refresh_revealed_coins " + "(rc " + ",newcoin_index " + ",denom_pub_hash " + ",coin_ev" + ",ev_sig" + ") VALUES " + "($1, $2, $3, $4, $5);", + 5), + /* Obtain information about the coins created in a refresh + operation, used in #postgres_get_refresh_reveal() */ + GNUNET_PQ_make_prepare ("get_refresh_revealed_coins", + "SELECT " + " newcoin_index" ",denom.denom_pub" - ",coin_pub" - ",amount_with_fee_val" - ",amount_with_fee_frac" - ",amount_with_fee_curr" - ",refund_serial_id" - " FROM refunds" - " JOIN known_coins kc USING (coin_pub)" - " JOIN denominations denom ON (kc.denom_pub_hash = denom.denom_pub_hash)" - " WHERE refund_serial_id>=$1" - " ORDER BY refund_serial_id ASC;", + ",coin_ev" + ",ev_sig" + " FROM refresh_revealed_coins" + " JOIN denominations denom " + " USING (denom_pub_hash)" + " WHERE rc=$1" + " ORDER BY newcoin_index ASC;", + 1), + + /* Used in #postgres_insert_refresh_reveal() to store the transfer + keys we learned */ + GNUNET_PQ_make_prepare ("insert_refresh_transfer_keys", + "INSERT INTO refresh_transfer_keys " + "(rc" + ",transfer_pub" + ",transfer_privs" + ") VALUES " + "($1, $2, $3);", + 3), + /* Used in #postgres_get_refresh_reveal() to retrieve transfer + keys from /refresh/reveal */ + GNUNET_PQ_make_prepare ("get_refresh_transfer_keys", + "SELECT" + " transfer_pub" + ",transfer_privs" + " FROM refresh_transfer_keys" + " WHERE rc=$1;", 1), + + + /* Used in #postgres_insert_refund() to store refund information */ + GNUNET_PQ_make_prepare ("insert_refund", + "INSERT INTO refunds " + "(coin_pub " + ",merchant_pub " + ",merchant_sig " + ",h_contract_terms " + ",rtransaction_id " + ",amount_with_fee_val " + ",amount_with_fee_frac " + ",amount_with_fee_curr " + ") VALUES " + "($1, $2, $3, $4, $5, $6, $7, $8);", + 8), /* Query the 'refunds' by coin public key */ GNUNET_PQ_make_prepare ("get_refunds_by_coin", "SELECT" @@ -881,43 +884,26 @@ postgres_prepare (PGconn *db_conn) " JOIN denominations denom USING (denom_pub_hash)" " WHERE coin_pub=$1;", 1), - /* Used in #postgres_insert_transfer_public_key() to - store commitments */ - GNUNET_PQ_make_prepare ("insert_transfer_public_key", - "INSERT INTO refresh_transfer_public_key " - "(session_hash" - ",transfer_pub" - ") VALUES " - "($1, $2);", - 2), - /* Used in #postgres_get_refresh_transfer_public_key() to - retrieve original commitments during /refresh/reveal */ - GNUNET_PQ_make_prepare ("get_refresh_transfer_public_key", + /* Fetch refunds with rowid '\geq' the given parameter */ + GNUNET_PQ_make_prepare ("audit_get_refunds_incr", "SELECT" - " transfer_pub" - " FROM refresh_transfer_public_key" - " WHERE session_hash=$1;", + " merchant_pub" + ",merchant_sig" + ",h_contract_terms" + ",rtransaction_id" + ",denom.denom_pub" + ",coin_pub" + ",amount_with_fee_val" + ",amount_with_fee_frac" + ",amount_with_fee_curr" + ",refund_serial_id" + " FROM refunds" + " JOIN known_coins kc USING (coin_pub)" + " JOIN denominations denom ON (kc.denom_pub_hash = denom.denom_pub_hash)" + " WHERE refund_serial_id>=$1" + " ORDER BY refund_serial_id ASC;", 1), - /* Used in #postgres_insert_refresh_commit_coins() to - store coin commitments. */ - GNUNET_PQ_make_prepare ("insert_refresh_commit_coin", - "INSERT INTO refresh_commit_coin " - "(session_hash" - ",newcoin_index" - ",coin_ev" - ") VALUES " - "($1, $2, $3);", - 3), - /* Used in #postgres_get_refresh_commit_coins() to - retrieve the original coin envelopes, to either be - verified or signed. */ - GNUNET_PQ_make_prepare ("get_refresh_commit_coin", - "SELECT" - " coin_ev" - " FROM refresh_commit_coin" - " WHERE session_hash=$1" - " AND newcoin_index=$2;", - 2), + /* Store information about a /deposit the exchange is to execute. Used in #postgres_insert_deposit(). */ GNUNET_PQ_make_prepare ("insert_deposit", @@ -938,20 +924,6 @@ postgres_prepare (PGconn *db_conn) "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10," " $11, $12);", 12), - /* Used in #postgres_insert_refund() to store refund information */ - GNUNET_PQ_make_prepare ("insert_refund", - "INSERT INTO refunds " - "(coin_pub " - ",merchant_pub " - ",merchant_sig " - ",h_contract_terms " - ",rtransaction_id " - ",amount_with_fee_val " - ",amount_with_fee_frac " - ",amount_with_fee_curr " - ") VALUES " - "($1, $2, $3, $4, $5, $6, $7, $8);", - 8), /* Fetch an existing deposit request, used to ensure idempotency during /deposit processing. Used in #postgres_have_deposit(). */ GNUNET_PQ_make_prepare ("get_deposit", @@ -1113,68 +1085,22 @@ postgres_prepare (PGconn *db_conn) " USING (denom_pub_hash)" " WHERE coin_pub=$1;", 1), - /* Used in #postgres_insert_refresh_out() to store the - generated signature(s) for future requests, i.e. /refresh/link */ - GNUNET_PQ_make_prepare ("insert_refresh_out", - "INSERT INTO refresh_out " - "(session_hash" - ",newcoin_index" - ",ev_sig" - ") VALUES " - "($1, $2, $3);", - 3), - /* Used in #postgres_get_refresh_out() to test if the - generated signature(s) already exists */ - GNUNET_PQ_make_prepare ("get_refresh_out", - "SELECT ev_sig" - " FROM refresh_out" - " WHERE session_hash=$1" - " AND newcoin_index=$2;", - 2), - /* Used in #postgres_get_link_data_list(). We use the session_hash - to obtain the "noreveal_index" for that session, and then select 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 exchangeed in the refresh ops. NOTE: There may be more - efficient ways to express the same query. */ + + /* Used in #postgres_get_link_data(). */ GNUNET_PQ_make_prepare ("get_link", "SELECT " - " ev_sig" + " tp.transfer_pub" ",denoms.denom_pub" - " FROM refresh_sessions" - " JOIN refresh_order ro" - " USING (session_hash)" - " JOIN refresh_commit_coin rcc" - " USING (session_hash)" - " JOIN refresh_out rc" - " USING (session_hash)" + ",rrc.ev_sig" + " FROM refresh_commitments" + " JOIN refresh_revealed_coins rrc" + " USING (rc)" + " JOIN refresh_transfer_keys tp" + " USING (rc)" " JOIN denominations denoms" - " ON (ro.denom_pub_hash = denoms.denom_pub_hash)" - " WHERE ro.session_hash=$1" - " AND ro.newcoin_index=rcc.newcoin_index" - " AND ro.newcoin_index=rc.newcoin_index;", - 1), - /* 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". - NOTE: This may (in theory) return multiple results, one per session - that the old coin was melted into. */ - GNUNET_PQ_make_prepare ("get_transfer", - "SELECT" - " transfer_pub" - ",session_hash" - " FROM refresh_sessions rs" - " JOIN refresh_transfer_public_key rcl" - " USING (session_hash)" - " WHERE rs.old_coin_pub=$1;", + " ON (rrc.denom_pub_hash = denoms.denom_pub_hash)" + " WHERE old_coin_pub=$1" + " ORDER BY tp.transfer_pub", 1), /* Used in #postgres_lookup_wire_transfer */ GNUNET_PQ_make_prepare ("lookup_transactions", @@ -3008,7 +2934,11 @@ ensure_coin_known (struct PostgresClosure *cls, return GNUNET_SYSERR; } if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + { + GNUNET_CRYPTO_rsa_public_key_free (known_coin.denom_pub.rsa_public_key); + GNUNET_CRYPTO_rsa_signature_free (known_coin.denom_sig.rsa_signature); return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* no change! */ + } GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs); /* if not known, insert it */ qs = insert_known_coin (cls, @@ -3101,461 +3031,340 @@ postgres_insert_refund (void *cls, /** - * Lookup refresh session data under the given @a session_hash. + * Lookup refresh melt commitment data under the given @a rc. * * @param cls the `struct PostgresClosure` with the plugin-specific state * @param session database handle to use - * @param session_hash hash over the melt to use to locate the session - * @param[out] refresh_session where to store the result + * @param rc commitment hash to use to locate the operation + * @param[out] refresh_melt where to store the result * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_get_refresh_session (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TALER_EXCHANGEDB_RefreshSession *refresh_session) +postgres_get_melt (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_RefreshCommitmentP *rc, + struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt) { struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), + GNUNET_PQ_query_param_auto_from_type (rc), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint16 ("num_newcoins", - &refresh_session->num_newcoins), - GNUNET_PQ_result_spec_uint16 ("noreveal_index", - &refresh_session->noreveal_index), + GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", + &refresh_melt->session.coin.denom_pub.rsa_public_key), + TALER_PQ_result_spec_amount ("fee_refresh", + &refresh_melt->melt_fee), + GNUNET_PQ_result_spec_rsa_signature ("denom_sig", + &refresh_melt->session.coin.denom_sig.rsa_signature), + GNUNET_PQ_result_spec_uint32 ("noreveal_index", + &refresh_melt->session.noreveal_index), GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub", - &refresh_session->melt.coin.coin_pub), + &refresh_melt->session.coin.coin_pub), GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", - &refresh_session->melt.coin_sig), + &refresh_melt->session.coin_sig), TALER_PQ_result_spec_amount ("amount_with_fee", - &refresh_session->melt.amount_with_fee), - TALER_PQ_result_spec_amount ("fee_refresh", - &refresh_session->melt.melt_fee), + &refresh_melt->session.amount_with_fee), GNUNET_PQ_result_spec_end }; enum GNUNET_DB_QueryStatus qs; - memset (refresh_session, - 0, - sizeof (struct TALER_EXCHANGEDB_RefreshSession)); qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_refresh_session", + "get_melt", params, rs); - if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && - (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - (qs = get_known_coin (cls, - session, - &refresh_session->melt.coin.coin_pub, - &refresh_session->melt.coin)) ) ) - { - GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - return qs; - } - refresh_session->melt.session_hash = *session_hash; + refresh_melt->session.rc = *rc; return qs; } /** - * Store new refresh session data under the given @a session_hash. + * Store new refresh melt commitment data. * * @param cls the `struct PostgresClosure` with the plugin-specific state * @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 query status for the transaction */ static enum GNUNET_DB_QueryStatus -postgres_create_refresh_session (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - const struct TALER_EXCHANGEDB_RefreshSession *refresh_session) +postgres_insert_melt (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_EXCHANGEDB_RefreshSession *refresh_session) { struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_auto_from_type (&refresh_session->melt.coin.coin_pub), - GNUNET_PQ_query_param_auto_from_type (&refresh_session->melt.coin_sig), - TALER_PQ_query_param_amount (&refresh_session->melt.amount_with_fee), - GNUNET_PQ_query_param_uint16 (&refresh_session->num_newcoins), - GNUNET_PQ_query_param_uint16 (&refresh_session->noreveal_index), + GNUNET_PQ_query_param_auto_from_type (&refresh_session->rc), + GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin.coin_pub), + GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin_sig), + TALER_PQ_query_param_amount (&refresh_session->amount_with_fee), + GNUNET_PQ_query_param_uint32 (&refresh_session->noreveal_index), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; if (0 > (qs = ensure_coin_known (cls, session, - &refresh_session->melt.coin))) + &refresh_session->coin))) return qs; return GNUNET_PQ_eval_prepared_non_select (session->conn, - "insert_refresh_session", + "insert_melt", params); } /** - * Store in the database which coin(s) we want to create - * in a given refresh operation. + * Store in the database which coin(s) the wallet wanted to create + * in a given refresh operation and all of the other information + * we learned or created in the /refresh/reveal step. * - * @param cls the `struct PostgresClosure` with the plugin-specific state + * @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 num_newcoins number of coins to generate, size of the @a denom_pubs array - * @param denom_pubs array denominations of the coins to create + * @param rc identify commitment and thus refresh operation + * @param num_rrcs_newcoins number of coins to generate, size of the + * @a rrcs array + * @param rrcs information about the new coins + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs transfer private keys to store + * @param tp public key to store * @return query status for the transaction */ static enum GNUNET_DB_QueryStatus -postgres_insert_refresh_order (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - const struct TALER_DenominationPublicKey *denom_pubs) -{ - for (unsigned int i=0;i<(unsigned int) num_newcoins;i++) - { - uint16_t newcoin_off = (uint16_t) i; - - { - struct GNUNET_HashCode denom_pub_hash; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_uint16 (&newcoin_off), - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash), - GNUNET_PQ_query_param_end - }; - enum GNUNET_DB_QueryStatus qs; - - GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key, - &denom_pub_hash); - qs = GNUNET_PQ_eval_prepared_non_select (session->conn, - "insert_refresh_order", - params); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - return qs; - } - } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** - * We allocated some @a denom_pubs information, but now need - * to abort. Free allocated memory. - * - * @param denom_pubs data to free (but not the array itself) - * @param denom_pubs_len length of @a denom_pubs array - */ -static void -free_dpk_result (struct TALER_DenominationPublicKey *denom_pubs, - unsigned int denom_pubs_len) +postgres_insert_refresh_reveal (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_RefreshCommitmentP *rc, + uint32_t num_rrcs, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp) { - for (unsigned int i=0;i<denom_pubs_len;i++) + if (TALER_CNC_KAPPA != num_tprivs + 1) { - GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key); - denom_pubs[i].rsa_public_key = NULL; + GNUNET_break (0); + return GNUNET_DB_STATUS_HARD_ERROR; } -} - - -/** - * Lookup in the database the coins that we want to - * create in the given refresh operation. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection - * @param session_hash hash to identify refresh session - * @param num_newcoins size of the array of the @a denom_pubs array - * @param denom_pubs where to store the deomination keys - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_refresh_order (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - struct TALER_DenominationPublicKey *denom_pubs) -{ - for (unsigned i=0;i<(unsigned int) num_newcoins;i++) + for (uint32_t i=0;i<num_rrcs;i++) { - uint16_t newcoin_off = (uint16_t) i; - enum GNUNET_DB_QueryStatus qs; + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i]; + struct GNUNET_HashCode denom_pub_hash; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_uint16 (&newcoin_off), + GNUNET_PQ_query_param_auto_from_type (rc), + GNUNET_PQ_query_param_uint32 (&i), + GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash), + GNUNET_PQ_query_param_fixed_size (rrc->coin_ev, + rrc->coin_ev_size), + GNUNET_PQ_query_param_rsa_signature (rrc->coin_sig.rsa_signature), GNUNET_PQ_query_param_end }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", - &denom_pubs[i].rsa_public_key), - GNUNET_PQ_result_spec_end - }; + enum GNUNET_DB_QueryStatus qs; - qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_refresh_order", - params, - rs); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - case GNUNET_DB_STATUS_SOFT_ERROR: - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - free_dpk_result (denom_pubs, i); + GNUNET_CRYPTO_rsa_public_key_hash (rrc->denom_pub.rsa_public_key, + &denom_pub_hash); + + qs = GNUNET_PQ_eval_prepared_non_select (session->conn, + "insert_refresh_revealed_coin", + params); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) return qs; - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - default: - GNUNET_break (0); - break; - } } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - -/** - * Store information about the commitment of the - * given coin for the given refresh session in the database. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param num_newcoins coin index size of the @a commit_coins array - * @param commit_coins array of coin commitments to store - * @return query transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_refresh_commit_coins (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins) -{ - for (uint16_t coin_off=0;coin_off<num_newcoins;coin_off++) { struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_uint16 (&coin_off), - GNUNET_PQ_query_param_fixed_size (commit_coins[coin_off].coin_ev, - commit_coins[coin_off].coin_ev_size), + GNUNET_PQ_query_param_auto_from_type (rc), + GNUNET_PQ_query_param_auto_from_type (tp), + GNUNET_PQ_query_param_fixed_size (tprivs, + num_tprivs * sizeof (struct TALER_TransferPrivateKeyP)), GNUNET_PQ_query_param_end }; - enum GNUNET_DB_QueryStatus qs; - qs = GNUNET_PQ_eval_prepared_non_select (session->conn, - "insert_refresh_commit_coin", - params); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - return qs; + return GNUNET_PQ_eval_prepared_non_select (session->conn, + "insert_refresh_transfer_keys", + params); } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } /** - * We allocated some @a commit_coin information, but now need - * to abort. Free allocated memory. - * - * @param cls unused - * @param commit_coins_len length of @a commit_coins array - * @param commit_coins data to free (but not the array itself) + * Context where we aggregate data from the database. + * Closure for #add_revealed_coins(). */ -static void -postgres_free_refresh_commit_coins (void *cls, - unsigned int commit_coins_len, - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins) +struct GetRevealContext { - for (unsigned int i=0;i<commit_coins_len;i++) - { - GNUNET_free (commit_coins[i].coin_ev); - commit_coins[i].coin_ev = NULL; - commit_coins[i].coin_ev_size = 0; - } -} + /** + * Array of revealed coins we obtained from the DB. + */ + struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs; + + /** + * Length of the @a rrcs array. + */ + unsigned int rrcs_len; + + /** + * Set to an error code if we ran into trouble. + */ + enum TALER_ErrorCode ec; +}; /** - * Obtain information about the commitment of the - * given coin of the given refresh session from the database. + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results. * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param num_newcoins size of the @a commit_coins array - * @param[out] commit_coins array of coin commitments to return - * @return transaction status + * @param cls closure of type `struct GetRevealContext` + * @param result the postgres result + * @param num_result the number of results in @a result */ -static enum GNUNET_DB_QueryStatus -postgres_get_refresh_commit_coins (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins) +static void +add_revealed_coins (void *cls, + PGresult *result, + unsigned int num_results) { - for (unsigned int i=0;i<(unsigned int) num_newcoins;i++) + struct GetRevealContext *grctx = cls; + + if (0 == num_results) + return; + grctx->rrcs = GNUNET_new_array (num_results, + struct TALER_EXCHANGEDB_RefreshRevealedCoin); + grctx->rrcs_len = num_results; + for (unsigned int i = 0; i < num_results; i++) { - uint16_t newcoin_off = (uint16_t) i; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_uint16 (&newcoin_off), - GNUNET_PQ_query_param_end - }; - void *c_buf; - size_t c_buf_size; + struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx->rrcs[i]; + uint32_t off; struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_uint32 ("newcoin_index", + &off), + GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", + &rrc->denom_pub.rsa_public_key), GNUNET_PQ_result_spec_variable_size ("coin_ev", - &c_buf, - &c_buf_size), + (void **) &rrc->coin_ev, + &rrc->coin_ev_size), + GNUNET_PQ_result_spec_rsa_signature ("ev_sig", + &rrc->coin_sig.rsa_signature), GNUNET_PQ_result_spec_end }; - enum GNUNET_DB_QueryStatus qs; - qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_refresh_commit_coin", - params, - rs); - if (0 >= qs) + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) { - postgres_free_refresh_commit_coins (cls, - i, - commit_coins); - return qs; + GNUNET_break (0); + grctx->ec = GNUNET_DB_STATUS_HARD_ERROR; + return; + } + if (off != i) + { + GNUNET_break (0); + grctx->ec = GNUNET_DB_STATUS_HARD_ERROR; + return; } - commit_coins[i].coin_ev = c_buf; - commit_coins[i].coin_ev_size = c_buf_size; } - return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; -} - - -/** - * Store the commitment to the given (encrypted) refresh link data - * for the given refresh session. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param tp transfer public key to store - * @return transaction status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_refresh_transfer_public_key (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - const struct TALER_TransferPublicKeyP *tp) -{ - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), - GNUNET_PQ_query_param_auto_from_type (tp), - GNUNET_PQ_query_param_end - }; - - return GNUNET_PQ_eval_prepared_non_select (session->conn, - "insert_transfer_public_key", - params); } /** - * Obtain the commited (encrypted) refresh link data - * for the given refresh session. + * Lookup in the database the coins that we want to + * create in the given refresh operation. * * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param[out] tp information to return + * @param session database connection + * @param rc identify commitment and thus refresh operation + * @param cb function to call with the results + * @param cb_cls closure for @a cb * @return transaction status */ static enum GNUNET_DB_QueryStatus -postgres_get_refresh_transfer_public_key (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TALER_TransferPublicKeyP *tp) +postgres_get_refresh_reveal (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_RefreshCommitmentP *rc, + TALER_EXCHANGEDB_RefreshCallback cb, + void *cb_cls) { + struct GetRevealContext grctx; + enum GNUNET_DB_QueryStatus qs; + struct TALER_TransferPublicKeyP tp; + void *tpriv; + size_t tpriv_size; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), + GNUNET_PQ_query_param_auto_from_type (rc), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", - tp), + &tp), + GNUNET_PQ_result_spec_variable_size ("transfer_privs", + &tpriv, + &tpriv_size), GNUNET_PQ_result_spec_end }; - return GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_refresh_transfer_public_key", - params, - rs); -} - + /* First get the coins */ + memset (&grctx, + 0, + sizeof (grctx)); + qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, + "get_refresh_revealed_coins", + params, + &add_revealed_coins, + &grctx); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + default: /* can have more than one result */ + break; + } -/** - * 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 `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 transaction result status - */ -static enum GNUNET_DB_QueryStatus -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) -{ - 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 - }; + /* now also get the transfer keys (public and private) */ + qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn, + "get_refresh_transfer_keys", + params, + rs); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + goto cleanup; + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + default: + GNUNET_assert (0); + } + if ( (0 != tpriv_size % sizeof (struct TALER_TransferPrivateKeyP)) || + (TALER_CNC_KAPPA - 1 != tpriv_size / sizeof (struct TALER_TransferPrivateKeyP)) ) + { + GNUNET_break (0); + qs = GNUNET_DB_STATUS_HARD_ERROR; + GNUNET_PQ_cleanup_result (rs); + goto cleanup; + } - return GNUNET_PQ_eval_prepared_singleton_select (session->conn, - "get_refresh_out", - params, - rs); -} + /* Pass result back to application */ + cb (cb_cls, + grctx.rrcs_len, + grctx.rrcs, + tpriv_size / sizeof (struct TALER_TransferPrivateKeyP), + (const struct TALER_TransferPrivateKeyP *) tpriv, + &tp); + GNUNET_PQ_cleanup_result (rs); + cleanup: + for (unsigned int i = 0; i < grctx.rrcs_len; i++) + { + struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx.rrcs[i]; -/** - * 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 transaction result status - */ -static enum GNUNET_DB_QueryStatus -postgres_insert_refresh_out (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t newcoin_index, - const struct TALER_DenominationSignature *ev_sig) -{ - 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_rsa_signature (ev_sig->rsa_signature), - GNUNET_PQ_query_param_end - }; + if (NULL != rrc->denom_pub.rsa_public_key) + GNUNET_CRYPTO_rsa_public_key_free (rrc->denom_pub.rsa_public_key); + if (NULL != rrc->coin_sig.rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (rrc->coin_sig.rsa_signature); + GNUNET_free_non_null (rrc->coin_ev); + } + GNUNET_free_non_null (grctx.rrcs); - return GNUNET_PQ_eval_prepared_non_select (session->conn, - "insert_refresh_out", - params); + return qs; } @@ -3565,9 +3374,25 @@ postgres_insert_refresh_out (void *cls, struct LinkDataContext { /** - * List we are building. + * Function to call on each result. */ - struct TALER_EXCHANGEDB_LinkDataList *ldl; + TALER_EXCHANGEDB_LinkDataCallback ldc; + + /** + * Closure for @e ldc. + */ + void *ldc_cls; + + /** + * Last transfer public key for which we have information in @e last. + * Only valid if @e last is non-NULL. + */ + struct TALER_TransferPublicKeyP transfer_pub; + + /** + * Link data for @e transfer_pub + */ + struct TALER_EXCHANGEDB_LinkDataList *last; /** * Status, set to #GNUNET_SYSERR on errors, @@ -3577,6 +3402,31 @@ struct LinkDataContext /** + * Free memory of the link data list. + * + * @param cls the @e cls of this struct with the plugin-specific state (unused) + * @param ldl link data list to release + */ +static void +free_link_data_list (void *cls, + struct TALER_EXCHANGEDB_LinkDataList *ldl) +{ + struct TALER_EXCHANGEDB_LinkDataList *next; + + while (NULL != ldl) + { + next = ldl->next; + if (NULL != ldl->denom_pub.rsa_public_key) + GNUNET_CRYPTO_rsa_public_key_free (ldl->denom_pub.rsa_public_key); + if (NULL != ldl->ev_sig.rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (ldl->ev_sig.rsa_signature); + GNUNET_free (ldl); + ldl = next; + } +} + + +/** * Function to be called with the results of a SELECT statement * that has returned @a num_results results. * @@ -3589,21 +3439,22 @@ add_ldl (void *cls, PGresult *result, unsigned int num_results) { - struct LinkDataContext *ldc = cls; + struct LinkDataContext *ldctx = cls; for (int i = num_results - 1; i >= 0; i--) { - struct GNUNET_CRYPTO_RsaPublicKey *denom_pub; - struct GNUNET_CRYPTO_RsaSignature *sig; struct TALER_EXCHANGEDB_LinkDataList *pos; + struct TALER_TransferPublicKeyP transfer_pub; pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkDataList); { struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", + &transfer_pub), GNUNET_PQ_result_spec_rsa_signature ("ev_sig", - &sig), + &pos->ev_sig.rsa_signature), GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", - &denom_pub), + &pos->denom_pub.rsa_public_key), GNUNET_PQ_result_spec_end }; @@ -3613,18 +3464,31 @@ add_ldl (void *cls, i)) { GNUNET_break (0); - common_free_link_data_list (cls, - ldc->ldl); - ldc->ldl = NULL; GNUNET_free (pos); - ldc->status = GNUNET_SYSERR; + ldctx->status = GNUNET_SYSERR; return; } } - pos->next = ldc->ldl; - pos->denom_pub.rsa_public_key = denom_pub; - pos->ev_sig.rsa_signature = sig; - ldc->ldl = pos; + if ( (NULL != ldctx->last) && + (0 == memcmp (&transfer_pub, + &ldctx->transfer_pub, + sizeof (struct TALER_TransferPublicKeyP))) ) + { + pos->next = ldctx->last; + } + else + { + if (NULL != ldctx->last) + { + ldctx->ldc (ldctx->ldc_cls, + &ldctx->transfer_pub, + ldctx->last); + free_link_data_list (cls, + ldctx->last); + } + ldctx->transfer_pub = transfer_pub; + } + ldctx->last = pos; } } @@ -3635,137 +3499,49 @@ add_ldl (void *cls, * * @param cls the `struct PostgresClosure` with the plugin-specific state * @param session database connection - * @param session_hash refresh session to get linkage data for - * @param[out] ldlp set to all known link data for the session + * @param coin_pub public key of the coin + * @param ldc function to call for each session the coin was melted into + * @param ldc_cls closure for @a tdc * @return transaction status code */ static enum GNUNET_DB_QueryStatus -postgres_get_link_data_list (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TALER_EXCHANGEDB_LinkDataList **ldlp) +postgres_get_link_data (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + TALER_EXCHANGEDB_LinkDataCallback ldc, + void *ldc_cls) { - struct LinkDataContext ldc; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (session_hash), + GNUNET_PQ_query_param_auto_from_type (coin_pub), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; + struct LinkDataContext ldctx; - ldc.status = GNUNET_OK; - ldc.ldl = NULL; + ldctx.ldc = ldc; + ldctx.ldc_cls = ldc_cls; + ldctx.last = NULL; + ldctx.status = GNUNET_OK; qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, "get_link", params, &add_ldl, - &ldc); - *ldlp = ldc.ldl; - if (GNUNET_OK != ldc.status) - return GNUNET_DB_STATUS_HARD_ERROR; - return qs; -} - - -/** - * Closure for #add_link(). - */ -struct AddLinkContext -{ - /** - * Function to call on each result. - */ - TALER_EXCHANGEDB_TransferDataCallback tdc; - - /** - * Closure for @e tdc. - */ - void *tdc_cls; - - /** - * Status code, set to #GNUNET_SYSERR on errors. - */ - int status; -}; - - -/** - * Function to be called with the results of a SELECT statement - * that has returned @a num_results results. - * - * @param cls closure of type `struct AddLinkContext *` - * @param result the postgres result - * @param num_result the number of results in @a result - */ -static void -add_link (void *cls, - PGresult *result, - unsigned int num_results) -{ - struct AddLinkContext *alc = cls; - - for (unsigned int i=0;i<num_results;i++) + &ldctx); + if (NULL != ldctx.last) { - struct GNUNET_HashCode session_hash; - struct TALER_TransferPublicKeyP transfer_pub; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", &transfer_pub), - GNUNET_PQ_result_spec_auto_from_type ("session_hash", &session_hash), - GNUNET_PQ_result_spec_end - }; - - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - i)) + if (GNUNET_OK == ldctx.status) { - GNUNET_break (0); - alc->status = GNUNET_SYSERR; - return; + /* call callback one more time! */ + ldc (ldc_cls, + &ldctx.transfer_pub, + ldctx.last); } - alc->tdc (alc->tdc_cls, - &session_hash, - &transfer_pub); + free_link_data_list (cls, + ldctx.last); + ldctx.last = NULL; } -} - - -/** - * Obtain shared secret and transfer public key from the public key of - * the coin. This information and the link information returned by - * #postgres_get_link_data_list() enable the owner of an old coin to - * determine the private keys of the new coins after the melt. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @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 - * @return statement execution status - */ -static enum GNUNET_DB_QueryStatus -postgres_get_transfer (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - TALER_EXCHANGEDB_TransferDataCallback tdc, - void *tdc_cls) -{ - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (coin_pub), - GNUNET_PQ_query_param_end - }; - struct AddLinkContext al_ctx; - enum GNUNET_DB_QueryStatus qs; - - al_ctx.tdc = tdc; - al_ctx.tdc_cls = tdc_cls; - al_ctx.status = GNUNET_OK; - qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, - "get_transfer", - params, - &add_link, - &al_ctx); - if (GNUNET_OK != al_ctx.status) - qs = GNUNET_DB_STATUS_HARD_ERROR; + if (GNUNET_OK != ldctx.status) + return GNUNET_DB_STATUS_HARD_ERROR; return qs; } @@ -3905,13 +3681,13 @@ add_coin_melt (void *cls, melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt); { struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("session_hash", - &melt->session_hash), + GNUNET_PQ_result_spec_auto_from_type ("rc", + &melt->session.rc), /* oldcoin_index not needed */ GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig", - &melt->coin_sig), + &melt->session.coin_sig), TALER_PQ_result_spec_amount ("amount_with_fee", - &melt->amount_with_fee), + &melt->session.amount_with_fee), TALER_PQ_result_spec_amount ("fee_refresh", &melt->melt_fee), GNUNET_PQ_result_spec_end @@ -3927,16 +3703,17 @@ add_coin_melt (void *cls, chc->status = GNUNET_DB_STATUS_HARD_ERROR; return; } - melt->coin.coin_pub = *chc->coin_pub; + melt->session.coin.coin_pub = *chc->coin_pub; } tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList); tl->next = chc->head; tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT; tl->details.melt = melt; + /* FIXME: integrate via JOIN in main select, instead of using separate query */ qs = get_known_coin (chc->db_cls, chc->session, chc->coin_pub, - &melt->coin); + &melt->session.coin); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); @@ -5184,10 +4961,9 @@ refreshs_serial_helper_cb (void *cls, struct TALER_CoinSpendPublicKeyP coin_pub; struct TALER_CoinSpendSignatureP coin_sig; struct TALER_Amount amount_with_fee; - uint16_t num_newcoins; - uint16_t noreveal_index; + uint32_t noreveal_index; uint64_t rowid; - struct GNUNET_HashCode session_hash; + struct TALER_RefreshCommitmentP rc; struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", &denom_pub.rsa_public_key), @@ -5197,14 +4973,12 @@ refreshs_serial_helper_cb (void *cls, &coin_sig), TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee), - GNUNET_PQ_result_spec_uint16 ("num_newcoins", - &num_newcoins), - GNUNET_PQ_result_spec_uint16 ("noreveal_index", + GNUNET_PQ_result_spec_uint32 ("noreveal_index", &noreveal_index), GNUNET_PQ_result_spec_uint64 ("melt_serial_id", &rowid), - GNUNET_PQ_result_spec_auto_from_type ("session_hash", - &session_hash), + GNUNET_PQ_result_spec_auto_from_type ("rc", + &rc), GNUNET_PQ_result_spec_end }; int ret; @@ -5224,9 +4998,8 @@ refreshs_serial_helper_cb (void *cls, &coin_pub, &coin_sig, &amount_with_fee, - num_newcoins, noreveal_index, - &session_hash); + &rc); GNUNET_PQ_cleanup_result (rs); if (GNUNET_OK != ret) break; @@ -5264,7 +5037,7 @@ postgres_select_refreshs_above_serial_id (void *cls, enum GNUNET_DB_QueryStatus qs; qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, - "audit_get_refresh_sessions_incr", + "audit_get_refresh_commitments_incr", params, &refreshs_serial_helper_cb, &rsc); @@ -6451,20 +6224,11 @@ libtaler_plugin_exchangedb_postgres_init (void *cls) plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits; plugin->insert_deposit = &postgres_insert_deposit; plugin->insert_refund = &postgres_insert_refund; - plugin->get_refresh_session = &postgres_get_refresh_session; - plugin->create_refresh_session = &postgres_create_refresh_session; - plugin->insert_refresh_order = &postgres_insert_refresh_order; - plugin->get_refresh_order = &postgres_get_refresh_order; - plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins; - plugin->get_refresh_commit_coins = &postgres_get_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->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->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->insert_melt = &postgres_insert_melt; + plugin->get_melt = &postgres_get_melt; + plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal; + plugin->get_refresh_reveal = &postgres_get_refresh_reveal; + plugin->get_link_data = &postgres_get_link_data; plugin->get_coin_transactions = &postgres_get_coin_transactions; plugin->free_coin_transaction_list = &common_free_coin_transaction_list; plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer; diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index f04eaf129..33dc74cd6 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -308,162 +308,85 @@ static struct TALER_Amount amount_with_fee; */ #define MELT_NOREVEAL_INDEX 1 - -static struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins; - /** - * Test APIs related to the "insert_refresh_commit_coins" function. - * - * @param session database sesison to use - * @param refresh_session details about the refresh session to use - * @param session_hash refresh melt session hash to use - * @return #GNUNET_OK on success + * How big do we make the coin envelopes? */ -static int -test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session *session, - const struct TALER_EXCHANGEDB_RefreshSession *refresh_session, - const struct GNUNET_HashCode *session_hash) -{ - struct TALER_EXCHANGEDB_RefreshCommitCoin *ret_commit_coins; - struct TALER_EXCHANGEDB_RefreshCommitCoin *a_ccoin; - struct TALER_EXCHANGEDB_RefreshCommitCoin *b_ccoin; - unsigned int cnt; - int ret; - #define COIN_ENC_MAX_SIZE 512 - ret = GNUNET_SYSERR; - ret_commit_coins = NULL; - commit_coins - = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_EXCHANGEDB_RefreshCommitCoin); - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) - { - struct TALER_EXCHANGEDB_RefreshCommitCoin *ccoin; - ccoin = &commit_coins[cnt]; - ccoin->coin_ev_size = GNUNET_CRYPTO_random_u64 - (GNUNET_CRYPTO_QUALITY_WEAK, COIN_ENC_MAX_SIZE); - ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size); - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, - ccoin->coin_ev, - ccoin->coin_ev_size); - } - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refresh_commit_coins (plugin->cls, - session, - session_hash, - MELT_NEW_COINS, - commit_coins)); - ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_EXCHANGEDB_RefreshCommitCoin); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_refresh_commit_coins (plugin->cls, - session, - session_hash, - MELT_NEW_COINS, - ret_commit_coins)); - /* compare the refresh commit coin arrays */ - for (cnt = 0; cnt < MELT_NEW_COINS; cnt++) - { - a_ccoin = &commit_coins[cnt]; - b_ccoin = &ret_commit_coins[cnt]; - FAILIF (a_ccoin->coin_ev_size != b_ccoin->coin_ev_size); - FAILIF (0 != memcmp (a_ccoin->coin_ev, - a_ccoin->coin_ev, - a_ccoin->coin_ev_size)); - GNUNET_free (ret_commit_coins[cnt].coin_ev); - } - GNUNET_free (ret_commit_coins); - ret_commit_coins = NULL; - ret = GNUNET_OK; - drop: - if (NULL != ret_commit_coins) - { - plugin->free_refresh_commit_coins (plugin->cls, - MELT_NEW_COINS, - ret_commit_coins); - GNUNET_free (ret_commit_coins); - } - return ret; -} +static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins; +static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA]; -static struct TALER_TransferPublicKeyP rctp[TALER_CNC_KAPPA]; +static struct TALER_TransferPublicKeyP tpub; /** - * Test APIs related to the "insert_refresh_commit_coins" function. + * Function called with information about a refresh order. This + * one should not be called in a successful test. * - * @param session database sesison to use - * @param refresh_session details about the refresh session to use - * @param session_hash refresh melt session hash to use - * @return #GNUNET_OK on success + * @param cls closure + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs array of @e num_tprivs transfer private keys + * @param tp transfer public key information */ -static int -test_refresh_commit_links (struct TALER_EXCHANGEDB_Session *session, - const struct TALER_EXCHANGEDB_RefreshSession *refresh_session, - const struct GNUNET_HashCode *session_hash) +static void +never_called_cb (void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp) { - int ret; - struct TALER_TransferPublicKeyP tp; - unsigned int i; - - ret = GNUNET_SYSERR; - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->get_refresh_transfer_public_key (plugin->cls, - session, - session_hash, - &tp)); - for (i=0;i<TALER_CNC_KAPPA;i++) - RND_BLK (&rctp[i]); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refresh_transfer_public_key (plugin->cls, - session, - session_hash, - &rctp[MELT_NOREVEAL_INDEX])); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_refresh_transfer_public_key (plugin->cls, - session, - session_hash, - &tp)); - FAILIF (0 != - memcmp (&rctp[MELT_NOREVEAL_INDEX], - &tp, - sizeof (struct TALER_TransferPublicKeyP))); - ret = GNUNET_OK; - drop: - return ret; + GNUNET_assert (0); /* should never be called! */ } -static struct GNUNET_HashCode session_hash; - - /** - * Function called with the session hashes and transfer secret - * information for a given coin. Checks if they are as expected. + * Function called with information about a refresh order. + * Checks that the response matches what we expect to see. * * @param cls closure - * @param sh a session the coin was melted in - * @param transfer_pub public transfer key for the session + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivsr array of @e num_tprivs transfer private keys + * @param tpr transfer public key information */ static void -check_transfer_data (void *cls, - const struct GNUNET_HashCode *sh, - const struct TALER_TransferPublicKeyP *transfer_pub) +check_refresh_reveal_cb (void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivsr, + const struct TALER_TransferPublicKeyP *tpr) { - int *ok = cls; - - FAILIF (0 != memcmp (&rctp[MELT_NOREVEAL_INDEX], - transfer_pub, - sizeof (struct TALER_TransferPublicKeyP))); - FAILIF (0 != memcmp (&session_hash, - sh, - sizeof (struct GNUNET_HashCode))); - *ok = GNUNET_OK; - return; - drop: - *ok = GNUNET_SYSERR; + /* compare the refresh commit coin arrays */ + for (unsigned int cnt = 0; cnt < num_newcoins; cnt++) + { + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *acoin = &revealed_coins[cnt]; + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *bcoin = &rrcs[cnt]; + + GNUNET_assert (acoin->coin_ev_size == bcoin->coin_ev_size); + GNUNET_assert (0 == + memcmp (acoin->coin_ev, + bcoin->coin_ev, + acoin->coin_ev_size)); + GNUNET_assert (0 == + GNUNET_CRYPTO_rsa_public_key_cmp (acoin->denom_pub.rsa_public_key, + bcoin->denom_pub.rsa_public_key)); + } + GNUNET_assert (0 == + memcmp (&tpub, + tpr, + sizeof (tpub))); + GNUNET_assert (0 == + memcmp (tprivs, + tprivsr, + sizeof (struct TALER_TransferPrivateKeyP) * (TALER_CNC_KAPPA - 1))); } @@ -487,7 +410,7 @@ static unsigned int auditor_row_cnt; * @param amount_with_fee amount that was deposited including fee * @param num_newcoins how many coins were issued * @param noreveal_index which index was picked by the exchange in cut-and-choose - * @param session_hash what is the session hash + * @param rc what is the session hash * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ static int @@ -497,9 +420,8 @@ audit_refresh_session_cb (void *cls, const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, - uint16_t num_newcoins, - uint16_t noreveal_index, - const struct GNUNET_HashCode *session_hash) + uint32_t noreveal_index, + const struct TALER_RefreshCommitmentP *rc) { auditor_row_cnt++; return GNUNET_OK; @@ -507,6 +429,51 @@ audit_refresh_session_cb (void *cls, /** + * Denomination keys used for fresh coins in melt test. + */ +static struct DenomKeyPair **new_dkp; + + +/** + * Function called with the session hashes and transfer secret + * information for a given coin. + * + * @param cls closure + * @param transfer_pub public transfer key for the session + * @param ldl link data for @a transfer_pub + */ +static void +handle_link_data_cb (void *cls, + const struct TALER_TransferPublicKeyP *transfer_pub, + const struct TALER_EXCHANGEDB_LinkDataList *ldl) +{ + for (const struct TALER_EXCHANGEDB_LinkDataList *ldlp = ldl; + NULL != ldlp; + ldlp = ldlp->next) + { + int found; + + found = GNUNET_NO; + for (unsigned int cnt=0;cnt < MELT_NEW_COINS;cnt++) + { + GNUNET_assert (NULL != ldlp->ev_sig.rsa_signature); + if ( (0 == + GNUNET_CRYPTO_rsa_public_key_cmp (ldlp->denom_pub.rsa_public_key, + new_dkp[cnt]->pub.rsa_public_key)) && + (0 == + GNUNET_CRYPTO_rsa_signature_cmp (ldlp->ev_sig.rsa_signature, + revealed_coins[cnt].coin_sig.rsa_signature)) ) + { + found = GNUNET_YES; + break; + } + } + GNUNET_assert (GNUNET_NO != found); + } +} + + +/** * Function to test melting of coins as part of a refresh session * * @param session the database session @@ -517,30 +484,20 @@ static int test_melting (struct TALER_EXCHANGEDB_Session *session) { struct TALER_EXCHANGEDB_RefreshSession refresh_session; - struct TALER_EXCHANGEDB_RefreshSession ret_refresh_session; + struct TALER_EXCHANGEDB_RefreshMelt ret_refresh_session; struct DenomKeyPair *dkp; - struct DenomKeyPair **new_dkp; - /* struct TALER_CoinPublicInfo *coins; */ - struct TALER_EXCHANGEDB_RefreshMelt *meltp; struct TALER_DenominationPublicKey *new_denom_pubs; struct TALER_DenominationPublicKey *ret_denom_pubs; - struct TALER_EXCHANGEDB_LinkDataList *ldl; - struct TALER_EXCHANGEDB_LinkDataList *ldlp; - struct TALER_DenominationSignature ev_sigs[MELT_NEW_COINS]; - unsigned int cnt; int ret; enum GNUNET_DB_QueryStatus qs; ret = GNUNET_SYSERR; - memset (ev_sigs, 0, sizeof (ev_sigs)); RND_BLK (&refresh_session); - RND_BLK (&session_hash); dkp = NULL; new_dkp = NULL; new_denom_pubs = NULL; ret_denom_pubs = NULL; /* create and test a refresh session */ - refresh_session.num_newcoins = MELT_NEW_COINS; refresh_session.noreveal_index = MELT_NOREVEAL_INDEX; /* create a denomination (value: 1; fraction: 100) */ dkp = create_denom_key_pair (512, @@ -556,33 +513,62 @@ test_melting (struct TALER_EXCHANGEDB_Session *session) { struct GNUNET_HashCode hc; - meltp = &refresh_session.melt; - RND_BLK (&meltp->coin.coin_pub); - GNUNET_CRYPTO_hash (&meltp->coin.coin_pub, - sizeof (meltp->coin.coin_pub), + RND_BLK (&refresh_session.coin.coin_pub); + GNUNET_CRYPTO_hash (&refresh_session.coin.coin_pub, + sizeof (refresh_session.coin.coin_pub), &hc); - meltp->coin.denom_sig.rsa_signature = + refresh_session.coin.denom_sig.rsa_signature = GNUNET_CRYPTO_rsa_sign_fdh (dkp->priv.rsa_private_key, &hc); - GNUNET_assert (NULL != meltp->coin.denom_sig.rsa_signature); - meltp->coin.denom_pub = dkp->pub; - RND_BLK (&meltp->coin_sig); - meltp->session_hash = session_hash; - meltp->amount_with_fee = amount_with_fee; - meltp->melt_fee = fee_refresh; + GNUNET_assert (NULL != refresh_session.coin.denom_sig.rsa_signature); + refresh_session.coin.denom_pub = dkp->pub; + refresh_session.amount_with_fee = amount_with_fee; } + /* test insert_melt & get_melt */ + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + plugin->get_melt (plugin->cls, + session, + &refresh_session.rc, + &ret_refresh_session)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->create_refresh_session (plugin->cls, - session, - &session_hash, - &refresh_session)); + plugin->insert_melt (plugin->cls, + session, + &refresh_session)); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_refresh_session (plugin->cls, - session, - &session_hash, - &ret_refresh_session)); + plugin->get_melt (plugin->cls, + session, + &refresh_session.rc, + &ret_refresh_session)); + FAILIF (refresh_session.noreveal_index != + ret_refresh_session.session.noreveal_index); + FAILIF (0 != + TALER_amount_cmp (&refresh_session.amount_with_fee, + &ret_refresh_session.session.amount_with_fee)); + FAILIF (0 != + TALER_amount_cmp (&fee_refresh, + &ret_refresh_session.melt_fee)); + FAILIF (0 != + memcmp (&refresh_session.rc, + &ret_refresh_session.session.rc, + sizeof (struct TALER_RefreshCommitmentP))); + FAILIF (0 != + memcmp (&refresh_session.coin_sig, + &ret_refresh_session.session.coin_sig, + sizeof (struct TALER_CoinSpendSignatureP))); + FAILIF (0 != + GNUNET_CRYPTO_rsa_signature_cmp (refresh_session.coin.denom_sig.rsa_signature, + ret_refresh_session.session.coin.denom_sig.rsa_signature)); + FAILIF (0 != memcmp (&refresh_session.coin.coin_pub, + &ret_refresh_session.session.coin.coin_pub, + sizeof (refresh_session.coin.coin_pub))); + FAILIF (0 != + GNUNET_CRYPTO_rsa_public_key_cmp (refresh_session.coin.denom_pub.rsa_public_key, + ret_refresh_session.session.coin.denom_pub.rsa_public_key)); + GNUNET_CRYPTO_rsa_signature_free (ret_refresh_session.session.coin.denom_sig.rsa_signature); + GNUNET_CRYPTO_rsa_public_key_free (ret_refresh_session.session.coin.denom_pub.rsa_public_key); + /* test 'select_refreshs_above_serial_id' */ auditor_row_cnt = 0; FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->select_refreshs_above_serial_id (plugin->cls, @@ -591,41 +577,19 @@ test_melting (struct TALER_EXCHANGEDB_Session *session) &audit_refresh_session_cb, NULL)); FAILIF (1 != auditor_row_cnt); - FAILIF (ret_refresh_session.num_newcoins != refresh_session.num_newcoins); - FAILIF (ret_refresh_session.noreveal_index != refresh_session.noreveal_index); - /* check refresh session melt data */ - { - struct TALER_EXCHANGEDB_RefreshMelt *ret_melt; - - ret_melt = &ret_refresh_session.melt; - FAILIF (0 != GNUNET_CRYPTO_rsa_signature_cmp - (ret_melt->coin.denom_sig.rsa_signature, - meltp->coin.denom_sig.rsa_signature)); - FAILIF (0 != memcmp (&ret_melt->coin.coin_pub, - &meltp->coin.coin_pub, - sizeof (ret_melt->coin.coin_pub))); - FAILIF (0 != GNUNET_CRYPTO_rsa_public_key_cmp - (ret_melt->coin.denom_pub.rsa_public_key, - meltp->coin.denom_pub.rsa_public_key)); - FAILIF (0 != memcmp (&ret_melt->coin_sig, - &meltp->coin_sig, - sizeof (ret_melt->coin_sig))); - FAILIF (0 != memcmp (&ret_melt->session_hash, - &meltp->session_hash, - sizeof (ret_melt->session_hash))); - FAILIF (0 != TALER_amount_cmp (&ret_melt->amount_with_fee, - &meltp->amount_with_fee)); - FAILIF (0 != TALER_amount_cmp (&ret_melt->melt_fee, - &meltp->melt_fee)); - GNUNET_CRYPTO_rsa_signature_free (ret_melt->coin.denom_sig.rsa_signature); - GNUNET_CRYPTO_rsa_public_key_free (ret_melt->coin.denom_pub.rsa_public_key); - } - new_dkp = GNUNET_new_array (MELT_NEW_COINS, struct DenomKeyPair *); + new_dkp = GNUNET_new_array (MELT_NEW_COINS, + struct DenomKeyPair *); new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, struct TALER_DenominationPublicKey); - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) + revealed_coins + = GNUNET_new_array (MELT_NEW_COINS, + struct TALER_EXCHANGEDB_RefreshRevealedCoin); + for (unsigned int cnt=0; cnt < MELT_NEW_COINS; cnt++) { + struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin; + struct GNUNET_HashCode hc; + new_dkp[cnt] = create_denom_key_pair (1024, session, GNUNET_TIME_absolute_get (), @@ -636,100 +600,50 @@ test_melting (struct TALER_EXCHANGEDB_Session *session) &fee_refund); GNUNET_assert (NULL != new_dkp[cnt]); new_denom_pubs[cnt] = new_dkp[cnt]->pub; - } - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refresh_order (plugin->cls, - session, - &session_hash, - MELT_NEW_COINS, - new_denom_pubs)); - ret_denom_pubs = GNUNET_new_array (MELT_NEW_COINS, - struct TALER_DenominationPublicKey); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_refresh_order (plugin->cls, - session, - &session_hash, - MELT_NEW_COINS, - ret_denom_pubs)); - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) - { - FAILIF (0 != GNUNET_CRYPTO_rsa_public_key_cmp - (ret_denom_pubs[cnt].rsa_public_key, - new_denom_pubs[cnt].rsa_public_key)); - } - FAILIF (GNUNET_OK != - test_refresh_commit_coins (session, - &refresh_session, - &session_hash)); - FAILIF (GNUNET_OK != - test_refresh_commit_links (session, - &refresh_session, - &session_hash)); - - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) - { - struct GNUNET_HashCode hc; - struct TALER_DenominationSignature test_sig; - + ccoin = &revealed_coins[cnt]; + ccoin->coin_ev_size = (size_t) GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, + COIN_ENC_MAX_SIZE); + ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size); + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, + ccoin->coin_ev, + ccoin->coin_ev_size); RND_BLK (&hc); - ev_sigs[cnt].rsa_signature + ccoin->denom_pub = new_dkp[cnt]->pub; + ccoin->coin_sig.rsa_signature = GNUNET_CRYPTO_rsa_sign_fdh (new_dkp[cnt]->priv.rsa_private_key, &hc); - GNUNET_assert (NULL != ev_sigs[cnt].rsa_signature); - FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != - plugin->get_refresh_out (plugin->cls, - session, - &session_hash, - cnt, - &test_sig)); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->insert_refresh_out (plugin->cls, - session, - &session_hash, - cnt, - &ev_sigs[cnt])); - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_refresh_out (plugin->cls, - session, - &session_hash, - cnt, - &test_sig)); - FAILIF (0 != - GNUNET_CRYPTO_rsa_signature_cmp (test_sig.rsa_signature, - ev_sigs[cnt].rsa_signature)); - GNUNET_CRYPTO_rsa_signature_free (test_sig.rsa_signature); } + RND_BLK (&tprivs); + RND_BLK (&tpub); + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != + plugin->get_refresh_reveal (plugin->cls, + session, + &refresh_session.rc, + &never_called_cb, + NULL)); + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != + plugin->insert_refresh_reveal (plugin->cls, + session, + &refresh_session.rc, + MELT_NEW_COINS, + revealed_coins, + TALER_CNC_KAPPA - 1, + tprivs, + &tpub)); + FAILIF (0 >= + plugin->get_refresh_reveal (plugin->cls, + session, + &refresh_session.rc, + &check_refresh_reveal_cb, + NULL)); - qs = plugin->get_link_data_list (plugin->cls, - session, - &session_hash, - &ldl); - FAILIF (0 >= qs); - FAILIF (NULL == ldl); - for (ldlp = ldl; NULL != ldlp; ldlp = ldlp->next) - { - int found; - - found = GNUNET_NO; - for (cnt=0;cnt < MELT_NEW_COINS;cnt++) - { - FAILIF (NULL == ldlp->ev_sig.rsa_signature); - if ( (0 == - GNUNET_CRYPTO_rsa_public_key_cmp (ldlp->denom_pub.rsa_public_key, - new_dkp[cnt]->pub.rsa_public_key)) && - (0 == - GNUNET_CRYPTO_rsa_signature_cmp (ldlp->ev_sig.rsa_signature, - ev_sigs[cnt].rsa_signature)) ) - { - found = GNUNET_YES; - break; - } - } - FAILIF (GNUNET_NO == found); - } - plugin->free_link_data_list (plugin->cls, - ldl); + qs = plugin->get_link_data (plugin->cls, + session, + &refresh_session.coin.coin_pub, + &handle_link_data_cb, + NULL); + FAILIF (0 >= qs); { /* Just to test fetching a coin with melt history */ struct TALER_EXCHANGEDB_TransactionList *tl; @@ -737,7 +651,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session) qs = plugin->get_coin_transactions (plugin->cls, session, - &meltp->coin.coin_pub, + &refresh_session.coin.coin_pub, &tl); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs); plugin->free_coin_transaction_list (plugin->cls, @@ -745,42 +659,29 @@ test_melting (struct TALER_EXCHANGEDB_Session *session) } - { - int ok; - - ok = GNUNET_NO; - FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != - plugin->get_transfer (plugin->cls, - session, - &meltp->coin.coin_pub, - &check_transfer_data, - &ok)); - FAILIF (GNUNET_OK != ok); - } - ret = GNUNET_OK; drop: - for (cnt=0; cnt < MELT_NEW_COINS; cnt++) - if (NULL != ev_sigs[cnt].rsa_signature) - GNUNET_CRYPTO_rsa_signature_free (ev_sigs[cnt].rsa_signature); - if (NULL != commit_coins) + if (NULL != revealed_coins) { - plugin->free_refresh_commit_coins (plugin->cls, - MELT_NEW_COINS, - commit_coins); - GNUNET_free (commit_coins); - commit_coins = NULL; + for (unsigned int cnt=0; cnt < MELT_NEW_COINS; cnt++) + { + if (NULL != revealed_coins[cnt].coin_sig.rsa_signature) + GNUNET_CRYPTO_rsa_signature_free (revealed_coins[cnt].coin_sig.rsa_signature); + GNUNET_free (revealed_coins[cnt].coin_ev); + } + GNUNET_free (revealed_coins); + revealed_coins = NULL; } destroy_denom_key_pair (dkp); - GNUNET_CRYPTO_rsa_signature_free (meltp->coin.denom_sig.rsa_signature); - for (cnt = 0; + GNUNET_CRYPTO_rsa_signature_free (refresh_session.coin.denom_sig.rsa_signature); + for (unsigned int cnt = 0; (NULL != ret_denom_pubs) && (cnt < MELT_NEW_COINS) && (NULL != ret_denom_pubs[cnt].rsa_public_key); cnt++) GNUNET_CRYPTO_rsa_public_key_free (ret_denom_pubs[cnt].rsa_public_key); GNUNET_free_non_null (ret_denom_pubs); GNUNET_free_non_null (new_denom_pubs); - for (cnt = 0; + for (unsigned int cnt = 0; (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]); cnt++) destroy_denom_key_pair (new_dkp[cnt]); @@ -1439,7 +1340,7 @@ wire_missing_cb (void *cls, /* bool? */ int tiny, /* bool? */ int done) { - struct TALER_EXCHANGEDB_Deposit *deposit = cls; + const struct TALER_EXCHANGEDB_Deposit *deposit = cls; struct GNUNET_HashCode h_wire; if (NULL != wire) @@ -1615,6 +1516,7 @@ run (void *cls) session, &rr, &rr_size)); + GNUNET_free (rr); FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_latest_reserve_in_reference (plugin->cls, session, diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index d5024e6c8..d45e7bb28 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA 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 @@ -289,6 +289,19 @@ struct TALER_DenominationBlindingKeyP }; +/** + * Commitment value for the refresh protocol. + * See #TALER_refresh_get_commitment(). + */ +struct TALER_RefreshCommitmentP +{ + /** + * The commitment is a hash code. + */ + struct GNUNET_HashCode session_hash; +}; + + GNUNET_NETWORK_STRUCT_END @@ -557,12 +570,12 @@ GNUNET_NETWORK_STRUCT_END * * @param secret_seed seed to use for KDF to derive coin keys * @param coin_num_salt number of the coin to include in KDF - * @param[out] fc value to initialize + * @param[out] ps value to initialize */ void TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, - unsigned int coin_num_salt, - struct TALER_PlanchetSecretsP *fc); + uint32_t coin_num_salt, + struct TALER_PlanchetSecretsP *ps); /** @@ -656,4 +669,65 @@ TALER_link_recover_transfer_secret (const struct TALER_TransferPublicKeyP *trans struct TALER_TransferSecretP *transfer_secret); +/** + * Information about a coin to be created during a refresh operation. + */ +struct TALER_RefreshCoinData +{ + + /** + * The denomination's public key. + */ + const struct TALER_DenominationPublicKey *dk; + + /** + * The envelope with the blinded coin. + */ + char *coin_ev; + + /** + * Number of bytes in @a coin_ev + */ + size_t coin_ev_size; + +}; + + +/** + * One of the #TALER_CNC_KAPPA commitments. + */ +struct TALER_RefreshCommitmentEntry +{ + /** + * Transfer public key of this commitment. + */ + struct TALER_TransferPublicKeyP transfer_pub; + + /** + * Array of @e num_new_coins new coins to be created. + */ + struct TALER_RefreshCoinData *new_coins; +}; + + +/** + * Compute the commitment for a /refresh/melt operation from + * the respective public inputs. + * + * @param[out] rc set to the value the wallet must commit to + * @param kappa number of transfer public keys involved (must be #TALER_CNC_KAPPA) + * @param num_new_coins number of new coins to be created + * @param commitments array of @a kappa commitments + * @param coin_pub public key of the coin to be melted + * @param amount_with_fee amount to be melted, including fee + */ +void +TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, + uint32_t kappa, + uint32_t num_new_coins, + const struct TALER_RefreshCommitmentEntry *rcs, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee); + + #endif diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h index acc7228b1..6047e1643 100644 --- a/src/include/taler_error_codes.h +++ b/src/include/taler_error_codes.h @@ -68,6 +68,11 @@ enum TALER_ErrorCode */ TALER_EC_TIMEOUT = 6, + /** + * Exchange failed to allocate memory for building JSON reply. + */ + TALER_EC_JSON_ALLOCATION_FAILURE = 7, + /* ********** generic error codes ************* */ /** @@ -428,6 +433,7 @@ enum TALER_ErrorCode */ TALER_EC_DEPOSIT_INVALID_TIMESTAMP = 1218, + /** * The respective coin did not have sufficient residual value * for the /refresh/melt operation. The "history" in this @@ -469,85 +475,25 @@ enum TALER_ErrorCode TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304, /** - * The exchange failed to store commit data in the - * database. - * This response is provided with HTTP status code - * MHD_HTTP_INTERNAL_ERROR. - */ - TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306, - - /** - * The exchange is unaware of the denomination key that was - * requested for one of the fresh coins. This response is provided - * with HTTP status code MHD_HTTP_BAD_REQUEST. - */ - TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND = 1308, - - /** - * The exchange encountered a numeric overflow totaling up - * the cost for the refresh operation. This response is provided - * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR. - */ - TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW = 1309, - - /** - * During the transaction phase, the exchange could suddenly - * no longer find the denomination key that was - * used to sign the melted coin. This response is provided - * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR. - */ - TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND = 1310, - - /** * The exchange encountered melt fees exceeding the melted * coin's contribution. This response is provided * with HTTP status code MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION = 1311, - - /** - * The exchange's cost calculation does not add up to the - * melt fees specified in the request. This response is provided - * with HTTP status code MHD_HTTP_BAD_REQUEST. - */ - TALER_EC_REFRESH_MELT_FEES_MISSMATCH = 1312, + TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION = 1305, /** * The denomination key signature on the melted coin is invalid. * This response is provided with HTTP status code * MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID = 1313, - - /** - * The exchange's cost calculation shows that the melt amount - * is below the costs of the transaction. This response is provided - * with HTTP status code MHD_HTTP_BAD_REQUEST. - */ - TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT = 1314, + TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID = 1306, /** * The signature made with the coin to be melted is invalid. * This response is provided with HTTP status code * MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID = 1315, - - /** - * The size of the cut-and-choose dimension of the - * blinded coins request does not match #TALER_CNC_KAPPA. - * This response is provided with HTTP status code - * MHD_HTTP_BAD_REQUEST. - */ - TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID = 1316, - - /** - * The size of the cut-and-choose dimension of the - * transfer keys request does not match #TALER_CNC_KAPPA. - * This response is provided with HTTP status code - * MHD_HTTP_BAD_REQUEST. - */ - TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1317, + TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID = 1307, /** * The exchange failed to obtain the transaction history of the @@ -556,7 +502,8 @@ enum TALER_ErrorCode * This response is provided with HTTP status code * MHD_HTTP_INTERNAL_SERVER_ERROR. */ - TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1318, + TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1308, + /** * The provided transfer keys do not match up with the @@ -567,20 +514,12 @@ enum TALER_ErrorCode TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION = 1350, /** - * Failed to blind the envelope to reconstruct the blinded - * coins for revealation checks. - * This response is provided with HTTP status code - * MHD_HTTP_INTERNAL_ERROR. - */ - TALER_EC_REFRESH_REVEAL_BLINDING_ERROR = 1351, - - /** * Failed to produce the blinded signatures over the coins * to be returned. * This response is provided with HTTP status code * MHD_HTTP_INTERNAL_ERROR. */ - TALER_EC_REFRESH_REVEAL_SIGNING_ERROR = 1352, + TALER_EC_REFRESH_REVEAL_SIGNING_ERROR = 1351, /** * The exchange is unaware of the refresh sessino specified in @@ -588,7 +527,7 @@ enum TALER_ErrorCode * This response is provided with HTTP status code * MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN = 1353, + TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN = 1352, /** * The exchange failed to retrieve valid session data from the @@ -596,39 +535,68 @@ enum TALER_ErrorCode * This response is provided with HTTP status code * MHD_HTTP_INTERNAL_ERROR. */ - TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR = 1354, + TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR = 1353, /** - * The exchange failed to retrieve order data from the - * database. - * This response is provided with HTTP status code + * The exchange failed to retrieve previously revealed data from the + * database. This response is provided with HTTP status code * MHD_HTTP_INTERNAL_ERROR. */ - TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR = 1355, + TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR = 1354, /** - * The exchange failed to retrieve transfer keys from the + * The exchange failed to retrieve commitment data from the * database. * This response is provided with HTTP status code * MHD_HTTP_INTERNAL_ERROR. */ - TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR = 1356, + TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR = 1355, /** - * The exchange failed to retrieve commitment data from the - * database. + * The size of the cut-and-choose dimension of the + * private transfer keys request does not match #TALER_CNC_KAPPA - 1. * This response is provided with HTTP status code - * MHD_HTTP_INTERNAL_ERROR. + * MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR = 1357, + TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1356, /** - * The size of the cut-and-choose dimension of the + * The number of coins to be created in refresh exceeds the limits + * of the exchange. * private transfer keys request does not match #TALER_CNC_KAPPA - 1. * This response is provided with HTTP status code * MHD_HTTP_BAD_REQUEST. */ - TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1358, + TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE = 1357, + + /** + * The number of envelopes given does not match the number + * of denomination keys given. + * This response is provided with HTTP status code + * MHD_HTTP_BAD_REQUEST. + */ + TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH = 1358, + + /** + * The exchange encountered a numeric overflow totaling up + * the cost for the refresh operation. This response is provided + * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR. + */ + TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW = 1359, + + /** + * The exchange's cost calculation shows that the melt amount + * is below the costs of the transaction. This response is provided + * with HTTP status code MHD_HTTP_BAD_REQUEST. + */ + TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT = 1360, + + /** + * The exchange is unaware of the denomination key that was + * requested for one of the fresh coins. This response is provided + * with HTTP status code MHD_HTTP_BAD_REQUEST. + */ + TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND = 1361, /** diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h index fadcbf8b2..7e9ad914d 100644 --- a/src/include/taler_exchange_service.h +++ b/src/include/taler_exchange_service.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017 GNUnet e.V. + Copyright (C) 2014-2017 Taler Systems SA 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 @@ -1110,7 +1110,7 @@ struct TALER_EXCHANGE_RefreshMeltHandle; * 0 if the exchange's reply is bogus (fails to follow the protocol) * @param ec taler-specific error code, #TALER_EC_NONE on success * @param noreveal_index choice by the exchange in the cut-and-choose protocol, - * UINT16_MAX on error + * UINT32_MAX on error * @param sign_key exchange key used to sign @a full_response, or NULL * @param full_response full response from the exchange (for logging, in case of errors) */ @@ -1118,7 +1118,7 @@ typedef void (*TALER_EXCHANGE_RefreshMeltCallback) (void *cls, unsigned int http_status, enum TALER_ErrorCode ec, - uint16_t noreveal_index, + uint32_t noreveal_index, const struct TALER_ExchangePublicKeyP *sign_key, const json_t *full_response); @@ -1223,7 +1223,7 @@ struct TALER_EXCHANGE_RefreshRevealHandle * TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange, size_t refresh_data_length, const char *refresh_data, - uint16_t noreveal_index, + uint32_t noreveal_index, TALER_EXCHANGE_RefreshRevealCallback reveal_cb, void *reveal_cb_cls); diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 006ea3981..e64b0ad4c 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014-2017 GNUnet e.V. + Copyright (C) 2014-2017 Taler Systems SA 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 @@ -465,7 +465,7 @@ struct TALER_EXCHANGEDB_Refund /** * @brief Specification for coin in a /refresh/melt operation. */ -struct TALER_EXCHANGEDB_RefreshMelt +struct TALER_EXCHANGEDB_RefreshSession { /** * Information about the coin that is being melted. @@ -478,9 +478,9 @@ struct TALER_EXCHANGEDB_RefreshMelt struct TALER_CoinSpendSignatureP coin_sig; /** - * Hash of the refresh session this coin is melted into. + * Refresh commitment this coin is melted into. */ - struct GNUNET_HashCode session_hash; + struct TALER_RefreshCommitmentP rc; /** * How much value is being melted? This amount includes the fees, @@ -493,64 +493,29 @@ struct TALER_EXCHANGEDB_RefreshMelt struct TALER_Amount amount_with_fee; /** - * Melting fee charged by the exchange. This must match the Exchange's - * denomination key's melting fee. If the client puts in an invalid - * melting fee (too high or too low) that does not match the Exchange's - * denomination key, the melting operation is invalid and will be - * rejected by the exchange. The @e amount_with_fee minus the @e - * melt_fee is the amount that will be credited to the melting - * session. - */ - struct TALER_Amount melt_fee; - -}; - - -/** - * @brief Global information for a refreshing session. Includes - * dimensions of the operation, security parameters and - * client signatures from "/refresh/melt" and "/refresh/commit". - */ -struct TALER_EXCHANGEDB_RefreshSession -{ - - /** - * Melt operation details. - */ - struct TALER_EXCHANGEDB_RefreshMelt melt; - - /** - * Number of new coins we are creating. - */ - uint16_t num_newcoins; - - /** * Index (smaller #TALER_CNC_KAPPA) which the exchange has chosen to not * have revealed during cut and choose. */ - uint16_t noreveal_index; + uint32_t noreveal_index; }; /** - * @brief We have as many `struct TALER_EXCHANGEDB_RefreshCommitCoin` as there are new - * coins being created by the refresh (for each of the #TALER_CNC_KAPPA - * sets). These are the coins we ask the exchange to sign if the - * respective set is selected. + * Information about a /refresh/melt operation in the transaction history. */ -struct TALER_EXCHANGEDB_RefreshCommitCoin +struct TALER_EXCHANGEDB_RefreshMelt { /** - * Blinded message to be signed (in envelope), with @e coin_env_size bytes. + * Overall session data. */ - char *coin_ev; + struct TALER_EXCHANGEDB_RefreshSession session; /** - * Number of bytes in @e coin_ev. + * Melt fee the exchange charged. */ - size_t coin_ev_size; + struct TALER_Amount melt_fee; }; @@ -752,9 +717,8 @@ typedef int * @param coin_pub public key of the coin * @param coin_sig signature from the coin * @param amount_with_fee amount that was deposited including fee - * @param num_newcoins how many coins were issued * @param noreveal_index which index was picked by the exchange in cut-and-choose - * @param session_hash what is the session hash + * @param rc what is the commitment * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop */ typedef int @@ -764,9 +728,56 @@ typedef int const struct TALER_CoinSpendPublicKeyP *coin_pub, const struct TALER_CoinSpendSignatureP *coin_sig, const struct TALER_Amount *amount_with_fee, - uint16_t num_newcoins, - uint16_t noreveal_index, - const struct GNUNET_HashCode *session_hash); + uint32_t noreveal_index, + const struct TALER_RefreshCommitmentP *rc); + + +/** + * Information about a coin that was revealed to the exchange + * during /refresh/reveal. + */ +struct TALER_EXCHANGEDB_RefreshRevealedCoin +{ + /** + * Public denomination key of the coin. + */ + struct TALER_DenominationPublicKey denom_pub; + + /** + * Blinded message to be signed (in envelope), with @e coin_env_size bytes. + */ + char *coin_ev; + + /** + * Number of bytes in @e coin_ev. + */ + size_t coin_ev_size; + + /** + * Signature generated by the exchange over the coin (in blinded format). + */ + struct TALER_DenominationSignature coin_sig; +}; + + +/** + * Function called with information about a refresh order. + * + * @param cls closure + * @param rowid unique serial ID for the row in our database + * @param num_newcoins size of the @a rrcs array + * @param rrcs array of @a num_newcoins information about coins to be created + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs array of @e num_tprivs transfer private keys + * @param tp transfer public key information + */ +typedef void +(*TALER_EXCHANGEDB_RefreshCallback)(void *cls, + uint32_t num_newcoins, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp); /** @@ -851,14 +862,13 @@ typedef int * 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 + * @param ldl link data for @a transfer_pub */ typedef void -(*TALER_EXCHANGEDB_TransferDataCallback)(void *cls, - const struct GNUNET_HashCode *session_hash, - const struct TALER_TransferPublicKeyP *transfer_pub); +(*TALER_EXCHANGEDB_LinkDataCallback)(void *cls, + const struct TALER_TransferPublicKeyP *transfer_pub, + const struct TALER_EXCHANGEDB_LinkDataList *ldl); /** @@ -1235,7 +1245,7 @@ struct TALER_EXCHANGEDB_Plugin * @param[out] wire_reference_size set to number of bytes in @a wire_reference * @return transaction status code */ - enum GNUNET_DB_QueryStatus + enum GNUNET_DB_QueryStatus (*get_latest_reserve_in_reference)(void *cls, struct TALER_EXCHANGEDB_Session *db, void **wire_reference, @@ -1254,7 +1264,7 @@ struct TALER_EXCHANGEDB_Plugin * if a coin is found * @return statement execution status */ - enum GNUNET_DB_QueryStatus + enum GNUNET_DB_QueryStatus (*get_withdraw_info) (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct GNUNET_HashCode *h_blind, @@ -1454,225 +1464,79 @@ struct TALER_EXCHANGEDB_Plugin /** - * Lookup refresh session data under the given @a session_hash. + * Store new refresh melt commitment data. * * @param cls the @e cls of this struct with the plugin-specific state * @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 transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_refresh_session) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TALER_EXCHANGEDB_RefreshSession *refresh_session); - - - /** - * 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 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 query status for the transaction - */ - enum GNUNET_DB_QueryStatus - (*create_refresh_session) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - const struct TALER_EXCHANGEDB_RefreshSession *refresh_session); - - - /** - * Store in the database which coin(s) we want to create - * in a given refresh operation. - * - * @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 num_newcoins number of coins to generate, size of the @a denom_pubs array - * @param denom_pubs array denominations of the coins to create - * @return query status for the transaction - */ - enum GNUNET_DB_QueryStatus - (*insert_refresh_order) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - const struct TALER_DenominationPublicKey *denom_pubs); - - - /** - * Lookup in the database for the @a num_newcoins coins that we want to - * create in the given refresh operation. - * - * @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 num_newcoins size of the @a denom_pubs array - * @param[out] denom_pubs where to write @a num_newcoins denomination keys - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_refresh_order) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - struct TALER_DenominationPublicKey *denom_pubs); - - - /** - * Store information about the commitments of the given index @a i - * for the given refresh session in the database. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param num_newcoins coin index size of the @a commit_coins array - * @param commit_coin array of coin commitments to store + * @param refresh_session operational data to store * @return query status for the transaction */ enum GNUNET_DB_QueryStatus - (*insert_refresh_commit_coins) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_newcoins, - const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins); + (*insert_melt) (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_EXCHANGEDB_RefreshSession *refresh_session); /** - * Obtain information about the commitment of the - * given coin of the given refresh session from the database. + * Lookup refresh metl commitment data under the given @a rc. * * @param cls the @e cls of this struct with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param num_coins size of the @a commit_coins array - * @param[out] commit_coins array of coin commitments to return + * @param session database handle to use + * @param rc commitment to use for the lookup + * @param[out] refresh_melt where to store the result * @return transaction status */ enum GNUNET_DB_QueryStatus - (*get_refresh_commit_coins) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t num_coins, - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins); - - /** - * Free refresh @a commit_coins data obtained via @e get_refresh_commit_coins. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param num_coins size of the @a commit_coins array - * @param commit_coins array of coin commitments to free - */ - void - (*free_refresh_commit_coins) (void *cls, - unsigned int num_coins, - struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins); + (*get_melt) (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_RefreshCommitmentP *rc, + struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt); /** - * Store the commitment to the given (encrypted) refresh link data - * for the given refresh session. + * Store in the database which coin(s) the wallet wanted to create + * in a given refresh operation and all of the other information + * we learned or created in the /refresh/reveal step. * * @param cls the @e cls of this struct with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session + * @param session database connection + * @param rc identify commitment and thus refresh operation + * @param num_rrcs_newcoins number of coins to generate, size of the + * @a rrcs array + * @param rrcs information about the new coins + * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1 + * @param tprivs transfer private keys to store * @param tp public key to store * @return query status for the transaction */ enum GNUNET_DB_QueryStatus - (*insert_refresh_transfer_public_key) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - const struct TALER_TransferPublicKeyP *tp); - - /** - * Obtain the commited (encrypted) refresh link data - * for the given refresh session. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param session database connection to use - * @param session_hash hash to identify refresh session - * @param[out] tp information to return - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*get_refresh_transfer_public_key) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - 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 transaction result status - */ - enum GNUNET_DB_QueryStatus - (*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 - * 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 @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 ev_sig coin signature - * @return transaction result status - */ - enum GNUNET_DB_QueryStatus - (*insert_refresh_out) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - uint16_t newcoin_index, - const struct TALER_DenominationSignature *ev_sig); + (*insert_refresh_reveal) (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_RefreshCommitmentP *rc, + uint32_t num_rrcs, + const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs, + unsigned int num_tprivs, + const struct TALER_TransferPrivateKeyP *tprivs, + const struct TALER_TransferPublicKeyP *tp); /** - * Obtain the link data of a coin, that is the encrypted link - * information, the denomination keys and the signatures. + * Lookup in the database for the @a num_newcoins coins that we + * created in the given refresh operation. * * @param cls the @e cls of this struct with the plugin-specific state * @param session database connection - * @param session_hash session to get linkage data for - * @param[out] ldldp set to all known link data for the session - * @return status of the transaction + * @param rc identify commitment and thus refresh operation + * @param cb function to call with the results + * @param cb_cls closure for @a cb + * @return transaction status */ enum GNUNET_DB_QueryStatus - (*get_link_data_list) (void *cls, + (*get_refresh_reveal) (void *cls, struct TALER_EXCHANGEDB_Session *session, - const struct GNUNET_HashCode *session_hash, - struct TALER_EXCHANGEDB_LinkDataList **ldlp); - - - /** - * Free memory of the link data list. - * - * @param cls the @e cls of this struct with the plugin-specific state - * @param ldl link data list to release - */ - void - (*free_link_data_list) (void *cls, - struct TALER_EXCHANGEDB_LinkDataList *ldl); + const struct TALER_RefreshCommitmentP *rc, + TALER_EXCHANGEDB_RefreshCallback cb, + void *cb_cls); /** @@ -1684,16 +1548,16 @@ struct TALER_EXCHANGEDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @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 + * @param ldc function to call for each session the coin was melted into + * @param ldc_cls closure for @a tdc * @return statement execution status */ enum GNUNET_DB_QueryStatus - (*get_transfer) (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct TALER_CoinSpendPublicKeyP *coin_pub, - TALER_EXCHANGEDB_TransferDataCallback tdc, - void *tdc_cls); + (*get_link_data) (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + TALER_EXCHANGEDB_LinkDataCallback ldc, + void *tdc_cls); /** @@ -1806,7 +1670,7 @@ struct TALER_EXCHANGEDB_Plugin const struct TALER_Amount *wire_fee, const struct TALER_MasterSignatureP *master_sig); - + /** * Obtain wire fee from database. * @@ -2233,7 +2097,7 @@ struct TALER_EXCHANGEDB_Plugin struct GNUNET_TIME_Absolute end_date, TALER_EXCHANGEDB_WireMissingCallback cb, void *cb_cls); - + }; diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h index db71cca68..3bc0505f9 100644 --- a/src/include/taler_signatures.h +++ b/src/include/taler_signatures.h @@ -532,9 +532,9 @@ struct TALER_RefreshMeltCoinAffirmationPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Which melting session should the coin become a part of. + * Which melt commitment is made by the wallet. */ - struct GNUNET_HashCode session_hash GNUNET_PACKED; + struct TALER_RefreshCommitmentP rc GNUNET_PACKED; /** * How much of the value of the coin should be melted? This amount @@ -581,20 +581,16 @@ struct TALER_RefreshMeltConfirmationPS struct GNUNET_CRYPTO_EccSignaturePurpose purpose; /** - * Hash of the refresh session. + * Commitment made in the /refresh/melt. */ - struct GNUNET_HashCode session_hash GNUNET_PACKED; + struct TALER_RefreshCommitmentP rc GNUNET_PACKED; /** * Index that the client will not have to reveal, in NBO. * Must be smaller than #TALER_CNC_KAPPA. */ - uint16_t noreveal_index GNUNET_PACKED; + uint32_t noreveal_index GNUNET_PACKED; - /** - * Zero. - */ - uint16_t reserved GNUNET_PACKED; }; diff --git a/src/util/crypto.c b/src/util/crypto.c index efc74850d..b44c31ff7 100644 --- a/src/util/crypto.c +++ b/src/util/crypto.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015 GNUnet e.V. + Copyright (C) 2014-2017 Taler Systems SA 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 @@ -206,7 +206,7 @@ patch_private_key (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) */ void TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed, - unsigned int coin_num_salt, + uint32_t coin_num_salt, struct TALER_PlanchetSecretsP *ps) { uint32_t be_salt = htonl (coin_num_salt); @@ -314,4 +314,87 @@ TALER_planchet_to_coin (const struct TALER_DenominationPublicKey *dk, return GNUNET_OK; } + +/** + * Compute the commitment for a /refresh/melt operation from + * the respective public inputs. + * + * @param[out] rc set to the value the wallet must commit to + * @param kappa number of transfer public keys involved (must be #TALER_CNC_KAPPA) + * @param num_new_coins number of new coins to be created + * @param commitments array of @a kappa commitments + * @param coin_pub public key of the coin to be melted + * @param amount_with_fee amount to be melted, including fee + */ +void +TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc, + uint32_t kappa, + uint32_t num_new_coins, + const struct TALER_RefreshCommitmentEntry *rcs, + const struct TALER_CoinSpendPublicKeyP *coin_pub, + const struct TALER_Amount *amount_with_fee) +{ + struct GNUNET_HashContext *hash_context; + + hash_context = GNUNET_CRYPTO_hash_context_start (); + /* first, iterate over transfer public keys for hash_context */ + for (unsigned int i=0;i<kappa;i++) + { + GNUNET_CRYPTO_hash_context_read (hash_context, + &rcs[i].transfer_pub, + sizeof (struct TALER_TransferPublicKeyP)); + } + /* next, add all of the hashes from the denomination keys to the + hash_context */ + for (unsigned int i=0;i<num_new_coins;i++) + { + char *buf; + size_t buf_size; + + /* The denomination keys should / must all be identical regardless + of what offset we use, so we use [0]. */ + GNUNET_assert (kappa > 0); /* sanity check */ + buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rcs[0].new_coins[i].dk->rsa_public_key, + &buf); + GNUNET_CRYPTO_hash_context_read (hash_context, + buf, + buf_size); + GNUNET_free (buf); + } + + /* next, add public key of coin and amount being refreshed */ + { + struct TALER_AmountNBO melt_amountn; + + GNUNET_CRYPTO_hash_context_read (hash_context, + coin_pub, + sizeof (struct TALER_CoinSpendPublicKeyP)); + TALER_amount_hton (&melt_amountn, + amount_with_fee); + GNUNET_CRYPTO_hash_context_read (hash_context, + &melt_amountn, + sizeof (struct TALER_AmountNBO)); + } + + /* finally, add all the envelopes */ + for (unsigned int i=0;i<kappa;i++) + { + const struct TALER_RefreshCommitmentEntry *rce = &rcs[i]; + + for (unsigned int j=0;j<num_new_coins;j++) + { + const struct TALER_RefreshCoinData *rcd = &rce->new_coins[j]; + + GNUNET_CRYPTO_hash_context_read (hash_context, + rcd->coin_ev, + rcd->coin_ev_size); + } + } + + /* Conclude */ + GNUNET_CRYPTO_hash_context_finish (hash_context, + &rc->session_hash); +} + + /* end of crypto.c */ |