From 956e3c3065ddb762dbe01fd720cc5ef403232564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96zg=C3=BCr=20Kesim?= Date: Sun, 9 Oct 2022 22:11:09 +0200 Subject: [PATCH] WIP: policy handling advancements - introduction of policy states. - introduction of default state on timeout. - introduction of TALER_PolicyFulfilmentOutcome - parsing for policy_details now also provides default state on timeout. - introduction of check_serial as preflight func before heavy operations in policy post handler - brandt auction now returns outcomes. --- src/exchange/taler-exchange-httpd.c | 62 ++++- src/exchange/taler-exchange-httpd_deposit.c | 15 +- src/exchangedb/exchange-0001-part.sql | 14 +- src/exchangedb/irbt_callbacks.c | 9 +- src/exchangedb/lrbt_callbacks.c | 15 +- src/exchangedb/plugin_exchangedb_postgres.c | 13 +- src/exchangedb/procedures.sql | 5 +- .../age_restriction/age_restriction.c | 4 +- src/extensions/extensions.c | 72 ++++- .../policy_brandt_vickrey_auction.c | 248 +++++++++++++----- src/include/taler_exchangedb_plugin.h | 8 +- src/include/taler_extensions.h | 149 +++++++++-- 12 files changed, 492 insertions(+), 122 deletions(-) diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c index d97c68e33..32cbd8dd1 100644 --- a/src/exchange/taler-exchange-httpd.c +++ b/src/exchange/taler-exchange-httpd.c @@ -1070,18 +1070,32 @@ handle_get_extensions (struct TEH_RequestContext *rc, "/extensions/$EXTENSION unknown"); } - if (NULL == ext->http_get_handler) + 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->http_get_handler ( + 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. * @@ -1096,6 +1110,7 @@ handle_post_extensions (struct TEH_RequestContext *rc, const char *const args[]) { const struct TALER_Extension *ext = NULL; + json_t *output; if (NULL == args[0]) { @@ -1112,16 +1127,49 @@ handle_post_extensions (struct TEH_RequestContext *rc, "/extensions/$EXTENSION unknown"); } - if (NULL == ext->http_post_handler) + 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"); - return ext->http_post_handler ( - rc->connection, - root, - &args[1]); + { + 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); } diff --git a/src/exchange/taler-exchange-httpd_deposit.c b/src/exchange/taler-exchange-httpd_deposit.c index 5d38de82e..12741c6b5 100644 --- a/src/exchange/taler-exchange-httpd_deposit.c +++ b/src/exchange/taler-exchange-httpd_deposit.c @@ -337,15 +337,22 @@ TEH_handler_deposit (struct MHD_Connection *connection, if (deposit.has_policy_details) { const char *error_hint = NULL; + enum TALER_PolicyFulfilmentState state_on_timeout; + if (GNUNET_OK != - TALER_extensions_serial_from_policy_details (deposit.policy_details, - &deposit.policy_serial_id, - &deposit.policy_deadline, - &error_hint)) + 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 */ diff --git a/src/exchangedb/exchange-0001-part.sql b/src/exchangedb/exchange-0001-part.sql index 7e2547db1..4a116d127 100644 --- a/src/exchangedb/exchange-0001-part.sql +++ b/src/exchangedb/exchange-0001-part.sql @@ -561,7 +561,8 @@ CREATE TABLE IF NOT EXISTS policy_details ,serial_id BYTEA PRIMARY KEY CHECK(LENGTH(serial_id)=64) ,policy_options VARCHAR ,deadline INT8 NOT NULL - ,fulfilment_state INT4 NOT NULL CHECK(fulfilment_state between 0 and 3)) + ,timeout_fulfilment_state smallint NOT NULL CHECK(timeout_fulfilment_state in (5, 6)) + ,fulfilment_state smallint NOT NULL CHECK(fulfilment_state between 0 and 6)) PARTITION BY HASH (serial_id); COMMENT ON TABLE policy_details IS 'Policies that were provided with deposits via policy extensions.'; @@ -571,8 +572,17 @@ COMMENT ON COLUMN policy_details.policy_options IS 'JSON object with options set that the exchange needs to consider when executing a deposit. Supported details depend on the policy extensions supported by the exchange.'; COMMENT ON COLUMN policy_details.deadline IS 'Deadline until the policy must be marked as fulfilled or unfulfilled (maybe "forever")'; +COMMENT ON COLUMN policy_details.timeout_fulfilment_state + IS 'State that a pending policy should be put into, once the deadline is reached. Allowed values are 5 (TIMEOUT, transfer coins) or 6 (TIMEOUT, coins refreshable)'; COMMENT ON COLUMN policy_details.fulfilment_state - IS 'State of the fulfilment: 0 (PENDING), 1 (FULFILLED), 2 (NOT FULFILLED), 3 (TIMED OUT)'; + IS 'State of the fulfilment: + - 0 (PENDING) + - 1 (SUCCESS, transfer coins) + - 2 (SUCCESS, coins refreshable) + - 3 (FAILURE, transfer coins) + - 4 (FAILURE, coins refreshable) + - 5 (TIMEOUT, tranfer coins) + - 6 (TIMEOUT, coins refrehsable)'; CREATE TABLE IF NOT EXISTS policy_details_default PARTITION OF policy_details diff --git a/src/exchangedb/irbt_callbacks.c b/src/exchangedb/irbt_callbacks.c index 7c4944183..e9cce43cb 100644 --- a/src/exchangedb/irbt_callbacks.c +++ b/src/exchangedb/irbt_callbacks.c @@ -929,11 +929,16 @@ irbt_cb_table_policy_details (struct PostgresClosure *pg, { struct GNUNET_PQ_QueryParam params[] = { GNUNET_PQ_query_param_uint64 (&td->serial), - NULL == - td->details.policy_details.policy_options ? + NULL == td->details.policy_details.policy_options ? GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_string ( td->details.policy_details.policy_options), + GNUNET_PQ_query_param_timestamp ( + &td->details.policy_details.deadline), + GNUNET_PQ_query_param_uint16 ( + &td->details.policy_details.timeout_fulfilment_state), + GNUNET_PQ_query_param_uint16 ( + &td->details.policy_details.fulfilment_state), GNUNET_PQ_query_param_end }; diff --git a/src/exchangedb/lrbt_callbacks.c b/src/exchangedb/lrbt_callbacks.c index a2c654f4f..5fe0817e0 100644 --- a/src/exchangedb/lrbt_callbacks.c +++ b/src/exchangedb/lrbt_callbacks.c @@ -1456,7 +1456,10 @@ lrbt_cb_table_policy_details (void *cls, GNUNET_PQ_result_spec_timestamp ("deadline", &td.details.policy_details. deadline), - GNUNET_PQ_result_spec_uint64 ("fulfilment_state", + GNUNET_PQ_result_spec_uint16 ("timeout_fulfilment_state", + &td.details.policy_details. + timeout_fulfilment_state), + GNUNET_PQ_result_spec_uint16 ("fulfilment_state", &td.details.policy_details. fulfilment_state), GNUNET_PQ_result_spec_end @@ -1502,16 +1505,16 @@ lrbt_cb_table_policy_fulfilments (void *cls, struct GNUNET_PQ_ResultSpec rs[] = { GNUNET_PQ_result_spec_uint64 ("fulfilment_id", &td.serial), - GNUNET_PQ_result_spec_allow_null ( - GNUNET_PQ_result_spec_string ("fulfilment_proof", - &td.details.policy_fulfilments. - fulfilment_proof), - &no_config), GNUNET_PQ_result_spec_allow_null ( GNUNET_PQ_result_spec_timestamp ("fulfilment_timestamp", &td.details.policy_fulfilments. fulfilment_timestamp), &no_timestamp), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("fulfilment_proof", + &td.details.policy_fulfilments. + fulfilment_proof), + &no_config), GNUNET_PQ_result_spec_end }; diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index ebac70eab..415417cbc 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -822,8 +822,8 @@ prepare_statements (struct PostgresClosure *pg) ",out_balance_ok AS balance_ok" ",out_conflict AS conflicted" " FROM exchange_do_deposit" - " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19);", - 19), + " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20);", + 20), /* used in postgres_do_purse_deposit() */ GNUNET_PQ_make_prepare ( "call_purse_deposit", @@ -3933,10 +3933,11 @@ prepare_statements (struct PostgresClosure *pg) ",serial_id" ",policy_options" ",deadline" + ",timeout_fulfilment_state" ",fulfilment_state" ") VALUES " - "($1, $2, $3, $4, $5);", - 5), + "($1, $2, $3, $4, $5, $6);", + 6), GNUNET_PQ_make_prepare ( "insert_into_table_policy_fulfilments", "INSERT INTO policy_fulfilments" @@ -6292,6 +6293,9 @@ postgres_do_deposit ( (deposit->has_policy_details) ? GNUNET_PQ_query_param_timestamp (&deposit->policy_deadline) : GNUNET_PQ_query_param_null (), + (deposit->has_policy_details) + ? GNUNET_PQ_query_param_uint16 (&deposit->policy_state_on_timeout) + : GNUNET_PQ_query_param_null (), GNUNET_PQ_query_param_end }; struct GNUNET_PQ_ResultSpec rs[] = { @@ -14651,6 +14655,7 @@ postgres_insert_records_by_table (void *cls, case TALER_EXCHANGEDB_RT_POLICY_DETAILS: rh = &irbt_cb_table_policy_details; break; + /* TODO: policy_details_fulfilments and policy_fulfilments */ case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: rh = &irbt_cb_table_purse_requests; break; diff --git a/src/exchangedb/procedures.sql b/src/exchangedb/procedures.sql index 3d49bab1e..aeed5de03 100644 --- a/src/exchangedb/procedures.sql +++ b/src/exchangedb/procedures.sql @@ -513,7 +513,8 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit( IN in_policy_blocked BOOLEAN, IN in_policy_details VARCHAR, IN in_policy_serial_id BYTEA, - IN in_policy_deadline INT8, + IN in_policy_deadline SMALLINT, + IN in_policy_timeout_fulfilment_state SMALLINT, OUT out_exchange_timestamp INT8, OUT out_balance_ok BOOLEAN, OUT out_conflict BOOLEAN) @@ -535,11 +536,13 @@ THEN (serial_id ,policy_options ,deadline + ,timeout_fulfilment_state ,fulfilment_state) VALUES (in_policy_serial_id ,in_policy_details ,in_policy_deadline + ,in_policy_timeout_fulfilment_state ,0) -- 0 == pending RETURNING policy_details_serial_id INTO xdi; ELSE diff --git a/src/extensions/age_restriction/age_restriction.c b/src/extensions/age_restriction/age_restriction.c index 576a60391..3c38d0f8a 100644 --- a/src/extensions/age_restriction/age_restriction.c +++ b/src/extensions/age_restriction/age_restriction.c @@ -154,8 +154,8 @@ struct TALER_Extension TE_age_restriction = { /* This extension is not a policy extension */ .parse_policy_details = NULL, - .http_get_handler = NULL, - .http_post_handler = NULL, + .policy_get_handler = NULL, + .policy_post_handler = NULL, }; diff --git a/src/extensions/extensions.c b/src/extensions/extensions.c index 52f9061a6..cfc109400 100644 --- a/src/extensions/extensions.c +++ b/src/extensions/extensions.c @@ -353,13 +353,38 @@ TALER_extensions_load_manifests ( } +/* + * Policy related + */ + +static char *fulfilment2str[] = { + [TALER_PolicyFulfilmentPending] = "Pending", + [TALER_PolicyFulfilmentSuccessTransfer] = "SuccessTransfer", + [TALER_PolicyFulfilmentSuccessRefreshable] = "SuccessRefreshable", + [TALER_PolicyFulfilmentFailureTransfer] = "FailureTransfer", + [TALER_PolicyFulfilmentFailureRefreshable] = "FailureRefreshable", + [TALER_PolicyFulfilmentTimeoutTransfer] = "TimeoutTransfer", + [TALER_PolicyFulfilmentTimeoutRefreshable] = "TimeoutRefreshable", +}; + +const char * +TALER_policy_fulfilment_state_str ( + enum TALER_PolicyFulfilmentState state) +{ + GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state); + return fulfilment2str[state]; +} + + enum GNUNET_GenericReturnValue -TALER_extensions_serial_from_policy_details ( +TALER_extensions_extract_meta_data_from_policy_details ( const json_t *policy_details, struct GNUNET_HashCode *serial, struct GNUNET_TIME_Timestamp *deadline, + enum TALER_PolicyFulfilmentState *state_on_deadline, const char **error_hint) { + enum GNUNET_GenericReturnValue ret; const struct TALER_Extension *extension; const json_t *jtype; const char *type; @@ -399,12 +424,49 @@ TALER_extensions_serial_from_policy_details ( } *deadline = GNUNET_TIME_UNIT_FOREVER_TS; - return extension->parse_policy_details (policy_details, - serial, - deadline, - error_hint); + ret = extension->parse_policy_details (policy_details, + serial, + deadline, + state_on_deadline, + error_hint); + + GNUNET_assert ((TALER_PolicyFulfilmentTimeoutRefreshable == + *state_on_deadline) || + (TALER_PolicyFulfilmentTimeoutTransfer == + *state_on_deadline)); + + return ret; } +struct TALER_PolicyFulfilmentOutcome * +TALER_policy_fulfilment_outcome_new (size_t len) +{ + struct TALER_PolicyFulfilmentOutcome *out; + + out = GNUNET_new (struct TALER_PolicyFulfilmentOutcome); + out->len = len; + out->positions = GNUNET_new_array (len, + struct + TALER_PolicyFulfilmentOutcomePosition); + + return out; +} + + +void +TALER_policy_fulfilment_outcome_free ( + struct TALER_PolicyFulfilmentOutcome *outcome) +{ + if (NULL == outcome) + return; + + if (NULL != outcome->positions) + GNUNET_free (outcome->positions); + + GNUNET_free (outcome); +} + + /* end of extensions.c */ diff --git a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c index 8521711b6..bfd7b813f 100644 --- a/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c +++ b/src/extensions/policy_brandt_vickrey_auction/policy_brandt_vickrey_auction.c @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see */ /** - * @file policy_brandt_vickery_auction.c + * @file policy_brandt_vickrey_auction.c * @brief Extension for replay of auctions of type Brandt * @author Özgür Kesim */ @@ -26,8 +26,8 @@ #include "stdint.h" #include -#define POLICY_AUCTION "policy_brandt_vickery_auction" -#define LOG_PREFIX "[policy_brandt_vickery_auction] " +#define POLICY_AUCTION "policy_brandt_vickrey_auction" +#define LOG_PREFIX "[policy_brandt_vickrey_auction] " #define MAX_RESULT_SIZE 10 * 1024 /* (public) configuration of this extension */ @@ -54,7 +54,7 @@ struct result { uint16_t bidder; uint16_t price_idx; - const char *price; + const char *price; }; /* @@ -73,12 +73,19 @@ struct transcript /* Payto URL */ const char *payto; - /* Number of bidders + 1 (for seller) */ + /* Number of bidders */ uint16_t n; /* (n-1) public keys of bidders */ struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub; + /* Hash of the auction */ + struct GNUNET_HashCode h_auction; + + /* (n-1) calculated serial_ids */ + struct GNUNET_HashCode *serial_ids; + + /* Type of auction, see libbrandt */ uint16_t m; @@ -137,6 +144,25 @@ json_error (json_t **output, }; +/* Create serial as H(bidder_pub, h_auction) */ +static void +calculate_serial ( + struct GNUNET_CRYPTO_EddsaPublicKey *pub, + struct GNUNET_HashCode *hc, + struct GNUNET_HashCode *serial) +{ + struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start (); + GNUNET_CRYPTO_hash_context_read (ctx, + pub, + sizeof(*pub)); + GNUNET_CRYPTO_hash_context_read (ctx, + hc, + sizeof(*hc)); + GNUNET_CRYPTO_hash_context_finish (ctx, + serial); +} + + /* * @brief Parses a given json as transcript. * @@ -165,8 +191,8 @@ parse_transcript (const json_t *jtr, char *perr; unsigned int eline; struct GNUNET_JSON_Specification au_spec[] = { - GNUNET_JSON_spec_bool ("public", &tr->public), - GNUNET_JSON_spec_uint16 ("type", &tr->m), + GNUNET_JSON_spec_bool ("outcome_public", &tr->public), + GNUNET_JSON_spec_uint16 ("auction_type", &tr->m), GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub), GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start), GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round), @@ -187,6 +213,13 @@ parse_transcript (const json_t *jtr, return json_error (jerror, perr); + { + const char *auc_js = json_dumps (auc, JSON_COMPACT); + GNUNET_CRYPTO_hash (auc_js, + strlen (auc_js), + &tr->h_auction); + } + // Prices... { size_t idx; @@ -235,15 +268,23 @@ parse_transcript (const json_t *jtr, tr->n = json_array_size (bidders); - tr->bidder_pub = GNUNET_new_array (tr->n, struct - GNUNET_CRYPTO_EddsaPublicKey); + tr->bidder_pub = GNUNET_new_array ( + tr->n, + struct GNUNET_CRYPTO_EddsaPublicKey); + + tr->serial_ids = GNUNET_new_array ( + tr->n, + struct GNUNET_HashCode); + json_array_foreach (bidders, idx, val) { struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_fixed_auto (NULL, - &(tr->bidder_pub[idx])), + &tr->bidder_pub[idx]), GNUNET_JSON_spec_end (), }; + + /* TODO: cleanup */ if (GNUNET_OK != GNUNET_JSON_parse (val, spec, @@ -252,12 +293,13 @@ parse_transcript (const json_t *jtr, return json_error (jerror, "bidder no %ld public key couldn't be parsed", idx + 1); + + calculate_serial (&tr->bidder_pub[idx], + &tr->h_auction, + &tr->serial_ids[idx]); } } - // TODO: parse and verify signatures from bidders of the auction - - // Messages { size_t nm; @@ -322,7 +364,6 @@ parse_transcript (const json_t *jtr, DONE: - *jerror = NULL; return GNUNET_OK; } @@ -331,7 +372,8 @@ DONE: * @brief replay an auction using the external program * * @param[in] root The original JSON transcript - * @param[in] transcript The transcript object parsed so far + * @param[in,out] transcript The transcript object parsed so far + * @param[out] outcome Outcome object to fill * @param[out] result The JSON result from the program * @return GNUNET_OK on success * @@ -340,8 +382,10 @@ DONE: static enum GNUNET_GenericReturnValue replay_transcript (const json_t*root, struct transcript *tr, + struct TALER_PolicyFulfilmentOutcome **outcome, json_t **result) { + enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR; struct GNUNET_DISK_PipeHandle *pi; struct GNUNET_DISK_PipeHandle *po; const struct GNUNET_DISK_FileHandle *fd; @@ -444,32 +488,106 @@ replay_transcript (const json_t*root, return json_error (result, "internal error"); } + // TODO: check each winner with tr->expected, if applicable + + + // Create the outcome object { - // TODO: check each winner with tr->expected, if applicable - json_object_set (res, "exchange_sig", json_string ( - "sig(priv_E, winners)")); + json_t *w; + size_t idx; + struct TALER_PolicyFulfilmentOutcomePosition *pos; + *outcome = + TALER_policy_fulfilment_outcome_new (tr->n); + + /* Set outcome for all bidders to a default value first */ + for (uint16_t i = 0; in; i++) + { + pos = &((*outcome)->positions[i]); + pos->serial_id = tr->serial_ids[i]; + pos->has_new_amount = false; + pos->state = TALER_PolicyFulfilmentFailureRefreshable; + } + + /* Set the outcome of the winners */ + json_array_foreach (winners, idx, w) + { + enum GNUNET_GenericReturnValue ret; + const char *jerror; + uint16_t bidder; + uint16_t price_idx; + struct TALER_Amount price; + + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint16 ("bidder", + &bidder), + GNUNET_JSON_spec_uint16 ("price_idx", + &price_idx), + TALER_JSON_spec_amount ("price", + BV_config.auction_fee.currency, + &price), + GNUNET_JSON_spec_end () + }; + + ret = GNUNET_JSON_parse (w, + spec, + &jerror, + NULL); + + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX + "couldn't parse output of replay program: %s\n", + jerror); + ret = json_error (result, "internal error"); + goto DONE; + } + + if (bidder > tr->n - 1) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX + "replay program sent a bidder out of range\n"); + ret = json_error (result, "internal error"); + goto DONE; + } + + // Fill the outcome position for this winning bidder. + pos = &((*outcome)->positions[idx]); + pos->has_new_amount = true; + pos->new_amount = price; + pos->state = TALER_PolicyFulfilmentSuccessTransfer; + } + + // TODO: return own result object. + // TODO: sign the result by the exchange + *result = json_copy (res); + json_decref (res); } - - // TODO: return own result object. - *result = json_copy (res); - json_decref (res); } - + ret = GNUNET_OK; } - if (GNUNET_OK != GNUNET_OS_process_wait (proc)) + ret = GNUNET_OS_process_wait (proc); + if (GNUNET_OK != ret) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX "error while launching auction replay program '%s'\n", + LOG_PREFIX + "error waiting for auction replay program '%s'\n", replay_program); json_object_clear (*result); - return json_error (result, "internal error"); + ret = json_error (result, "internal error"); } - - return GNUNET_OK; +DONE: + if (GNUNET_OK != ret) + { + TALER_policy_fulfilment_outcome_free (*outcome); + *outcome = NULL; + } + return ret; } @@ -531,60 +649,58 @@ auction_load_config ( /** - * @brief implements the TALER_Extension.http_get_handler + * @brief implements the TALER_Extension.policy_get_handler */ static MHD_RESULT -auction_http_get_handler ( +auction_policy_get_handler ( struct MHD_Connection *connection, const char *const args[]) { /* TODO: return some meta-data about supported version, limits, etc.*/ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - LOG_PREFIX "auction_http_get_handler not implemented yet\n"); + LOG_PREFIX "auction_policy_get_handler not implemented yet\n"); return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_IMPLEMENTED, TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, - "auction_http_get_handler not implemented yet\n"); + "auction_policy_get_handler not implemented yet\n"); } /** - * @brief implements the TALER_Extension.http_post_handler + * @brief implements the TALER_Extension.policy_post_handler * * TODO: make this non-blocking */ -static MHD_RESULT -auction_http_post_handler ( - struct MHD_Connection *connection, +enum GNUNET_GenericReturnValue +auction_policy_post_handler ( const json_t *root, - const char *const args[]) + const char *const args[], + enum GNUNET_GenericReturnValue (*serial_id_check)(struct GNUNET_HashCode + serial_ids[], size_t size), + struct TALER_PolicyFulfilmentOutcome **outcome, + json_t **output) { struct transcript tr = {}; enum GNUNET_GenericReturnValue ret; - json_t *result1; - json_t *result2; + *outcome = NULL; ret = parse_transcript (root, &tr, - &result1); + output); + + /* TODO: cleanups! */ if (GNUNET_OK != ret) - return TALER_MHD_reply_json_steal (connection, - result1, - MHD_HTTP_BAD_REQUEST); - GNUNET_assert (NULL == result1); + return ret; - ret = replay_transcript (root, - &tr, - &result2); + serial_id_check (tr.serial_ids, tr.n); - return TALER_MHD_reply_json_steal (connection, - result2, - GNUNET_OK == ret? - MHD_HTTP_OK : - MHD_HTTP_BAD_REQUEST); + return replay_transcript (root, + &tr, + outcome, + output); } @@ -594,6 +710,7 @@ auction_http_post_handler ( * @param[in] input The policy_details for this handler during deposit * @param[out] serial On success will contain the serial-ID under which the * @param[out] deadline On success will contain a deadline, might be "forever" + * @param[out] state_on_timeout On success, will be set to the default state that the policy shall be put in case of a timeout. * @param[out] error_hint On error, will contain a hint * exchange should store the policy_details in the policy_details table. * @return GNUNET_OK if the request was OK @@ -603,6 +720,7 @@ auction_parse_policy_details ( const json_t *input, struct GNUNET_HashCode *serial, struct GNUNET_TIME_Timestamp *deadline, + enum TALER_PolicyFulfilmentState *state_on_timeout, const char **error_hint) { enum GNUNET_GenericReturnValue ret = GNUNET_NO; @@ -636,22 +754,14 @@ auction_parse_policy_details ( LOG_PREFIX "check of deadline %ld not implemented!\n", deadline->abs_time.abs_value_us); - /* Create serial as H(bidder_pub, h_auction) */ - { - struct GNUNET_HashContext *hc = GNUNET_CRYPTO_hash_context_start (); - GNUNET_CRYPTO_hash_context_read (hc, - &pub, - sizeof(pub)); - GNUNET_CRYPTO_hash_context_read (hc, - &hc, - sizeof(hc)); - GNUNET_CRYPTO_hash_context_finish (hc, - serial); - } + calculate_serial (&pub, &hc, serial); ret = GNUNET_OK; } while(0); + /* In case of timeout, the coin shall be refreshable by the owner */ + *state_on_timeout = TALER_PolicyFulfilmentTimeoutRefreshable; + return ret; } @@ -668,8 +778,8 @@ struct TALER_Extension TE_auction_brandt = { .load_config = &auction_load_config, .manifest = &auction_manifest, .parse_policy_details = &auction_parse_policy_details, - .http_get_handler = &auction_http_get_handler, - .http_post_handler = &auction_http_post_handler, + .policy_get_handler = &auction_policy_get_handler, + .policy_post_handler = &auction_policy_post_handler, }; @@ -687,7 +797,7 @@ struct TALER_Extension TE_auction_brandt = { * @return Pointer to TE_auction_brandt */ struct TALER_Extension * -libtaler_extension_policy_brandt_vickery_auction_init (void *arg) +libtaler_extension_policy_brandt_vickrey_auction_init (void *arg) { const struct GNUNET_CONFIGURATION_Handle *cfg = arg; @@ -751,7 +861,7 @@ libtaler_extension_policy_brandt_vickery_auction_init (void *arg) * @return null */ void * -libtaler_extension_policy_brandt_vickery_auction_done (void *arg) +libtaler_extension_policy_brandt_vickrey_auction_done (void *arg) { auction_disable (&TE_auction_brandt); GNUNET_free (replay_program); @@ -761,4 +871,4 @@ libtaler_extension_policy_brandt_vickery_auction_done (void *arg) } -/* end of policy_brandt_vickery_auction.c */ +/* end of policy_brandt_vickrey_auction.c */ diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 10e1a8159..80a19b676 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -531,7 +531,8 @@ struct TALER_EXCHANGEDB_TableData char *policy_options; struct GNUNET_HashCode serial_id; struct GNUNET_TIME_Timestamp deadline; - uint64_t fulfilment_state; + uint16_t timeout_fulfilment_state; + uint16_t fulfilment_state; } policy_details; struct @@ -1464,6 +1465,11 @@ struct TALER_EXCHANGEDB_Deposit */ struct GNUNET_TIME_Timestamp policy_deadline; + /** + * The state that a _pending_ policy should be put into once the timeout triggers + */ + uint16_t policy_state_on_timeout; + /** * Hash over the @e policy_details. Only filled if has_policy_details is diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index b2d256783..5a5841e52 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -53,6 +53,10 @@ struct TALER_Extensions const struct TALER_Extension *extension; }; +/* Forward declarations */ +enum TALER_PolicyFulfilmentState; +struct TALER_PolicyFulfilmentOutcome; + /* * @brief Represents the implementation of an extension. * @@ -159,6 +163,7 @@ struct TALER_Extension * Policy related handlers * ========================= */ + /** * @brief Handler to check a policy. Can be NULL; * @@ -166,44 +171,55 @@ struct TALER_Extension * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler * will be called before the deposit transaction. * - * @param[in] policy_details Details about the policy, provided by the client - * during a deposit request. + * @param[in] policy_details Details about the policy, provided by the client + * during a deposit request. * @param[out] serial On success, will contain the serial-ID under which the - * exchange should save the policy_details in the deposit table. + * exchange should save the policy_details in the deposit table. * @param[out] deadline On success, set to the deadline until the policy must - * be fulfilled. Might be "forever". This value is used by an external + * be fulfilled. Might be "forever". This value is used by an + * external mechanism to detect timeouts. + * @param[out] state_on_timeout On GNUNET_OK, which state shall the + * fulfilment of this policy be put in. MUST be either + * TALER_PolicyFulfilmentTimeoutTransfer or + * TALER_PolicyFulfilmentTimeoutRefreshable * @param[out] error_hint On error, will contain a hint - * mechanism to detect timeouts. - * @return GNUNET_OK if the data was accepted by the extension. + * @return GNUNET_OK if the data was accepted by the extension. */ enum GNUNET_GenericReturnValue (*parse_policy_details)( const json_t *policy_details, struct GNUNET_HashCode *serial, struct GNUNET_TIME_Timestamp *deadline, + enum TALER_PolicyFulfilmentState *state_on_timeout, const char **error_hint); /** - * @brief Handler for POST-requests to the /policy/$name endpoint. Can be NULL. + * @brief Handler for POST-requests to the /extensions/$name endpoint. Can be NULL. * - * @param connection The current connection - * @param root The JSON body from the request - * @param args Additional query parameters of the request. - * @return MDH result + * @param[in] root The JSON body from the request + * @param[in] args Additional query parameters of the request. + * @param[in] serial_id_check Helper function to check the presence of serial_ids in policy_details. Can be used by the handler to ensure the presence of entries before starting calculations. + * @param[out] outcome Result of the operation. Must be freed via TALER_policy_fulfilment_outcome_free after use. + * @param[out] output JSON output to return to the client + * @return GNUNET_OK on success. */ - MHD_RESULT (*http_post_handler)( - struct MHD_Connection *connection, + enum GNUNET_GenericReturnValue (*policy_post_handler)( const json_t *root, - const char *const args[]); + const char *const args[], + enum GNUNET_GenericReturnValue (*serial_id_check)(struct GNUNET_HashCode + serial_ids[], size_t + size), + struct TALER_PolicyFulfilmentOutcome **outcome, + json_t **output); /** - * @brief Handler for GET-requests to the /policy/$name endpoint. Can be NULL. + * @brief Handler for GET-requests to the /extensions/$name endpoint. Can be NULL. * * @param connection The current connection * @param root The JSON body from the request * @param args Additional query parameters of the request. * @return MDH result */ - MHD_RESULT (*http_get_handler)( + MHD_RESULT (*policy_get_handler)( struct MHD_Connection *connection, const char *const args[]); }; @@ -382,28 +398,123 @@ TALER_extensions_get_age_restriction_mask (); /* * =================================== - * Policy extensions related functions + * Policy extensions related API * =================================== */ + /* - * @brief Finds the extension for a given policy + * @brief Describes the states of fulfilment of a policy bound to a deposit + */ +enum TALER_PolicyFulfilmentState +{ + /* Initial state of the policy */ + TALER_PolicyFulfilmentPending = 0, + + /* + * Policy provably fulfilled. The semantics of the policy require that + * the exchange MUST transfer amount in the associated deposit to the + * payto-URI */ + TALER_PolicyFulfilmentSuccessTransfer = 1, + + /* + * Policy provably fulfilled. The semantics of the policy require that + * the coins' value in the associated deposit remains and the owner can + * refresh them. */ + TALER_PolicyFulfilmentSuccessRefreshable = 2, + + /* + * Policy provably UNfulfilled. The semantics of the policy require + * that the exchange MUST transfer amount in the associated deposit to + * the payto-URI. */ + TALER_PolicyFulfilmentFailureTransfer = 3, + + /* + * Policy provably UNfulfilled. The semantics of the policy require that + * the coins' value in the associated deposit remains and the owner can + * refresh them. */ + TALER_PolicyFulfilmentFailureRefreshable = 4, + + /* + * Policy timed out. The semantics of the policy require that the + * exchange MUST transfer amount in the associated deposit to the + * payto-URI. */ + TALER_PolicyFulfilmentTimeoutTransfer = 5, + + /* + * Policy timed out. The semantics of the policy require that the + * coins' value in the associated deposit remains and the owner can + * refresh them. */ + TALER_PolicyFulfilmentTimeoutRefreshable = 6, + + TALER_PolicyFulfilmentStateMax = TALER_PolicyFulfilmentTimeoutRefreshable +}; + + +const char * +TALER_policy_fulfilment_state_str (enum TALER_PolicyFulfilmentState state); + +/* + * @brief Respresents the outcome of a policy fulfilment evaluation + * returned by a http_post_handler. + */ +struct TALER_PolicyFulfilmentOutcome +{ + size_t len; + struct TALER_PolicyFulfilmentOutcomePosition + { + /* Identifies the particular policy in the policy_details */ + struct GNUNET_HashCode serial_id; + + /* The state that the policy should be be put into. */ + enum TALER_PolicyFulfilmentState state; + + /* If @e has_new_amount is true, the actual amount to be transfered + * according to the @e state is not taken from the associated deposit + * entry, but provided with @new_amount + */ + bool has_new_amount; + struct TALER_Amount new_amount; + + } *positions; +}; + + +/* + * @brief allocate a TALER_PolicyFulfilmentOutcome + */ +struct TALER_PolicyFulfilmentOutcome * +TALER_policy_fulfilment_outcome_new (size_t len); + +/* + * @brief free the content of a TALER_PolicyFulfilmentOutcome + */ +void +TALER_policy_fulfilment_outcome_free ( + struct TALER_PolicyFulfilmentOutcome *outcome); + + +/* + * @brief Extracts meta information from policy_details * * @param[in] policy_details JSON of the policy detail from a deposit request * @param[out] serial On GNUNET_OK, the hash code that should be used to save the policy_details in the policy_details table * @param[out] deadline On GNUNET_OK, the deadline that should be saved in the policy_details table + * @param[out] state_on_timeout On GNUNET_OK, which state shall the fulfilment of this policy be put in * @param[out] error_hint On GNUNET_SYSERR, will contain a hint for the reason why it failed * @return GNUNET_OK on success, with *extension set to the correct extension. * GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was * invalid, with *error_hint maybe non-NULL. */ enum GNUNET_GenericReturnValue -TALER_extensions_serial_from_policy_details ( +TALER_extensions_extract_meta_data_from_policy_details ( const json_t *policy_details, struct GNUNET_HashCode *serial, struct GNUNET_TIME_Timestamp *deadline, + enum TALER_PolicyFulfilmentState *state_on_timeout, const char **error_hint); + /* * ================================ * Merchant refund policy