diff options
author | Özgür Kesim <oec-taler@kesim.org> | 2022-10-14 18:56:59 +0200 |
---|---|---|
committer | Özgür Kesim <oec-taler@kesim.org> | 2022-10-14 18:56:59 +0200 |
commit | 054e157af8c768062dd0a8e66614da18407fca28 (patch) | |
tree | fcd792c6ed7f4384e9b29f8e200f62837cd00f1e /src/exchange | |
parent | 956e3c3065ddb762dbe01fd720cc5ef403232564 (diff) |
WIP: policy_details handling continued
- policy details generated on deposit/batch-deposit requests
- insert or update of policy details in the DB
- accumulation of amounts of multiple deposits for the same
policy_details
Diffstat (limited to 'src/exchange')
-rw-r--r-- | src/exchange/taler-exchange-httpd.c | 141 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_batch-deposit.c | 91 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_deposit.c | 100 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.c | 166 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_extensions.h | 15 | ||||
-rw-r--r-- | src/exchange/taler-exchange-httpd_metrics.h | 3 |
6 files changed, 316 insertions, 200 deletions
diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index 32cbd8dd..1cbaee96 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1041,138 +1041,6 @@ handle_post_auditors (struct TEH_RequestContext *rc, root); } - -/** - * Handle GET "/extensions/..." requests. - * - * @param rc request context - * @param args array of additional options - * @return MHD result code - */ -static MHD_RESULT -handle_get_extensions (struct TEH_RequestContext *rc, - const char *const args[]) -{ - const struct TALER_Extension *ext = NULL; - - if (NULL == args[0]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/extensions/$EXTENSION"); - } - - ext = TALER_extensions_get_by_name (args[0]); - if (NULL == ext) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/extensions/$EXTENSION unknown"); - } - - if (NULL == ext->policy_get_handler) - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_IMPLEMENTED, - TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, - "GET /extensions/$EXTENSION not supported"); - - return ext->policy_get_handler ( - rc->connection, - &args[1]); -} - - -/* @brief function pointer for TALER_extension.check */ -static enum GNUNET_GenericReturnValue -check_serial_ids ( - struct GNUNET_HashCode serial_ids[], - size_t size) -{ - for (size_t i = 0; i < size; i++) - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got to check serial_id: %s\n", - GNUNET_h2s (&serial_ids[i])); - return GNUNET_OK; -} - - -/** - * Handle POST "/extensions/..." requests. - * - * @param rc request context - * @param root uploaded JSON data - * @param args array of additional options - * @return MHD result code - */ -static MHD_RESULT -handle_post_extensions (struct TEH_RequestContext *rc, - const json_t *root, - const char *const args[]) -{ - const struct TALER_Extension *ext = NULL; - json_t *output; - - if (NULL == args[0]) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/extensions/$EXTENSION"); - } - - ext = TALER_extensions_get_by_name (args[0]); - if (NULL == ext) - { - GNUNET_break_op (0); - return r404 (rc->connection, - "/extensions/$EXTENSION unknown"); - } - - if (NULL == ext->policy_post_handler) - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_NOT_IMPLEMENTED, - TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, - "POST /extensions/$EXTENSION not supported"); - - { - enum GNUNET_GenericReturnValue ret; - struct TALER_PolicyFulfilmentOutcome *outcome; - - ret = ext->policy_post_handler (root, - &args[1], - &check_serial_ids, - &outcome, - &output); - - if (GNUNET_OK != ret) - { - TALER_policy_fulfilment_outcome_free (outcome); - TALER_MHD_reply_json_steal ( - rc->connection, - output, - MHD_HTTP_BAD_REQUEST); - } - - /* TODO: deal with outcome */ - { - for (size_t i = 0; i < outcome->len; i++) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got outcome for serial_id: %s, state: %s, amount: %s\n", - GNUNET_h2s (&outcome->positions[i].serial_id), - TALER_policy_fulfilment_state_str ( - outcome->positions[i].state), - TALER_amount_to_string (&outcome->positions[i].new_amount)); - } - } - - } - - return TALER_MHD_reply_json_steal (rc->connection, - output, - MHD_HTTP_OK); -} - - /** * Handle incoming HTTP request. * @@ -1393,15 +1261,8 @@ handle_mhd_request (void *cls, /* extensions endpoints */ { .url = "extensions", - .method = MHD_HTTP_METHOD_GET, - .handler.get = &handle_get_extensions, - .nargs = 4, /* Arbitrary upper bound */ - .nargs_is_upper_bound = true, - }, - { - .url = "extensions", .method = MHD_HTTP_METHOD_POST, - .handler.post = &handle_post_extensions, + .handler.post = &TEH_extensions_post_handler, .nargs = 4, /* Arbitrary upper bound */ .nargs_is_upper_bound = true, }, diff --git a/src/exchange/taler-exchange-httpd_batch-deposit.c b/src/exchange/taler-exchange-httpd_batch-deposit.c index d4a9666e..14267e2a 100644 --- a/src/exchange/taler-exchange-httpd_batch-deposit.c +++ b/src/exchange/taler-exchange-httpd_batch-deposit.c @@ -90,8 +90,12 @@ struct BatchDepositContext * Additional details for policy relevant for this * deposit operation, possibly NULL! */ - json_t *policy_details; - bool has_policy_details; + json_t *policy_json; + + /** + * Will be true if policy_json were provided + */ + bool has_policy; /** * Hash over @e policy_details, might be all zero; @@ -99,6 +103,19 @@ struct BatchDepositContext struct TALER_ExtensionPolicyHashP h_policy; /** + * If @e policy_json was present, the corresponding policy extension + * calculates these details. These will be persisted in the policy_details + * table. + */ + struct TALER_PolicyDetails policy_details; + + /** + * When @e policy_details are persisted, this contains the id of the record + * in the policy_details table. + */ + uint64_t policy_details_serial_id; + + /** * Time when this request was generated. Used, for example, to * assess when (roughly) the income was achieved for tax purposes. * Note that the Exchange will only check that the timestamp is not "too @@ -174,7 +191,7 @@ again: &TEH_keys_exchange_sign_, &bdc->h_contract_terms, &bdc->h_wire, - bdc->has_policy_details ? &bdc->h_policy : NULL, + bdc->has_policy ? &bdc->h_policy : NULL, bdc->exchange_timestamp, bdc->wire_deadline, bdc->refund_deadline, @@ -247,6 +264,21 @@ batch_deposit_transaction (void *cls, bool balance_ok; bool in_conflict; + + /* If the deposit has a policy associated to it, persist it. This will + * insert or update the record. */ + if (dc->has_policy) + { + qs = TEH_plugin->persist_policy_details (TEH_plugin->cls, + &dc->policy_details, + &dc->policy_details_serial_id, + &dc->policy_details.accumulated_total, + &dc->policy_details.fulfillment_state); + if (qs < 0) + return qs; + } + + /* deposit the individutal coins */ for (unsigned int i = 0; i<dc->num_coins; i++) { const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i]; @@ -258,13 +290,19 @@ batch_deposit_transaction (void *cls, mhd_ret); if (qs < 0) return qs; - qs = TEH_plugin->do_deposit (TEH_plugin->cls, - deposit, - known_coin_id, - &dc->h_payto, - &dc->exchange_timestamp, - &balance_ok, - &in_conflict); + + qs = TEH_plugin->do_deposit ( + TEH_plugin->cls, + deposit, + known_coin_id, + &dc->h_payto, + (dc->has_policy) + ? &dc->policy_details_serial_id + : NULL, + &dc->exchange_timestamp, + &balance_ok, + &in_conflict); + if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -474,7 +512,7 @@ parse_coin (struct MHD_Connection *connection, &dc->h_wire, &dc->h_contract_terms, &deposit->coin.h_age_commitment, - dc->has_policy_details ? &dc->h_policy : + dc->has_policy ? &dc->h_policy : NULL, &deposit->coin.denom_pub_hash, dc->timestamp, @@ -501,7 +539,7 @@ parse_coin (struct MHD_Connection *connection, but rather insert them ONCE and then per-coin only use the resulting extension UUID/serial; so the data structure here should be changed once we look at extensions in earnest. */ - deposit->policy_details = dc->policy_details; + deposit->policy_json = dc->policy_json; deposit->timestamp = dc->timestamp; deposit->refund_deadline = dc->refund_deadline; deposit->wire_deadline = dc->wire_deadline; @@ -518,7 +556,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, struct BatchDepositContext dc; json_t *coins; bool no_refund_deadline = true; - bool no_policy_details = true; + bool no_policy_json = true; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("merchant_payto_uri", &dc.payto_uri), @@ -532,8 +570,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, &coins), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("policy", - &dc.policy_details), - &no_policy_details), + &dc.policy_json), + &no_policy_json), GNUNET_JSON_spec_timestamp ("timestamp", &dc.timestamp), GNUNET_JSON_spec_mark_optional ( @@ -564,7 +602,7 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, return MHD_YES; /* failure */ } - dc.has_policy_details = ! no_policy_details; + dc.has_policy = ! no_policy_json; /* validate merchant's wire details (as far as we can) */ { @@ -610,11 +648,26 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, TALER_merchant_wire_signature_hash (dc.payto_uri, &dc.wire_salt, &dc.h_wire); - if (dc.has_policy_details) + + /* handle policy, if present */ + if (dc.has_policy) { - TALER_deposit_policy_hash (dc.policy_details, + const char *error_hint = NULL; + + if (GNUNET_OK != + TALER_extensions_create_policy_details ( + dc.policy_json, + &dc.policy_details, + &error_hint)) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, + error_hint); + + TALER_deposit_policy_hash (dc.policy_json, &dc.h_policy); } + dc.num_coins = json_array_size (coins); if (0 == dc.num_coins) { @@ -651,6 +704,8 @@ TEH_handler_batch_deposit (struct TEH_RequestContext *rc, GNUNET_JSON_parse_free (spec); return (GNUNET_NO == res) ? MHD_YES : MHD_NO; } + + /* FIXME: sum all contributions for the policy_details.accumulated_total */ } dc.exchange_timestamp = GNUNET_TIME_timestamp_get (); diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 12741c6b..f44775c0 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -132,6 +132,12 @@ struct DepositContext */ uint64_t known_coin_id; + /** + * When deposit->has_policy is true, and deposit->policy_details are + * persisted, this contains the id of the record in the policy_details table. + */ + uint64_t policy_details_serial_id; + }; @@ -165,14 +171,36 @@ deposit_transaction (void *cls, if (qs < 0) return qs; + /* If the deposit has a policy associated to it, persist it. This will + * insert or update the record. */ + if (dc->deposit->has_policy) + { + struct TALER_Amount accumulated_total; + enum TALER_PolicyFulfillmentState fulfillment_state; + qs = TEH_plugin->persist_policy_details (TEH_plugin->cls, + &dc->deposit->policy_details, + &dc->policy_details_serial_id, + &accumulated_total, + &fulfillment_state); + + /* FIXME: what to do with accumulated_total and fulfillment_state ? */ + + if (qs < 0) + return qs; + } + + qs = TEH_plugin->do_deposit ( + TEH_plugin->cls, + dc->deposit, + dc->known_coin_id, + &dc->h_payto, + (dc->deposit->has_policy) + ? &dc->policy_details_serial_id + : NULL, + &dc->exchange_timestamp, + &balance_ok, + &in_conflict); - qs = TEH_plugin->do_deposit (TEH_plugin->cls, - dc->deposit, - dc->known_coin_id, - &dc->h_payto, - &dc->exchange_timestamp, - &balance_ok, - &in_conflict); if (qs < 0) { if (GNUNET_DB_STATUS_SOFT_ERROR == qs) @@ -219,7 +247,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, struct TALER_EXCHANGEDB_Deposit deposit; const char *payto_uri; struct TALER_ExtensionPolicyHashP *ph_policy = NULL; - bool no_policy_details; + bool no_policy_json; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_string ("merchant_payto_uri", &payto_uri), @@ -255,8 +283,8 @@ TEH_handler_deposit (struct MHD_Connection *connection, &deposit.wire_deadline), GNUNET_JSON_spec_mark_optional ( GNUNET_JSON_spec_json ("policy", - &deposit.policy_details), - &no_policy_details), + &deposit.policy_json), + &no_policy_json), GNUNET_JSON_spec_end () }; struct TALER_MerchantWireHashP h_wire; @@ -284,7 +312,7 @@ TEH_handler_deposit (struct MHD_Connection *connection, } /* set the state of the policy presence in the deposit struct */ - deposit.has_policy_details = ! no_policy_details; + deposit.has_policy = ! no_policy_json; /* validate merchant's wire details (as far as we can) */ { @@ -333,28 +361,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, &h_wire); dc.deposit = &deposit; - /* Check policy */ - if (deposit.has_policy_details) - { - const char *error_hint = NULL; - enum TALER_PolicyFulfilmentState state_on_timeout; - - if (GNUNET_OK != - TALER_extensions_extract_meta_data_from_policy_details ( - deposit.policy_details, - &deposit.policy_serial_id, - &deposit.policy_deadline, - &state_on_timeout, - &error_hint)) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, - error_hint); - - GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state_on_timeout); - deposit.policy_state_on_timeout = state_on_timeout; - } - /* new deposit */ dc.exchange_timestamp = GNUNET_TIME_timestamp_get (); /* check denomination exists and is valid */ @@ -420,13 +426,6 @@ TEH_handler_deposit (struct MHD_Connection *connection, NULL); } - if (deposit.has_policy_details) - { - TALER_deposit_policy_hash (deposit.policy_details, - &deposit.h_policy); - ph_policy = &deposit.h_policy; - } - deposit.deposit_fee = dk->meta.fees.deposit; /* check coin signature */ switch (dk->denom_pub.cipher) @@ -463,6 +462,27 @@ TEH_handler_deposit (struct MHD_Connection *connection, NULL); } + /* Check policy input and create policy details */ + if (deposit.has_policy) + { + const char *error_hint = NULL; + + if (GNUNET_OK != + TALER_extensions_create_policy_details ( + deposit.policy_json, + &deposit.policy_details, + &error_hint)) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, + error_hint); + + TALER_deposit_policy_hash (deposit.policy_json, + &deposit.h_policy); + ph_policy = &deposit.h_policy; + } + + TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++; if (GNUNET_OK != TALER_wallet_deposit_verify (&deposit.amount_with_fee, diff --git a/src/exchange/taler-exchange-httpd_extensions.c b/src/exchange/taler-exchange-httpd_extensions.c index e8d3e4af..8e8f0e01 100644 --- a/src/exchange/taler-exchange-httpd_extensions.c +++ b/src/exchange/taler-exchange-httpd_extensions.c @@ -14,7 +14,7 @@ */ /** * @file taler-exchange-httpd_extensions.c - * @brief Handle extensions (age-restriction, peer2peer) + * @brief Handle extensions (age-restriction, policy extensions) * @author Özgür Kesim */ #include "platform.h" @@ -232,4 +232,168 @@ TEH_extensions_done () } +/* + * @brief Execute database transactions for /extensions/policy_* POST requests. + * + * @param cls a `struct TALER_PolicyFulfillmentOutcome` + * @param connection MHD request context + * @param[out] mhd_ret set to MHD status on error + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +policy_fulfillment_transaction ( + void *cls, + struct MHD_Connection *connection, + MHD_RESULT *mhd_ret) +{ + struct TALER_PolicyFulfillmentTransactionData *fulfillment = cls; + + return TEH_plugin->add_policy_fulfillment_proof (TEH_plugin->cls, + fulfillment); +} + + +MHD_RESULT +TEH_extensions_post_handler ( + struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[]) +{ + const struct TALER_Extension *ext = NULL; + json_t *output; + struct TALER_PolicyDetails *policy_details = NULL; + size_t policy_details_count = 0; + + + if (NULL == args[0]) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "/extensions/$EXTENSION"); + } + + ext = TALER_extensions_get_by_name (args[0]); + if (NULL == ext) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "/extensions/$EXTENSION unknown"); + } + + if (NULL == ext->policy_post_handler) + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_IMPLEMENTED, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "POST /extensions/$EXTENSION not supported"); + + /* Extract hash_codes and retrieve related policy_details from the DB */ + { + enum GNUNET_GenericReturnValue ret; + enum GNUNET_DB_QueryStatus qs; + const char *error_msg; + struct GNUNET_HashCode *hcs; + size_t len; + json_t*val; + size_t idx; + json_t *jhash_codes = json_object_get (root, + "policy_hash_codes"); + if (! json_is_array (jhash_codes)) + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + "policy_hash_codes are missing"); + + len = json_array_size (jhash_codes); + hcs = GNUNET_new_array (len, + struct GNUNET_HashCode); + policy_details = GNUNET_new_array (len, + struct TALER_PolicyDetails); + + json_array_foreach (jhash_codes, idx, val) + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, &hcs[idx]), + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (val, + spec, + &error_msg, + NULL); + if (GNUNET_OK != ret) + break; + + qs = TEH_plugin->get_policy_details (TEH_plugin->cls, + &hcs[idx], + &policy_details[idx]); + if (qs < 0) + { + error_msg = "a policy_hash_code couldn't be found"; + break; + } + } + + GNUNET_free (hcs); + if (GNUNET_OK != ret) + { + GNUNET_free (policy_details); + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, + error_msg); + } + } + + + { + enum GNUNET_GenericReturnValue ret; + + ret = ext->policy_post_handler (root, + &args[1], + policy_details, + policy_details_count, + &output); + + if (GNUNET_OK != ret) + { + TALER_MHD_reply_json_steal ( + rc->connection, + output, + MHD_HTTP_BAD_REQUEST); + } + + /* execute fulfillment transaction */ + { + MHD_RESULT mhd_ret; + struct TALER_PolicyFulfillmentTransactionData fulfillment = { + .proof = root, + .timestamp = GNUNET_TIME_timestamp_get (), + .details = policy_details, + .details_count = policy_details_count + }; + + if (GNUNET_OK != + TEH_DB_run_transaction (rc->connection, + "execute policy fulfillment", + TEH_MT_REQUEST_POLICY_FULFILMENT, + &mhd_ret, + &policy_fulfillment_transaction, + &fulfillment)) + { + json_decref (output); + return mhd_ret; + } + } + } + + return TALER_MHD_reply_json_steal (rc->connection, + output, + MHD_HTTP_OK); +} + + /* end of taler-exchange-httpd_extensions.c */ diff --git a/src/exchange/taler-exchange-httpd_extensions.h b/src/exchange/taler-exchange-httpd_extensions.h index 4659b653..e435f8f0 100644 --- a/src/exchange/taler-exchange-httpd_extensions.h +++ b/src/exchange/taler-exchange-httpd_extensions.h @@ -40,4 +40,19 @@ TEH_extensions_init (void); void TEH_extensions_done (void); + +/** + * Handle POST "/extensions/..." requests. + * + * @param rc request context + * @param root uploaded JSON data + * @param args array of additional options + * @return MHD result code + */ +MHD_RESULT +TEH_extensions_post_handler ( + struct TEH_RequestContext *rc, + const json_t *root, + const char *const args[]); + #endif diff --git a/src/exchange/taler-exchange-httpd_metrics.h b/src/exchange/taler-exchange-httpd_metrics.h index c1b7326f..5acd3605 100644 --- a/src/exchange/taler-exchange-httpd_metrics.h +++ b/src/exchange/taler-exchange-httpd_metrics.h @@ -44,7 +44,8 @@ enum TEH_MetricTypeRequest TEH_MT_REQUEST_IDEMPOTENT_MELT = 10, TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11, TEH_MT_REQUEST_BATCH_DEPOSIT = 12, - TEH_MT_REQUEST_COUNT = 13 /* MUST BE LAST! */ + TEH_MT_REQUEST_POLICY_FULFILMENT = 13, + TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */ }; /** |