exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit ea640ba2e9aebc71dbf6d368d7764e5ae6f1f3e9
parent d8cac4841d463da6083fa32e9160b10a9aa2ecb8
Author: Özgür Kesim <oec-taler@kesim.org>
Date:   Tue, 17 Dec 2024 13:03:38 +0100

[withdraw] structured error handling

The ad-hoc error responses in the withdraw handler has been changed
to set a error type and potential witness data in the context, and
change the next phase to generate an error response.

Error types are also marked if they require a idempotency check (and
returning success, if so) before generating an error response.

Fixes #9377

Diffstat:
Msrc/exchange/taler-exchange-httpd_withdraw.c | 1366+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/include/taler_crypto_lib.h | 1+
Msrc/lib/exchange_api_age_withdraw.c | 2+-
3 files changed, 818 insertions(+), 551 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c @@ -38,38 +38,10 @@ #include "taler-exchange-httpd_keys.h" #include "taler_util.h" - -/** - * Information per planchet in a batch withdraw. - */ -struct PlanchetContext -{ - - /** - * Value of the coin being exchanged (matching the denomination key) - * plus the transaction fee. We include this in what is being - * signed so that we can verify a reserve's remaining total balance - * without needing to access the respective denomination key - * information each time. - */ - struct TALER_Amount amount_with_fee; - - /** - * Blinded planchet. - */ - struct TALER_BlindedPlanchet blinded_planchet; - - /** - * Set to the resulting signed coin data to be returned to the client. - */ - struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; - -}; - /** - * Context for both, - * 1.) #batch_withdraw_transaction - * 2.) #age_withdraw_transaction + * Context for both types of requests + * 1.) #batch-withdraw + * 2.) #age-withdraw */ struct WithdrawContext { @@ -97,17 +69,17 @@ struct WithdrawContext */ enum { - WC_PHASE_CHECK_KEYS, - WC_PHASE_CHECK_RESERVE_SIGNATURE, - WC_PHASE_RUN_LEGI_CHECK, - WC_PHASE_SUSPENDED, - WC_PHASE_CHECK_KYC_RESULT, - WC_PHASE_PREPARE_TRANSACTION, - WC_PHASE_RUN_TRANSACTION, - WC_PHASE_GENERATE_REPLY_SUCCESS, - WC_PHASE_GENERATE_REPLY_FAILURE, - WC_PHASE_RETURN_NO, - WC_PHASE_RETURN_YES, + PHASE_CHECK_KEYS, + PHASE_CHECK_RESERVE_SIGNATURE, + PHASE_RUN_LEGI_CHECK, + PHASE_SUSPENDED, + PHASE_CHECK_KYC_RESULT, + PHASE_PREPARE_TRANSACTION, + PHASE_RUN_TRANSACTION, + PHASE_GENERATE_REPLY_SUCCESS, + PHASE_GENERATE_REPLY_ERROR, + PHASE_RETURN_NO, + PHASE_RETURN_YES, } phase; @@ -122,11 +94,6 @@ struct WithdrawContext const struct TEH_RequestContext *rc; /** - * Response to return, if set. - */ - struct MHD_Response *response; - - /** * Public key of the reserve. */ struct TALER_ReservePublicKeyP reserve_pub; @@ -148,10 +115,11 @@ struct WithdrawContext struct TALER_NormalizedPaytoHashP h_normalized_payto; /** - * HTTP status to return with @e response, or 0. - */ - unsigned int http_status; - + * Number of coins, depending on the @e withdraw_type: + * 1) WITHDRAW_TYPE_BATCH: #elements in the @e typ.batch.planchets array. + * 2) WITHDRAW_TYPE_AGE: #elements in the @e typ.age.planchets array + */ + unsigned int num_coins; /** * Depending on @e withdraw_type, this union @@ -167,19 +135,38 @@ struct WithdrawContext struct { /** - * Array of @e planchets_length planchets we are processing. + * Array of @e num_coins planchets we are processing, + * containing the information per planchet in a batch withdraw. */ - struct PlanchetContext *planchets; + struct PlanchetContext + { + + /** + * Value of the coin being exchanged (matching the denomination key) + * plus the transaction fee. We include this in what is being + * signed so that we can verify a reserve's remaining total balance + * without needing to access the respective denomination key + * information each time. + */ + struct TALER_Amount amount_with_fee; + + /** + * Blinded planchet. + */ + struct TALER_BlindedPlanchet blinded_planchet; + + /** + * Set to the resulting signed coin data to be returned to the client. + */ + struct TALER_EXCHANGEDB_CollectableBlindcoin collectable; + + } *planchets; /** * Total amount from all coins with fees. */ struct TALER_Amount batch_total; - /** - * Length of the @e planchets array. - */ - unsigned int planchets_length; } batch; /** @@ -198,14 +185,9 @@ struct WithdrawContext struct TALER_EXCHANGEDB_AgeWithdraw commitment; /** - * Number of coins/denonations in the reveal - */ - unsigned int num_coins; - - /** - * #num_coins * TALER_CNC_KAPPA hashes of blinded coin planchets. + * # @e num_coins * TALER_CNC_KAPPA hashes of blinded coin planchets. */ - struct TALER_BlindedPlanchet (*coin_evs) [ TALER_CNC_KAPPA]; + struct TALER_BlindedPlanchet (*planchets) [ TALER_CNC_KAPPA]; /** * #num_coins hashes of the denominations from which the coins are withdrawn. @@ -216,8 +198,164 @@ struct WithdrawContext } typ; + /** + * Errors occuring during evaluation of the request are captured in this struct. + * In phase PHASE_GENERATE_REPLY_ERROR an appropriate error message is prepared + * and sent to the client. + */ + struct + { + /** + * The different type of errors that might occur, sorted by name. + * Some of them require idempotency checks, which are marked + * in array @a needs_idempotency_check_error below. + */ + enum + { + ERROR_NONE, + + ERROR_AGE_AMOUNT_OVERFLOW, + ERROR_AGE_INSUFFICIENT_FUNDS, + ERROR_AGE_MAXIMUM_AGE_TOO_LARGE, + ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, + ERROR_AGE_RESTRICTION_REQUIRED, + ERROR_AGE_CONFIRMATION_SIGN, + ERROR_BATCH_AMOUNT_FEE_OVERFLOW, + ERROR_BATCH_IDEMPOTENT_PLANCHET, + ERROR_BATCH_INSUFFICIENT_FUNDS, + ERROR_BATCH_NONCE_RESUSE, + ERROR_CIPHER_MISMATCH, + ERROR_DB_FETCH_FAILED, + ERROR_DB_INVARIANT_FAILURE, + ERROR_DENOMINATION_BATCH_SIGN, + ERROR_DENOMINATION_EXPIRED, + ERROR_DENOMINATION_KEY_UNKNOWN, + ERROR_DENOMINATION_REVOKED, + ERROR_DENOMINATION_VALIDITY_IN_FUTURE, + ERROR_INTERNAL_INVARIANT_FAILURE, + ERROR_KEYS_MISSING, + ERROR_KYC_REQUIRED, + ERROR_LEGITIMIZATION_RESULT, + ERROR_RESERVE_SIGNATURE_INVALID, + ERROR_RESERVE_UNKNOWN, + ERROR_REQUEST_PARAMETER_MALFORMED, + + ERROR_MAX + } code; + + /** + * Some errors require details to be sent to the client. + * These are captured in this union. + * Each field is marked with a comment, referring to the error(s) + * that is/are using it. + */ + union + { + /* ERROR_REQUEST_PARAMETER_MALFORMED */ + const char *hint; + + /* ERROR_AGE_AMOUNT_OVERFLOW */ + const char *which; + + /* ERROR_DENOMINATION_KEY_UNKNOWN */ + const struct TALER_DenominationHashP *denom_h; + + /* ERROR_DB_FETCH_FAILED */ + const char *db_fetch_context; + + /* ERROR_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE */ + struct + { + uint16_t max; + uint32_t birthday; + } age; + + /* ERROR_AGE_RESTRICTION_REQUIRED */ + uint16_t lowest_age; + + /* ERROR_AGE_INSUFFICIENT_FUNDS */ + /* ERROR_BATCH_INSUFFICIENT_FUNDS */ + struct TALER_Amount reserve_balance; + + /* ERROR_AGE_CONFIRMATION_SIGN */ + /* ERROR_DENOMINATION_BATCH_SIGN */ + enum TALER_ErrorCode ec; + + /* ERROR_LEGITIMIZATION_RESULT */ + struct + { + struct MHD_Response *response; + unsigned int http_status; + } legi; + + } details; + } error; +}; + +/** + * This table marks which @a WithdrawContext.error.code + * needs a idempotency check prior to actually sending an error message. + */ +static const bool + needs_idempotency_check[] = { + [ERROR_NONE] = false, + + [ERROR_AGE_AMOUNT_OVERFLOW] = false, + [ERROR_AGE_CONFIRMATION_SIGN] = false, + [ERROR_AGE_MAXIMUM_AGE_TOO_LARGE] = false, + [ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION] = false, + [ERROR_AGE_RESTRICTION_REQUIRED] = false, + [ERROR_BATCH_AMOUNT_FEE_OVERFLOW] = false, + [ERROR_BATCH_NONCE_RESUSE] = false, + [ERROR_CIPHER_MISMATCH] = false, + [ERROR_DB_FETCH_FAILED] = false, + [ERROR_DB_INVARIANT_FAILURE] = false, + [ERROR_DENOMINATION_BATCH_SIGN] = false, + [ERROR_DENOMINATION_VALIDITY_IN_FUTURE] = false, + [ERROR_INTERNAL_INVARIANT_FAILURE] = false, + [ERROR_LEGITIMIZATION_RESULT] = false, + [ERROR_REQUEST_PARAMETER_MALFORMED] = false, + [ERROR_RESERVE_SIGNATURE_INVALID] = false, + [ERROR_RESERVE_UNKNOWN] = false, + + /* These require idempotency checks */ + [ERROR_AGE_INSUFFICIENT_FUNDS] = true, + [ERROR_BATCH_IDEMPOTENT_PLANCHET] = true, + [ERROR_BATCH_INSUFFICIENT_FUNDS] = true, + [ERROR_DENOMINATION_EXPIRED] = true, + [ERROR_DENOMINATION_KEY_UNKNOWN] = true, + [ERROR_DENOMINATION_REVOKED] = true, + [ERROR_KEYS_MISSING] = true, + [ERROR_KYC_REQUIRED] = true, }; +_Static_assert ( + (sizeof (needs_idempotency_check) == + ERROR_MAX), + "needs_idempotency_check size mismatch with enum WithdrawErrorCode"); + +/** + * The following macros set the given error code, + * set the phase to PHASE_GENERATE_REPLY_ERROR, + * and optionally set the given field (with an optionally given value). + */ +#define SET_ERROR(wc, ec) \ + do \ + { (wc)->error.code = (ec); \ + (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) + +#define SET_ERROR_WITH_FIELD(wc, ec, field) \ + do \ + { (wc)->error.code = (ec); \ + (wc)->error.details.field = (field); \ + (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) + +#define SET_ERROR_WITH_DETAIL(wc, ec, field, value) \ + do \ + { (wc)->error.code = (ec); \ + (wc)->error.details.field = (value); \ + (wc)->phase = PHASE_GENERATE_REPLY_ERROR; } while (0) + /** * All withdraw context is kept in a DLL. @@ -252,8 +390,8 @@ finish_loop (struct WithdrawContext *wc, MHD_RESULT mres) { wc->phase = (MHD_YES == mres) - ? WC_PHASE_RETURN_YES - : WC_PHASE_RETURN_NO; + ? PHASE_RETURN_YES + : PHASE_RETURN_NO; } @@ -263,14 +401,14 @@ finish_loop (struct WithdrawContext *wc, * @param wc operation context */ static void -batch_withdraw_generate_reply_success (struct WithdrawContext *wc) +batch_withdraw_phase_generate_reply_success (struct WithdrawContext *wc) { const struct TEH_RequestContext *rc = wc->rc; json_t *sigs; sigs = json_array (); GNUNET_assert (NULL != sigs); - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; @@ -283,7 +421,7 @@ batch_withdraw_generate_reply_success (struct WithdrawContext *wc) "ev_sig", &pc->collectable.sig)))); } - TEH_METRICS_batch_withdraw_num_coins += wc->typ.batch.planchets_length; + TEH_METRICS_batch_withdraw_num_coins += wc->num_coins; finish_loop (wc, TALER_MHD_REPLY_JSON_PACK ( rc->connection, @@ -294,75 +432,6 @@ batch_withdraw_generate_reply_success (struct WithdrawContext *wc) /** - * Send a response to a "age-withdraw" request. - * - * @param[in,out] wc context for the operation - */ -static void -age_withdraw_generate_reply_success ( - struct WithdrawContext *wc) -{ - struct MHD_Connection *connection - = wc->rc->connection; - const struct TALER_AgeWithdrawCommitmentHashP *ach - = &wc->typ.age.commitment.h_commitment; - uint32_t noreveal_index - = wc->typ.age.commitment.noreveal_index; - struct TALER_ExchangePublicKeyP pub; - struct TALER_ExchangeSignatureP sig; - enum TALER_ErrorCode ec; - - ec = TALER_exchange_online_age_withdraw_confirmation_sign ( - &TEH_keys_exchange_sign_, - ach, - noreveal_index, - &pub, - &sig); - if (TALER_EC_NONE != ec) - { - finish_loop (wc, - TALER_MHD_reply_with_ec (connection, - ec, - NULL)); - return; - } - - finish_loop (wc, - TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_uint64 ("noreveal_index", - noreveal_index), - GNUNET_JSON_pack_data_auto ("exchange_sig", - &sig), - GNUNET_JSON_pack_data_auto ("exchange_pub", - &pub))); -} - - -/** - * Generates response for the batch- or age-withdraw request. - * - * @param wc withdraw operation context - */ -static void -generate_reply_success (struct WithdrawContext *wc) -{ - switch (wc->withdraw_type) - { - case WITHDRAW_TYPE_BATCH: - batch_withdraw_generate_reply_success (wc); - break; - case WITHDRAW_TYPE_AGE: - age_withdraw_generate_reply_success (wc); - break; - default: - GNUNET_break (0); - } -} - - -/** * Check if the batch withdraw in @a wc is replayed * and we already have an answer. * If so, replay the existing answer and return the HTTP response. @@ -375,10 +444,9 @@ static bool batch_withdraw_check_idempotency ( struct WithdrawContext *wc) { - const struct TEH_RequestContext *rc = wc->rc; GNUNET_assert (wc->withdraw_type == WITHDRAW_TYPE_BATCH); - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; enum GNUNET_DB_QueryStatus qs; @@ -392,21 +460,20 @@ batch_withdraw_check_idempotency ( { /* FIXME: soft error not handled correctly! */ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "get_withdraw_info")); - return true; + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "get_withdraw_info"); + return true; /* Well, kind-of. */ } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) return false; pc->collectable = collectable; } + /* generate idempotent reply */ TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++; - wc->phase = WC_PHASE_GENERATE_REPLY_SUCCESS; + wc->phase = PHASE_GENERATE_REPLY_SUCCESS; return true; } @@ -438,11 +505,10 @@ age_withdraw_check_idempotency ( /* FIXME: soft error not handled correctly! */ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (GNUNET_DB_STATUS_HARD_ERROR == qs) - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "get_age_withdraw")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "get_age_withdraw"); return true; /* Well, kind-of. */ } if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) @@ -450,7 +516,7 @@ age_withdraw_check_idempotency ( /* Generate idempotent reply */ TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_AGE_WITHDRAW]++; - wc->phase = WC_PHASE_GENERATE_REPLY_SUCCESS; + wc->phase = PHASE_GENERATE_REPLY_SUCCESS; return true; } @@ -465,7 +531,7 @@ age_withdraw_check_idempotency ( * false if we did not find the request in the DB and did not set @a mret */ static bool -check_request_idempotent ( +check_idempotency ( struct WithdrawContext *wc) { switch (wc->withdraw_type) @@ -525,61 +591,53 @@ age_withdraw_transaction ( &reserve_birthday, &conflict); if (0 > qs) + { if (GNUNET_DB_STATUS_HARD_ERROR == qs) - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_age_withdraw")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "do_age_withdraw"); return qs; } + if (! found) { - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, - NULL)); + SET_ERROR (wc, + ERROR_RESERVE_UNKNOWN); return GNUNET_DB_STATUS_HARD_ERROR; } + if (! age_ok) { - finish_loop (wc, - TALER_MHD_REPLY_JSON_PACK ( - wc->rc->connection, - MHD_HTTP_CONFLICT, - TALER_MHD_PACK_EC ( - TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE), - GNUNET_JSON_pack_uint64 ( - "allowed_maximum_age", - allowed_maximum_age), - GNUNET_JSON_pack_uint64 ( - "reserve_birthday", - reserve_birthday))); + wc->error.details.age.max = allowed_maximum_age; + wc->error.details.age.birthday = reserve_birthday; + SET_ERROR (wc, + ERROR_AGE_MAXIMUM_AGE_TOO_LARGE); return GNUNET_DB_STATUS_HARD_ERROR; } + if (! balance_ok) { TEH_plugin->rollback (TEH_plugin->cls); - finish_loop (wc, - TEH_RESPONSE_reply_reserve_insufficient_balance ( - wc->rc->connection, - TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS, - &reserve_balance, - &wc->typ.age.commitment.amount_with_fee, - &wc->typ.age.commitment.reserve_pub)); + + SET_ERROR_WITH_FIELD (wc, + ERROR_AGE_INSUFFICIENT_FUNDS, + reserve_balance); + return GNUNET_DB_STATUS_HARD_ERROR; } + if (conflict) { /* do_age_withdraw signaled a conflict, so there MUST be an entry * in the DB. Put that into the response */ - if (check_request_idempotent (wc)) + if (check_idempotency (wc)) return GNUNET_DB_STATUS_HARD_ERROR; GNUNET_break (0); return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW]++; return qs; @@ -630,29 +688,25 @@ batch_withdraw_transaction ( &age_ok, &allowed_maximum_age, &ruuid); + if (0 > qs) { if (GNUNET_DB_STATUS_HARD_ERROR == qs) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "update_reserve_batch_withdraw")); - return qs; + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "update_reserve_batch_withdraw"); } return qs; } + if (! found) { - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, - NULL)); + GNUNET_break_op (0); + SET_ERROR (wc, + ERROR_RESERVE_UNKNOWN); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -663,30 +717,23 @@ batch_withdraw_transaction ( uint16_t lowest_age = TALER_get_lowest_age ( &TEH_age_restriction_config.mask, allowed_maximum_age); - - finish_loop (wc, - TEH_RESPONSE_reply_reserve_age_restriction_required ( - connection, - lowest_age)); + SET_ERROR_WITH_FIELD (wc, + ERROR_AGE_RESTRICTION_REQUIRED, + lowest_age); return GNUNET_DB_STATUS_HARD_ERROR; } if (! balance_ok) { - if (check_request_idempotent (wc)) - return GNUNET_DB_STATUS_HARD_ERROR; - finish_loop (wc, - TEH_RESPONSE_reply_reserve_insufficient_balance ( - connection, - TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS, - &reserve_balance, - &wc->typ.batch.batch_total, - &wc->reserve_pub)); + GNUNET_break_op (0); + SET_ERROR_WITH_FIELD (wc, + ERROR_BATCH_INSUFFICIENT_FUNDS, + reserve_balance); return GNUNET_DB_STATUS_HARD_ERROR; } /* Add information about each planchet in the batch */ - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; const struct TALER_BlindedPlanchet *bp = &pc->blinded_planchet; @@ -706,6 +753,7 @@ batch_withdraw_transaction ( &bp->blinded_message->details.cs_blinded_message.nonce; break; } + qs = TEH_plugin->do_batch_withdraw_insert ( TEH_plugin->cls, nonce, @@ -715,54 +763,39 @@ batch_withdraw_transaction ( &denom_unknown, &conflict, &nonce_reuse); + if (0 > qs) { if (GNUNET_DB_STATUS_HARD_ERROR == qs) - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_batch_withdraw_insert")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "do_batch_withdraw_insert"); return qs; } + if (denom_unknown) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_INVARIANT_FAILURE, - NULL)); + SET_ERROR (wc, + ERROR_DB_INVARIANT_FAILURE); return GNUNET_DB_STATUS_HARD_ERROR; } + if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) || (conflict) ) { - if (check_request_idempotent (wc)) - return GNUNET_DB_STATUS_HARD_ERROR; - /* We do not support *some* of the coins of the request being - idempotent while others being fresh. */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Idempotent coin in batch, not allowed. Aborting.\n"); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_CONFLICT, - TALER_EC_EXCHANGE_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET, - NULL)); + SET_ERROR (wc, + ERROR_BATCH_IDEMPOTENT_PLANCHET); return GNUNET_DB_STATUS_HARD_ERROR; } + if (nonce_reuse) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE, - NULL)); + SET_ERROR (wc, + ERROR_BATCH_NONCE_RESUSE); + return GNUNET_DB_STATUS_HARD_ERROR; } } @@ -778,13 +811,13 @@ batch_withdraw_transaction ( * @param wc The context for the current withdraw request */ static void -run_transaction ( +phase_run_transaction ( struct WithdrawContext *wc) { MHD_RESULT mhd_ret; enum GNUNET_GenericReturnValue qs; - GNUNET_assert (WC_PHASE_RUN_TRANSACTION == + GNUNET_assert (PHASE_RUN_TRANSACTION == wc->phase); switch (wc->withdraw_type) @@ -809,9 +842,11 @@ run_transaction ( GNUNET_break (0); qs = GNUNET_SYSERR; } + if (GNUNET_OK != qs) { - if (WC_PHASE_RUN_TRANSACTION == wc->phase) + /* TODO[oec]: Logic still ok with new error handling? */ + if (PHASE_RUN_TRANSACTION == wc->phase) finish_loop (wc, mhd_ret); return; @@ -828,13 +863,12 @@ run_transaction ( * @return GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -batch_withdraw_prepare_transaction (struct WithdrawContext *wc) +batch_withdraw_phase_prepare_transaction (struct WithdrawContext *wc) { - const struct TEH_RequestContext *rc = wc->rc; - struct TALER_BlindedDenominationSignature bss[wc->typ.batch.planchets_length]; - struct TEH_CoinSignData csds[wc->typ.batch.planchets_length]; + struct TALER_BlindedDenominationSignature bss[wc->num_coins]; + struct TEH_CoinSignData csds[wc->num_coins]; - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; struct TEH_CoinSignData *csdsi = &csds[i]; @@ -846,23 +880,21 @@ batch_withdraw_prepare_transaction (struct WithdrawContext *wc) enum TALER_ErrorCode ec; ec = TEH_keys_denomination_batch_sign ( - wc->typ.batch.planchets_length, + wc->num_coins, csds, false, bss); if (TALER_EC_NONE != ec) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - rc->connection, - ec, - NULL)); + SET_ERROR_WITH_FIELD (wc, + ERROR_DENOMINATION_BATCH_SIGN, + ec); return GNUNET_SYSERR; } } - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; @@ -881,18 +913,18 @@ batch_withdraw_prepare_transaction (struct WithdrawContext *wc) * @return GNUNET_OK on success */ static enum GNUNET_GenericReturnValue -age_withdraw_prepare_transaction ( +age_withdraw_phase_prepare_transaction ( struct WithdrawContext *wc) { uint8_t noreveal_index; wc->typ.age.commitment.denom_sigs = GNUNET_new_array ( - wc->typ.age.num_coins, + wc->num_coins, struct TALER_BlindedDenominationSignature); wc->typ.age.commitment.h_coin_evs = GNUNET_new_array ( - wc->typ.age.num_coins, + wc->num_coins, struct TALER_BlindedCoinHashP); /* Pick the challenge */ noreveal_index = @@ -902,39 +934,37 @@ age_withdraw_prepare_transaction ( /* Choose and sign the coins */ { - struct TEH_CoinSignData csds[wc->typ.age.num_coins]; + struct TEH_CoinSignData csds[wc->num_coins]; enum TALER_ErrorCode ec; /* Pick the chosen blinded coins */ - for (uint32_t i = 0; i<wc->typ.age.num_coins; i++) + for (uint32_t i = 0; i<wc->num_coins; i++) { struct TEH_CoinSignData *csdsi = &csds[i]; - csdsi->bp = &wc->typ.age.coin_evs[i][noreveal_index]; + csdsi->bp = &wc->typ.age.planchets[i][noreveal_index]; csdsi->h_denom_pub = &wc->typ.age.denom_hs[i]; } ec = TEH_keys_denomination_batch_sign ( - wc->typ.age.num_coins, + wc->num_coins, csds, false, wc->typ.age.commitment.denom_sigs); if (TALER_EC_NONE != ec) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - ec, - NULL)); + SET_ERROR_WITH_FIELD (wc, + ERROR_DENOMINATION_BATCH_SIGN, + ec); return GNUNET_SYSERR; } } /* Prepare the hashes of the coins for insertion */ - for (uint32_t i = 0; i<wc->typ.age.num_coins; i++) + for (uint32_t i = 0; i<wc->num_coins; i++) { - TALER_coin_ev_hash (&wc->typ.age.coin_evs[i][noreveal_index], + TALER_coin_ev_hash (&wc->typ.age.planchets[i][noreveal_index], &wc->typ.age.denom_hs[i], &wc->typ.age.commitment.h_coin_evs[i]); } @@ -947,17 +977,17 @@ age_withdraw_prepare_transaction ( * Choose the appropriate preparation step depending on @e withdraw_type */ static void -prepare_transaction ( +phase_prepare_transaction ( struct WithdrawContext *wc) { enum GNUNET_GenericReturnValue r; switch (wc->withdraw_type) { case WITHDRAW_TYPE_BATCH: - r = batch_withdraw_prepare_transaction (wc); + r = batch_withdraw_phase_prepare_transaction (wc); break; case WITHDRAW_TYPE_AGE: - r = age_withdraw_prepare_transaction (wc); + r = age_withdraw_phase_prepare_transaction (wc); break; default: GNUNET_break (0); @@ -975,24 +1005,13 @@ prepare_transaction ( * @param wc context for request processing */ static void -check_kyc_result (struct WithdrawContext *wc) +phase_check_kyc_result (struct WithdrawContext *wc) { /* return final positive response */ if (! wc->kyc.ok) { - if (check_request_idempotent (wc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Request is idempotent!\n"); - return; - } - /* KYC required */ - finish_loop (wc, - TEH_RESPONSE_reply_kyc_required ( - wc->rc->connection, - &wc->h_normalized_payto, - &wc->kyc, - false)); + SET_ERROR (wc, + ERROR_KYC_REQUIRED); return; } wc->phase++; @@ -1014,7 +1033,7 @@ withdraw_legi_cb ( struct WithdrawContext *wc = cls; wc->lch = NULL; - GNUNET_assert (WC_PHASE_SUSPENDED == + GNUNET_assert (PHASE_SUSPENDED == wc->phase); MHD_resume_connection (wc->rc->connection); GNUNET_CONTAINER_DLL_remove (wc_head, @@ -1023,13 +1042,14 @@ withdraw_legi_cb ( TALER_MHD_daemon_trigger (); if (NULL != lcr->response) { - wc->response = lcr->response; - wc->http_status = lcr->http_status; - wc->phase = WC_PHASE_GENERATE_REPLY_FAILURE; + wc->error.details.legi.response = lcr->response; + wc->error.details.legi.http_status = lcr->http_status; + SET_ERROR (wc, + ERROR_LEGITIMIZATION_RESULT); return; } wc->kyc = lcr->kyc; - wc->phase = WC_PHASE_CHECK_KYC_RESULT; + wc->phase = PHASE_CHECK_KYC_RESULT; } @@ -1107,24 +1127,31 @@ withdraw_amount_cb ( "Signaling amount %s for KYC check during %sal\n", TALER_amount2s (withdraw_amount_with_fee (wc)), typ2str (wc)); + ret = cb (cb_cls, withdraw_amount_with_fee (wc), wc->now.abs_time); + GNUNET_break (GNUNET_SYSERR != ret); + if (GNUNET_OK != ret) return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; + qs = TEH_plugin->select_withdraw_amounts_for_kyc_check ( TEH_plugin->cls, &wc->h_normalized_payto, limit, cb, cb_cls); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got %d additional transactions for this %sal and limit %llu\n", qs, typ2str (wc), (unsigned long long) limit.abs_value_us); + GNUNET_break (qs >= 0); + return qs; } @@ -1135,7 +1162,7 @@ withdraw_amount_cb ( * @param wc operation context */ static void -run_legi_check (struct WithdrawContext *wc) +phase_run_legi_check (struct WithdrawContext *wc) { enum GNUNET_DB_QueryStatus qs; struct TALER_FullPayto payto_uri; @@ -1149,12 +1176,10 @@ run_legi_check (struct WithdrawContext *wc) &payto_uri); if (qs < 0) { - finish_loop (wc, - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "reserves_get_origin")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_DB_FETCH_FAILED, + db_fetch_context, + "reserves_get_origin"); return; } /* If _no_ results, reserve was created by merge, @@ -1162,7 +1187,7 @@ run_legi_check (struct WithdrawContext *wc) merge already did that. */ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - wc->phase = WC_PHASE_PREPARE_TRANSACTION; + wc->phase = PHASE_PREPARE_TRANSACTION; return; } TALER_full_payto_normalize_and_hash (payto_uri, @@ -1183,7 +1208,7 @@ run_legi_check (struct WithdrawContext *wc) wc_tail, wc); MHD_suspend_connection (wc->rc->connection); - wc->phase = WC_PHASE_SUSPENDED; + wc->phase = PHASE_SUSPENDED; } @@ -1206,7 +1231,6 @@ find_denomination ( const struct TALER_DenominationHashP *denom_h, struct TEH_DenominationKey **pdk) { - struct MHD_Connection *connection = wc->rc->connection; struct TEH_DenominationKey *dk; *pdk = NULL; @@ -1219,59 +1243,35 @@ find_denomination ( if (NULL == dk) { - /* The denomination doesn't exist */ - if (check_request_idempotent (wc)) - return GNUNET_NO; - GNUNET_break_op (0); - finish_loop (wc, - TEH_RESPONSE_reply_unknown_denom_pub_hash ( - connection, - denom_h)); + SET_ERROR_WITH_FIELD (wc, + ERROR_DENOMINATION_KEY_UNKNOWN, + denom_h); return GNUNET_NO; } if (GNUNET_TIME_absolute_is_past ( dk->meta.expire_withdraw.abs_time)) { - /* This denomination is past the expiration time for withdraw */ - if (check_request_idempotent (wc)) - return GNUNET_NO; - GNUNET_break_op (0); - finish_loop (wc, - TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - denom_h, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, - typ2str (wc))); + SET_ERROR_WITH_FIELD (wc, + ERROR_DENOMINATION_EXPIRED, + denom_h); return GNUNET_SYSERR; } if (GNUNET_TIME_absolute_is_future ( dk->meta.start.abs_time)) { - /* This denomination is not yet valid, no need to check - for idempotency! */ GNUNET_break_op (0); - finish_loop (wc, - TEH_RESPONSE_reply_expired_denom_pub_hash ( - connection, - denom_h, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, - typ2str (wc))); + SET_ERROR_WITH_FIELD (wc, + ERROR_DENOMINATION_VALIDITY_IN_FUTURE, + denom_h); return GNUNET_SYSERR; } if (dk->recoup_possible) { - /* This denomination has been revoked */ - if (check_request_idempotent (wc)) - return GNUNET_NO; - GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - connection, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, - typ2str (wc))); + SET_ERROR (wc, + ERROR_DENOMINATION_REVOKED); return GNUNET_SYSERR; } @@ -1280,19 +1280,10 @@ find_denomination ( { if (0 == dk->denom_pub.age_mask.bits) { - /* This denomation does not support age restriction */ - char msg[256]; - - GNUNET_snprintf (msg, - sizeof(msg), - "denomination %s does not support age restriction", - GNUNET_h2s (&denom_h->hash)); - - finish_loop (wc, - TALER_MHD_reply_with_ec ( - connection, - TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, - msg)); + GNUNET_break_op (0); + SET_ERROR_WITH_FIELD (wc, + ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, + denom_h); return GNUNET_SYSERR; } } @@ -1314,14 +1305,11 @@ find_denomination ( * GNUNET_NO on error (and response being sent) */ static enum GNUNET_GenericReturnValue -age_withdraw_check_keys ( +age_withdraw_phase_check_keys ( struct WithdrawContext *wc, struct TEH_KeyStateHandle *ksh) { - struct MHD_Connection *connection - = wc->rc->connection; - unsigned int len - = wc->typ.age.num_coins; + unsigned int len = wc->num_coins; struct TALER_Amount total_amount; struct TALER_Amount total_fee; @@ -1352,14 +1340,11 @@ age_withdraw_check_keys ( for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++) { if (dk->denom_pub.bsign_pub_key->cipher != - wc->typ.age.coin_evs[i][k].blinded_message->cipher) + wc->typ.age.planchets[i][k].blinded_message->cipher) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - connection, - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, - NULL)); + SET_ERROR (wc, + ERROR_CIPHER_MISMATCH); return GNUNET_NO; } } @@ -1370,12 +1355,10 @@ age_withdraw_check_keys ( &dk->meta.value)) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW, - "amount")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_AGE_AMOUNT_OVERFLOW, + which, + "amount"); return GNUNET_NO; } @@ -1385,12 +1368,10 @@ age_withdraw_check_keys ( &dk->meta.fees.withdraw)) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW, - "fee")); + SET_ERROR_WITH_DETAIL (wc, + ERROR_AGE_AMOUNT_OVERFLOW, + which, + "fee"); return GNUNET_NO; } wc->typ.age.commitment.denom_serials[i] = dk->meta.serial; @@ -1415,11 +1396,8 @@ age_withdraw_check_keys ( &wc->typ.age.commitment.reserve_sig)) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL)); + SET_ERROR (wc, + ERROR_RESERVE_SIGNATURE_INVALID); return GNUNET_NO; } @@ -1436,13 +1414,11 @@ age_withdraw_check_keys ( * GNUNET_NO on error (and response being sent) */ static enum GNUNET_GenericReturnValue -batch_withdraw_check_keys ( +batch_withdraw_phase_check_keys ( struct WithdrawContext *wc, struct TEH_KeyStateHandle *ksh) { - const struct TEH_RequestContext *rc = wc->rc; - - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; struct TEH_DenominationKey *dk; @@ -1463,12 +1439,8 @@ batch_withdraw_check_keys ( { /* denomination cipher and blinded planchet cipher not the same */ GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, - NULL)); + SET_ERROR (wc, + ERROR_CIPHER_MISMATCH); return GNUNET_NO; } @@ -1478,25 +1450,19 @@ batch_withdraw_check_keys ( &dk->meta.fees.withdraw)) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL)); + SET_ERROR (wc, + ERROR_BATCH_AMOUNT_FEE_OVERFLOW); return GNUNET_NO; } + if (0 > TALER_amount_add (&wc->typ.batch.batch_total, &wc->typ.batch.batch_total, &pc->collectable.amount_with_fee)) { GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, - NULL)); + SET_ERROR (wc, + ERROR_BATCH_AMOUNT_FEE_OVERFLOW); return GNUNET_NO; } @@ -1514,12 +1480,8 @@ batch_withdraw_check_keys ( &pc->collectable.reserve_sig)) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL)); + SET_ERROR (wc, + ERROR_RESERVE_SIGNATURE_INVALID); return GNUNET_NO; } } @@ -1535,34 +1497,27 @@ batch_withdraw_check_keys ( * @param[in,out] wc context for request processing */ static void -check_keys (struct WithdrawContext *wc) +phase_check_keys (struct WithdrawContext *wc) { - const struct TEH_RequestContext *rc = wc->rc; struct TEH_KeyStateHandle *ksh; enum GNUNET_GenericReturnValue r; ksh = TEH_keys_get_state (); if (NULL == ksh) { - if (check_request_idempotent (wc)) - return; GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL)); + SET_ERROR (wc, + ERROR_KEYS_MISSING); return; } switch (wc->withdraw_type) { case WITHDRAW_TYPE_BATCH: - r = batch_withdraw_check_keys (wc, ksh); + r = batch_withdraw_phase_check_keys (wc, ksh); break; case WITHDRAW_TYPE_AGE: - r = age_withdraw_check_keys (wc, ksh); + r = age_withdraw_phase_check_keys (wc, ksh); break; default: GNUNET_break (0); @@ -1579,12 +1534,8 @@ check_keys (struct WithdrawContext *wc) break; case GNUNET_SYSERR: GNUNET_break (0); - finish_loop (wc, - TALER_MHD_reply_with_error ( - rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - typ2str (wc))); + SET_ERROR (wc, + ERROR_KEYS_MISSING); break; default: GNUNET_break (0); @@ -1596,18 +1547,18 @@ check_keys (struct WithdrawContext *wc) * Check that the client signature authorizing the withdrawal is valid. * NOTE: this is only applicable to age-withdraw; the existing * batch-withdraw REST-API signs each planchet and they have to be - * checked during the call to check_keys. + * checked during the call to phase_check_keys. * * @param[in,out] wc request context to check */ static void -check_reserve_signature ( +phase_check_reserve_signature ( struct WithdrawContext *wc) { switch (wc->withdraw_type) { case WITHDRAW_TYPE_BATCH: - /* signature checks has occurred in batch_withdraw_check_keys */ + /* signature checks has occurred in batch_withdraw_phase_check_keys */ break; case WITHDRAW_TYPE_AGE: TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; @@ -1621,11 +1572,8 @@ check_reserve_signature ( &wc->typ.age.commitment.reserve_sig)) { GNUNET_break_op (0); - finish_loop (wc, - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL)); + SET_ERROR (wc, + ERROR_RESERVE_SIGNATURE_INVALID); return; } break; @@ -1659,7 +1607,7 @@ clean_withdraw_rc (struct TEH_RequestContext *rc) switch (wc->withdraw_type) { case WITHDRAW_TYPE_BATCH: - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; @@ -1670,21 +1618,21 @@ clean_withdraw_rc (struct TEH_RequestContext *rc) break; case WITHDRAW_TYPE_AGE: - for (unsigned int i = 0; i<wc->typ.age.num_coins; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { for (unsigned int kappa = 0; kappa<TALER_CNC_KAPPA; kappa++) { - TALER_blinded_planchet_free (&wc->typ.age.coin_evs[i][kappa]); + TALER_blinded_planchet_free (&wc->typ.age.planchets[i][kappa]); } } - for (unsigned int i = 0; i<wc->typ.age.num_coins; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { TALER_blinded_denom_sig_free (&wc->typ.age.commitment.denom_sigs[i]); } GNUNET_free (wc->typ.age.commitment.h_coin_evs); GNUNET_free (wc->typ.age.commitment.denom_sigs); GNUNET_free (wc->typ.age.denom_hs); - GNUNET_free (wc->typ.age.coin_evs); + GNUNET_free (wc->typ.age.planchets); GNUNET_free (wc->typ.age.commitment.denom_serials); break; @@ -1692,10 +1640,11 @@ clean_withdraw_rc (struct TEH_RequestContext *rc) GNUNET_break (0); } - if (NULL != wc->response) + if (ERROR_LEGITIMIZATION_RESULT == wc->error.code && + NULL != wc->error.details.legi.response) { - MHD_destroy_response (wc->response); - wc->response = NULL; + MHD_destroy_response (wc->error.details.legi.response); + wc->error.details.legi.response = NULL; } GNUNET_free (wc); @@ -1703,17 +1652,355 @@ clean_withdraw_rc (struct TEH_RequestContext *rc) /** + * Send a response to a "age-withdraw" request. + * + * @param[in,out] wc context for the operation + */ +static void +age_withdraw_phase_generate_reply_success ( + struct WithdrawContext *wc) +{ + struct MHD_Connection *connection + = wc->rc->connection; + const struct TALER_AgeWithdrawCommitmentHashP *ach + = &wc->typ.age.commitment.h_commitment; + uint32_t noreveal_index + = wc->typ.age.commitment.noreveal_index; + struct TALER_ExchangePublicKeyP pub; + struct TALER_ExchangeSignatureP sig; + enum TALER_ErrorCode ec; + + ec = TALER_exchange_online_age_withdraw_confirmation_sign ( + &TEH_keys_exchange_sign_, + ach, + noreveal_index, + &pub, + &sig); + if (TALER_EC_NONE != ec) + { + SET_ERROR_WITH_FIELD (wc, + ERROR_AGE_CONFIRMATION_SIGN, + ec); + return; + } + + finish_loop (wc, + TALER_MHD_REPLY_JSON_PACK ( + connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_uint64 ("noreveal_index", + noreveal_index), + GNUNET_JSON_pack_data_auto ("exchange_sig", + &sig), + GNUNET_JSON_pack_data_auto ("exchange_pub", + &pub))); +} + + +/** + * Generates response for the batch- or age-withdraw request. + * + * @param wc withdraw operation context + */ +static void +phase_generate_reply_success (struct WithdrawContext *wc) +{ + switch (wc->withdraw_type) + { + case WITHDRAW_TYPE_BATCH: + batch_withdraw_phase_generate_reply_success (wc); + break; + case WITHDRAW_TYPE_AGE: + age_withdraw_phase_generate_reply_success (wc); + break; + default: + GNUNET_break (0); + } +} + + +/** + * Reports an error, potentially with details. + * That is, it puts a error-type specific repsonse into the MHD queue. + * It will do a idempotency check first, if needed for the error type. + * + * @param wc withdraw context + */ +static void +phase_generate_reply_error ( + struct WithdrawContext *wc) +{ + GNUNET_assert (PHASE_GENERATE_REPLY_ERROR == wc->phase); + GNUNET_assert (ERROR_NONE != wc->error.code); + GNUNET_assert (ERROR_MAX != wc->error.code); + + if (needs_idempotency_check[wc->error.code] && + check_idempotency (wc)) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "request is idempotent\n"); + return; + } + + switch (wc->error.code) + { + case ERROR_MAX: + case ERROR_NONE: + { + GNUNET_break (0); + wc->phase = PHASE_RETURN_YES; + return; + } + + case ERROR_REQUEST_PARAMETER_MALFORMED: + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + wc->error.details.hint); + break; + + case ERROR_KEYS_MISSING: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + NULL)); + break; + + case ERROR_DB_FETCH_FAILED: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + wc->error.details.db_fetch_context)); + break; + + case ERROR_DB_INVARIANT_FAILURE: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_INVARIANT_FAILURE, + NULL)); + break; + + case ERROR_INTERNAL_INVARIANT_FAILURE: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, + NULL)); + break; + + case ERROR_RESERVE_UNKNOWN: + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN, + NULL)); + break; + + case ERROR_DENOMINATION_BATCH_SIGN: + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + wc->error.details.ec, + NULL)); + break; + + case ERROR_KYC_REQUIRED: + finish_loop (wc, + TEH_RESPONSE_reply_kyc_required ( + wc->rc->connection, + &wc->h_normalized_payto, + &wc->kyc, + false)); + break; + + case ERROR_DENOMINATION_KEY_UNKNOWN: + { + GNUNET_break_op (0); + finish_loop (wc, + TEH_RESPONSE_reply_unknown_denom_pub_hash ( + wc->rc->connection, + wc->error.details.denom_h)); + break; + } + + case ERROR_DENOMINATION_EXPIRED: + GNUNET_break_op (0); + finish_loop (wc, + TEH_RESPONSE_reply_expired_denom_pub_hash ( + wc->rc->connection, + wc->error.details.denom_h, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED, + typ2str (wc))); + break; + + case ERROR_DENOMINATION_VALIDITY_IN_FUTURE: + finish_loop (wc, + TEH_RESPONSE_reply_expired_denom_pub_hash ( + wc->rc->connection, + wc->error.details.denom_h, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE, + typ2str (wc))); + break; + + case ERROR_DENOMINATION_REVOKED: + GNUNET_break_op (0); + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED, + typ2str (wc))); + break; + + case ERROR_CIPHER_MISMATCH: + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, + NULL)); + break; + + case ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION: + { + char msg[256]; + GNUNET_snprintf (msg, + sizeof(msg), + "denomination %s does not support age restriction", + GNUNET_h2s (&wc->error.details.denom_h->hash)); + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN, + msg)); + break; + } + + case ERROR_AGE_MAXIMUM_AGE_TOO_LARGE: + finish_loop (wc, + TALER_MHD_REPLY_JSON_PACK ( + wc->rc->connection, + MHD_HTTP_CONFLICT, + TALER_MHD_PACK_EC ( + TALER_EC_EXCHANGE_AGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE), + GNUNET_JSON_pack_uint64 ( + "allowed_maximum_age", + wc->error.details.age.max), + GNUNET_JSON_pack_uint64 ( + "reserve_birthday", + wc->error.details.age.birthday))); + break; + + case ERROR_AGE_RESTRICTION_REQUIRED: + finish_loop (wc, + TEH_RESPONSE_reply_reserve_age_restriction_required ( + wc->rc->connection, + wc->error.details.lowest_age)); + break; + + case ERROR_AGE_INSUFFICIENT_FUNDS: + finish_loop (wc, + TEH_RESPONSE_reply_reserve_insufficient_balance ( + wc->rc->connection, + TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS, + &wc->error.details.reserve_balance, + &wc->typ.age.commitment.amount_with_fee, + &wc->reserve_pub)); + break; + + case ERROR_AGE_AMOUNT_OVERFLOW: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_AGE_WITHDRAW_AMOUNT_OVERFLOW, + wc->error.details.which)); + break; + + case ERROR_AGE_CONFIRMATION_SIGN: + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + wc->error.details.ec, + NULL)); + break; + + case ERROR_BATCH_INSUFFICIENT_FUNDS: + finish_loop (wc, + TEH_RESPONSE_reply_reserve_insufficient_balance ( + wc->rc->connection, + TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS, + &wc->error.details.reserve_balance, + &wc->typ.batch.batch_total, + &wc->reserve_pub)); + break; + + case ERROR_BATCH_IDEMPOTENT_PLANCHET: + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Idempotent coin in batch, not allowed. Aborting.\n"); + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_CONFLICT, + TALER_EC_EXCHANGE_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET, + NULL)); + break; + } + + case ERROR_BATCH_NONCE_RESUSE: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE, + NULL)); + break; + + case ERROR_BATCH_AMOUNT_FEE_OVERFLOW: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, + NULL)); + break; + + case ERROR_RESERVE_SIGNATURE_INVALID: + finish_loop (wc, + TALER_MHD_reply_with_ec ( + wc->rc->connection, + TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, + NULL)); + break; + + case ERROR_LEGITIMIZATION_RESULT: { + finish_loop (wc, + MHD_queue_response (wc->rc->connection, + wc->error.details.legi.http_status, + wc->error.details.legi.response)); + break; + } + } +} + + +/** * Creates a new context for the incoming batch-withdraw request * * @param[in,out] wc context of the batch-witrhdraw, to be filled - * @param reserve_pub public key of the reserve for the withdraw * @param root json body of the request * @return GNUNET_OK on success, GNUNET_SYSERR otherwise (response sent) */ static enum GNUNET_GenericReturnValue batch_withdraw_new_request ( struct WithdrawContext *wc, - const struct TALER_ReservePublicKeyP *reserve_pub, const json_t *root) { const json_t *planchets; @@ -1740,34 +2027,32 @@ batch_withdraw_new_request ( } } - wc->typ.batch.planchets_length = json_array_size (planchets); - if (0 == wc->typ.batch.planchets_length) + wc->num_coins = json_array_size (planchets); + if (0 == wc->num_coins) { GNUNET_break_op (0); - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "planchets"); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "planchets"); return GNUNET_SYSERR; } - if (wc->typ.batch.planchets_length > TALER_MAX_FRESH_COINS) + if (wc->num_coins > TALER_MAX_FRESH_COINS) { GNUNET_break_op (0); - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "too many planchets"); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "too many planchets"); return GNUNET_SYSERR; } wc->typ.batch.planchets - = GNUNET_new_array (wc->typ.batch.planchets_length, + = GNUNET_new_array (wc->num_coins, struct PlanchetContext); - for (unsigned int i = 0; i<wc->typ.batch.planchets_length; i++) + for (unsigned int i = 0; i<wc->num_coins; i++) { struct PlanchetContext *pc = &wc->typ.batch.planchets[i]; struct GNUNET_JSON_Specification ispec[] = { @@ -1805,11 +2090,10 @@ batch_withdraw_new_request ( &pc->blinded_planchet)) { GNUNET_break_op (0); - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "duplicate planchet"); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "duplicate planchet"); return GNUNET_SYSERR; } } @@ -1822,28 +2106,26 @@ batch_withdraw_new_request ( * Creates a new context for the incoming age-withdraw request * * @param wc withdraw request context - * @param reserve_pub public key of the reserve for the withdraw * @param root json body of the request * @return GNUNET_OK on success, GNUNET_SYSERR otherwise (response sent) */ static enum GNUNET_GenericReturnValue age_withdraw_new_request ( struct WithdrawContext *wc, - const struct TALER_ReservePublicKeyP *reserve_pub, const json_t *root) { - wc->typ.age.commitment.reserve_pub = *reserve_pub; + wc->typ.age.commitment.reserve_pub = wc->reserve_pub; /* parse the json body */ { const json_t *j_denom_hs; - const json_t *j_blinded_coin_evs; + const json_t *j_blinded_planchets; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_array_const ("denom_hs", &j_denom_hs), - GNUNET_JSON_spec_array_const ("blinded_coin_evs", - &j_blinded_coin_evs), + GNUNET_JSON_spec_array_const ("blinded_planchets", + &j_blinded_planchets), GNUNET_JSON_spec_uint16 ("max_age", &wc->typ.age.commitment.max_age), GNUNET_JSON_spec_fixed_auto ("reserve_sig", @@ -1864,9 +2146,10 @@ age_withdraw_new_request ( wc->typ.age.commitment.max_age)) { GNUNET_break_op (0); - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, + SET_ERROR_WITH_DETAIL ( + wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, "max_age must be the lower edge of an age group"); return GNUNET_SYSERR; } @@ -1880,7 +2163,7 @@ age_withdraw_new_request ( "TALER_MAX_FRESH_COINS too large"); if (0 == num_coins) error = "denoms_h must not be empty"; - else if (num_coins != json_array_size (j_blinded_coin_evs)) + else if (num_coins != json_array_size (j_blinded_planchets)) error = "denoms_h and coins_evs must be arrays of the same size"; else if (num_coins > TALER_MAX_FRESH_COINS) /** @@ -1894,18 +2177,18 @@ age_withdraw_new_request ( if (NULL != error) { GNUNET_break_op (0); - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - error); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + error); return GNUNET_SYSERR; } - wc->typ.age.num_coins = (unsigned int) num_coins; + wc->num_coins = (unsigned int) num_coins; wc->typ.age.commitment.num_coins = (unsigned int) num_coins; } wc->typ.age.denom_hs - = GNUNET_new_array (wc->typ.age.num_coins, + = GNUNET_new_array (wc->num_coins, struct TALER_DenominationHashP); { size_t idx; @@ -1930,8 +2213,8 @@ age_withdraw_new_request ( typedef struct TALER_BlindedPlanchet _array_of_kappa_planchets[TALER_CNC_KAPPA]; - wc->typ.age.coin_evs = GNUNET_new_array (wc->typ.age.num_coins, - _array_of_kappa_planchets); + wc->typ.age.planchets = GNUNET_new_array (wc->num_coins, + _array_of_kappa_planchets); } /* calculate the hash over the data */ @@ -1943,40 +2226,27 @@ age_withdraw_new_request ( /* Parse blinded envelopes. */ { - json_t *j_kappa_coin_evs; + json_t *j_kappa_planchets; size_t idx; - json_array_foreach (j_blinded_coin_evs, idx, j_kappa_coin_evs) { - if (! json_is_array (j_kappa_coin_evs)) + json_array_foreach (j_blinded_planchets, idx, j_kappa_planchets) { + if (! json_is_array (j_kappa_planchets)) { - char buf[256]; - - GNUNET_snprintf ( - buf, - sizeof(buf), - "entry %u in array blinded_coin_evs must be an array", - (unsigned int) (idx + 1)); GNUNET_break_op (0); - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - buf); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "all entries in array blinded_planchets must be arrays"); return GNUNET_SYSERR; } - if (TALER_CNC_KAPPA != json_array_size (j_kappa_coin_evs)) + if (TALER_CNC_KAPPA != json_array_size (j_kappa_planchets)) { - char buf[256]; - - GNUNET_snprintf (buf, - sizeof(buf), - "array no. %u in coin_evs must have length %u", - (unsigned int) (idx + 1), - (unsigned int) TALER_CNC_KAPPA); GNUNET_break_op (0); - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - buf); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "all entries in array blinded_planchets must be arrays of size " + TALER_CNC_KAPPA_STR); return GNUNET_SYSERR; } @@ -1986,10 +2256,10 @@ age_withdraw_new_request ( size_t kappa; json_t *kvalue; - json_array_foreach (j_kappa_coin_evs, kappa, kvalue) { + json_array_foreach (j_kappa_planchets, kappa, kvalue) { struct GNUNET_JSON_Specification kspec[] = { TALER_JSON_spec_blinded_planchet (NULL, - &wc->typ.age.coin_evs[idx][ + &wc->typ.age.planchets[idx][ kappa]), GNUNET_JSON_spec_end () }; @@ -2004,7 +2274,7 @@ age_withdraw_new_request ( { struct TALER_BlindedCoinHashP bch; - TALER_coin_ev_hash (&wc->typ.age.coin_evs[idx][kappa], + TALER_coin_ev_hash (&wc->typ.age.planchets[idx][kappa], &wc->typ.age.denom_hs[idx], &bch); GNUNET_CRYPTO_hash_context_read (hash_context, @@ -2019,21 +2289,21 @@ age_withdraw_new_request ( { if (0 == TALER_blinded_planchet_cmp ( - &wc->typ.age.coin_evs[idx][kappa], - &wc->typ.age.coin_evs[i][kappa])) + &wc->typ.age.planchets[idx][kappa], + &wc->typ.age.planchets[i][kappa])) { GNUNET_break_op (0); - TALER_MHD_reply_with_ec ( - wc->rc->connection, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "duplicate planchet"); + SET_ERROR_WITH_DETAIL (wc, + ERROR_REQUEST_PARAMETER_MALFORMED, + hint, + "duplicate planchet"); return GNUNET_SYSERR; } } /* end duplicate check */ - } /* json_array_foreach over j_kappa_coin_evs */ + } /* json_array_foreach over j_kappa_planchets */ } /* scope of kappa/kvalue */ - } /* json_array_foreach over j_blinded_coin_evs */ - } /* scope of j_kappa_coin_evs, idx */ + } /* json_array_foreach over j_blinded_planchets */ + } /* scope of j_kappa_planchets, idx */ /* Finally, calculate the h_commitment from all blinded envelopes */ GNUNET_CRYPTO_hash_context_finish (hash_context, @@ -2041,7 +2311,7 @@ age_withdraw_new_request ( hash); } /* scope of hash_context */ - } /* scope of j_denom_hs, j_blinded_coin_evs */ + } /* scope of j_denom_hs, j_blinded_planchets */ return GNUNET_OK; } @@ -2079,26 +2349,23 @@ handler_withdraw ( switch (typ) { case WITHDRAW_TYPE_BATCH: - r = batch_withdraw_new_request (wc, reserve_pub, root); + r = batch_withdraw_new_request (wc, root); break; case WITHDRAW_TYPE_AGE: - r = age_withdraw_new_request (wc, reserve_pub, root); + r = age_withdraw_new_request (wc, root); break; default: GNUNET_break (0); r = GNUNET_SYSERR; - TALER_MHD_reply_with_error ( - wc->rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - /* TODO: find better error code here:? */ - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE, - NULL); + /* TODO: find better error code here:? */ + SET_ERROR (wc, + ERROR_INTERNAL_INVARIANT_FAILURE); } if (GNUNET_OK != r) return (GNUNET_SYSERR == r) ? MHD_NO : MHD_YES; - wc->phase = WC_PHASE_CHECK_KEYS; + wc->phase = PHASE_CHECK_KEYS; } while (true) @@ -2110,36 +2377,35 @@ handler_withdraw ( switch (wc->phase) { - case WC_PHASE_CHECK_KEYS: - check_keys (wc); + case PHASE_CHECK_KEYS: + phase_check_keys (wc); break; - case WC_PHASE_CHECK_RESERVE_SIGNATURE: - check_reserve_signature (wc); + case PHASE_CHECK_RESERVE_SIGNATURE: + phase_check_reserve_signature (wc); break; - case WC_PHASE_RUN_LEGI_CHECK: - run_legi_check (wc); + case PHASE_RUN_LEGI_CHECK: + phase_run_legi_check (wc); break; - case WC_PHASE_SUSPENDED: + case PHASE_SUSPENDED: return MHD_YES; - case WC_PHASE_CHECK_KYC_RESULT: - check_kyc_result (wc); + case PHASE_CHECK_KYC_RESULT: + phase_check_kyc_result (wc); + break; + case PHASE_PREPARE_TRANSACTION: + phase_prepare_transaction (wc); break; - case WC_PHASE_PREPARE_TRANSACTION: - prepare_transaction (wc); + case PHASE_RUN_TRANSACTION: + phase_run_transaction (wc); break; - case WC_PHASE_RUN_TRANSACTION: - run_transaction (wc); + case PHASE_GENERATE_REPLY_SUCCESS: + phase_generate_reply_success (wc); break; - case WC_PHASE_GENERATE_REPLY_SUCCESS: - generate_reply_success (wc); + case PHASE_GENERATE_REPLY_ERROR: + phase_generate_reply_error (wc); break; - case WC_PHASE_GENERATE_REPLY_FAILURE: - return MHD_queue_response (rc->connection, - wc->http_status, - wc->response); - case WC_PHASE_RETURN_YES: + case PHASE_RETURN_YES: return MHD_YES; - case WC_PHASE_RETURN_NO: + case PHASE_RETURN_NO: return MHD_NO; } } diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h @@ -48,6 +48,7 @@ * fixed and part of the protocol. */ #define TALER_CNC_KAPPA 3 +#define TALER_CNC_KAPPA_STR "3" #define TALER_CNC_KAPPA_MINUS_ONE_STR "2" diff --git a/src/lib/exchange_api_age_withdraw.c b/src/lib/exchange_api_age_withdraw.c @@ -624,7 +624,7 @@ perform_protocol ( /* Initiate the POST-request */ j_request_body = GNUNET_JSON_PACK ( GNUNET_JSON_pack_array_steal ("denom_hs", j_denoms), - GNUNET_JSON_pack_array_steal ("blinded_coin_evs", j_array_candidates), + GNUNET_JSON_pack_array_steal ("blinded_planchets", j_array_candidates), GNUNET_JSON_pack_uint64 ("max_age", awbh->max_age), GNUNET_JSON_pack_data_auto ("reserve_sig", &awbh->reserve_sig)); FAIL_IF (NULL == j_request_body);