-clean up refresh reveal API

This commit is contained in:
Christian Grothoff 2022-02-12 14:00:58 +01:00
parent 4d26042b5a
commit 7cedf3f0bf
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
3 changed files with 137 additions and 75 deletions

View File

@ -1637,6 +1637,72 @@ struct TALER_EXCHANGE_RefreshData
struct TALER_EXCHANGE_MeltHandle; struct TALER_EXCHANGE_MeltHandle;
/**
* Information we obtain per coin during melting.
*/
struct TALER_EXCHANGE_MeltBlindingDetail
{
/**
* Exchange values contributed to the refresh operation
*/
struct TALER_ExchangeWithdrawValues alg_value;
/**
* Blinding keys used to blind the fresh coins
*/
union TALER_DenominationBlindingKeyP bks;
};
/**
* Response returned to a /melt request.
*/
struct TALER_EXCHANGE_MeltResponse
{
/**
* Full HTTP response details.
*/
struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union
{
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct
{
/**
* Length of the @a mbds array with the exchange values
* and blinding keys we are using.
*/
unsigned int num_mbds;
/**
* Information returned per coin.
*/
const struct TALER_EXCHANGE_MeltBlindingDetail *mbds;
/**
* Key used by the exchange to sign the response.
*/
struct TALER_ExchangePublicKeyP sign_key;
/**
* Gamma value chosen by the exchange.
*/
uint32_t noreveal_index;
} success;
} details;
};
/** /**
* Callbacks of this type are used to notify the application about the result * Callbacks of this type are used to notify the application about the result
* of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index * of the /coins/$COIN_PUB/melt stage. If successful, the @a noreveal_index
@ -1650,7 +1716,7 @@ struct TALER_EXCHANGE_MeltHandle;
* @param bks array of @a num_coins blinding keys used to blind the fresh coins * @param bks array of @a num_coins blinding keys used to blind the fresh coins
* @param noreveal_index choice by the exchange in the cut-and-choose protocol, * @param noreveal_index choice by the exchange in the cut-and-choose protocol,
* UINT32_MAX on error * UINT32_MAX on error
* @param sign_key exchange key used to sign @a full_response, or NULL * @param sign_key exchange key used to sign the response, or NULL
*/ */
typedef void typedef void
(*TALER_EXCHANGE_MeltCallback) ( (*TALER_EXCHANGE_MeltCallback) (
@ -1734,13 +1800,22 @@ struct TALER_EXCHANGE_RevealResult
*/ */
struct TALER_EXCHANGE_HttpResponse hr; struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union union
{ {
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct struct
{ {
/** /**
* Array of @e num_coins values about the * Array of @e num_coins values about the coins obtained via the refresh
* coins obtained via the refresh operation. * operation. The array give the coins in the same order (and should
* have the same length) in which the original melt request specified the
* respective denomination keys.
*/ */
const struct TALER_EXCHANGE_RevealedCoinInfo *coins; const struct TALER_EXCHANGE_RevealedCoinInfo *coins;
@ -1759,25 +1834,15 @@ struct TALER_EXCHANGE_RevealResult
* Callbacks of this type are used to return the final result of * Callbacks of this type are used to return the final result of
* submitting a refresh request to a exchange. If the operation was * submitting a refresh request to a exchange. If the operation was
* successful, this function returns the signatures over the coins * successful, this function returns the signatures over the coins
* that were remelted. The @a coin_privs and @a sigs arrays give the * that were remelted.
* coins in the same order (and should have the same length) in which
* the original request specified the respective denomination keys.
* *
* @param cls closure * @param cls closure
* @param hr HTTP response data * @param rr result of the reveal operation
* @param num_coins number of fresh coins created, length of the @a sigs, @a psa and @a coin_privs arrays, 0 if the operation failed
* @param coin_privs array of @a num_coins private keys for the coins that were created, NULL on error
* @param psa array of @a num_coins planchet secrets (derived from the transfer secret) for each of the coins
* @param sigs array of signature over @a num_coins coins, NULL on error
*/ */
typedef void typedef void
(*TALER_EXCHANGE_RefreshesRevealCallback)( (*TALER_EXCHANGE_RefreshesRevealCallback)(
void *cls, void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_RevealResult *rr);
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_PlanchetMasterSecretP *psa,
const struct TALER_DenominationSignature *sigs);
/** /**
@ -1877,8 +1942,15 @@ struct TALER_EXCHANGE_LinkResult
*/ */
struct TALER_EXCHANGE_HttpResponse hr; struct TALER_EXCHANGE_HttpResponse hr;
/**
* Parsed response details, variant depending on the
* @e hr.http_status.
*/
union union
{ {
/**
* Results for status #MHD_HTTP_SUCCESS.
*/
struct struct
{ {
/** /**

View File

@ -99,15 +99,13 @@ struct TALER_EXCHANGE_RefreshesRevealHandle
* *
* @param rrh operation handle * @param rrh operation handle
* @param json reply from the exchange * @param json reply from the exchange
* @param[out] coin_privs array of length `num_fresh_coins`, initialized to contain the coin private keys * @param[out] rcis array of length `num_fresh_coins`, initialized to contain the coin data
* @param[out] sigs array of length `num_fresh_coins`, initialized to contain signatures
* @return #GNUNET_OK on success, #GNUNET_SYSERR on errors * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh, refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
const json_t *json, const json_t *json,
struct TALER_CoinSpendPrivateKeyP *coin_privs, struct TALER_EXCHANGE_RevealedCoinInfo *rcis)
struct TALER_DenominationSignature *sigs)
{ {
json_t *jsona; json_t *jsona;
struct GNUNET_JSON_Specification outer_spec[] = { struct GNUNET_JSON_Specification outer_spec[] = {
@ -140,7 +138,8 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
} }
for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++)
{ {
const struct TALER_PlanchetMasterSecretP *fc; struct TALER_EXCHANGE_RevealedCoinInfo *rci =
&rcis[i];
struct TALER_DenominationPublicKey *pk; struct TALER_DenominationPublicKey *pk;
json_t *jsonai; json_t *jsonai;
struct TALER_BlindedDenominationSignature blind_sig; struct TALER_BlindedDenominationSignature blind_sig;
@ -154,7 +153,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
struct TALER_FreshCoin coin; struct TALER_FreshCoin coin;
union TALER_DenominationBlindingKeyP bks; union TALER_DenominationBlindingKeyP bks;
fc = &rrh->md.fresh_coins[rrh->noreveal_index][i]; rci->ps = rrh->md.fresh_coins[rrh->noreveal_index][i];
pk = &rrh->md.fresh_pks[i]; pk = &rrh->md.fresh_pks[i];
jsonai = json_array_get (jsona, i); jsonai = json_array_get (jsona, i);
GNUNET_assert (NULL != jsonai); GNUNET_assert (NULL != jsonai);
@ -169,15 +168,15 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
TALER_planchet_setup_coin_priv (fc, TALER_planchet_setup_coin_priv (&rci->ps,
&rrh->alg_values[i], &rrh->alg_values[i],
&coin_privs[i]); &rci->coin_priv);
TALER_planchet_blinding_secret_create (fc, TALER_planchet_blinding_secret_create (&rci->ps,
&rrh->alg_values[i], &rrh->alg_values[i],
&bks); &bks);
/* needed to verify the signature, and we didn't store it earlier, /* needed to verify the signature, and we didn't store it earlier,
hence recomputing it here... */ hence recomputing it here... */
GNUNET_CRYPTO_eddsa_key_get_public (&coin_privs[i].eddsa_priv, GNUNET_CRYPTO_eddsa_key_get_public (&rci->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub); &coin_pub.eddsa_pub);
/* FIXME-Oec: Age commitment hash. */ /* FIXME-Oec: Age commitment hash. */
TALER_coin_pub_hash (&coin_pub, TALER_coin_pub_hash (&coin_pub,
@ -187,7 +186,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
TALER_planchet_to_coin (pk, TALER_planchet_to_coin (pk,
&blind_sig, &blind_sig,
&bks, &bks,
&coin_privs[i], &rci->coin_priv,
&coin_hash, &coin_hash,
&rrh->alg_values[i], &rrh->alg_values[i],
&coin)) &coin))
@ -198,7 +197,7 @@ refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
return GNUNET_SYSERR; return GNUNET_SYSERR;
} }
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
sigs[i] = coin.sig; rci->sig = coin.sig;
} }
GNUNET_JSON_parse_free (outer_spec); GNUNET_JSON_parse_free (outer_spec);
return GNUNET_OK; return GNUNET_OK;
@ -220,94 +219,86 @@ handle_refresh_reveal_finished (void *cls,
{ {
struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls; struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls;
const json_t *j = response; const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = { struct TALER_EXCHANGE_RevealResult rr = {
.reply = j, .hr.reply = j,
.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
rrh->job = NULL; rrh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; rr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
{ {
struct TALER_DenominationSignature sigs[rrh->md.num_fresh_coins]; struct TALER_EXCHANGE_RevealedCoinInfo rcis[rrh->md.num_fresh_coins];
struct TALER_CoinSpendPrivateKeyP coin_privs[rrh->md.num_fresh_coins];
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
memset (sigs, memset (rcis,
0, 0,
sizeof (sigs)); sizeof (rcis));
ret = refresh_reveal_ok (rrh, ret = refresh_reveal_ok (rrh,
j, j,
coin_privs, rcis);
sigs);
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
{ {
hr.http_status = 0; rr.hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; rr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
else else
{ {
GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA); GNUNET_assert (rrh->noreveal_index < TALER_CNC_KAPPA);
rr.details.success.num_coins = rrh->md.num_fresh_coins;
rr.details.success.coins = rcis;
rrh->reveal_cb (rrh->reveal_cb_cls, rrh->reveal_cb (rrh->reveal_cb_cls,
&hr, &rr);
rrh->md.num_fresh_coins,
coin_privs,
rrh->md.fresh_coins[rrh->noreveal_index],
sigs);
rrh->reveal_cb = NULL; rrh->reveal_cb = NULL;
} }
for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++) for (unsigned int i = 0; i<rrh->md.num_fresh_coins; i++)
TALER_denom_sig_free (&sigs[i]); TALER_denom_sig_free (&rcis[i].sig);
TALER_EXCHANGE_refreshes_reveal_cancel (rrh); TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
return; return;
} }
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the exchange is buggy /* This should never happen, either us or the exchange is buggy
(or API version conflict); just pass JSON reply to the application */ (or API version conflict); just pass JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j); rr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); rr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_CONFLICT: case MHD_HTTP_CONFLICT:
/* Nothing really to verify, exchange says our reveal is inconsistent /* Nothing really to verify, exchange says our reveal is inconsistent
with our commitment, so either side is buggy; we with our commitment, so either side is buggy; we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
hr.ec = TALER_JSON_get_error_code (j); rr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); rr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_GONE: case MHD_HTTP_GONE:
/* Server claims key expired or has been revoked */ /* Server claims key expired or has been revoked */
hr.ec = TALER_JSON_get_error_code (j); rr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); rr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API /* Server had an internal issue; we should retry, but this API
leaves this to the application */ leaves this to the application */
hr.ec = TALER_JSON_get_error_code (j); rr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); rr.hr.hint = TALER_JSON_get_error_hint (j);
break; break;
default: default:
/* unexpected response code */ /* unexpected response code */
GNUNET_break_op (0); GNUNET_break_op (0);
hr.ec = TALER_JSON_get_error_code (j); rr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); rr.hr.hint = TALER_JSON_get_error_hint (j);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u/%d for exchange refreshes reveal\n", "Unexpected response code %u/%d for exchange refreshes reveal\n",
(unsigned int) response_code, (unsigned int) response_code,
(int) hr.ec); (int) rr.hr.ec);
break; break;
} }
if (NULL != rrh->reveal_cb) if (NULL != rrh->reveal_cb)
rrh->reveal_cb (rrh->reveal_cb_cls, rrh->reveal_cb (rrh->reveal_cb_cls,
&hr, &rr);
0,
NULL,
NULL,
NULL);
TALER_EXCHANGE_refreshes_reveal_cancel (rrh); TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
} }

View File

@ -358,13 +358,10 @@ do_reveal_retry (void *cls)
*/ */
static void static void
reveal_cb (void *cls, reveal_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_RevealResult *rr)
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_PlanchetMasterSecretP *psa,
const struct TALER_DenominationSignature *sigs)
{ {
struct RefreshRevealState *rrs = cls; struct RefreshRevealState *rrs = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
const struct TALER_TESTING_Command *melt_cmd; const struct TALER_TESTING_Command *melt_cmd;
rrs->rrh = NULL; rrs->rrh = NULL;
@ -417,20 +414,22 @@ reveal_cb (void *cls,
TALER_TESTING_interpreter_fail (rrs->is); TALER_TESTING_interpreter_fail (rrs->is);
return; return;
} }
rrs->num_fresh_coins = num_coins;
switch (hr->http_status) switch (hr->http_status)
{ {
case MHD_HTTP_OK: case MHD_HTTP_OK:
rrs->psa = GNUNET_memdup (psa, rrs->num_fresh_coins = rr->details.success.num_coins;
num_coins rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
* sizeof (struct TALER_PlanchetMasterSecretP)); struct TALER_PlanchetMasterSecretP);
rrs->fresh_coins = GNUNET_new_array (num_coins, rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
struct TALER_TESTING_FreshCoinData); struct TALER_TESTING_FreshCoinData);
for (unsigned int i = 0; i<num_coins; i++) for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
{ {
const struct TALER_EXCHANGE_RevealedCoinInfo *coin
= &rr->details.success.coins[i];
struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i]; struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
const union TALER_DenominationBlindingKeyP *bks; const union TALER_DenominationBlindingKeyP *bks;
rrs->psa[i] = coin->ps;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_TESTING_get_trait_denom_pub (melt_cmd, TALER_TESTING_get_trait_denom_pub (melt_cmd,
i, i,
@ -449,10 +448,10 @@ reveal_cb (void *cls,
TALER_TESTING_interpreter_fail (rrs->is); TALER_TESTING_interpreter_fail (rrs->is);
return; return;
} }
fc->coin_priv = coin_privs[i]; fc->coin_priv = coin->coin_priv;
fc->blinding_key = *bks; fc->blinding_key = *bks;
TALER_denom_sig_deep_copy (&fc->sig, TALER_denom_sig_deep_copy (&fc->sig,
&sigs[i]); &coin->sig);
} }
if (0 != rrs->total_backoff.rel_value_us) if (0 != rrs->total_backoff.rel_value_us)
{ {