-modify link API to return 'ps' so that linked coins can be refreshed

This commit is contained in:
Christian Grothoff 2022-02-12 13:39:58 +01:00
parent 730d8c893c
commit 4d26042b5a
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
3 changed files with 181 additions and 84 deletions

View File

@ -1701,6 +1701,60 @@ TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
/* ********************* /refreshes/$RCH/reveal ***************************** */ /* ********************* /refreshes/$RCH/reveal ***************************** */
/**
* Information about a coin obtained via /refreshes/$RCH/reveal.
*/
struct TALER_EXCHANGE_RevealedCoinInfo
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Master secret of this coin.
*/
struct TALER_PlanchetMasterSecretP ps;
/**
* Signature affirming the validity of the coin.
*/
struct TALER_DenominationSignature sig;
};
/**
* Result of a /refreshes/$RCH/reveal request.
*/
struct TALER_EXCHANGE_RevealResult
{
/**
* HTTP status.
*/
struct TALER_EXCHANGE_HttpResponse hr;
union
{
struct
{
/**
* Array of @e num_coins values about the
* coins obtained via the refresh operation.
*/
const struct TALER_EXCHANGE_RevealedCoinInfo *coins;
/**
* Number of coins returned.
*/
unsigned int num_coins;
} success;
} details;
};
/** /**
* 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
@ -1786,6 +1840,64 @@ TALER_EXCHANGE_refreshes_reveal_cancel (
struct TALER_EXCHANGE_LinkHandle; struct TALER_EXCHANGE_LinkHandle;
/**
* Information about a coin obtained via /link.
*/
struct TALER_EXCHANGE_LinkedCoinInfo
{
/**
* Private key of the coin.
*/
struct TALER_CoinSpendPrivateKeyP coin_priv;
/**
* Master secret of this coin.
*/
struct TALER_PlanchetMasterSecretP ps;
/**
* Signature affirming the validity of the coin.
*/
struct TALER_DenominationSignature sig;
/**
* Denomination public key of the coin.
*/
struct TALER_DenominationPublicKey pub;
};
/**
* Result of a /link request.
*/
struct TALER_EXCHANGE_LinkResult
{
/**
* HTTP status.
*/
struct TALER_EXCHANGE_HttpResponse hr;
union
{
struct
{
/**
* Array of @e num_coins values about the
* coins obtained via linkage.
*/
const struct TALER_EXCHANGE_LinkedCoinInfo *coins;
/**
* Number of coins returned.
*/
unsigned int num_coins;
} success;
} details;
};
/** /**
* Callbacks of this type are used to return the final result of submitting a * Callbacks of this type are used to return the final result of submitting a
* /coins/$COIN_PUB/link request to a exchange. If the operation was * /coins/$COIN_PUB/link request to a exchange. If the operation was
@ -1793,20 +1905,12 @@ struct TALER_EXCHANGE_LinkHandle;
* created when the original coin was melted. * created when the original coin was melted.
* *
* @param cls closure * @param cls closure
* @param hr HTTP response data * @param lr result of the /link operation
* @param num_coins number of fresh coins created, length of the @a sigs 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 sigs array of signature over @a num_coins coins, NULL on error
* @param pubs array of public keys for the @a sigs, NULL on error
*/ */
typedef void typedef void
(*TALER_EXCHANGE_LinkCallback) ( (*TALER_EXCHANGE_LinkCallback) (
void *cls, void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_LinkResult *lr);
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs,
const struct TALER_DenominationPublicKey *pubs);
/** /**

View File

@ -77,9 +77,7 @@ struct TALER_EXCHANGE_LinkHandle
* @param json json reply with the data for one coin * @param json json reply with the data for one coin
* @param coin_num number of the coin * @param coin_num number of the coin
* @param trans_pub our transfer public key * @param trans_pub our transfer public key
* @param[out] coin_priv where to return private coin key * @param[out] lci where to return coin details
* @param[out] sig where to return private coin signature
* @param[out] pub where to return the public key for the coin
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
@ -87,9 +85,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
const json_t *json, const json_t *json,
uint32_t coin_num, uint32_t coin_num,
const struct TALER_TransferPublicKeyP *trans_pub, const struct TALER_TransferPublicKeyP *trans_pub,
struct TALER_CoinSpendPrivateKeyP *coin_priv, struct TALER_EXCHANGE_LinkedCoinInfo *lci)
struct TALER_DenominationSignature *sig,
struct TALER_DenominationPublicKey *pub)
{ {
struct TALER_BlindedDenominationSignature bsig; struct TALER_BlindedDenominationSignature bsig;
struct TALER_DenominationPublicKey rpub; struct TALER_DenominationPublicKey rpub;
@ -111,7 +107,6 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
GNUNET_JSON_spec_end () GNUNET_JSON_spec_end ()
}; };
struct TALER_TransferSecretP secret; struct TALER_TransferSecretP secret;
struct TALER_PlanchetMasterSecretP ps;
struct TALER_PlanchetDetail pd; struct TALER_PlanchetDetail pd;
struct TALER_CoinPubHash c_hash; struct TALER_CoinPubHash c_hash;
@ -129,18 +124,18 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
&secret); &secret);
TALER_transfer_secret_to_planchet_secret (&secret, TALER_transfer_secret_to_planchet_secret (&secret,
coin_num, coin_num,
&ps); &lci->ps);
TALER_planchet_setup_coin_priv (&ps, TALER_planchet_setup_coin_priv (&lci->ps,
&alg_values, &alg_values,
coin_priv); &lci->coin_priv);
TALER_planchet_blinding_secret_create (&ps, TALER_planchet_blinding_secret_create (&lci->ps,
&alg_values, &alg_values,
&bks); &bks);
if (GNUNET_OK != if (GNUNET_OK !=
TALER_planchet_prepare (&rpub, TALER_planchet_prepare (&rpub,
&alg_values, &alg_values,
&bks, &bks,
coin_priv, &lci->coin_priv,
&c_hash, &c_hash,
&pd)) &pd))
{ {
@ -150,7 +145,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
} }
/* extract coin and signature */ /* extract coin and signature */
if (GNUNET_OK != if (GNUNET_OK !=
TALER_denom_sig_unblind (sig, TALER_denom_sig_unblind (&lci->sig,
&bsig, &bsig,
&bks, &bks,
&c_hash, &c_hash,
@ -186,7 +181,7 @@ parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
} }
/* clean up */ /* clean up */
TALER_denom_pub_deep_copy (pub, TALER_denom_pub_deep_copy (&lci->pub,
&rpub); &rpub);
GNUNET_JSON_parse_free (spec); GNUNET_JSON_parse_free (spec);
return GNUNET_OK; return GNUNET_OK;
@ -208,9 +203,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
unsigned int session; unsigned int session;
unsigned int num_coins; unsigned int num_coins;
int ret; int ret;
struct TALER_EXCHANGE_HttpResponse hr = { struct TALER_EXCHANGE_LinkResult lr = {
.reply = json, .hr.reply = json,
.http_status = MHD_HTTP_OK .hr.http_status = MHD_HTTP_OK
}; };
if (! json_is_array (json)) if (! json_is_array (json))
@ -263,12 +258,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
{ {
unsigned int off_coin; /* index into 1d array */ unsigned int off_coin; /* index into 1d array */
unsigned int i; unsigned int i;
struct TALER_CoinSpendPrivateKeyP coin_privs[GNUNET_NZL (num_coins)]; struct TALER_EXCHANGE_LinkedCoinInfo lcis[GNUNET_NZL (num_coins)];
struct TALER_DenominationSignature sigs[GNUNET_NZL (num_coins)];
struct TALER_DenominationPublicKey pubs[GNUNET_NZL (num_coins)];
memset (sigs, 0, sizeof (sigs)); memset (lcis, 0, sizeof (lcis));
memset (pubs, 0, sizeof (pubs));
off_coin = 0; off_coin = 0;
for (session = 0; session<json_array_size (json); session++) for (session = 0; session<json_array_size (json); session++)
{ {
@ -301,6 +293,9 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
/* decode all coins */ /* decode all coins */
for (i = 0; i<json_array_size (jsona); i++) for (i = 0; i<json_array_size (jsona); i++)
{ {
struct TALER_EXCHANGE_LinkedCoinInfo *lci;
lci = &lcis[i + off_coin];
GNUNET_assert (i + off_coin < num_coins); GNUNET_assert (i + off_coin < num_coins);
if (GNUNET_OK != if (GNUNET_OK !=
parse_link_coin (lh, parse_link_coin (lh,
@ -308,9 +303,7 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
i), i),
i, i,
&trans_pub, &trans_pub,
&coin_privs[i + off_coin], lci))
&sigs[i + off_coin],
&pubs[i + off_coin]))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
break; break;
@ -330,12 +323,10 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
if (off_coin == num_coins) if (off_coin == num_coins)
{ {
lr.details.success.num_coins = num_coins;
lr.details.success.coins = lcis;
lh->link_cb (lh->link_cb_cls, lh->link_cb (lh->link_cb_cls,
&hr, &lr);
num_coins,
coin_privs,
sigs,
pubs);
lh->link_cb = NULL; lh->link_cb = NULL;
ret = GNUNET_OK; ret = GNUNET_OK;
} }
@ -349,8 +340,8 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
GNUNET_assert (off_coin <= num_coins); GNUNET_assert (off_coin <= num_coins);
for (i = 0; i<off_coin; i++) for (i = 0; i<off_coin; i++)
{ {
TALER_denom_sig_free (&sigs[i]); TALER_denom_sig_free (&lcis[i].sig);
TALER_denom_pub_free (&pubs[i]); TALER_denom_pub_free (&lcis[i].pub);
} }
} }
return ret; return ret;
@ -372,16 +363,16 @@ handle_link_finished (void *cls,
{ {
struct TALER_EXCHANGE_LinkHandle *lh = cls; struct TALER_EXCHANGE_LinkHandle *lh = cls;
const json_t *j = response; const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = { struct TALER_EXCHANGE_LinkResult lr = {
.reply = j, .hr.reply = j,
.http_status = (unsigned int) response_code .hr.http_status = (unsigned int) response_code
}; };
lh->job = NULL; lh->job = NULL;
switch (response_code) switch (response_code)
{ {
case 0: case 0:
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; lr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
break; break;
case MHD_HTTP_OK: case MHD_HTTP_OK:
if (GNUNET_OK != if (GNUNET_OK !=
@ -389,49 +380,45 @@ handle_link_finished (void *cls,
j)) j))
{ {
GNUNET_break_op (0); GNUNET_break_op (0);
hr.http_status = 0; lr.hr.http_status = 0;
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; lr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
break; break;
} }
GNUNET_assert (NULL == lh->link_cb); GNUNET_assert (NULL == lh->link_cb);
TALER_EXCHANGE_link_cancel (lh); TALER_EXCHANGE_link_cancel (lh);
return; return;
case MHD_HTTP_BAD_REQUEST: case MHD_HTTP_BAD_REQUEST:
hr.ec = TALER_JSON_get_error_code (j); lr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); lr.hr.hint = TALER_JSON_get_error_hint (j);
/* 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 */
break; break;
case MHD_HTTP_NOT_FOUND: case MHD_HTTP_NOT_FOUND:
hr.ec = TALER_JSON_get_error_code (j); lr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); lr.hr.hint = TALER_JSON_get_error_hint (j);
/* Nothing really to verify, exchange says this coin was not melted; we /* Nothing really to verify, exchange says this coin was not melted; we
should pass the JSON reply to the application */ should pass the JSON reply to the application */
break; break;
case MHD_HTTP_INTERNAL_SERVER_ERROR: case MHD_HTTP_INTERNAL_SERVER_ERROR:
hr.ec = TALER_JSON_get_error_code (j); lr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); lr.hr.hint = TALER_JSON_get_error_hint (j);
/* 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 */
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); lr.hr.ec = TALER_JSON_get_error_code (j);
hr.hint = TALER_JSON_get_error_hint (j); lr.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 link\n", "Unexpected response code %u/%d for exchange link\n",
(unsigned int) response_code, (unsigned int) response_code,
(int) hr.ec); (int) lr.hr.ec);
break; break;
} }
if (NULL != lh->link_cb) if (NULL != lh->link_cb)
lh->link_cb (lh->link_cb_cls, lh->link_cb (lh->link_cb_cls,
&hr, &lr);
0,
NULL,
NULL,
NULL);
TALER_EXCHANGE_link_cancel (lh); TALER_EXCHANGE_link_cancel (lh);
} }

