From 377d74134b83a21b2eab9b2317e1258e96cb2579 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 22 Jan 2023 20:12:40 +0100 Subject: [PATCH] WIP: age-withdraw: http-handler implmemented --- .../taler-exchange-httpd_age-withdraw.c | 258 ++++++++---------- src/exchange/taler-exchange-httpd_metrics.h | 35 +-- src/include/taler_crypto_lib.h | 59 ++++ src/include/taler_exchangedb_plugin.h | 41 ++- src/include/taler_kyclogic_lib.h | 6 +- src/kyclogic/kyclogic_api.c | 3 + 6 files changed, 238 insertions(+), 164 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_age-withdraw.c b/src/exchange/taler-exchange-httpd_age-withdraw.c index 7b77096ff..4257e4c1d 100644 --- a/src/exchange/taler-exchange-httpd_age-withdraw.c +++ b/src/exchange/taler-exchange-httpd_age-withdraw.c @@ -46,30 +46,27 @@ reply_age_withdraw_success (struct MHD_Connection *connection, { struct TALER_ExchangePublicKeyP pub; struct TALER_ExchangeSignatureP sig; - enum TALER_ErrorCode ec; + enum TALER_ErrorCode ec = + TALER_exchange_online_age_withdraw_confirmation_sign ( + &TEH_keys_exchange_sign_, + ach, + noreveal_index, + &pub, + &sig); - if (TALER_EC_NONE != - (ec = TALER_exchange_online_age_withdraw_confirmation_sign ( - &TEH_keys_exchange_sign_, - ach, - noreveal_index, - &pub, - &sig))) - { + if (TALER_EC_NONE != ec) return TALER_MHD_reply_with_ec (connection, ec, NULL); - } - return 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)); + return 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)); } @@ -84,7 +81,12 @@ struct AgeWithdrawContext struct TALER_EXCHANGEDB_KycStatus kyc; /** - * The commitment request, for n*kappa coins. + * Hash of the wire source URL, needed when kyc is needed. + */ + struct TALER_PaytoHashP h_payto; + + /** + * The data from the age-withdraw request */ struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment; @@ -95,7 +97,6 @@ struct AgeWithdrawContext }; -#if 0 /** * Function called to iterate over KYC-relevant * transaction amounts for a particular time range. @@ -112,28 +113,27 @@ struct AgeWithdrawContext * @param cb_cls closure for @a cb */ static void -withdraw_amount_cb (void *cls, - struct GNUNET_TIME_Absolute limit, - TALER_EXCHANGEDB_KycAmountCallback cb, - void *cb_cls) +age_withdraw_amount_cb (void *cls, + struct GNUNET_TIME_Absolute limit, + TALER_EXCHANGEDB_KycAmountCallback cb, + void *cb_cls) { struct AgeWithdrawContext *awc = cls; enum GNUNET_DB_QueryStatus qs; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Signaling amount %s for KYC check during age-withdrawal\n", - TALER_amount2s (&awc->amount_with_fee)); + TALER_amount2s (&awc->commitment.amount_with_fee)); if (GNUNET_OK != cb (cb_cls, - &awc->amount_with_fee, + &awc->commitment.amount_with_fee, awc->now.abs_time)) return; - qs = TEH_plugin->select_withdraw_amounts_for_kyc_check ( - TEH_plugin->cls, - &wc->h_payto, - limit, - cb, - cb_cls); + qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (TEH_plugin->cls, + &awc->h_payto, + limit, + cb, + cb_cls); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Got %d additional transactions for this age-withdrawal and limit %llu\n", qs, @@ -142,12 +142,7 @@ withdraw_amount_cb (void *cls, } -#endif - - -#if 0 /** - * TODO: REWRITE * Function implementing age withdraw transaction. Runs the * transaction logic; IF it returns a non-error code, the transaction * logic MUST NOT queue a MHD response. IF it returns an hard error, @@ -155,10 +150,10 @@ withdraw_amount_cb (void *cls, * IF it returns the soft error code, the function MAY be called again * to retry and MUST not queue a MHD response. * - * Note that "wc->collectable.sig" is set before entering this function as we + * Note that "awc->commitment.sig" is set before entering this function as we * signed before entering the transaction. * - * @param cls a `struct WithdrawContext *` + * @param cls a `struct AgeWithdrawContext *` * @param connection MHD request which triggered the transaction * @param[out] mhd_ret set to MHD response status for @a connection, * if transaction failed (!) @@ -169,21 +164,19 @@ age_withdraw_transaction (void *cls, struct MHD_Connection *connection, MHD_RESULT *mhd_ret) { - struct AgeWithdrawContext *wc = cls; + struct AgeWithdrawContext *awc = cls; enum GNUNET_DB_QueryStatus qs; bool found = false; bool balance_ok = false; - bool nonce_ok = false; uint64_t ruuid; - const struct TALER_CsNonce *nonce; - const struct TALER_BlindedPlanchet *bp; - wc->now = GNUNET_TIME_timestamp_get (); + awc->now = GNUNET_TIME_timestamp_get (); qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls, - &wc->collectable.reserve_pub, - &wc->h_payto); + &awc->commitment.reserve_pub, + &awc->h_payto); if (qs < 0) return qs; + /* If no results, reserve was created by merge, in which case no KYC check is required as the merge already did that. */ @@ -192,46 +185,42 @@ age_withdraw_transaction (void *cls, const char *kyc_required; kyc_required = TALER_KYCLOGIC_kyc_test_required ( - TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW, - &wc->h_payto, + TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW, + &awc->h_payto, TEH_plugin->select_satisfied_kyc_processes, TEH_plugin->cls, - &withdraw_amount_cb, - wc); + &age_withdraw_amount_cb, + awc); + if (NULL != kyc_required) { /* insert KYC requirement into DB! */ - wc->kyc.ok = false; + awc->kyc.ok = false; return TEH_plugin->insert_kyc_requirement_for_account ( TEH_plugin->cls, kyc_required, - &wc->h_payto, - &wc->kyc.requirement_row); + &awc->h_payto, + &awc->kyc.requirement_row); } } - wc->kyc.ok = true; - bp = &wc->blinded_planchet; - nonce = (TALER_DENOMINATION_CS == bp->cipher) - ? &bp->details.cs_blinded_planchet.nonce - : NULL; - qs = TEH_plugin->do_withdraw (TEH_plugin->cls, - nonce, - &wc->collectable, - wc->now, - &found, - &balance_ok, - &nonce_ok, - &ruuid); + + awc->kyc.ok = true; + qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls, + &awc->commitment, + awc->now, + &found, + &balance_ok, + &ruuid); if (0 > qs) { if (GNUNET_DB_STATUS_HARD_ERROR == qs) *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_FETCH_FAILED, - "do_withdraw"); + "do_age_withdraw"); return qs; } - if (! found) + else if (! found) { *mhd_ret = TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, @@ -239,34 +228,23 @@ age_withdraw_transaction (void *cls, NULL); return GNUNET_DB_STATUS_HARD_ERROR; } - if (! balance_ok) + else if (! balance_ok) { TEH_plugin->rollback (TEH_plugin->cls); *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance ( connection, - TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS, - &wc->collectable.amount_with_fee, - &wc->collectable.reserve_pub); - return GNUNET_DB_STATUS_HARD_ERROR; - } - if (! nonce_ok) - { - TEH_plugin->rollback (TEH_plugin->cls); - *mhd_ret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_CONFLICT, - TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE, - NULL); + TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS, + &awc->commitment.amount_with_fee, + &awc->commitment.reserve_pub); return GNUNET_DB_STATUS_HARD_ERROR; } + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) - TEH_METRICS_num_success[TEH_MT_SUCCESS_WITHDRAW]++; + TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW]++; return qs; } -#endif - - /** * Check if the @a rc is replayed and we already have an * answer. If so, replay the existing answer and return the @@ -284,10 +262,12 @@ request_is_idempotent (struct TEH_RequestContext *rc, MHD_RESULT *mret) { enum GNUNET_DB_QueryStatus qs; + struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment; qs = TEH_plugin->get_age_withdraw_info (TEH_plugin->cls, - &awc->reserve_pub, - &awc->commitment); + &awc->commitment.reserve_pub, + &awc->commitment.h_commitment, + &commitment); if (0 > qs) { GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); @@ -304,16 +284,9 @@ request_is_idempotent (struct TEH_RequestContext *rc, /* generate idempotent reply */ TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_AGE_WITHDRAW]++; - *mret = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_uint64 ("noreveal_index", - &awc->commitment.noreveal_index), - GNUNET_JSON_pack_data_auto ("exchange_sig", - &awc->commitment.sig), - GNUNET_JSON_pack_data_auto ("exchange_pub", - /* TODO:oec: where does the pub come from? */ - &pub)); + *mret = reply_age_withdraw_success (rc->connection, + &commitment.h_commitment, + commitment.noreveal_index); return true; } @@ -323,6 +296,7 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc, const struct TALER_ReservePublicKeyP *reserve_pub, const json_t *root) { + MHD_RESULT mhd_ret; struct AgeWithdrawContext awc; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto ("reserve_sig", @@ -330,12 +304,12 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc, GNUNET_JSON_spec_fixed_auto ("h_commitment", &awc.commitment.h_commitment), TALER_JSON_spec_amount ("amount", - &awc.commitment.amount_with_fee); + TEH_currency, + &awc.commitment.amount_with_fee), GNUNET_JSON_spec_uint8 ("max_age_group", &awc.commitment.max_age_group), GNUNET_JSON_spec_end () }; - enum TALER_ErrorCode ec; memset (&awc, 0, sizeof (awc)); awc.commitment.reserve_pub = *reserve_pub; @@ -352,36 +326,31 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc, return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } - /* If request was made before successfully, return the previous answer */ - if (request_is_idempotent (rc, - &awc, - &mret)) - { - GNUNET_JSON_parse_free (spec); - return mret; - } + do { + /* If request was made before successfully, return the previous answer */ + if (request_is_idempotent (rc, + &awc, + &mhd_ret)) + break; - /* Verify the signature of the request body with the reserve key */ - TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; - if (GNUNET_OK != - TALER_wallet_age_withdraw_verify (&awc.commitment.h_commitment, - &awc.commitment.amount_with_fee, - &awc.commitment.max_age_group, - &awc.commitment.reserve_pub, - &awc.commitment.reserve_sig)) - { - GNUNET_break_op (0); - GNUNET_JSON_parse_free (spec); - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, - NULL); - } - - /* run transaction */ - { - MHD_RESULT mhd_ret; + /* Verify the signature of the request body with the reserve key */ + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; + if (GNUNET_OK != + TALER_wallet_age_withdraw_verify (&awc.commitment.h_commitment, + &awc.commitment.amount_with_fee, + awc.commitment.max_age_group, + &awc.commitment.reserve_pub, + &awc.commitment.reserve_sig)) + { + GNUNET_break_op (0); + mhd_ret = TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID, + NULL); + break; + } + /* Run the transaction */ if (GNUNET_OK != TEH_DB_run_transaction (rc->connection, "run age withdraw", @@ -389,27 +358,24 @@ TEH_handler_age_withdraw (struct TEH_RequestContext *rc, &mhd_ret, &age_withdraw_transaction, &awc)) - { - /* Even if #withdraw_transaction() failed, it may have created a signature - (or we might have done it optimistically above). */ - /* TODO:oec:which function to call here!? */ - TALER_blinded_denom_sig_free (&awc.commitment.sig); - GNUNET_JSON_parse_free (spec); - return mhd_ret; - } - } + break; + + /* Clean up and send back final response */ + GNUNET_JSON_parse_free (spec); + + if (! awc.kyc.ok) + return TEH_RESPONSE_reply_kyc_required (rc->connection, + &awc.h_payto, + &awc.kyc); + + return reply_age_withdraw_success (rc->connection, + &awc.commitment.h_commitment, + awc.commitment.noreveal_index); + } while(0); - /* Clean up and send back final response */ GNUNET_JSON_parse_free (spec); + return mhd_ret; - if (! awc.kyc.ok) - return TEH_RESPONSE_reply_kyc_required (rc->connection, - &awc.h_payto, - &awc.kyc); - - return reply_age_withdraw_success (rc->connection, - &awc.commitment.h_commitment, - awc.commitment.noreveal_index); } diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h index cae371f7c..8f6804355 100644 --- a/src/exchange/taler-exchange-httpd_metrics.h +++ b/src/exchange/taler-exchange-httpd_metrics.h @@ -34,18 +34,20 @@ enum TEH_MetricTypeRequest TEH_MT_REQUEST_OTHER = 0, TEH_MT_REQUEST_DEPOSIT = 1, TEH_MT_REQUEST_WITHDRAW = 2, - TEH_MT_REQUEST_MELT = 3, - TEH_MT_REQUEST_PURSE_CREATE = 4, - TEH_MT_REQUEST_PURSE_MERGE = 5, - TEH_MT_REQUEST_RESERVE_PURSE = 6, - TEH_MT_REQUEST_PURSE_DEPOSIT = 7, - TEH_MT_REQUEST_IDEMPOTENT_DEPOSIT = 8, - TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW = 9, - TEH_MT_REQUEST_IDEMPOTENT_MELT = 10, - TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11, - TEH_MT_REQUEST_BATCH_DEPOSIT = 12, - TEH_MT_REQUEST_POLICY_FULFILLMENT = 13, - TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */ + TEH_MT_REQUEST_AGE_WITHDRAW = 3, + TEH_MT_REQUEST_MELT = 4, + TEH_MT_REQUEST_PURSE_CREATE = 5, + TEH_MT_REQUEST_PURSE_MERGE = 6, + TEH_MT_REQUEST_RESERVE_PURSE = 7, + TEH_MT_REQUEST_PURSE_DEPOSIT = 8, + TEH_MT_REQUEST_IDEMPOTENT_DEPOSIT = 9, + TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW = 10, + TEH_MT_REQUEST_IDEMPOTENT_AGE_WITHDRAW = 11, + TEH_MT_REQUEST_IDEMPOTENT_MELT = 12, + TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 13, + TEH_MT_REQUEST_BATCH_DEPOSIT = 14, + TEH_MT_REQUEST_POLICY_FULFILLMENT = 15, + TEH_MT_REQUEST_COUNT = 16 /* MUST BE LAST! */ }; /** @@ -55,10 +57,11 @@ enum TEH_MetricTypeSuccess { TEH_MT_SUCCESS_DEPOSIT = 0, TEH_MT_SUCCESS_WITHDRAW = 1, - TEH_MT_SUCCESS_BATCH_WITHDRAW = 2, - TEH_MT_SUCCESS_MELT = 3, - TEH_MT_SUCCESS_REFRESH_REVEAL = 4, - TEH_MT_SUCCESS_COUNT = 5 /* MUST BE LAST! */ + TEH_MT_SUCCESS_AGE_WITHDRAW = 2, + TEH_MT_SUCCESS_BATCH_WITHDRAW = 3, + TEH_MT_SUCCESS_MELT = 4, + TEH_MT_SUCCESS_REFRESH_REVEAL = 5, + TEH_MT_SUCCESS_COUNT = 6 /* MUST BE LAST! */ }; /** diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h index 73aac95b5..460ab60d4 100644 --- a/src/include/taler_crypto_lib.h +++ b/src/include/taler_crypto_lib.h @@ -435,6 +435,13 @@ struct TALER_AgeCommitmentPublicKeyP #endif }; +/* + * @brief Hash to represent the commitment to n*kappa blinded keys during a age-withdrawal. + */ +struct TALER_AgeWithdrawCommitmentHashP +{ + struct GNUNET_HashCode hash; +}; /** * @brief Type of online public keys used by the wallet to establish a purse and the associated contract meta data. @@ -3630,6 +3637,40 @@ TALER_wallet_withdraw_verify ( const struct TALER_ReservePublicKeyP *reserve_pub, const struct TALER_ReserveSignatureP *reserve_sig); +/** + * Sign age-withdraw request. + * + * @param h_commitment hash all n*kappa blinded coins in the commitment for the age-withdraw + * @param amount_with_fee amount to debit the reserve for + * @param max_age_group maximum age group that the withdrawn coins must be restricted to + * @param reserve_priv private key to sign with + * @param[out] reserve_sig resulting signature + */ +void +TALER_wallet_age_withdraw_sign ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_Amount *amount_with_fee, + uint32_t max_age_group, + const struct TALER_ReservePrivateKeyP *reserve_priv, + struct TALER_ReserveSignatureP *reserve_sig); + +/** + * Verify an age-withdraw request. + * + * @param h_commitment hash all n*kappa blinded coins in the commitment for the age-withdraw + * @param amount_with_fee amount to debit the reserve for + * @param max_age_group maximum age group that the withdrawn coins must be restricted to + * @param reserve_pub public key of the reserve + * @param reserve_sig resulting signature + * @return #GNUNET_OK if the signature is valid + */ +enum GNUNET_GenericReturnValue +TALER_wallet_age_withdraw_verify ( + const struct TALER_AgeWithdrawCommitmentHashP *h_commitment, + const struct TALER_Amount *amount_with_fee, + uint32_t max_age_group, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_ReserveSignatureP *reserve_sig); /** * Verify exchange melt confirmation. @@ -5752,6 +5793,24 @@ TALER_age_commitment_verify ( const struct TALER_AgeAttestation *attest); +/** + * Create age-withdraw confirmation signature. + * + * @param scb function to call to create the signature + * @param awch age-withdraw commitment that identifies the n*kappa blinded coins + * @param noreveal_index gamma cut-and-choose value chosen by the exchange + * @param[out] pub where to write the exchange public key + * @param[out] sig where to write the exchange signature + * @return #TALER_EC_NONE on success + */ +enum TALER_ErrorCode +TALER_exchange_online_age_withdraw_confirmation_sign ( + TALER_ExchangeSignCallback scb, + const struct TALER_AgeWithdrawCommitmentHashP *awch, + uint32_t noreveal_index, + struct TALER_ExchangePublicKeyP *pub, + struct TALER_ExchangeSignatureP *sig); + /** * @brief helper function to free memory of a struct TALER_AgeCommitment * diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index f59916ef2..2c23f7d64 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1094,7 +1094,7 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment * The exchange's signature of the response. */ struct TALER_ExchangeSignatureP sig; -} +}; /** @@ -3693,6 +3693,45 @@ struct TALER_EXCHANGEDB_Plugin bool *conflict, bool *nonce_reuse); + /** + * Locate the response for a age-withdraw request under a hash that uniquely + * identifies the age-withdraw operation. Used to ensure idempotency of the + * request. + * + * @param cls the @e cls of this struct with the plugin-specific state + * @param reserve_pub public key of the reserve for which the age-withdraw request is made + * @param ach hash that uniquely identifies the age-withdraw operation + * @param[out] awc corresponding details of the previous age-withdraw request if an entry was found + * @return statement execution status + */ + enum GNUNET_DB_QueryStatus + (*get_age_withdraw_info)(void *cls, + const struct TALER_ReservePublicKeyP *reserve_pub, + const struct TALER_AgeWithdrawCommitmentHashP *ach, + struct TALER_EXCHANGEDB_AgeWithdrawCommitment *awc); + + /** + * Perform an age-withdraw operation, checking for sufficient balance + * and possibly persisting the withdrawal details. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param commitment corresponding commitment for the age-withdraw + * @param now current time (rounded) + * @param[out] found set to true if the reserve was found + * @param[out] balance_ok set to true if the balance was sufficient + * @param[out] ruuid set to the reserve's UUID (reserves table row) + * @return query execution status + */ + enum GNUNET_DB_QueryStatus + (*do_age_withdraw)( + void *cls, + const struct TALER_EXCHANGEDB_AgeWithdrawCommitment *commitment, + struct GNUNET_TIME_Timestamp now, + bool *found, + bool *balance_ok, + uint64_t *ruuid); + + /** * Retrieve the details to a policy given by its hash_code * diff --git a/src/include/taler_kyclogic_lib.h b/src/include/taler_kyclogic_lib.h index 60a7c23d2..954df21c2 100644 --- a/src/include/taler_kyclogic_lib.h +++ b/src/include/taler_kyclogic_lib.h @@ -73,8 +73,12 @@ enum TALER_KYCLOGIC_KycTriggerEvent /** * Reserve is being closed by force. */ - TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE = 4 + TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE = 4, + /** + * Customer withdraws coins via age-withdraw. + */ + TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW = 5, }; diff --git a/src/kyclogic/kyclogic_api.c b/src/kyclogic/kyclogic_api.c index 3954ae4ce..67dbc222a 100644 --- a/src/kyclogic/kyclogic_api.c +++ b/src/kyclogic/kyclogic_api.c @@ -180,6 +180,7 @@ TALER_KYCLOGIC_kyc_trigger_from_string (const char *trigger_s, enum TALER_KYCLOGIC_KycTriggerEvent out; } map [] = { { "withdraw", TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW }, + { "age-withdraw", TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW }, { "deposit", TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT }, { "merge", TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE }, { "balance", TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE }, @@ -208,6 +209,8 @@ TALER_KYCLOGIC_kyc_trigger2s (enum TALER_KYCLOGIC_KycTriggerEvent trigger) { case TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW: return "withdraw"; + case TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW: + return "age-withdraw"; case TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT: return "deposit"; case TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE: