aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2023-07-20 18:15:33 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2023-07-20 18:15:33 +0200
commitdb9fb04edcdafd0052a5652084ede7ca75885cef (patch)
tree0dce53e9324ce5d2bba77fd00ecda280d4a1e3f3 /src
parent2f8e3d31e7872e696e14ead5b264ada4e2500dde (diff)
parent430bb0624ae60e78c3e9855bd7827cc407471871 (diff)
downloadexchange-db9fb04edcdafd0052a5652084ede7ca75885cef.tar.gz
exchange-db9fb04edcdafd0052a5652084ede7ca75885cef.tar.bz2
exchange-db9fb04edcdafd0052a5652084ede7ca75885cef.zip
Merge branch 'age-withdraw', after implementing testing-commands
Diffstat (limited to 'src')
-rw-r--r--src/include/taler_exchange_service.h137
-rw-r--r--src/include/taler_testing_lib.h4
-rw-r--r--src/lib/exchange_api_age_withdraw.c97
-rw-r--r--src/lib/exchange_api_age_withdraw_reveal.c111
-rw-r--r--src/lib/exchange_api_common.c2
-rw-r--r--src/lib/notizen.md236
-rw-r--r--src/testing/Makefile.am1
-rw-r--r--src/testing/testing_api_cmd_age_withdraw.c684
-rw-r--r--src/testing/testing_api_cmd_batch_withdraw.c8
-rw-r--r--src/testing/testing_api_cmd_common.c14
-rw-r--r--src/testing/testing_api_loop.c3
11 files changed, 1046 insertions, 251 deletions
diff --git a/src/include/taler_exchange_service.h b/src/include/taler_exchange_service.h
index 1902eb298..7bd1b3248 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1514,6 +1514,11 @@ enum TALER_EXCHANGE_ReserveTransactionType
TALER_EXCHANGE_RTT_WITHDRAWAL,
/**
+ * Age-Withdrawal from the reserve.
+ */
+ TALER_EXCHANGE_RTT_AGEWITHDRAWAL,
+
+ /**
* /recoup operation.
*/
TALER_EXCHANGE_RTT_RECOUP,
@@ -1609,6 +1614,28 @@ struct TALER_EXCHANGE_ReserveHistoryEntry
} withdraw;
/**
+ * Information about withdraw operation.
+ * @e type is #TALER_EXCHANGE_RTT_AGEWITHDRAWAL.
+ */
+ struct
+ {
+ /**
+ * Signature authorizing the withdrawal for outgoing transaction.
+ */
+ json_t *out_authorization_sig;
+
+ /**
+ * Maximum age commited
+ */
+ uint8_t max_age;
+
+ /**
+ * Fee that was charged for the withdrawal.
+ */
+ struct TALER_Amount fee;
+ } age_withdraw;
+
+ /**
* Information provided if the reserve was filled via /recoup.
* @e type is #TALER_EXCHANGE_RTT_RECOUP.
*/
@@ -2670,13 +2697,59 @@ struct TALER_EXCHANGE_AgeWithdrawCoinInput
* The master secret from which we derive all other relevant values for
* the coin: private key, nonces (if applicable) and age restriction
*/
- const struct TALER_PlanchetMasterSecretP secrets[TALER_CNC_KAPPA];
+ struct TALER_PlanchetMasterSecretP secrets[TALER_CNC_KAPPA];
/**
* The denomination of the coin. Must support age restriction, i.e
* its .keys.age_mask MUST not be 0
*/
- const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+ struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
+};
+
+
+/**
+ * All the details about a coin that are generated during age-withdrawal and
+ * that may be needed for future operations on the coin.
+ */
+struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails
+{
+ /**
+ * Private key of the coin.
+ */
+ struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+ /**
+ * Hash of the public key of the coin.
+ */
+ struct TALER_CoinPubHashP h_coin_pub;
+
+ /**
+ * Value used to blind the key for the signature.
+ * Needed for recoup operations.
+ */
+ union TALER_DenominationBlindingKeyP blinding_key;
+
+ /**
+ * The age commitment, proof for the coin, derived from the
+ * Master secret and maximum age in the originating request
+ */
+ struct TALER_AgeCommitmentProof age_commitment_proof;
+
+ /**
+ * The hash of the age commitment
+ */
+ struct TALER_AgeCommitmentHash h_age_commitment;
+
+ /**
+ * Values contributed from the exchange during the
+ * withdraw protocol.
+ */
+ struct TALER_ExchangeWithdrawValues alg_values;
+
+ /**
+ * The planchet constructed
+ */
+ struct TALER_PlanchetDetail planchet;
};
/**
@@ -2705,39 +2778,38 @@ struct TALER_EXCHANGE_AgeWithdrawResponse
struct
{
/**
- * Index that should not be revealed during the age-withdraw reveal phase.
- * The struct TALER_PlanchetMasterSecretP * from the request
- * with this index are the ones to keep.
+ * Index that should not be revealed during the age-withdraw reveal
+ * phase.
*/
uint8_t noreveal_index;
/**
- * The commitment of the call to /age-withdraw
+ * The commitment of the age-withdraw request, needed for the
+ * subsequent call to /age-withdraw/$ACH/reveal
*/
struct TALER_AgeWithdrawCommitmentHashP h_commitment;
/**
- * The algorithm specific values (for CS) need for the coins that were
- * retrieved from /csr-withdraw.
+ * The number of elements in @e coins, each referring to
+ * TALER_CNC_KAPPA elements
*/
- struct TALER_ExchangeWithdrawValues *alg_values;
+ size_t num_coins;
/**
- * Number of elements in @e alg_values, same as number coin candidates.from
- * the request.
+ * The computed details of the non-revealed @e num_coins coins to keep.
*/
- size_t num_alg_values;
+ const struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails *coin_details;
/**
- * Signature of the exchange over the origina TALER_AgeWithdrawRequestPS
+ * The array of blinded hashes of the non-revealed
+ * @e num_coins coins, needed for the reveal step;
*/
- struct TALER_ExchangeSignatureP exchange_sig;
+ const struct TALER_BlindedCoinHashP *blinded_coin_hs;
/**
- * Key used by the exchange for @e exchange_sig
+ * Key used by the exchange to sign the response.
*/
struct TALER_ExchangePublicKeyP exchange_pub;
-
} ok;
} details;
};
@@ -2809,7 +2881,6 @@ struct TALER_EXCHANGE_AgeWithdrawBlindedInput
* Blinded Planchets
*/
struct TALER_PlanchetDetail planchet_details[TALER_CNC_KAPPA];
-
};
/**
@@ -2840,19 +2911,16 @@ struct TALER_EXCHANGE_AgeWithdrawBlindedResponse
uint8_t noreveal_index;
/**
- * The commitment of the call to /age-withdraw
+ * The commitment of the call to age-withdraw, needed for the subsequent
+ * call to /age-withdraw/$ACH/reveal.
*/
struct TALER_AgeWithdrawCommitmentHashP h_commitment;
/**
- * Signature of the exchange over the origina TALER_AgeWithdrawRequestPS
- */
- struct TALER_ExchangeSignatureP exchange_sig;
-
- /**
- * Key used by the exchange for @e exchange_sig
+ * Key used by the exchange to sign the response.
*/
struct TALER_ExchangePublicKeyP exchange_pub;
+
} ok;
} details;
@@ -2959,17 +3027,17 @@ struct TALER_EXCHANGE_AgeWithdrawRevealResponse
struct
{
/**
- * Number of coins returned.
+ * Number of signatures returned.
*/
- unsigned int num_coins;
+ unsigned int num_sigs;
/**
- * Array of @e num_coins values about the coins obtained via the reveal
- * operation. The array give these coins in the same order (and should
- * have the same length) in which the original age-withdraw request
- * specified the respective denomination keys.
+ * Array of @e num_coins blinded denomination signatures, giving each
+ * coin its value and validity. The array give these coins in the same
+ * order (and should have the same length) in which the original
+ * age-withdraw request specified the respective denomination keys.
*/
- const struct TALER_EXCHANGE_RevealedCoinInfo *revealed_coins;
+ const struct TALER_BlindedDenominationSignature *blinded_denom_sigs;
} ok;
} details;
@@ -2994,10 +3062,8 @@ typedef void
* @param exchange_url The base url of the exchange
* @param num_coins The number of elements in @e coin_inputs and @e alg_values
* @param coin_inputs The input for the coins to withdraw, same as in the previous call to /age-withdraw
- * @param alg_values The algorithm specific parameters per coin, from the result to the previous call to /age-withdraw
* @param noreveal_index The index into each of the kappa coin candidates, that should not be revealed to the exchange
* @param h_commitment The commmitment from the previous call to /age-withdraw
- * @param max_age maximum age, as used in the to /age-withdraw
* @param res_cb A callback for the result, maybe NULL
* @param res_cb_cls A closure for @e res_cb, maybe NULL
* @return a handle for this request; NULL if the argument was invalid.
@@ -3010,10 +3076,8 @@ TALER_EXCHANGE_age_withdraw_reveal (
size_t num_coins,
const struct TALER_EXCHANGE_AgeWithdrawCoinInput coin_inputs[static
num_coins],
- const struct TALER_ExchangeWithdrawValues alg_values[static num_coins],
uint8_t noreveal_index,
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
- uint8_t max_age,
TALER_EXCHANGE_AgeWithdrawRevealCallback res_cb,
void *res_cb_cls);
@@ -3034,7 +3098,8 @@ TALER_EXCHANGE_age_withdraw_reveal_cancel (
/**
* Information needed to melt (partially spent) coins to obtain fresh coins
* that are unlinkable to the original coin(s). Note that melting more than
- * one coin in a single request will make those coins linkable, so we only melt one coin at a time.
+ * one coin in a single request will make those coins linkable, so we only melt
+ * one coin at a time.
*/
struct TALER_EXCHANGE_RefreshData
{
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 3cd0b145f..c99f7c3b3 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -2715,8 +2715,8 @@ TALER_TESTING_get_trait (const struct TALER_TESTING_Trait *traits,
op (refund_deadline, const struct GNUNET_TIME_Timestamp) \
op (exchange_pub, const struct TALER_ExchangePublicKeyP) \
op (exchange_sig, const struct TALER_ExchangeSignatureP) \
- op (blinding_key, const union TALER_DenominationBlindingKeyP)
-
+ op (blinding_key, const union TALER_DenominationBlindingKeyP) \
+ op (h_blinded_coin, const struct TALER_BlindedCoinHashP)
TALER_TESTING_SIMPLE_TRAITS (TALER_TESTING_MAKE_DECL_SIMPLE_TRAIT)
diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c
index b5da232ed..c68fe67d2 100644
--- a/src/lib/exchange_api_age_withdraw.c
+++ b/src/lib/exchange_api_age_withdraw.c
@@ -47,36 +47,9 @@ struct CoinCandidate
struct TALER_PlanchetMasterSecretP secret;
/**
- * Age commitment for the coin candidates, calculated from the @e ps and a
- * given maximum age
+ * The details derived form the master secrets
*/
- struct TALER_AgeCommitmentProof age_commitment_proof;
-
- /**
- * Age commitment for the coin.
- */
- struct TALER_AgeCommitmentHash h_age_commitment;
-
- /**
- * blinding secret
- */
- union TALER_DenominationBlindingKeyP blinding_key;
-
- /**
- * Private key of the coin we are withdrawing.
- */
- struct TALER_CoinSpendPrivateKeyP coin_priv;
-
- /**
- * Values of the @cipher selected
- */
- struct TALER_ExchangeWithdrawValues alg_values;
-
- /**
- * Hash of the public key of the coin we are signing.
- */
- struct TALER_CoinPubHashP h_coin_pub;
-
+ struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details;
/**
* Blinded hash of the coin
@@ -340,11 +313,12 @@ reserve_age_withdraw_ok (
.hr.http_status = MHD_HTTP_OK,
.details.ok.h_commitment = awbh->h_commitment
};
+ struct TALER_ExchangeSignatureP exchange_sig;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_uint8 ("noreaveal_index",
&response.details.ok.noreveal_index),
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
- &response.details.ok.exchange_sig),
+ &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
&response.details.ok.exchange_pub)
};
@@ -363,7 +337,7 @@ reserve_age_withdraw_ok (
&awbh->h_commitment,
response.details.ok.noreveal_index,
&response.details.ok.exchange_pub,
- &response.details.ok.exchange_sig))
+ &exchange_sig))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
@@ -785,21 +759,26 @@ copy_results (
{
struct TALER_EXCHANGE_AgeWithdrawHandle *awh = cls;
uint8_t idx = awbr->details.ok.noreveal_index;
- struct TALER_ExchangeWithdrawValues alg_values[awh->num_coins];
+ struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details[awh->num_coins];
+ struct TALER_BlindedCoinHashP blinded_coin_hs[awh->num_coins];
struct TALER_EXCHANGE_AgeWithdrawResponse resp = {
.hr = awbr->hr,
.details = {
.ok = { .noreveal_index = awbr->details.ok.noreveal_index,
.h_commitment = awbr->details.ok.h_commitment,
.exchange_pub = awbr->details.ok.exchange_pub,
- .exchange_sig = awbr->details.ok.exchange_sig,
- .num_alg_values = awh->num_coins,
- .alg_values = alg_values},
+ .num_coins = awh->num_coins,
+ .coin_details = details,
+ .blinded_coin_hs = blinded_coin_hs},
},
};
for (size_t n = 0; n< awh->num_coins; n++)
- alg_values[n] = awh->coin_data[n].coin_candidates[idx].alg_values;
+ {
+ details[n] = awh->coin_data[n].coin_candidates[idx].details;
+ details[n].planchet = awh->coin_data[n].planchet_details[idx];
+ blinded_coin_hs[n] = awh->coin_data[n].coin_candidates[idx].blinded_coin_h;
+ }
awh->callback (awh->callback_cls,
&resp);
@@ -915,23 +894,23 @@ csr_withdraw_done (
{
bool success = false;
/* Complete the initialization of the coin with CS denomination */
- can->alg_values = csrr->details.ok.alg_values;
+ can->details.alg_values = csrr->details.ok.alg_values;
TALER_planchet_setup_coin_priv (&can->secret,
- &can->alg_values,
- &can->coin_priv);
+ &can->details.alg_values,
+ &can->details.coin_priv);
TALER_planchet_blinding_secret_create (&can->secret,
- &can->alg_values,
- &can->blinding_key);
+ &can->details.alg_values,
+ &can->details.blinding_key);
/* This initializes the 2nd half of the
can->planchet_detail.blinded_planchet! */
do {
if (GNUNET_OK !=
TALER_planchet_prepare (&csr->denom_pub->key,
- &can->alg_values,
- &can->blinding_key,
- &can->coin_priv,
- &can->h_age_commitment,
- &can->h_coin_pub,
+ &can->details.alg_values,
+ &can->details.blinding_key,
+ &can->details.coin_priv,
+ &can->details.h_age_commitment,
+ &can->details.h_coin_pub,
planchet))
{
GNUNET_break (0);
@@ -1029,29 +1008,29 @@ prepare_coins (
&can->secret,
&input->denom_pub->key.age_mask,
awh->max_age,
- &can->age_commitment_proof));
+ &can->details.age_commitment_proof));
- TALER_age_commitment_hash (&can->age_commitment_proof.commitment,
- &can->h_age_commitment);
+ TALER_age_commitment_hash (&can->details.age_commitment_proof.commitment,
+ &can->details.h_age_commitment);
switch (input->denom_pub->key.cipher)
{
case TALER_DENOMINATION_RSA:
{
- can->alg_values.cipher = TALER_DENOMINATION_RSA;
+ can->details.alg_values.cipher = TALER_DENOMINATION_RSA;
TALER_planchet_setup_coin_priv (&can->secret,
- &can->alg_values,
- &can->coin_priv);
+ &can->details.alg_values,
+ &can->details.coin_priv);
TALER_planchet_blinding_secret_create (&can->secret,
- &can->alg_values,
- &can->blinding_key);
+ &can->details.alg_values,
+ &can->details.blinding_key);
FAIL_IF (GNUNET_OK !=
TALER_planchet_prepare (&cd->denom_pub.key,
- &can->alg_values,
- &can->blinding_key,
- &can->coin_priv,
- &can->h_age_commitment,
- &can->h_coin_pub,
+ &can->details.alg_values,
+ &can->details.blinding_key,
+ &can->details.coin_priv,
+ &can->details.h_age_commitment,
+ &can->details.h_coin_pub,
planchet));
FAIL_IF (GNUNET_OK !=
TALER_coin_ev_hash (&planchet->blinded_planchet,
diff --git a/src/lib/exchange_api_age_withdraw_reveal.c b/src/lib/exchange_api_age_withdraw_reveal.c
index fcb551a9e..75707a4e4 100644
--- a/src/lib/exchange_api_age_withdraw_reveal.c
+++ b/src/lib/exchange_api_age_withdraw_reveal.c
@@ -47,19 +47,12 @@ struct TALER_EXCHANGE_AgeWithdrawRevealHandle
/* The age-withdraw commitment */
struct TALER_AgeWithdrawCommitmentHashP h_commitment;
- /* The maximum age */
- uint8_t max_age;
-
/* Number of coins */
size_t num_coins;
/* The @e num_coins * kappa coin secrets from the age-withdraw commitment */
const struct TALER_EXCHANGE_AgeWithdrawCoinInput *coins_input;
- /* The @e num_coins algorithm- and coin-specific parameters from the
- * previous call to /age-withdraw. */
- const struct TALER_ExchangeWithdrawValues *alg_values;
-
/* The url for the reveal request */
const char *request_url;
@@ -81,82 +74,6 @@ struct TALER_EXCHANGE_AgeWithdrawRevealHandle
};
-static enum GNUNET_GenericReturnValue
-reveal_coin (
- const struct TALER_PlanchetMasterSecretP *secret,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_ExchangeWithdrawValues *alg_values,
- const struct TALER_BlindedDenominationSignature *blind_sig,
- uint8_t max_age,
- struct TALER_EXCHANGE_RevealedCoinInfo *revealed_coin)
-{
- enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
-
-#define BREAK_ON_FAILURE(call) \
- do { \
- if (GNUNET_OK != (call)) { GNUNET_break (0); break; } \
- } while(0)
-
- do {
- struct TALER_CoinPubHashP h_coin_pub;
- struct TALER_PlanchetDetail planchet_detail;
- const struct TALER_AgeCommitmentHash *hac = NULL;
- struct TALER_FreshCoin fresh_coin;
-
- TALER_planchet_setup_coin_priv (secret,
- alg_values,
- &revealed_coin->coin_priv);
-
- TALER_planchet_blinding_secret_create (secret,
- alg_values,
- &revealed_coin->bks);
-
- revealed_coin->age_commitment_proof = NULL;
- if (0 < max_age)
- {
- BREAK_ON_FAILURE (
- TALER_age_restriction_from_secret (
- secret,
- &denom_pub->age_mask,
- max_age,
- revealed_coin->age_commitment_proof));
-
- TALER_age_commitment_hash (
- &revealed_coin->age_commitment_proof->commitment,
- &revealed_coin->h_age_commitment);
-
- hac = &revealed_coin->h_age_commitment;
- }
-
- BREAK_ON_FAILURE (
- TALER_planchet_prepare (denom_pub,
- alg_values,
- &revealed_coin->bks,
- &revealed_coin->coin_priv,
- hac,
- &h_coin_pub,
- &planchet_detail));
-
- BREAK_ON_FAILURE (
- TALER_planchet_to_coin (denom_pub,
- blind_sig,
- &revealed_coin->bks,
- &revealed_coin->coin_priv,
- &revealed_coin->h_age_commitment,
- &h_coin_pub,
- alg_values,
- &fresh_coin));
-
- /* success */
- revealed_coin->sig = fresh_coin.sig;
- ret = GNUNET_OK;
- } while(0);
-
- return ret;
-#undef BREAK_ON_FAILURE
-}
-
-
/**
* We got a 200 OK response for the /age-withdraw/$ACH/reveal operation.
* Extract the signed blindedcoins and return it to the caller.
@@ -197,16 +114,14 @@ age_withdraw_reveal_ok (
}
{
- struct TALER_EXCHANGE_RevealedCoinInfo revealed_coins[awrh->num_coins];
+ struct TALER_BlindedDenominationSignature denom_sigs[awrh->num_coins];
/* Reconstruct the coins and unblind the signatures */
for (size_t n = 0; n < awrh->num_coins; n++)
{
- enum GNUNET_GenericReturnValue ret;
- struct TALER_BlindedDenominationSignature blinded_sig;
json_t *j_sig = json_array_get (j_sigs, n);
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("", &blinded_sig),
+ GNUNET_JSON_spec_fixed_auto ("", &denom_sigs[n]),
GNUNET_JSON_spec_end ()
};
@@ -218,19 +133,10 @@ age_withdraw_reveal_ok (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
- ret = reveal_coin (&awrh->coins_input[n].secrets[awrh->noreveal_index],
- &awrh->coins_input[n].denom_pub->key,
- &awrh->alg_values[n],
- &blinded_sig,
- awrh->max_age,
- &revealed_coins[n]);
-
- if (GNUNET_OK != ret)
- return ret;
}
- response.details.ok.num_coins = awrh->num_coins;
- response.details.ok.revealed_coins = revealed_coins;
+ response.details.ok.num_sigs = awrh->num_coins;
+ response.details.ok.blinded_denom_sigs = denom_sigs;
awrh->callback (awrh->callback_cls,
&response);
/* Make sure the callback isn't called again */
@@ -510,24 +416,19 @@ TALER_EXCHANGE_age_withdraw_reveal (
size_t num_coins,
const struct TALER_EXCHANGE_AgeWithdrawCoinInput coins_input[static
num_coins],
- const struct TALER_ExchangeWithdrawValues alg_values[static num_coins],
uint8_t noreveal_index,
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
- uint8_t max_age,
TALER_EXCHANGE_AgeWithdrawRevealCallback reveal_cb,
void *reveal_cb_cls)
{
struct TALER_EXCHANGE_AgeWithdrawRevealHandle *awrh =
GNUNET_new (struct TALER_EXCHANGE_AgeWithdrawRevealHandle);
awrh->noreveal_index = noreveal_index;
- awrh->callback = reveal_cb;
- awrh->callback_cls = reveal_cb_cls;
awrh->h_commitment = *h_commitment;
awrh->num_coins = num_coins;
awrh->coins_input = coins_input;
- awrh->alg_values = alg_values;
- awrh->max_age = max_age;
-
+ awrh->callback = reveal_cb;
+ awrh->callback_cls = reveal_cb_cls;
if (GNUNET_OK !=
prepare_url (exchange_url,
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index 337fbabaf..08ca4b4e5 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -765,6 +765,8 @@ TALER_EXCHANGE_free_reserve_history (
break;
case TALER_EXCHANGE_RTT_WITHDRAWAL:
break;
+ case TALER_EXCHANGE_RTT_AGEWITHDRAWAL:
+ break;
case TALER_EXCHANGE_RTT_RECOUP:
break;
case TALER_EXCHANGE_RTT_CLOSING:
diff --git a/src/lib/notizen.md b/src/lib/notizen.md
new file mode 100644
index 000000000..835fad9e2
--- /dev/null
+++ b/src/lib/notizen.md
@@ -0,0 +1,236 @@
+# Notes re: planchets and blinding
+
+## `TALER_denom_blind()`
+
+```
+Blind coin for blind signing with @a dk using blinding secret @a coin_bks.
+```
+
+- `@param[out] c_hash resulting hashed coin`
+- `@param[out] blinded_planchet planchet data to initialize`
+
+## `TALER_planchet_prepare()`
+
+Prepare a planchet for withdrawal. Creates and blinds a coin.
+
+- calls `TALER_denom_blind()!`
+- `@param[out] c_hash set to the hash of the public key of the coin (needed later)`
+- `@param[out] pd set to the planchet detail for TALER_MERCHANT_tip_pickup() and other withdraw operations, pd->blinded_planchet.cipher will be set to cipher from @a dk`
+
+
+## `TALER_coin_ev_hash`
+
+Compute the hash of a blinded coin.
+
+- `@param blinded_planchet blinded planchet`
+- `@param denom_hash hash of the denomination publick key`
+- `@param[out] bch where to write the hash, type struct TALER_BlindedCoinHashP`
+
+**Where is this called!?**
+
+```
+taler-exchange-httpd_refreshes_reveal.c
+605: TALER_coin_ev_hash (&rrc->blinded_planchet,
+
+taler-exchange-httpd_recoup.c
+290: TALER_coin_ev_hash (&blinded_planchet,
+
+taler-exchange-httpd_withdraw.c
+581: TALER_coin_ev_hash (&wc.blinded_planchet,
+
+taler-exchange-httpd_age-withdraw_reveal.c
+350: ret = TALER_coin_ev_hash (&detail.blinded_planchet,
+
+taler-exchange-httpd_recoup-refresh.c
+284: TALER_coin_ev_hash (&blinded_planchet,
+
+taler-exchange-httpd_age-withdraw.c
+279: ret = TALER_coin_ev_hash (&awc->coin_evs[c],
+884: TALER_coin_ev_hash (&awc->coin_evs[i],
+
+taler-exchange-httpd_batch-withdraw.c
+832: TALER_coin_ev_hash (&pc->blinded_planchet,
+```
+
+
+## `TALER_coin_pub_hash`
+
+Compute the hash of a coin.
+
+- `@param coin_pub public key of the coin`
+- `@param age_commitment_hash hash of the age commitment vector. NULL, if no age commitment was set`
+- `@param[out] coin_h where to write the hash`
+
+**Where is this called!?**
+
+### In `lib/crypto.c`, function `TALER_test_coin_valid`.
+
+```
+Check if a coin is valid; that is, whether the denomination key
+exists, is not expired, and the signature is correct.
+
+@param coin_public_info the coin public info to check for validity
+@param denom_pub denomination key, must match @a coin_public_info's `denom_pub_hash`
+@return #GNUNET_YES if the coin is valid,
+ #GNUNET_NO if it is invalid
+ #GNUNET_SYSERR if an internal error occurred
+```
+
+It then calls `TALER_denom_pub_verify` on the result of `TALER_coin_pub_hash` and the signature
+
+
+### In `util/denom.c`, function `TALER_denom_blind`
+
+## `TALER_EXCHANGE_batch_withdraw` vs `TALER_EXCHANGE_batch_withdraw2`
+
+### `TALER_EXCHANGE_batch_withdraw`
+
+```
+/**
+ * Withdraw multiple coins from the exchange using a /reserves/$RESERVE_PUB/batch-withdraw
+ * request. This API is typically used by a wallet to withdraw many coins from a
+ * reserve.
+ *
+ * Note that to ensure that no money is lost in case of hardware
+ * failures, the caller must have committed (most of) the arguments to
+ * disk before calling, and be ready to repeat the request with the
+ * same arguments in case of failures.
+ *
+ * @param curl_ctx The curl context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
+ * @param reserve_priv private key of the reserve to withdraw from
+ * @param wci_length number of entries in @a wcis
+ * @param wcis inputs that determine the planchets
+ * @param res_cb the callback to call when the final result for this request is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ * if the inputs are invalid (i.e. denomination key not with this exchange).
+ * In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_BatchWithdrawHandle *
+TALER_EXCHANGE_batch_withdraw (
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ unsigned int wci_length,
+ const struct TALER_EXCHANGE_WithdrawCoinInput wcis[static wci_length],
+ TALER_EXCHANGE_BatchWithdrawCallback res_cb,
+ void *res_cb_cls);
+```
+
+### `TALER_EXCHANGE_batch_withdraw2`
+
+```
+/**
+ * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/batch-withdraw
+ * request. This API is typically used by a merchant to withdraw a tip
+ * where the blinding factor is unknown to the merchant.
+ *
+ * Note that to ensure that no money is lost in case of hardware
+ * failures, the caller must have committed (most of) the arguments to
+ * disk before calling, and be ready to repeat the request with the
+ * same arguments in case of failures.
+ *
+ * @param curl_ctx The curl context to use
+ * @param exchange_url The base-URL of the exchange
+ * @param keys The /keys material from the exchange
+ * @param pds array of planchet details of the planchet to withdraw
+ * @param pds_length number of entries in the @a pds array
+ * @param reserve_priv private key of the reserve to withdraw from
+ * @param res_cb the callback to call when the final result for this request is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ * if the inputs are invalid (i.e. denomination key not with this exchange).
+ * In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_BatchWithdraw2Handle *
+TALER_EXCHANGE_batch_withdraw2 (
+ struct GNUNET_CURL_Context *curl_ctx,
+ const char *exchange_url,
+ const struct TALER_EXCHANGE_Keys *keys,
+ const struct TALER_ReservePrivateKeyP *reserve_priv,
+ unsigned int pds_length,
+ const struct TALER_PlanchetDetail pds[static pds_length],
+ TALER_EXCHANGE_BatchWithdraw2Callback res_cb,
+ void *res_cb_cls);
+```
+
+### Differences
+
+| batch_withdraw | batch_withdraw2 |
+|------------------------------------|------------------------|
+| `TALER_EXCHANGE_WithdrawCoinInput` | `TALER_PlanchetDetail` |
+
+
+```
+struct TALER_EXCHANGE_WithdrawCoinInput
+{
+ /**
+ * Denomination of the coin.
+ */
+ const struct TALER_EXCHANGE_DenomPublicKey *pk;
+
+ /**
+ * Master key material for the coin.
+ */
+ const struct TALER_PlanchetMasterSecretP *ps;
+
+ /**
+ * Age commitment for the coin.
+ */
+ const struct TALER_AgeCommitmentHash *ach;
+
+};
+```
+
+
+```
+struct TALER_PlanchetDetail
+{
+ /**
+ * Hash of the denomination public key.
+ */
+ struct TALER_DenominationHashP denom_pub_hash;
+
+ /**
+ * The blinded planchet
+ */
+ struct TALER_BlindedPlanchet {
+ /**
+ * Type of the sign blinded message
+ */
+ enum TALER_DenominationCipher cipher;
+
+ /**
+ * Details, depending on @e cipher.
+ */
+ union
+ {
+ /**
+ * If we use #TALER_DENOMINATION_CS in @a cipher.
+ */
+ struct TALER_BlindedCsPlanchet cs_blinded_planchet;
+
+ /**
+ * If we use #TALER_DENOMINATION_RSA in @a cipher.
+ */
+ struct TALER_BlindedRsaPlanchet rsa_blinded_planchet;
+
+ } details;
+ } blinded_planchet;
+};
+
+```
+
+
+
+## TODOs
+
+### Update documentation
+
+- [x] batch-withdraw needs error code for AgeRestrictionRequired
+- [x] withdraw needs error code for AgeRestrictionRequired
+
+
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index c659f0ac6..068d94590 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -40,6 +40,7 @@ libtalertesting_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalertesting_la_SOURCES = \
+ testing_api_cmd_age_withdraw.c \
testing_api_cmd_auditor_add_denom_sig.c \
testing_api_cmd_auditor_add.c \
testing_api_cmd_auditor_del.c \
diff --git a/src/testing/testing_api_cmd_age_withdraw.c b/src/testing/testing_api_cmd_age_withdraw.c
index 56e65a990..edf292970 100644
--- a/src/testing/testing_api_cmd_age_withdraw.c
+++ b/src/testing/testing_api_cmd_age_withdraw.c
@@ -23,20 +23,76 @@
*/
#include "platform.h"
+#include "taler_exchange_service.h"
#include "taler_json_lib.h"
+#include <gnunet/gnunet_common.h>
#include <microhttpd.h>
#include <gnunet/gnunet_curl_lib.h>
#include "taler_signatures.h"
#include "taler_extensions.h"
#include "taler_testing_lib.h"
+/*
+ * The output state of coin
+ */
+struct CoinOutputState
+{
+
+ /**
+ * The calculated details during "age-withdraw", for the selected coin.
+ */
+ struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails details;
+
+ /**
+ * The (wanted) value of the coin, MUST be the same as input.denom_pub.value;
+ */
+ struct TALER_Amount amount;
+
+ /**
+ * Reserve history entry that corresponds to this coin.
+ * Will be of type #TALER_EXCHANGE_RTT_AGEWITHDRAWAL.
+ */
+ struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
+};
+
/**
* State for a "age withdraw" CMD:
*/
struct AgeWithdrawState
{
- /*
+
+ /**
+ * Interpreter state (during command)
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * The age-withdraw handle
+ */
+ struct TALER_EXCHANGE_AgeWithdrawHandle *handle;
+
+ /**
+ * Exchange base URL. Only used as offered trait.
+ */
+ char *exchange_url;
+
+ /**
+ * URI of the reserve we are withdrawing from.
+ */
+ char *reserve_payto_uri;
+
+ /**
+ * Private key of the reserve we are withdrawing from.
+ */
+ struct TALER_ReservePrivateKeyP reserve_priv;
+
+ /**
+ * Public key of the reserve we are withdrawing from.
+ */
+ struct TALER_ReservePublicKeyP reserve_pub;
+
+ /**
* Which reserve should we withdraw from?
*/
const char *reserve_reference;
@@ -47,6 +103,11 @@ struct AgeWithdrawState
unsigned int expected_response_code;
/**
+ * Age mask
+ */
+ struct TALER_AgeMask mask;
+
+ /**
* The maximum age we commit to
*/
uint8_t max_age;
@@ -55,8 +116,338 @@ struct AgeWithdrawState
* Number of coins to withdraw
*/
size_t num_coins;
+
+ /**
+ * The @e num_coins input that is provided to the
+ * `TALER_EXCHANGE_age_withdraw` API.
+ * Each contains kappa secrets, from which we will have
+ * to disclose kappa-1 in a subsequent age-withdraw-reveal operation.
+ */
+ struct TALER_EXCHANGE_AgeWithdrawCoinInput *coin_inputs;
+
+ /**
+ * The output state of @e num_coins coins, calculated during the
+ * "age-withdraw" operation.
+ */
+ struct CoinOutputState *coin_outputs;
+
+ /**
+ * The index returned by the exchange for the "age-withdraw" operation,
+ * of the kappa coin candidates that we do not disclose and keep.
+ */
+ uint8_t noreveal_index;
+
+ /**
+ * The blinded hashes of the non-revealed (to keep) @e num_coins coins.
+ */
+ const struct TALER_BlindedCoinHashP *blinded_coin_hs;
+
+ /**
+ * The hash of the commitment, needed for the reveal step.
+ */
+ struct TALER_AgeWithdrawCommitmentHashP h_commitment;
+
+ /**
+ * Set to the KYC requirement payto hash *if* the exchange replied with a
+ * request for KYC.
+ */
+ struct TALER_PaytoHashP h_payto;
+
+ /**
+ * Set to the KYC requirement row *if* the exchange replied with
+ * a request for KYC.
+ */
+ uint64_t requirement_row;
+
};
+/**
+ * Callback for the "age-withdraw" ooperation; It checks that the response
+ * code is expected and store the exchange signature in the state.
+ *
+ * @param cls Closure of type `struct AgeWithdrawState *`
+ * @param awr Repsonse details
+ */
+static void
+age_withdraw_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_AgeWithdrawResponse *response)
+{
+ struct AgeWithdrawState *aws = cls;
+ struct TALER_TESTING_Interpreter *is = aws->is;
+
+ aws->handle = NULL;
+ if (aws->expected_response_code != response->hr.http_status)
+ {
+ TALER_TESTING_unexpected_status_with_body (is,
+ response->hr.http_status,
+ aws->expected_response_code,
+ response->hr.reply);
+ return;
+ }
+
+ switch (response->hr.http_status)
+ {
+ case MHD_HTTP_OK:
+ aws->noreveal_index = response->details.ok.noreveal_index;
+ aws->h_commitment = response->details.ok.h_commitment;
+
+ GNUNET_assert (aws->num_coins == response->details.ok.num_coins);
+ for (size_t n = 0; n < aws->num_coins; n++)
+ {
+ aws->coin_outputs[n].details = response->details.ok.coin_details[n];
+ TALER_age_commitment_proof_deep_copy (
+ &response->details.ok.coin_details[n].age_commitment_proof,
+ &aws->coin_outputs[n].details.age_commitment_proof);
+ }
+ aws->blinded_coin_hs = response->details.ok.blinded_coin_hs;
+ break;
+ case MHD_HTTP_FORBIDDEN:
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_GONE:
+ /* nothing to check */
+ break;
+ case MHD_HTTP_CONFLICT:
+ /* TODO[oec]: Add this to the response-type and handle it here */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Age withdraw test command does not YET support status code %u\n",
+ response->hr.http_status);
+ break;
+ case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
+ /* TODO[oec]: Add this to response-type and handle it here */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Age withdraw test command does not YET support status code %u\n",
+ response->hr.http_status);
+ break;
+ default:
+ /* Unsupported status code (by test harness) */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Age withdraw test command does not support status code %u\n",
+ response->hr.http_status);
+ GNUNET_break (0);
+ break;
+ }
+
+ /* We are done with this command, pick the next one */
+ TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Run the command for age-withdraw.
+ */
+static void
+age_withdraw_run (
+ void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct AgeWithdrawState *aws = cls;
+ struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is);
+ const struct TALER_ReservePrivateKeyP *rp;
+ const struct TALER_TESTING_Command *create_reserve;
+ const struct TALER_EXCHANGE_DenomPublicKey *dpk;
+
+ aws->is = is;
+
+ /* Prepare the reserve related data */
+ create_reserve
+ = TALER_TESTING_interpreter_lookup_command (
+ is,
+ aws->reserve_reference);
+
+ if (NULL == create_reserve)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (GNUNET_OK !=
+ TALER_TESTING_get_trait_reserve_priv (create_reserve,
+ &rp))
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ if (NULL == aws->exchange_url)
+ aws->exchange_url
+ = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
+ aws->reserve_priv = *rp;
+ GNUNET_CRYPTO_eddsa_key_get_public (&aws->reserve_priv.eddsa_priv,
+ &aws->reserve_pub.eddsa_pub);
+ aws->reserve_payto_uri
+ = TALER_reserve_make_payto (aws->exchange_url,
+ &aws->reserve_pub);
+
+ aws->coin_inputs = GNUNET_new_array (
+ aws->num_coins,
+ struct TALER_EXCHANGE_AgeWithdrawCoinInput);
+
+ for (unsigned int i = 0; i<aws->num_coins; i++)
+ {
+ struct TALER_EXCHANGE_AgeWithdrawCoinInput *input = &aws->coin_inputs[i];
+ struct CoinOutputState *cos = &aws->coin_outputs[i];
+
+ /* randomly create the secrets for the kappa coin-candidates */
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+ &input->secrets,
+ sizeof(input->secrets));
+ /* Find denomination */
+ dpk = TALER_TESTING_find_pk (keys,
+ &cos->amount,
+ true); /* _always_ use denominations with age-striction */
+ if (NULL == dpk)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to determine denomination key at %s\n",
+ (NULL != cmd) ? cmd->label : "<retried command>");
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+ /* We copy the denomination key, as re-querying /keys
+ * would free the old one. */
+ input->denom_pub = TALER_EXCHANGE_copy_denomination_key (dpk);
+ cos->reserve_history.type = TALER_EXCHANGE_RTT_AGEWITHDRAWAL;
+ GNUNET_assert (0 <=
+ TALER_amount_add (&cos->reserve_history.amount,
+ &cos->amount,
+ &input->denom_pub->fees.withdraw));
+ cos->reserve_history.details.withdraw.fee = input->denom_pub->fees.withdraw;
+ }
+
+ /* Execute the age-withdraw protocol */
+ aws->handle =
+ TALER_EXCHANGE_age_withdraw (
+ TALER_TESTING_interpreter_get_context (is),
+ keys,
+ TALER_TESTING_get_exchange_url (is),
+ rp,
+ aws->num_coins,
+ aws->coin_inputs,
+ aws->max_age,
+ &age_withdraw_cb,
+ aws);
+
+ if (NULL == aws->handle)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ return;
+ }
+}
+
+
+/**
+ * Free the state of a "age withdraw" CMD, and possibly cancel a
+ * pending operation thereof
+ *
+ * @param cls Closure of type `struct AgeWithdrawState`
+ * @param cmd The command beeing freed.
+ */
+static void
+age_withdraw_cleanup (
+ void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct AgeWithdrawState *aws = cls;
+
+ if (NULL != aws->handle)
+ {
+ TALER_TESTING_command_incomplete (aws->is,
+ cmd->label);
+ TALER_EXCHANGE_age_withdraw_cancel (aws->handle);
+ aws->handle = NULL;
+ }
+
+ for (size_t n = 0; n < aws->num_coins; n++)
+ {
+ struct TALER_EXCHANGE_AgeWithdrawCoinInput *in = &aws->coin_inputs[n];
+ struct CoinOutputState *out = &aws->coin_outputs[n];
+
+ if (NULL != in->denom_pub)
+ {
+ TALER_EXCHANGE_destroy_denomination_key (in->denom_pub);
+ in->denom_pub = NULL;
+ }
+ TALER_age_commitment_proof_free (&out->details.age_commitment_proof);
+ }
+ GNUNET_free (aws->coin_inputs);
+ GNUNET_free (aws->coin_outputs);
+ GNUNET_free (aws->exchange_url);
+ GNUNET_free (aws->reserve_payto_uri);
+ GNUNET_free (aws);
+}
+
+
+/**
+ * Offer internal data of a "age withdraw" CMD state to other commands.
+ *
+ * @param cls Closure of type `struct AgeWithdrawState`
+ * @param[out] ret result (could be anything)
+ * @param trait name of the trait
+ * @param idx index number of the object to offer.
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+age_withdraw_traits (
+ void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int idx)
+{
+ struct AgeWithdrawState *aws = cls;
+ uint8_t k = aws->noreveal_index;
+ struct TALER_EXCHANGE_AgeWithdrawCoinInput *in = &aws->coin_inputs[idx];
+ struct CoinOutputState *out = &aws->coin_outputs[idx];
+ struct TALER_EXCHANGE_AgeWithdrawCoinPrivateDetails *details =
+ &aws->coin_outputs[idx].details;
+ struct TALER_TESTING_Trait traits[] = {
+ /* history entry MUST be first due to response code logic below! */
+ TALER_TESTING_make_trait_reserve_history (idx,
+ &out->reserve_history),
+ TALER_TESTING_make_trait_denom_pub (idx,
+ in->denom_pub),
+ TALER_TESTING_make_trait_reserve_priv (&aws->reserve_priv),
+ TALER_TESTING_make_trait_reserve_pub (&aws->reserve_pub),
+ TALER_TESTING_make_trait_amounts (idx,
+ &out->amount),
+ /* TODO[oec]: add legal requirement to response and handle it here, as well
+ TALER_TESTING_make_trait_legi_requirement_row (&aws->requirement_row),
+ TALER_TESTING_make_trait_h_payto (&aws->h_payto),
+ */
+ TALER_TESTING_make_trait_h_blinded_coin (idx,
+ &aws->blinded_coin_hs[idx]),
+ TALER_TESTING_make_trait_payto_uri (aws->reserve_payto_uri),
+ TALER_TESTING_make_trait_exchange_url (aws->exchange_url),
+ TALER_TESTING_make_trait_coin_priv (idx,
+ &details->coin_priv),
+ TALER_TESTING_make_trait_planchet_secrets (idx,
+ &in->secrets[k]),
+ TALER_TESTING_make_trait_blinding_key (idx,
+ &details->blinding_key),
+ TALER_TESTING_make_trait_exchange_wd_value (idx,
+ &details->alg_values),
+ TALER_TESTING_make_trait_age_commitment_proof (
+ idx,
+ &details->age_commitment_proof),
+ TALER_TESTING_make_trait_h_age_commitment (
+ idx,
+ &details->h_age_commitment),
+ };
+
+ if (idx >= aws->num_coins)
+ return GNUNET_NO;
+
+ return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK)
+ ? &traits[0] /* we have reserve history */
+ : &traits[1], /* skip reserve history */
+ ret,
+ trait,
+ idx);
+}
+
struct TALER_TESTING_Command
TALER_TESTING_cmd_age_withdraw (const char *label,
@@ -71,60 +462,27 @@ TALER_TESTING_cmd_age_withdraw (const char *label,
va_list ap;
aws = GNUNET_new (struct AgeWithdrawState);
- aws->max_age = max_age;
aws->reserve_reference = reserve_reference;
aws->expected_response_code = expected_response_code;
+ aws->mask = TALER_extensions_get_age_restriction_mask ();
+ aws->max_age = TALER_get_lowest_age (&aws->mask, max_age);
cnt = 1;
va_start (ap, amount);
while (NULL != (va_arg (ap, const char *)))
cnt++;
aws->num_coins = cnt;
- aws->coins = GNUNET_new_array (cnt,
- struct CoinState);
+ aws->coin_outputs = GNUNET_new_array (cnt,
+ struct CoinOutputState);
va_end (ap);
va_start (ap, amount);
- for (unsigned int i = 0; i<ws->num_coins; i++)
- {
- struct CoinState *cs = &ws->coins[i];
-
- if (0 < age)
- {
- struct TALER_AgeCommitmentProof *acp;
- struct TALER_AgeCommitmentHash *hac;
- struct GNUNET_HashCode seed;
- struct TALER_AgeMask mask;
-
- acp = GNUNET_new (struct TALER_AgeCommitmentProof);
- hac = GNUNET_new (struct TALER_AgeCommitmentHash);
- mask = TALER_extensions_get_age_restriction_mask ();
- GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
- &seed,
- sizeof(seed));
-
- if (GNUNET_OK !=
- TALER_age_restriction_commit (
- &mask,
- age,
- &seed,
- acp))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to generate age commitment for age %d at %s\n",
- age,
- label);
- GNUNET_assert (0);
- }
-
- TALER_age_commitment_hash (&acp->commitment,
- hac);
- cs->age_commitment_proof = acp;
- cs->h_age_commitment = hac;
- }
+ for (unsigned int i = 0; i<aws->num_coins; i++)
+ {
+ struct CoinOutputState *out = &aws->coin_outputs[i];
if (GNUNET_OK !=
TALER_string_to_amount (amount,
- &cs->amount))
+ &out->amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %s\n",
@@ -132,19 +490,20 @@ TALER_TESTING_cmd_age_withdraw (const char *label,
label);
GNUNET_assert (0);
}
- /* move on to next vararg! */
- amount = va_arg (ap, const char *);
}
+
+ /* move on to next vararg! */
+ amount = va_arg (ap, const char *);
GNUNET_assert (NULL == amount);
va_end (ap);
{
struct TALER_TESTING_Command cmd = {
- .cls = ws,
+ .cls = aws,
.label = label,
.run = &age_withdraw_run,
.cleanup = &age_withdraw_cleanup,
- .traits = &age_withdraw_traits
+ .traits = &age_withdraw_traits,
};
return cmd;
@@ -152,4 +511,237 @@ TALER_TESTING_cmd_age_withdraw (const char *label,
}
+/**
+ * The state for the age-withdraw-reveal operation
+ */
+struct AgeWithdrawRevealState
+{
+ /**
+ * The reference to the CMD resembling the previous call to age-withdraw
+ */
+ const char *age_withdraw_reference;
+
+ /**
+ * The state to the previous age-withdraw command
+ */
+ const struct AgeWithdrawState *aws;
+
+ /**
+ * The expected response code from the call to the
+ * age-withdraw-reveal operation
+ */
+ unsigned int expected_response_code;
+
+ /**
+ * Interpreter state (during command)
+ */
+ struct TALER_TESTING_Interpreter *is;
+
+ /**
+ * The handle to the reveal-operation
+ */
+ struct TALER_EXCHANGE_AgeWithdrawRevealHandle *handle;
+
+
+ /**
+ * Number of coins, extracted form the age withdraw command
+ */
+ size_t num_coins;
+
+ /**
+ * The signatures of the @e num_coins coins returned
+ */
+ struct TALER_DenominationSignature *denom_sigs;
+
+};
+
+/*
+ * Callback for the reveal response
+ *
+ * @param cls Closure of type `struct AgeWithdrawRevealState`
+ * @param awr The response
+ */
+static void
+age_withdraw_reveal_cb (
+ void *cls,
+ const struct TALER_EXCHANGE_AgeWithdrawRevealResponse *response)
+{
+ struct AgeWithdrawRevealState *awrs = cls;
+ struct TALER_TESTING_Interpreter *is = awrs->is;
+
+ awrs->handle = NULL;
+ if (awrs->expected_response_code != response->hr.http_status)
+ {
+ TALER_TESTING_unexpected_status_with_body (is,
+ response->hr.http_status,
+ awrs->expected_response_code,
+ response->hr.reply);
+ return;
+ }
+ switch (response->hr.http_status)
+ {
+ case MHD_HTTP_OK:
+ {
+ const struct AgeWithdrawState *aws = awrs->aws;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Got age-withdraw reveal success!\n");
+ GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs);
+ for (size_t n = 0; n < awrs->num_coins; n++)
+ TALER_denom_sig_unblind (&awrs->denom_sigs[n],
+ &response->details.ok.blinded_denom_sigs[n],
+ &aws->coin_outputs[n].details.blinding_key,
+ &aws->coin_outputs[n].details.h_coin_pub,
+ &aws->coin_outputs[n].details.alg_values,
+ &aws->coin_inputs[n].denom_pub->key);
+ }
+ break;
+ case MHD_HTTP_NOT_FOUND:
+ case MHD_HTTP_FORBIDDEN:
+ /* nothing to check */
+ break;
+ /* TODO[oec]: handle more cases !? */
+ default:
+ /* Unsupported status code (by test harness) */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Age withdraw reveal test command does not support status code %u\n",
+ response->hr.http_status);
+ GNUNET_break (0);
+ break;
+ }
+
+ /* We are done with this command, pick the next one */
+ TALER_TESTING_interpreter_next (is);
+}
+
+
+/**
+ * Run the command for age-withdraw-reveal
+ */
+static void
+age_withdraw_reveal_run (
+ void *cls,
+ const struct TALER_TESTING_Command *cmd,
+ struct TALER_TESTING_Interpreter *is)
+{
+ struct AgeWithdrawRevealState *awrs = cls;
+ const struct TALER_TESTING_Command *age_withdraw_cmd;
+ const struct AgeWithdrawState *aws;
+
+ (void) cmd;
+ awrs->is = is;
+
+ /*
+ * Get the command and state for the previous call to "age witdraw"
+ */
+ age_withdraw_cmd =
+ TALER_TESTING_interpreter_get_command (is,
+ awrs->age_withdraw_reference);
+ if (NULL == age_withdraw_cmd)
+ {
+ GNUNET_break (0);
+ TALER_TESTING_interpreter_fail (is);
+ }
+ GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run);
+ aws = age_withdraw_cmd->cls;
+ awrs->aws = aws;
+ awrs->num_coins = aws->num_coins;
+
+ awrs->handle =
+ TALER_EXCHANGE_age_withdraw_reveal (
+ TALER_TESTING_interpreter_get_context (is),
+ TALER_TESTING_get_exchange_url (is),
+ aws->num_coins,
+ aws->coin_inputs,
+ aws->noreveal_index,
+ &aws->h_commitment,
+ age_withdraw_reveal_cb,
+ awrs);
+}
+
+
+/**
+ * Free the state of a "age-withdraw-reveal" CMD, and possibly
+ * cancel a pending operation thereof
+ *
+ * @param cls Closure of type `struct AgeWithdrawRevealState`
+ * @param cmd The command being freed.
+ */
+static void
+age_withdraw_reveal_cleanup (
+ void *cls,
+ const struct TALER_TESTING_Command *cmd)
+{
+ struct AgeWithdrawRevealState *awrs = cls;
+
+ if (NULL != awrs->handle)
+ {
+ TALER_TESTING_command_incomplete (awrs->is,
+ cmd->label);
+ TALER_EXCHANGE_age_withdraw_reveal_cancel (awrs->handle);
+ awrs->handle = NULL;
+ }
+ GNUNET_free (awrs->denom_sigs);
+ awrs->denom_sigs = NULL;
+ GNUNET_free (awrs);
+}
+
+
+/**
+ * Offer internal data of a "age withdraw reveal" CMD state to other commands.
+ *
+ * @param cls Closure of they `struct AgeWithdrawRevealState`
+ * @param[out] ret result (could be anything)
+ * @param trait name of the trait
+ * @param idx index number of the object to offer.
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+age_withdraw_reveal_traits (
+ void *cls,
+ const void **ret,
+ const char *trait,
+ unsigned int idx)
+{
+ struct AgeWithdrawRevealState *awrs = cls;
+ struct TALER_TESTING_Trait traits[] = {
+ TALER_TESTING_make_trait_denom_sig (idx,
+ &awrs->denom_sigs[idx]),
+ /* FIXME: shall we provide the traits from the previous
+ * call to "age withdraw" as well? */
+ };
+
+ if (idx >= awrs->num_coins)
+ return GNUNET_NO;
+
+ return TALER_TESTING_get_trait (traits,
+ ret,
+ trait,
+ idx);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_age_withdraw_reveal (
+ const char *label,
+ const char *age_withdraw_reference,
+ unsigned int expected_response_code)
+{
+ struct AgeWithdrawRevealState *awrs =
+ GNUNET_new (struct AgeWithdrawRevealState);
+
+ awrs->age_withdraw_reference = age_withdraw_reference;
+ awrs->expected_response_code = expected_response_code;
+
+ struct TALER_TESTING_Command cmd = {
+ .cls = awrs,
+ .label = label,
+ .run = age_withdraw_reveal_run,
+ .cleanup = age_withdraw_reveal_cleanup,
+ .traits = age_withdraw_reveal_traits,
+ };
+
+ return cmd;
+}
+
+
/* end of testing_api_cmd_age_withdraw.c */
diff --git a/src/testing/testing_api_cmd_batch_withdraw.c b/src/testing/testing_api_cmd_batch_withdraw.c
index da43a9aa9..41c74c3e2 100644
--- a/src/testing/testing_api_cmd_batch_withdraw.c
+++ b/src/testing/testing_api_cmd_batch_withdraw.c
@@ -23,6 +23,7 @@
* @author Marcello Stanisci
*/
#include "platform.h"
+#include "taler_exchange_service.h"
#include "taler_json_lib.h"
#include <microhttpd.h>
#include <gnunet/gnunet_curl_lib.h>
@@ -217,7 +218,7 @@ reserve_batch_withdraw_cb (void *cls,
/* nothing to check */
break;
case MHD_HTTP_CONFLICT:
- /* nothing to check */
+ /* TODO[oec]: Check if age-requirement is the reason */
break;
case MHD_HTTP_GONE:
/* theoretically could check that the key was actually */
@@ -250,6 +251,7 @@ batch_withdraw_run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct BatchWithdrawState *ws = cls;
+ const struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is);
const struct TALER_ReservePrivateKeyP *rp;
const struct TALER_TESTING_Command *create_reserve;
const struct TALER_EXCHANGE_DenomPublicKey *dpk;
@@ -292,7 +294,7 @@ batch_withdraw_run (void *cls,
struct TALER_EXCHANGE_WithdrawCoinInput *wci = &wcis[i];
TALER_planchet_master_setup_random (&cs->ps);
- dpk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (is),
+ dpk = TALER_TESTING_find_pk (keys,
&cs->amount,
ws->age > 0);
if (NULL == dpk)
@@ -321,7 +323,7 @@ batch_withdraw_run (void *cls,
ws->wsh = TALER_EXCHANGE_batch_withdraw (
TALER_TESTING_interpreter_get_context (is),
TALER_TESTING_get_exchange_url (is),
- TALER_TESTING_get_keys (is),
+ keys,
rp,
ws->num_coins,
wcis,
diff --git a/src/testing/testing_api_cmd_common.c b/src/testing/testing_api_cmd_common.c
index 91138f361..2c29f4ec2 100644
--- a/src/testing/testing_api_cmd_common.c
+++ b/src/testing/testing_api_cmd_common.c
@@ -59,6 +59,20 @@ TALER_TESTING_history_entry_cmp (
that should be good enough. */
return 0;
return 1;
+ case TALER_EXCHANGE_RTT_AGEWITHDRAWAL:
+ /* testing_api_cmd_age_withdraw doesn't set the out_authorization_sig,
+ so we cannot test for it here. but if the amount matches,
+ that should be good enough. */
+ if ( (0 ==
+ TALER_amount_cmp (&h1->amount,
+ &h2->amount)) &&
+ (0 ==
+ TALER_amount_cmp (&h1->details.age_withdraw.fee,
+ &h2->details.age_withdraw.fee)) &&
+ (h1->details.age_withdraw.max_age ==
+ h2->details.age_withdraw.max_age))
+ return 0;
+ return 1;
case TALER_EXCHANGE_RTT_RECOUP:
/* exchange_sig, exchange_pub and timestamp are NOT available
from the original recoup response, hence here NOT check(able/ed) */
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index 2a8e3d0b7..781028bf1 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -31,6 +31,9 @@
#include "taler_testing_lib.h"
+/**
+ * The interpreter and its state
+ */
struct TALER_TESTING_Interpreter
{