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.
This commit is contained in:
Özgür Kesim 2022-10-09 22:11:09 +02:00
parent 18d8dcd1f9
commit 956e3c3065
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
12 changed files with 492 additions and 122 deletions

View File

@ -1070,18 +1070,32 @@ handle_get_extensions (struct TEH_RequestContext *rc,
"/extensions/$EXTENSION unknown"); "/extensions/$EXTENSION unknown");
} }
if (NULL == ext->http_get_handler) if (NULL == ext->policy_get_handler)
return TALER_MHD_reply_with_error (rc->connection, return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_IMPLEMENTED, MHD_HTTP_NOT_IMPLEMENTED,
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
"GET /extensions/$EXTENSION not supported"); "GET /extensions/$EXTENSION not supported");
return ext->http_get_handler ( return ext->policy_get_handler (
rc->connection, rc->connection,
&args[1]); &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. * Handle POST "/extensions/..." requests.
* *
@ -1096,6 +1110,7 @@ handle_post_extensions (struct TEH_RequestContext *rc,
const char *const args[]) const char *const args[])
{ {
const struct TALER_Extension *ext = NULL; const struct TALER_Extension *ext = NULL;
json_t *output;
if (NULL == args[0]) if (NULL == args[0])
{ {
@ -1112,16 +1127,49 @@ handle_post_extensions (struct TEH_RequestContext *rc,
"/extensions/$EXTENSION unknown"); "/extensions/$EXTENSION unknown");
} }
if (NULL == ext->http_post_handler) if (NULL == ext->policy_post_handler)
return TALER_MHD_reply_with_error (rc->connection, return TALER_MHD_reply_with_error (rc->connection,
MHD_HTTP_NOT_IMPLEMENTED, MHD_HTTP_NOT_IMPLEMENTED,
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
"POST /extensions/$EXTENSION not supported"); "POST /extensions/$EXTENSION not supported");
return ext->http_post_handler ( {
rc->connection, enum GNUNET_GenericReturnValue ret;
root, struct TALER_PolicyFulfilmentOutcome *outcome;
&args[1]);
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);
} }

View File

@ -337,15 +337,22 @@ TEH_handler_deposit (struct MHD_Connection *connection,
if (deposit.has_policy_details) if (deposit.has_policy_details)
{ {
const char *error_hint = NULL; const char *error_hint = NULL;
enum TALER_PolicyFulfilmentState state_on_timeout;
if (GNUNET_OK != if (GNUNET_OK !=
TALER_extensions_serial_from_policy_details (deposit.policy_details, TALER_extensions_extract_meta_data_from_policy_details (
&deposit.policy_serial_id, deposit.policy_details,
&deposit.policy_deadline, &deposit.policy_serial_id,
&error_hint)) &deposit.policy_deadline,
&state_on_timeout,
&error_hint))
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST, MHD_HTTP_BAD_REQUEST,
TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED, TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
error_hint); error_hint);
GNUNET_assert (TALER_PolicyFulfilmentStateMax >= state_on_timeout);
deposit.policy_state_on_timeout = state_on_timeout;
} }
/* new deposit */ /* new deposit */

View File

@ -561,7 +561,8 @@ CREATE TABLE IF NOT EXISTS policy_details
,serial_id BYTEA PRIMARY KEY CHECK(LENGTH(serial_id)=64) ,serial_id BYTEA PRIMARY KEY CHECK(LENGTH(serial_id)=64)
,policy_options VARCHAR ,policy_options VARCHAR
,deadline INT8 NOT NULL ,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); PARTITION BY HASH (serial_id);
COMMENT ON TABLE policy_details COMMENT ON TABLE policy_details
IS 'Policies that were provided with deposits via policy extensions.'; 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.'; 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 COMMENT ON COLUMN policy_details.deadline
IS 'Deadline until the policy must be marked as fulfilled or unfulfilled (maybe "forever")'; 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 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 CREATE TABLE IF NOT EXISTS policy_details_default
PARTITION OF policy_details PARTITION OF policy_details

View File

@ -929,11 +929,16 @@ irbt_cb_table_policy_details (struct PostgresClosure *pg,
{ {
struct GNUNET_PQ_QueryParam params[] = { struct GNUNET_PQ_QueryParam params[] = {
GNUNET_PQ_query_param_uint64 (&td->serial), GNUNET_PQ_query_param_uint64 (&td->serial),
NULL == NULL == td->details.policy_details.policy_options ?
td->details.policy_details.policy_options ?
GNUNET_PQ_query_param_null () : GNUNET_PQ_query_param_null () :
GNUNET_PQ_query_param_string ( GNUNET_PQ_query_param_string (
td->details.policy_details.policy_options), 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 GNUNET_PQ_query_param_end
}; };

View File

@ -1456,7 +1456,10 @@ lrbt_cb_table_policy_details (void *cls,
GNUNET_PQ_result_spec_timestamp ("deadline", GNUNET_PQ_result_spec_timestamp ("deadline",
&td.details.policy_details. &td.details.policy_details.
deadline), 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. &td.details.policy_details.
fulfilment_state), fulfilment_state),
GNUNET_PQ_result_spec_end GNUNET_PQ_result_spec_end
@ -1502,16 +1505,16 @@ lrbt_cb_table_policy_fulfilments (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
GNUNET_PQ_result_spec_uint64 ("fulfilment_id", GNUNET_PQ_result_spec_uint64 ("fulfilment_id",
&td.serial), &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_allow_null (
GNUNET_PQ_result_spec_timestamp ("fulfilment_timestamp", GNUNET_PQ_result_spec_timestamp ("fulfilment_timestamp",
&td.details.policy_fulfilments. &td.details.policy_fulfilments.
fulfilment_timestamp), fulfilment_timestamp),
&no_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 GNUNET_PQ_result_spec_end
}; };

View File

@ -822,8 +822,8 @@ prepare_statements (struct PostgresClosure *pg)
",out_balance_ok AS balance_ok" ",out_balance_ok AS balance_ok"
",out_conflict AS conflicted" ",out_conflict AS conflicted"
" FROM exchange_do_deposit" " FROM exchange_do_deposit"
" ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19);", " ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20);",
19), 20),
/* used in postgres_do_purse_deposit() */ /* used in postgres_do_purse_deposit() */
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"call_purse_deposit", "call_purse_deposit",
@ -3933,10 +3933,11 @@ prepare_statements (struct PostgresClosure *pg)
",serial_id" ",serial_id"
",policy_options" ",policy_options"
",deadline" ",deadline"
",timeout_fulfilment_state"
",fulfilment_state" ",fulfilment_state"
") VALUES " ") VALUES "
"($1, $2, $3, $4, $5);", "($1, $2, $3, $4, $5, $6);",
5), 6),
GNUNET_PQ_make_prepare ( GNUNET_PQ_make_prepare (
"insert_into_table_policy_fulfilments", "insert_into_table_policy_fulfilments",
"INSERT INTO policy_fulfilments" "INSERT INTO policy_fulfilments"
@ -6292,6 +6293,9 @@ postgres_do_deposit (
(deposit->has_policy_details) (deposit->has_policy_details)
? GNUNET_PQ_query_param_timestamp (&deposit->policy_deadline) ? GNUNET_PQ_query_param_timestamp (&deposit->policy_deadline)
: GNUNET_PQ_query_param_null (), : 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 GNUNET_PQ_query_param_end
}; };
struct GNUNET_PQ_ResultSpec rs[] = { struct GNUNET_PQ_ResultSpec rs[] = {
@ -14651,6 +14655,7 @@ postgres_insert_records_by_table (void *cls,
case TALER_EXCHANGEDB_RT_POLICY_DETAILS: case TALER_EXCHANGEDB_RT_POLICY_DETAILS:
rh = &irbt_cb_table_policy_details; rh = &irbt_cb_table_policy_details;
break; break;
/* TODO: policy_details_fulfilments and policy_fulfilments */
case TALER_EXCHANGEDB_RT_PURSE_REQUESTS: case TALER_EXCHANGEDB_RT_PURSE_REQUESTS:
rh = &irbt_cb_table_purse_requests; rh = &irbt_cb_table_purse_requests;
break; break;

View File

@ -513,7 +513,8 @@ CREATE OR REPLACE FUNCTION exchange_do_deposit(
IN in_policy_blocked BOOLEAN, IN in_policy_blocked BOOLEAN,
IN in_policy_details VARCHAR, IN in_policy_details VARCHAR,
IN in_policy_serial_id BYTEA, 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_exchange_timestamp INT8,
OUT out_balance_ok BOOLEAN, OUT out_balance_ok BOOLEAN,
OUT out_conflict BOOLEAN) OUT out_conflict BOOLEAN)
@ -535,11 +536,13 @@ THEN
(serial_id (serial_id
,policy_options ,policy_options
,deadline ,deadline
,timeout_fulfilment_state
,fulfilment_state) ,fulfilment_state)
VALUES VALUES
(in_policy_serial_id (in_policy_serial_id
,in_policy_details ,in_policy_details
,in_policy_deadline ,in_policy_deadline
,in_policy_timeout_fulfilment_state
,0) -- 0 == pending ,0) -- 0 == pending
RETURNING policy_details_serial_id INTO xdi; RETURNING policy_details_serial_id INTO xdi;
ELSE ELSE

View File

@ -154,8 +154,8 @@ struct TALER_Extension TE_age_restriction = {
/* This extension is not a policy extension */ /* This extension is not a policy extension */
.parse_policy_details = NULL, .parse_policy_details = NULL,
.http_get_handler = NULL, .policy_get_handler = NULL,
.http_post_handler = NULL, .policy_post_handler = NULL,
}; };

View File

@ -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 enum GNUNET_GenericReturnValue
TALER_extensions_serial_from_policy_details ( TALER_extensions_extract_meta_data_from_policy_details (
const json_t *policy_details, const json_t *policy_details,
struct GNUNET_HashCode *serial, struct GNUNET_HashCode *serial,
struct GNUNET_TIME_Timestamp *deadline, struct GNUNET_TIME_Timestamp *deadline,
enum TALER_PolicyFulfilmentState *state_on_deadline,
const char **error_hint) const char **error_hint)
{ {
enum GNUNET_GenericReturnValue ret;
const struct TALER_Extension *extension; const struct TALER_Extension *extension;
const json_t *jtype; const json_t *jtype;
const char *type; const char *type;
@ -399,12 +424,49 @@ TALER_extensions_serial_from_policy_details (
} }
*deadline = GNUNET_TIME_UNIT_FOREVER_TS; *deadline = GNUNET_TIME_UNIT_FOREVER_TS;
return extension->parse_policy_details (policy_details, ret = extension->parse_policy_details (policy_details,
serial, serial,
deadline, deadline,
error_hint); 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 */ /* end of extensions.c */

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
/** /**
* @file policy_brandt_vickery_auction.c * @file policy_brandt_vickrey_auction.c
* @brief Extension for replay of auctions of type Brandt * @brief Extension for replay of auctions of type Brandt
* @author Özgür Kesim * @author Özgür Kesim
*/ */
@ -26,8 +26,8 @@
#include "stdint.h" #include "stdint.h"
#include <microhttpd.h> #include <microhttpd.h>
#define POLICY_AUCTION "policy_brandt_vickery_auction" #define POLICY_AUCTION "policy_brandt_vickrey_auction"
#define LOG_PREFIX "[policy_brandt_vickery_auction] " #define LOG_PREFIX "[policy_brandt_vickrey_auction] "
#define MAX_RESULT_SIZE 10 * 1024 #define MAX_RESULT_SIZE 10 * 1024
/* (public) configuration of this extension */ /* (public) configuration of this extension */
@ -54,7 +54,7 @@ struct result
{ {
uint16_t bidder; uint16_t bidder;
uint16_t price_idx; uint16_t price_idx;
const char *price; const char *price;
}; };
/* /*
@ -73,12 +73,19 @@ struct transcript
/* Payto URL */ /* Payto URL */
const char *payto; const char *payto;
/* Number of bidders + 1 (for seller) */ /* Number of bidders */
uint16_t n; uint16_t n;
/* (n-1) public keys of bidders */ /* (n-1) public keys of bidders */
struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub; 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 */ /* Type of auction, see libbrandt */
uint16_t m; 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. * @brief Parses a given json as transcript.
* *
@ -165,8 +191,8 @@ parse_transcript (const json_t *jtr,
char *perr; char *perr;
unsigned int eline; unsigned int eline;
struct GNUNET_JSON_Specification au_spec[] = { struct GNUNET_JSON_Specification au_spec[] = {
GNUNET_JSON_spec_bool ("public", &tr->public), GNUNET_JSON_spec_bool ("outcome_public", &tr->public),
GNUNET_JSON_spec_uint16 ("type", &tr->m), GNUNET_JSON_spec_uint16 ("auction_type", &tr->m),
GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub), GNUNET_JSON_spec_fixed_auto ("pubkey", &tr->seller_pub),
GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start), GNUNET_JSON_spec_timestamp ("time_start", &tr->time_start),
GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round), GNUNET_JSON_spec_relative_time ("time_round", &tr->time_round),
@ -187,6 +213,13 @@ parse_transcript (const json_t *jtr,
return json_error (jerror, return json_error (jerror,
perr); perr);
{
const char *auc_js = json_dumps (auc, JSON_COMPACT);
GNUNET_CRYPTO_hash (auc_js,
strlen (auc_js),
&tr->h_auction);
}
// Prices... // Prices...
{ {
size_t idx; size_t idx;
@ -235,15 +268,23 @@ parse_transcript (const json_t *jtr,
tr->n = json_array_size (bidders); tr->n = json_array_size (bidders);
tr->bidder_pub = GNUNET_new_array (tr->n, struct tr->bidder_pub = GNUNET_new_array (
GNUNET_CRYPTO_EddsaPublicKey); tr->n,
struct GNUNET_CRYPTO_EddsaPublicKey);
tr->serial_ids = GNUNET_new_array (
tr->n,
struct GNUNET_HashCode);
json_array_foreach (bidders, idx, val) json_array_foreach (bidders, idx, val)
{ {
struct GNUNET_JSON_Specification spec[] = { struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto (NULL, GNUNET_JSON_spec_fixed_auto (NULL,
&(tr->bidder_pub[idx])), &tr->bidder_pub[idx]),
GNUNET_JSON_spec_end (), GNUNET_JSON_spec_end (),
}; };
/* TODO: cleanup */
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_JSON_parse (val, GNUNET_JSON_parse (val,
spec, spec,
@ -252,12 +293,13 @@ parse_transcript (const json_t *jtr,
return json_error (jerror, return json_error (jerror,
"bidder no %ld public key couldn't be parsed", "bidder no %ld public key couldn't be parsed",
idx + 1); 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 // Messages
{ {
size_t nm; size_t nm;
@ -322,7 +364,6 @@ parse_transcript (const json_t *jtr,
DONE: DONE:
*jerror = NULL;
return GNUNET_OK; return GNUNET_OK;
} }
@ -331,7 +372,8 @@ DONE:
* @brief replay an auction using the external program * @brief replay an auction using the external program
* *
* @param[in] root The original JSON transcript * @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 * @param[out] result The JSON result from the program
* @return GNUNET_OK on success * @return GNUNET_OK on success
* *
@ -340,8 +382,10 @@ DONE:
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
replay_transcript (const json_t*root, replay_transcript (const json_t*root,
struct transcript *tr, struct transcript *tr,
struct TALER_PolicyFulfilmentOutcome **outcome,
json_t **result) json_t **result)
{ {
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
struct GNUNET_DISK_PipeHandle *pi; struct GNUNET_DISK_PipeHandle *pi;
struct GNUNET_DISK_PipeHandle *po; struct GNUNET_DISK_PipeHandle *po;
const struct GNUNET_DISK_FileHandle *fd; const struct GNUNET_DISK_FileHandle *fd;
@ -444,32 +488,106 @@ replay_transcript (const json_t*root,
return json_error (result, "internal error"); 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_t *w;
json_object_set (res, "exchange_sig", json_string ( size_t idx;
"sig(priv_E, winners)")); 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; i<tr->n; 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, 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); replay_program);
json_object_clear (*result); json_object_clear (*result);
return json_error (result, "internal error"); ret = json_error (result, "internal error");
} }
DONE:
return GNUNET_OK; 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 static MHD_RESULT
auction_http_get_handler ( auction_policy_get_handler (
struct MHD_Connection *connection, struct MHD_Connection *connection,
const char *const args[]) const char *const args[])
{ {
/* TODO: return some meta-data about supported version, limits, etc.*/ /* TODO: return some meta-data about supported version, limits, etc.*/
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 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, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_IMPLEMENTED, MHD_HTTP_NOT_IMPLEMENTED,
TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN, 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 * TODO: make this non-blocking
*/ */
static MHD_RESULT enum GNUNET_GenericReturnValue
auction_http_post_handler ( auction_policy_post_handler (
struct MHD_Connection *connection,
const json_t *root, 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 = {}; struct transcript tr = {};
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
json_t *result1;
json_t *result2;
*outcome = NULL;
ret = parse_transcript (root, ret = parse_transcript (root,
&tr, &tr,
&result1); output);
/* TODO: cleanups! */
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
return TALER_MHD_reply_json_steal (connection, return ret;
result1,
MHD_HTTP_BAD_REQUEST);
GNUNET_assert (NULL == result1);
ret = replay_transcript (root, serial_id_check (tr.serial_ids, tr.n);
&tr,
&result2);
return TALER_MHD_reply_json_steal (connection, return replay_transcript (root,
result2, &tr,
GNUNET_OK == ret? outcome,
MHD_HTTP_OK : output);
MHD_HTTP_BAD_REQUEST);
} }
@ -594,6 +710,7 @@ auction_http_post_handler (
* @param[in] input The policy_details for this handler during deposit * @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] 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] 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 * @param[out] error_hint On error, will contain a hint
* exchange should store the policy_details in the policy_details table. * exchange should store the policy_details in the policy_details table.
* @return GNUNET_OK if the request was OK * @return GNUNET_OK if the request was OK
@ -603,6 +720,7 @@ auction_parse_policy_details (
const json_t *input, const json_t *input,
struct GNUNET_HashCode *serial, struct GNUNET_HashCode *serial,
struct GNUNET_TIME_Timestamp *deadline, struct GNUNET_TIME_Timestamp *deadline,
enum TALER_PolicyFulfilmentState *state_on_timeout,
const char **error_hint) const char **error_hint)
{ {
enum GNUNET_GenericReturnValue ret = GNUNET_NO; enum GNUNET_GenericReturnValue ret = GNUNET_NO;
@ -636,22 +754,14 @@ auction_parse_policy_details (
LOG_PREFIX "check of deadline %ld not implemented!\n", LOG_PREFIX "check of deadline %ld not implemented!\n",
deadline->abs_time.abs_value_us); deadline->abs_time.abs_value_us);
/* Create serial as H(bidder_pub, h_auction) */ calculate_serial (&pub, &hc, serial);
{
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);
}
ret = GNUNET_OK; ret = GNUNET_OK;
} while(0); } while(0);
/* In case of timeout, the coin shall be refreshable by the owner */
*state_on_timeout = TALER_PolicyFulfilmentTimeoutRefreshable;
return ret; return ret;
} }
@ -668,8 +778,8 @@ struct TALER_Extension TE_auction_brandt = {
.load_config = &auction_load_config, .load_config = &auction_load_config,
.manifest = &auction_manifest, .manifest = &auction_manifest,
.parse_policy_details = &auction_parse_policy_details, .parse_policy_details = &auction_parse_policy_details,
.http_get_handler = &auction_http_get_handler, .policy_get_handler = &auction_policy_get_handler,
.http_post_handler = &auction_http_post_handler, .policy_post_handler = &auction_policy_post_handler,
}; };
@ -687,7 +797,7 @@ struct TALER_Extension TE_auction_brandt = {
* @return Pointer to TE_auction_brandt * @return Pointer to TE_auction_brandt
*/ */
struct TALER_Extension * 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; const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
@ -751,7 +861,7 @@ libtaler_extension_policy_brandt_vickery_auction_init (void *arg)
* @return null * @return null
*/ */
void * void *
libtaler_extension_policy_brandt_vickery_auction_done (void *arg) libtaler_extension_policy_brandt_vickrey_auction_done (void *arg)
{ {
auction_disable (&TE_auction_brandt); auction_disable (&TE_auction_brandt);
GNUNET_free (replay_program); 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 */

View File

@ -531,7 +531,8 @@ struct TALER_EXCHANGEDB_TableData
char *policy_options; char *policy_options;
struct GNUNET_HashCode serial_id; struct GNUNET_HashCode serial_id;
struct GNUNET_TIME_Timestamp deadline; struct GNUNET_TIME_Timestamp deadline;
uint64_t fulfilment_state; uint16_t timeout_fulfilment_state;
uint16_t fulfilment_state;
} policy_details; } policy_details;
struct struct
@ -1464,6 +1465,11 @@ struct TALER_EXCHANGEDB_Deposit
*/ */
struct GNUNET_TIME_Timestamp policy_deadline; 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 * Hash over the @e policy_details. Only filled if has_policy_details is

View File

@ -53,6 +53,10 @@ struct TALER_Extensions
const struct TALER_Extension *extension; const struct TALER_Extension *extension;
}; };
/* Forward declarations */
enum TALER_PolicyFulfilmentState;
struct TALER_PolicyFulfilmentOutcome;
/* /*
* @brief Represents the implementation of an extension. * @brief Represents the implementation of an extension.
* *
@ -159,6 +163,7 @@ struct TALER_Extension
* Policy related handlers * Policy related handlers
* ========================= * =========================
*/ */
/** /**
* @brief Handler to check a policy. Can be NULL; * @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 * (see https://docs.taler.net/core/api-exchange.html#deposit), this handler
* will be called before the deposit transaction. * will be called before the deposit transaction.
* *
* @param[in] policy_details Details about the policy, provided by the client * @param[in] policy_details Details about the policy, provided by the client
* during a deposit request. * during a deposit request.
* @param[out] serial On success, will contain the serial-ID under which the * @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 * @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 * @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)( enum GNUNET_GenericReturnValue (*parse_policy_details)(
const json_t *policy_details, const json_t *policy_details,
struct GNUNET_HashCode *serial, struct GNUNET_HashCode *serial,
struct GNUNET_TIME_Timestamp *deadline, struct GNUNET_TIME_Timestamp *deadline,
enum TALER_PolicyFulfilmentState *state_on_timeout,
const char **error_hint); 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[in] root The JSON body from the request
* @param root The JSON body from the request * @param[in] args Additional query parameters of the request.
* @param 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.
* @return MDH result * @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)( enum GNUNET_GenericReturnValue (*policy_post_handler)(
struct MHD_Connection *connection,
const json_t *root, 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 connection The current connection
* @param root The JSON body from the request * @param root The JSON body from the request
* @param args Additional query parameters of the request. * @param args Additional query parameters of the request.
* @return MDH result * @return MDH result
*/ */
MHD_RESULT (*http_get_handler)( MHD_RESULT (*policy_get_handler)(
struct MHD_Connection *connection, struct MHD_Connection *connection,
const char *const args[]); 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[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] 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] 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 * @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. * @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 * GNUNET_NO, when no extension was found. GNUNET_SYSERR when the JSON was
* invalid, with *error_hint maybe non-NULL. * invalid, with *error_hint maybe non-NULL.
*/ */
enum GNUNET_GenericReturnValue enum GNUNET_GenericReturnValue
TALER_extensions_serial_from_policy_details ( TALER_extensions_extract_meta_data_from_policy_details (
const json_t *policy_details, const json_t *policy_details,
struct GNUNET_HashCode *serial, struct GNUNET_HashCode *serial,
struct GNUNET_TIME_Timestamp *deadline, struct GNUNET_TIME_Timestamp *deadline,
enum TALER_PolicyFulfilmentState *state_on_timeout,
const char **error_hint); const char **error_hint);
/* /*
* ================================ * ================================
* Merchant refund policy * Merchant refund policy