View File

@ -608,13 +608,10 @@ do_link_retry (void *cls)
*/ */
static void static void
link_cb (void *cls, link_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr, const struct TALER_EXCHANGE_LinkResult *lr)
unsigned int num_coins,
const struct TALER_CoinSpendPrivateKeyP *coin_privs,
const struct TALER_DenominationSignature *sigs,
const struct TALER_DenominationPublicKey *pubs)
{ {
struct RefreshLinkState *rls = cls; struct RefreshLinkState *rls = cls;
const struct TALER_EXCHANGE_HttpResponse *hr = &lr->hr;
const struct TALER_TESTING_Command *reveal_cmd; const struct TALER_TESTING_Command *reveal_cmd;
struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip]; struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip];
unsigned int found; unsigned int found;
@ -683,11 +680,11 @@ link_cb (void *cls,
TALER_TESTING_interpreter_fail (rls->is); TALER_TESTING_interpreter_fail (rls->is);
return; return;
} }
if (num_coins != *num_fresh_coins) if (lr->details.success.num_coins != *num_fresh_coins)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected number of fresh coins: %d vs %d in %s:%u\n", "Unexpected number of fresh coins: %d vs %d in %s:%u\n",
num_coins, lr->details.success.num_coins,
*num_fresh_coins, *num_fresh_coins,
__FILE__, __FILE__,
__LINE__); __LINE__);
@ -695,11 +692,11 @@ link_cb (void *cls,
return; return;
} }
/* check that the coins match */ /* check that the coins match */
for (unsigned int i = 0; i<num_coins; i++) for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
for (unsigned int j = i + 1; j<num_coins; j++) for (unsigned int j = i + 1; j<lr->details.success.num_coins; j++)
if (0 == if (0 ==
GNUNET_memcmp (&coin_privs[i], GNUNET_memcmp (&lr->details.success.coins[i].coin_priv,
&coin_privs[j])) &lr->details.success.coins[j].coin_priv))
GNUNET_break (0); GNUNET_break (0);
/* Note: coins might be legitimately permutated in here... */ /* Note: coins might be legitimately permutated in here... */
found = 0; found = 0;
@ -717,29 +714,38 @@ link_cb (void *cls,
return; return;
} }
for (unsigned int i = 0; i<num_coins; i++) for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
for (unsigned int j = 0; j<num_coins; j++) {
const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i
= &lr->details.success.coins[i];
for (unsigned int j = 0; j<lr->details.success.num_coins; j++)
{ {
const struct TALER_TESTING_FreshCoinData *fcj
= &(*fc)[j];
if ( (0 == if ( (0 ==
GNUNET_memcmp (&coin_privs[i], GNUNET_memcmp (&fcj->coin_priv,
&(*fc)[j].coin_priv)) && &lci_i->coin_priv)) &&
(0 == (0 ==
TALER_denom_sig_cmp (&(*fc)[i].sig, TALER_denom_sig_cmp (&fcj->sig,
&sigs[j])) && &lci_i->sig)) &&
(0 == (0 ==
TALER_denom_pub_cmp (&(*fc)[i].pk->key, TALER_denom_pub_cmp (&fcj->pk->key,
&pubs[j])) ) &lci_i->pub)) )
{ {
found++; found++;
break; break;
} }
} } /* for j*/
} /* for i */
} }
if (found != num_coins) if (found != lr->details.success.num_coins)
{ {
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Only %u/%u coins match expectations\n", "Only %u/%u coins match expectations\n",
found, num_coins); found,
lr->details.success.num_coins);
GNUNET_break (0); GNUNET_break (0);
TALER_TESTING_interpreter_fail (rls->is); TALER_TESTING_interpreter_fail (rls->is);
return; return;