exchange

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

commit d068ebf661c0cf02cde2107dd5344318d7c8f18d
parent 3afafbe4a33f0a768046c050921f422f28dfd74a
Author: Özgür Kesim <oec@codeblau.de>
Date:   Mon, 31 Mar 2025 18:01:20 +0200

[exchange] cleanup withdraw and reserves_history

- simplify code in withdraw
- adapt change to exchangedb struct

Diffstat:
Msrc/exchange/taler-exchange-httpd_reserves_history.c | 2+-
Msrc/exchange/taler-exchange-httpd_withdraw.c | 572++++++++++++++++++++++++++++++++++++-------------------------------------------
2 files changed, 264 insertions(+), 310 deletions(-)

diff --git a/src/exchange/taler-exchange-httpd_reserves_history.c b/src/exchange/taler-exchange-httpd_reserves_history.c @@ -165,7 +165,7 @@ compile_reserve_history ( &withdraw->amount_with_fee) ); - if (withdraw->age_restricted) + if (withdraw->age_proof_required) { if (0 != json_object_update_new ( diff --git a/src/exchange/taler-exchange-httpd_withdraw.c b/src/exchange/taler-exchange-httpd_withdraw.c @@ -37,6 +37,50 @@ #include "taler_util.h" /** + * This bit will be set on errors of type #WithdrawError + * that require a check for idempotency before actually + * returning an error. + * This value must be outside the range of enum #WithdrawError. + */ +#define IDEMPOTENCY_FLAG 1 << 5 + +/** + * 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 WithdrawError +{ + WITHDRAW_ERROR_NONE = 0, + WITHDRAW_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION = 1, + WITHDRAW_ERROR_AGE_RESTRICTION_REQUIRED = 2, + WITHDRAW_ERROR_AMOUNT_OVERFLOW = 3, + WITHDRAW_ERROR_AMOUNT_PLUS_FEE_OVERFLOW = 4, + WITHDRAW_ERROR_CIPHER_MISMATCH = 5, + WITHDRAW_ERROR_CONFIRMATION_SIGN = 6, + WITHDRAW_ERROR_DB_FETCH_FAILED = 7, + WITHDRAW_ERROR_DB_INVARIANT_FAILURE = 8, + WITHDRAW_ERROR_DENOMINATION_EXPIRED = 9 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_DENOMINATION_KEY_UNKNOWN = 10 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_DENOMINATION_REVOKED = 11 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_DENOMINATION_SIGN = 12, + WITHDRAW_ERROR_DENOMINATION_VALIDITY_IN_FUTURE = 13, + WITHDRAW_ERROR_FEE_OVERFLOW = 14, + WITHDRAW_ERROR_IDEMPOTENT_PLANCHET = 15 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_INSUFFICIENT_FUNDS = 16 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_INTERNAL_INVARIANT_FAILURE = 17, + WITHDRAW_ERROR_KEYS_MISSING = 18 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_KYC_REQUIRED = 19 | IDEMPOTENCY_FLAG, + WITHDRAW_ERROR_LEGITIMIZATION_RESULT = 20, + WITHDRAW_ERROR_MAXIMUM_AGE_TOO_LARGE = 21, + WITHDRAW_ERROR_NONCE_RESUSE = 22, + WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED = 23, + WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN = 24, + WITHDRAW_ERROR_RESERVE_SIGNATURE_INVALID = 25, + WITHDRAW_ERROR_RESERVE_UNKNOWN = 26, +}; + +/** * Context for a /withdraw requests */ struct WithdrawContext @@ -55,17 +99,17 @@ struct WithdrawContext */ enum { - 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, + WITHDRAW_PHASE_CHECK_KEYS, + WITHDRAW_PHASE_CHECK_RESERVE_SIGNATURE, + WITHDRAW_PHASE_RUN_LEGI_CHECK, + WITHDRAW_PHASE_SUSPENDED, + WITHDRAW_PHASE_CHECK_KYC_RESULT, + WITHDRAW_PHASE_PREPARE_TRANSACTION, + WITHDRAW_PHASE_RUN_TRANSACTION, + WITHDRAW_PHASE_GENERATE_REPLY_SUCCESS, + WITHDRAW_PHASE_GENERATE_REPLY_ERROR, + WITHDRAW_PHASE_RETURN_NO, + WITHDRAW_PHASE_RETURN_YES, } phase; @@ -135,7 +179,7 @@ struct WithdrawContext /** * Array of ``num_planchets`` coin planchets. * Note that the size depends on the age restriction: - * If ``age_restricted`` is false, this is an array of length ``num_coins``. + * If ``age_proof_required`` is false, this is an array of length ``num_coins``. * Otherwise it is an array of length ``kappa*num_coins``, arranged * in runs of ``num_coins`` coins, [0..num_coins)..[0..num_coins), * one for each kappa value. @@ -156,167 +200,86 @@ struct WithdrawContext /** - * Errors occurring 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. - */ + * Errors occurring during evaluation of the request are captured in this + * struct. In phase WITHDRAW_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_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, - ERROR_AGE_RESTRICTION_REQUIRED, - ERROR_AMOUNT_OVERFLOW, - ERROR_AMOUNT_PLUS_FEE_OVERFLOW, - ERROR_CIPHER_MISMATCH, - ERROR_CONFIRMATION_SIGN, - ERROR_DB_FETCH_FAILED, - ERROR_DB_INVARIANT_FAILURE, - ERROR_DENOMINATION_EXPIRED, - ERROR_DENOMINATION_KEY_UNKNOWN, - ERROR_DENOMINATION_REVOKED, - ERROR_DENOMINATION_SIGN, - ERROR_DENOMINATION_VALIDITY_IN_FUTURE, - ERROR_FEE_OVERFLOW, - ERROR_IDEMPOTENT_PLANCHET, - ERROR_INSUFFICIENT_FUNDS, - ERROR_INTERNAL_INVARIANT_FAILURE, - ERROR_KEYS_MISSING, - ERROR_KYC_REQUIRED, - ERROR_LEGITIMIZATION_RESULT, - ERROR_MAXIMUM_AGE_TOO_LARGE, - ERROR_NONCE_RESUSE, - ERROR_REQUEST_PARAMETER_MALFORMED, - ERROR_RESERVE_CIPHER_UNKNOWN, - ERROR_RESERVE_SIGNATURE_INVALID, - ERROR_RESERVE_UNKNOWN, - - ERROR_MAX - } code; + /* The (internal) error code */ + enum WithdrawError 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. + * Each field is named according to the error that is using it, except + * commented otherwise. */ union { - /* ERROR_REQUEST_PARAMETER_MALFORMED */ - const char *hint; - - /* ERROR_AMOUNT_OVERFLOW */ - /* ERROR_FEE_OVERFLOW */ - /* ERROR_AMOUNT_PLUS_FEE_OVERFLOW */ - const char *which; + const char *request_parameter_malformed; - /* ERROR_RESERVE_CIPHER_UNKNOWN */ - const char *cipher; + const char *reserve_cipher_unknown; - /* ERROR_DENOMINATION_KEY_UNKNOWN */ + /** + * For all errors related to a particular denomination, i.e. + * WITHDRAW_ERROR_DENOMINATION_KEY_UNKNOWN, + * WITHDRAW_ERROR_DENOMINATION_EXPIRED, + * WITHDRAW_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, + * WITHDRAW_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, + * we use this one field. + */ const struct TALER_DenominationHashP *denom_h; - /* ERROR_DB_FETCH_FAILED */ const char *db_fetch_context; - /* ERROR_MAXIMUM_AGE_TOO_LARGE */ struct { - uint16_t max; + uint16_t max_allowed; uint32_t birthday; - } age; + } maximum_age_too_large; + + /* The lowest age required */ + uint16_t age_restriction_required; - /* ERROR_AGE_RESTRICTION_REQUIRED */ - uint16_t lowest_age; + /* Balance of the reserve */ + struct TALER_Amount insufficient_funds; - /* ERROR_INSUFFICIENT_FUNDS */ - struct TALER_Amount reserve_balance; + enum TALER_ErrorCode ec_confirmation_sign; - /* ERROR_CONFIRMATION_SIGN */ - /* ERROR_DENOMINATION_SIGN */ - enum TALER_ErrorCode ec; + enum TALER_ErrorCode ec_denomination_sign; - /* ERROR_LEGITIMIZATION_RESULT */ struct { struct MHD_Response *response; unsigned int http_status; - } legi; + } legitimization_result; } 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_MAXIMUM_AGE_TOO_LARGE] = false, - [ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION] = false, - [ERROR_AGE_RESTRICTION_REQUIRED] = false, - [ERROR_AMOUNT_OVERFLOW] = false, - [ERROR_AMOUNT_PLUS_FEE_OVERFLOW] = false, - [ERROR_CIPHER_MISMATCH] = false, - [ERROR_CONFIRMATION_SIGN] = false, - [ERROR_DB_FETCH_FAILED] = false, - [ERROR_DB_INVARIANT_FAILURE] = false, - [ERROR_DENOMINATION_SIGN] = false, - [ERROR_DENOMINATION_VALIDITY_IN_FUTURE] = false, - [ERROR_INTERNAL_INVARIANT_FAILURE] = false, - [ERROR_LEGITIMIZATION_RESULT] = false, - [ERROR_NONCE_RESUSE] = false, - [ERROR_REQUEST_PARAMETER_MALFORMED] = false, - [ERROR_RESERVE_CIPHER_UNKNOWN] = false, - [ERROR_RESERVE_SIGNATURE_INVALID] = false, - [ERROR_RESERVE_UNKNOWN] = false, - - /* These require idempotency checks */ - [ERROR_INSUFFICIENT_FUNDS] = true, - [ERROR_IDEMPOTENT_PLANCHET] = 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 for errors"); - -/** * The following macros set the given error code, - * set the phase to PHASE_GENERATE_REPLY_ERROR, + * set the phase to WITHDRAW_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) + (wc)->phase = WITHDRAW_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) + (wc)->phase = WITHDRAW_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) + (wc)->phase = WITHDRAW_PHASE_GENERATE_REPLY_ERROR; } while (0) /** @@ -352,8 +315,8 @@ finish_loop (struct WithdrawContext *wc, MHD_RESULT mres) { wc->phase = (MHD_YES == mres) - ? PHASE_RETURN_YES - : PHASE_RETURN_NO; + ? WITHDRAW_PHASE_RETURN_YES + : WITHDRAW_PHASE_RETURN_NO; } @@ -382,7 +345,7 @@ withdraw_is_idempotent ( GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); if (GNUNET_DB_STATUS_HARD_ERROR == qs) SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, + WITHDRAW_ERROR_DB_FETCH_FAILED, db_fetch_context, "get_withdraw"); return true; /* Well, kind-of. */ @@ -396,7 +359,7 @@ withdraw_is_idempotent ( /* Generate idempotent reply */ TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW]++; - wc->phase = PHASE_GENERATE_REPLY_SUCCESS; + wc->phase = WITHDRAW_PHASE_GENERATE_REPLY_SUCCESS; return true; } @@ -423,50 +386,45 @@ withdraw_transaction ( { struct WithdrawContext *wc = cls; enum GNUNET_DB_QueryStatus qs; - bool found = false; - bool balance_ok = false; - bool age_ok = false; - bool conflict = false; - uint16_t allowed_maximum_age = 0; - uint32_t reserve_birthday = 0; - struct TALER_Amount reserve_balance; - + bool balance_ok; + bool age_ok; + bool conflict; + uint16_t allowed_maximum_age; + uint32_t reserve_birthday; + struct TALER_Amount insufficient_funds; qs = TEH_plugin->do_withdraw (TEH_plugin->cls, &wc->request.persist, wc->now, - &found, &balance_ok, - &reserve_balance, + &insufficient_funds, &age_ok, &allowed_maximum_age, &reserve_birthday, &conflict); - if (0 > qs) { if (GNUNET_DB_STATUS_HARD_ERROR == qs) SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, + WITHDRAW_ERROR_DB_FETCH_FAILED, db_fetch_context, "do_withdraw"); return qs; } - - if (! found) + if (0 == qs) { SET_ERROR (wc, - ERROR_RESERVE_UNKNOWN); + WITHDRAW_ERROR_RESERVE_UNKNOWN); return GNUNET_DB_STATUS_HARD_ERROR; } if (! age_ok) { - wc->error.details.age.max = allowed_maximum_age; - wc->error.details.age.birthday = reserve_birthday; + wc->error.details.maximum_age_too_large.max_allowed = allowed_maximum_age; + wc->error.details.maximum_age_too_large.birthday = reserve_birthday; SET_ERROR (wc, - ERROR_MAXIMUM_AGE_TOO_LARGE); + WITHDRAW_ERROR_MAXIMUM_AGE_TOO_LARGE); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -475,8 +433,8 @@ withdraw_transaction ( TEH_plugin->rollback (TEH_plugin->cls); SET_ERROR_WITH_FIELD (wc, - ERROR_INSUFFICIENT_FUNDS, - reserve_balance); + WITHDRAW_ERROR_INSUFFICIENT_FUNDS, + insufficient_funds); return GNUNET_DB_STATUS_HARD_ERROR; } @@ -510,10 +468,9 @@ phase_run_transaction ( MHD_RESULT mhd_ret; enum GNUNET_GenericReturnValue qs; - GNUNET_assert (PHASE_RUN_TRANSACTION == + GNUNET_assert (WITHDRAW_PHASE_RUN_TRANSACTION == wc->phase); - qs = TEH_DB_run_transaction (wc->rc->connection, "run withdraw", TEH_MT_REQUEST_WITHDRAW, @@ -524,7 +481,7 @@ phase_run_transaction ( if (GNUNET_OK != qs) { /* TODO[oec]: Logic still ok with new error handling? */ - if (PHASE_RUN_TRANSACTION == wc->phase) + if (WITHDRAW_PHASE_RUN_TRANSACTION == wc->phase) finish_loop (wc, mhd_ret); return; @@ -556,7 +513,7 @@ phase_prepare_transaction ( struct TALER_BlindedCoinHashP); /* Pick the challenge in case of age restriction */ - if (wc->request.persist.age_restricted) + if (wc->request.persist.age_proof_required) { wc->request.persist.noreveal_index = GNUNET_CRYPTO_random_u32 ( @@ -575,7 +532,7 @@ phase_prepare_transaction ( /* Choose and sign the coins */ { struct TEH_CoinSignData csds[wc->request.persist.num_coins]; - enum TALER_ErrorCode ec; + enum TALER_ErrorCode ec_denomination_sign; memset (csds, 0, @@ -588,17 +545,17 @@ phase_prepare_transaction ( csds[i].h_denom_pub = &wc->request.denoms_h[i]; } - ec = TEH_keys_denomination_batch_sign ( + ec_denomination_sign = TEH_keys_denomination_batch_sign ( wc->request.persist.num_coins, csds, false, wc->request.persist.denom_sigs); - if (TALER_EC_NONE != ec) + if (TALER_EC_NONE != ec_denomination_sign) { GNUNET_break (0); SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_SIGN, - ec); + WITHDRAW_ERROR_DENOMINATION_SIGN, + ec_denomination_sign); return; } @@ -627,7 +584,7 @@ phase_check_kyc_result (struct WithdrawContext *wc) if (! wc->kyc.ok) { SET_ERROR (wc, - ERROR_KYC_REQUIRED); + WITHDRAW_ERROR_KYC_REQUIRED); return; } wc->phase++; @@ -649,7 +606,7 @@ withdraw_legi_cb ( struct WithdrawContext *wc = cls; wc->lch = NULL; - GNUNET_assert (PHASE_SUSPENDED == + GNUNET_assert (WITHDRAW_PHASE_SUSPENDED == wc->phase); MHD_resume_connection (wc->rc->connection); GNUNET_CONTAINER_DLL_remove (wc_head, @@ -658,14 +615,14 @@ withdraw_legi_cb ( TALER_MHD_daemon_trigger (); if (NULL != lcr->response) { - wc->error.details.legi.response = lcr->response; - wc->error.details.legi.http_status = lcr->http_status; + wc->error.details.legitimization_result.response = lcr->response; + wc->error.details.legitimization_result.http_status = lcr->http_status; SET_ERROR (wc, - ERROR_LEGITIMIZATION_RESULT); + WITHDRAW_ERROR_LEGITIMIZATION_RESULT); return; } wc->kyc = lcr->kyc; - wc->phase = PHASE_CHECK_KYC_RESULT; + wc->phase = WITHDRAW_PHASE_CHECK_KYC_RESULT; } @@ -678,7 +635,7 @@ static const char * typ2str ( const struct WithdrawContext *wc) { - return (wc->request.persist.age_restricted) ? "age-withdraw" : + return (wc->request.persist.age_proof_required) ? "age-withdraw" : "batch-withdraw"; } @@ -762,7 +719,7 @@ phase_run_legi_check (struct WithdrawContext *wc) if (qs < 0) { SET_ERROR_WITH_DETAIL (wc, - ERROR_DB_FETCH_FAILED, + WITHDRAW_ERROR_DB_FETCH_FAILED, db_fetch_context, "reserves_get_origin"); return; @@ -772,7 +729,7 @@ phase_run_legi_check (struct WithdrawContext *wc) merge already did that. */ if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) { - wc->phase = PHASE_PREPARE_TRANSACTION; + wc->phase = WITHDRAW_PHASE_PREPARE_TRANSACTION; return; } TALER_full_payto_normalize_and_hash (payto_uri, @@ -793,7 +750,7 @@ phase_run_legi_check (struct WithdrawContext *wc) wc_tail, wc); MHD_suspend_connection (wc->rc->connection); - wc->phase = PHASE_SUSPENDED; + wc->phase = WITHDRAW_PHASE_SUSPENDED; } @@ -829,7 +786,7 @@ find_denomination ( if (NULL == dk) { SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_KEY_UNKNOWN, + WITHDRAW_ERROR_DENOMINATION_KEY_UNKNOWN, denom_h); return GNUNET_NO; } @@ -838,7 +795,7 @@ find_denomination ( dk->meta.expire_withdraw.abs_time)) { SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_EXPIRED, + WITHDRAW_ERROR_DENOMINATION_EXPIRED, denom_h); return GNUNET_SYSERR; } @@ -848,7 +805,7 @@ find_denomination ( { GNUNET_break_op (0); SET_ERROR_WITH_FIELD (wc, - ERROR_DENOMINATION_VALIDITY_IN_FUTURE, + WITHDRAW_ERROR_DENOMINATION_VALIDITY_IN_FUTURE, denom_h); return GNUNET_SYSERR; } @@ -856,18 +813,18 @@ find_denomination ( if (dk->recoup_possible) { SET_ERROR (wc, - ERROR_DENOMINATION_REVOKED); + WITHDRAW_ERROR_DENOMINATION_REVOKED); return GNUNET_SYSERR; } /* In case of age withdraw, make sure that the denomitation supports age restriction */ - if (wc->request.persist.age_restricted) + if (wc->request.persist.age_proof_required) { if (0 == dk->denom_pub.age_mask.bits) { GNUNET_break_op (0); SET_ERROR_WITH_FIELD (wc, - ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, + WITHDRAW_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION, denom_h); return GNUNET_SYSERR; } @@ -898,7 +855,7 @@ phase_check_keys ( { GNUNET_break (0); SET_ERROR (wc, - ERROR_KEYS_MISSING); + WITHDRAW_ERROR_KEYS_MISSING); return; } @@ -937,7 +894,7 @@ phase_check_keys ( * a) When age restriction is not applicable, we only have request.persist.num_coins * to deal with, matching the number of denominations. * - * b) However, when request.persist.age_restricted is set, the list of planchets + * b) However, when request.persist.age_proof_required is set, the list of planchets * is kappa*num_coins, and there are kappa many coin candidates per denomination. * At each index i, the coins at i+j*num_coins (for j from 0 to kappa) * belong to the same denomination. @@ -945,7 +902,8 @@ phase_check_keys ( * We try to be smart here and handle both variants in one loop * that either runs only once or kappa times. */ - uint8_t kappa = wc->request.persist.age_restricted ? TALER_CNC_KAPPA : 0; + uint8_t kappa = wc->request.persist.age_proof_required ? TALER_CNC_KAPPA : + 0; for (uint8_t k = 0; k < kappa; k++) { @@ -956,7 +914,7 @@ phase_check_keys ( { GNUNET_break_op (0); SET_ERROR (wc, - ERROR_CIPHER_MISMATCH); + WITHDRAW_ERROR_CIPHER_MISMATCH); return; } } @@ -968,10 +926,8 @@ phase_check_keys ( &dk->meta.value)) { GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_AMOUNT_OVERFLOW, - which, - "amount"); + SET_ERROR (wc, + WITHDRAW_ERROR_AMOUNT_OVERFLOW); return; } @@ -981,10 +937,8 @@ phase_check_keys ( &dk->meta.fees.withdraw)) { GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_AMOUNT_OVERFLOW, - which, - "fee"); + SET_ERROR (wc, + WITHDRAW_ERROR_FEE_OVERFLOW); return; } wc->request.persist.denom_serials[i] = dk->meta.serial; @@ -997,10 +951,8 @@ phase_check_keys ( &wc->request.fee)) { GNUNET_break_op (0); - SET_ERROR_WITH_DETAIL (wc, - ERROR_AMOUNT_OVERFLOW, - which, - "amount+fee"); + SET_ERROR (wc, + WITHDRAW_ERROR_AMOUNT_PLUS_FEE_OVERFLOW); return; } @@ -1024,16 +976,17 @@ phase_check_reserve_signature ( &wc->request.amount, &wc->request.fee, &wc->request.persist.h_planchets, - (wc->request.persist.age_restricted) ? + (wc->request.persist.age_proof_required) ? &TEH_age_restriction_config.mask : NULL, - (wc->request.persist.age_restricted) ? wc->request.persist.max_age : 0, + (wc->request.persist.age_proof_required) ? wc->request.persist.max_age : + 0, &wc->request.persist.reserve_pub, &wc->request.persist.reserve_sig)) { GNUNET_break_op (0); SET_ERROR (wc, - ERROR_RESERVE_SIGNATURE_INVALID); + WITHDRAW_ERROR_RESERVE_SIGNATURE_INVALID); return; } @@ -1045,15 +998,14 @@ phase_check_reserve_signature ( &wc->request.amount, &wc->request.fee, &wc->request.persist.h_planchets, - (wc->request.persist.age_restricted) ? - &TEH_age_restriction_config.mask : - NULL, - (wc->request.persist.age_restricted) ? - wc->request.persist.max_age : - 0, + wc->request.persist.age_proof_required + ? &TEH_age_restriction_config.mask + : NULL, + wc->request.persist.age_proof_required + ? wc->request.persist.max_age + : 0, &wc->request.persist.h_commitment); - wc->phase++; } @@ -1100,11 +1052,11 @@ clean_withdraw_rc (struct TEH_RequestContext *rc) GNUNET_free (wc->request.idem.denom_serials); } - if (ERROR_LEGITIMIZATION_RESULT == wc->error.code && - NULL != wc->error.details.legi.response) + if (WITHDRAW_ERROR_LEGITIMIZATION_RESULT == wc->error.code && + NULL != wc->error.details.legitimization_result.response) { - MHD_destroy_response (wc->error.details.legi.response); - wc->error.details.legi.response = NULL; + MHD_destroy_response (wc->error.details.legitimization_result.response); + wc->error.details.legitimization_result.response = NULL; } GNUNET_free (wc); @@ -1121,27 +1073,28 @@ phase_generate_reply_success (struct WithdrawContext *wc) { struct TALER_EXCHANGEDB_Withdraw *db_obj; - db_obj = wc->request.is_idempotent ? - &wc->request.idem : - &wc->request.persist; + db_obj = wc->request.is_idempotent + ? &wc->request.idem + : &wc->request.persist; - if (wc->request.persist.age_restricted) + if (wc->request.persist.age_proof_required) { struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; - enum TALER_ErrorCode ec; - - ec = TALER_exchange_online_withdraw_age_confirmation_sign ( - &TEH_keys_exchange_sign_, - &db_obj->h_commitment, - db_obj->noreveal_index, - &pub, - &sig); - if (TALER_EC_NONE != ec) + enum TALER_ErrorCode ec_confirmation_sign; + + ec_confirmation_sign = + TALER_exchange_online_withdraw_age_confirmation_sign ( + &TEH_keys_exchange_sign_, + &db_obj->h_commitment, + db_obj->noreveal_index, + &pub, + &sig); + if (TALER_EC_NONE != ec_confirmation_sign) { SET_ERROR_WITH_FIELD (wc, - ERROR_CONFIRMATION_SIGN, - ec); + WITHDRAW_ERROR_CONFIRMATION_SIGN, + ec_confirmation_sign); return; } @@ -1196,11 +1149,10 @@ 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); + GNUNET_assert (WITHDRAW_PHASE_GENERATE_REPLY_ERROR == wc->phase); + GNUNET_assert (WITHDRAW_ERROR_NONE != wc->error.code); - if (needs_idempotency_check[wc->error.code] && + if ((0 != (wc->error.code & IDEMPOTENCY_FLAG)) && withdraw_is_idempotent (wc)) { return; @@ -1208,23 +1160,22 @@ phase_generate_reply_error ( switch (wc->error.code) { - case ERROR_MAX: - case ERROR_NONE: + case WITHDRAW_ERROR_NONE: { GNUNET_break (0); - wc->phase = PHASE_RETURN_YES; + wc->phase = WITHDRAW_PHASE_RETURN_YES; return; } - case ERROR_REQUEST_PARAMETER_MALFORMED: + case WITHDRAW_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); + wc->error.details.request_parameter_malformed); break; - case ERROR_KEYS_MISSING: + case WITHDRAW_ERROR_KEYS_MISSING: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, @@ -1233,7 +1184,7 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_DB_FETCH_FAILED: + case WITHDRAW_ERROR_DB_FETCH_FAILED: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, @@ -1242,7 +1193,7 @@ phase_generate_reply_error ( wc->error.details.db_fetch_context)); break; - case ERROR_DB_INVARIANT_FAILURE: + case WITHDRAW_ERROR_DB_INVARIANT_FAILURE: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, @@ -1251,7 +1202,7 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_INTERNAL_INVARIANT_FAILURE: + case WITHDRAW_ERROR_INTERNAL_INVARIANT_FAILURE: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, @@ -1260,7 +1211,7 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_RESERVE_UNKNOWN: + case WITHDRAW_ERROR_RESERVE_UNKNOWN: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, @@ -1268,15 +1219,15 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_DENOMINATION_SIGN: + case WITHDRAW_ERROR_DENOMINATION_SIGN: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, - wc->error.details.ec, + wc->error.details.ec_denomination_sign, NULL)); break; - case ERROR_KYC_REQUIRED: + case WITHDRAW_ERROR_KYC_REQUIRED: finish_loop (wc, TEH_RESPONSE_reply_kyc_required ( wc->rc->connection, @@ -1285,17 +1236,15 @@ phase_generate_reply_error ( 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 WITHDRAW_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: + case WITHDRAW_ERROR_DENOMINATION_EXPIRED: GNUNET_break_op (0); finish_loop (wc, TEH_RESPONSE_reply_expired_denom_pub_hash ( @@ -1305,7 +1254,7 @@ phase_generate_reply_error ( typ2str (wc))); break; - case ERROR_DENOMINATION_VALIDITY_IN_FUTURE: + case WITHDRAW_ERROR_DENOMINATION_VALIDITY_IN_FUTURE: finish_loop (wc, TEH_RESPONSE_reply_expired_denom_pub_hash ( wc->rc->connection, @@ -1314,7 +1263,7 @@ phase_generate_reply_error ( typ2str (wc))); break; - case ERROR_DENOMINATION_REVOKED: + case WITHDRAW_ERROR_DENOMINATION_REVOKED: GNUNET_break_op (0); finish_loop (wc, TALER_MHD_reply_with_ec ( @@ -1323,7 +1272,7 @@ phase_generate_reply_error ( typ2str (wc))); break; - case ERROR_CIPHER_MISMATCH: + case WITHDRAW_ERROR_CIPHER_MISMATCH: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, @@ -1331,7 +1280,7 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_RESERVE_CIPHER_UNKNOWN: + case WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, @@ -1339,7 +1288,7 @@ phase_generate_reply_error ( "cipher")); break; - case ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION: + case WITHDRAW_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION: { char msg[256]; GNUNET_snprintf (msg, @@ -1354,7 +1303,7 @@ phase_generate_reply_error ( break; } - case ERROR_MAXIMUM_AGE_TOO_LARGE: + case WITHDRAW_ERROR_MAXIMUM_AGE_TOO_LARGE: finish_loop (wc, TALER_MHD_REPLY_JSON_PACK ( wc->rc->connection, @@ -1363,58 +1312,64 @@ phase_generate_reply_error ( TALER_EC_EXCHANGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE), GNUNET_JSON_pack_uint64 ( "allowed_maximum_age", - wc->error.details.age.max), + wc->error.details.maximum_age_too_large.max_allowed), GNUNET_JSON_pack_uint64 ( "reserve_birthday", - wc->error.details.age.birthday))); + wc->error.details.maximum_age_too_large.birthday))); break; - case ERROR_AGE_RESTRICTION_REQUIRED: + case WITHDRAW_ERROR_AGE_RESTRICTION_REQUIRED: finish_loop (wc, TEH_RESPONSE_reply_reserve_age_restriction_required ( wc->rc->connection, - wc->error.details.lowest_age)); + wc->error.details.age_restriction_required)); break; - case ERROR_AMOUNT_OVERFLOW: - case ERROR_FEE_OVERFLOW: + case WITHDRAW_ERROR_AMOUNT_OVERFLOW: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_OVERFLOW, - wc->error.details.which)); + "amount")); + case WITHDRAW_ERROR_FEE_OVERFLOW: + finish_loop (wc, + TALER_MHD_reply_with_error ( + wc->rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_OVERFLOW, + "fee")); break; - case ERROR_AMOUNT_PLUS_FEE_OVERFLOW: + case WITHDRAW_ERROR_AMOUNT_PLUS_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)); + "amount+fee")); break; - case ERROR_CONFIRMATION_SIGN: + case WITHDRAW_ERROR_CONFIRMATION_SIGN: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, - wc->error.details.ec, + wc->error.details.ec_confirmation_sign, NULL)); break; - case ERROR_INSUFFICIENT_FUNDS: + case WITHDRAW_ERROR_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->error.details.insufficient_funds, &wc->request.persist.amount_with_fee, &wc->request.persist.reserve_pub)); break; - case ERROR_IDEMPOTENT_PLANCHET: + case WITHDRAW_ERROR_IDEMPOTENT_PLANCHET: { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Idempotent coin in batch, not allowed. Aborting.\n"); @@ -1427,7 +1382,7 @@ phase_generate_reply_error ( break; } - case ERROR_NONCE_RESUSE: + case WITHDRAW_ERROR_NONCE_RESUSE: finish_loop (wc, TALER_MHD_reply_with_error ( wc->rc->connection, @@ -1436,7 +1391,7 @@ phase_generate_reply_error ( NULL)); break; - case ERROR_RESERVE_SIGNATURE_INVALID: + case WITHDRAW_ERROR_RESERVE_SIGNATURE_INVALID: finish_loop (wc, TALER_MHD_reply_with_ec ( wc->rc->connection, @@ -1444,11 +1399,12 @@ phase_generate_reply_error ( 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)); + case WITHDRAW_ERROR_LEGITIMIZATION_RESULT: { + finish_loop ( + wc, + MHD_queue_response (wc->rc->connection, + wc->error.details.legitimization_result.http_status, + wc->error.details.legitimization_result.response)); break; } } @@ -1501,18 +1457,20 @@ withdraw_new_request ( return res; /* For now, we only support cipher "ED25519" for signatures by the reserve */ - if (strcmp ("ED25519", cipher)) + if (0 != strcmp ("ED25519", + cipher)) { GNUNET_break_op (0); - SET_ERROR_WITH_FIELD (wc, - ERROR_RESERVE_CIPHER_UNKNOWN, - cipher); + SET_ERROR_WITH_DETAIL (wc, + WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN, + reserve_cipher_unknown, + cipher); return GNUNET_SYSERR; } - wc->request.persist.age_restricted = ! no_max_age; + wc->request.persist.age_proof_required = ! no_max_age; - if (wc->request.persist.age_restricted) + if (wc->request.persist.age_proof_required) { /* The age value MUST be on the beginning of an age group */ if (wc->request.persist.max_age != @@ -1522,8 +1480,8 @@ withdraw_new_request ( GNUNET_break_op (0); SET_ERROR_WITH_DETAIL ( wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, + WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED, + request_parameter_malformed, "max_age must be the lower edge of an age group"); return GNUNET_SYSERR; } @@ -1556,17 +1514,17 @@ withdraw_new_request ( BAIL_IF (num_coins > TALER_MAX_FRESH_COINS, "maximum number of coins that can be withdrawn has been exceeded") - BAIL_IF ((! wc->request.persist.age_restricted) && + BAIL_IF ((! wc->request.persist.age_proof_required) && (num_coins !=array_size), "denoms_h and coin_evs must be arrays of the same size") - BAIL_IF (wc->request.persist.age_restricted && + BAIL_IF (wc->request.persist.age_proof_required && ((TALER_CNC_KAPPA * num_coins) != array_size), "coin_evs must be an array of length " TALER_CNC_KAPPA_STR "*len(denoms_h)") - wc->request.persist.num_coins = num_coins; + wc->request.persist.num_coins = num_coins; wc->request.num_planchets = array_size; } while (0); @@ -1577,8 +1535,8 @@ withdraw_new_request ( { GNUNET_break_op (0); SET_ERROR_WITH_DETAIL (wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, + WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED, + request_parameter_malformed, error); return GNUNET_SYSERR; } @@ -1613,7 +1571,7 @@ withdraw_new_request ( GNUNET_new_array (wc->request.num_planchets, struct TALER_BlindedPlanchet); - /* calculate the hash over the blinded coin envelopes */ + /* Calculate the hash over the blinded coin envelopes */ { struct GNUNET_HashContext *hash_context; @@ -1658,7 +1616,7 @@ withdraw_new_request ( /* Check for duplicate planchets. Technically a bug on * the client side that is harmless for us, but still * not allowed per protocol */ - for (unsigned int i = 0; i < idx; i++) + for (size_t i = 0; i < idx; i++) { if (0 == TALER_blinded_planchet_cmp ( @@ -1667,8 +1625,8 @@ withdraw_new_request ( { GNUNET_break_op (0); SET_ERROR_WITH_DETAIL (wc, - ERROR_REQUEST_PARAMETER_MALFORMED, - hint, + WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED, + request_parameter_malformed, "duplicate planchet"); return GNUNET_SYSERR; } @@ -1679,10 +1637,8 @@ withdraw_new_request ( /* Finally, calculate the hash from all blinded envelopes */ GNUNET_CRYPTO_hash_context_finish (hash_context, &wc->request.persist.h_planchets.hash); - } /* scope of hash_context */ } /* scope of j_denoms_h, j_blinded_coin_evs */ - return GNUNET_OK; } @@ -1698,19 +1654,18 @@ TEH_handler_withdraw ( (void) args; - while (NULL == wc) + if (NULL == wc) { wc = GNUNET_new (struct WithdrawContext); rc->rh_ctx = wc; rc->rh_cleaner = &clean_withdraw_rc; wc->rc = rc; wc->now = GNUNET_TIME_timestamp_get (); - - r = withdraw_new_request (wc, root); + r = withdraw_new_request (wc, + root); if (GNUNET_OK != r) return (GNUNET_SYSERR == r) ? MHD_NO : MHD_YES; - - wc->phase = PHASE_CHECK_KEYS; + wc->phase = WITHDRAW_PHASE_CHECK_KEYS; } while (true) @@ -1719,38 +1674,37 @@ TEH_handler_withdraw ( "%s processing in phase %d\n", typ2str (wc), wc->phase); - switch (wc->phase) { - case PHASE_CHECK_KEYS: + case WITHDRAW_PHASE_CHECK_KEYS: phase_check_keys (wc); break; - case PHASE_CHECK_RESERVE_SIGNATURE: + case WITHDRAW_PHASE_CHECK_RESERVE_SIGNATURE: phase_check_reserve_signature (wc); break; - case PHASE_RUN_LEGI_CHECK: + case WITHDRAW_PHASE_RUN_LEGI_CHECK: phase_run_legi_check (wc); break; - case PHASE_SUSPENDED: + case WITHDRAW_PHASE_SUSPENDED: return MHD_YES; - case PHASE_CHECK_KYC_RESULT: + case WITHDRAW_PHASE_CHECK_KYC_RESULT: phase_check_kyc_result (wc); break; - case PHASE_PREPARE_TRANSACTION: + case WITHDRAW_PHASE_PREPARE_TRANSACTION: phase_prepare_transaction (wc); break; - case PHASE_RUN_TRANSACTION: + case WITHDRAW_PHASE_RUN_TRANSACTION: phase_run_transaction (wc); break; - case PHASE_GENERATE_REPLY_SUCCESS: + case WITHDRAW_PHASE_GENERATE_REPLY_SUCCESS: phase_generate_reply_success (wc); break; - case PHASE_GENERATE_REPLY_ERROR: + case WITHDRAW_PHASE_GENERATE_REPLY_ERROR: phase_generate_reply_error (wc); break; - case PHASE_RETURN_YES: + case WITHDRAW_PHASE_RETURN_YES: return MHD_YES; - case PHASE_RETURN_NO: + case WITHDRAW_PHASE_RETURN_NO: return MHD_NO; } }