From 0a089db4ef9f6c50ec3e52a667dfb0f6a3c17c59 Mon Sep 17 00:00:00 2001 From: Özgür Kesim Date: Tue, 4 Oct 2022 21:31:19 +0200 Subject: rename auction_brandt -> policy_auction --- configure.ac | 2 +- src/Makefile.am | 2 +- src/extensions/age_restriction/Makefile.am | 2 +- src/extensions/auction_brandt/Makefile.am | 34 - .../auction_brandt/extension_auction_brandt.c | 733 --------------------- src/extensions/policy_auction/Makefile.am | 34 + src/extensions/policy_auction/policy_auction.c | 731 ++++++++++++++++++++ src/include/taler_extensions.h | 11 +- src/testing/test_exchange_auction.conf | 3 +- 9 files changed, 775 insertions(+), 777 deletions(-) delete mode 100644 src/extensions/auction_brandt/Makefile.am delete mode 100644 src/extensions/auction_brandt/extension_auction_brandt.c create mode 100644 src/extensions/policy_auction/Makefile.am create mode 100644 src/extensions/policy_auction/policy_auction.c diff --git a/configure.ac b/configure.ac index 5bdfcecd..38944694 100644 --- a/configure.ac +++ b/configure.ac @@ -533,7 +533,7 @@ AC_CONFIG_FILES([Makefile src/exchange-tools/Makefile src/extensions/Makefile src/extensions/age_restriction/Makefile - src/extensions/auction_brandt/Makefile + src/extensions/policy_auction/Makefile src/lib/Makefile src/kyclogic/Makefile src/testing/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 82774061..9f064665 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,7 @@ SUBDIRS = \ auditor \ lib \ exchange-tools \ - extensions/auction_brandt \ + extensions/policy_auction \ extensions/age_restriction \ testing \ benchmark diff --git a/src/extensions/age_restriction/Makefile.am b/src/extensions/age_restriction/Makefile.am index c81fd0b9..e90c1962 100644 --- a/src/extensions/age_restriction/Makefile.am +++ b/src/extensions/age_restriction/Makefile.am @@ -24,7 +24,7 @@ libtaler_extension_age_restriction_la_LDFLAGS = \ libtaler_extension_age_restriction_la_SOURCES = \ extension_age_restriction.c -libtaler_extension_auctionbrandt_la_LIBADD = \ +libtaler_extension_age_restriction_la_LIBADD = \ $(top_builddir)/src/json/libtalerjson.la \ $(top_builddir)/src/util/libtalerutil.la \ -lgnunetjson \ diff --git a/src/extensions/auction_brandt/Makefile.am b/src/extensions/auction_brandt/Makefile.am deleted file mode 100644 index f616c164..00000000 --- a/src/extensions/auction_brandt/Makefile.am +++ /dev/null @@ -1,34 +0,0 @@ -# This Makefile.am is in the public domain - -AM_CPPFLAGS = \ - -I$(top_srcdir)/src/include \ - $(LIBGCRYPT_CFLAGS) \ - $(POSTGRESQL_CPPFLAGS) - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - - -# Auction of Brandt type as extension library - -plugindir = $(libdir)/taler - -plugin_LTLIBRARIES = \ - libtaler_extension_auction_brandt.la - -libtaler_extension_auction_brandt_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined - -libtaler_extension_auction_brandt_la_SOURCES = \ - extension_auction_brandt.c - -libtaler_extension_auction_brandt_la_LIBADD = \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetjson \ - -lgnunetutil \ - -ljansson \ - $(XLIB) diff --git a/src/extensions/auction_brandt/extension_auction_brandt.c b/src/extensions/auction_brandt/extension_auction_brandt.c deleted file mode 100644 index 358c6b7c..00000000 --- a/src/extensions/auction_brandt/extension_auction_brandt.c +++ /dev/null @@ -1,733 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2021-2022 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see - */ -/** - * @file extension_auction_brandt.c - * @brief Extension for replay of auctions of type brandt - * @author Özgür Kesim - */ -#include "platform.h" -#include "taler_util.h" -#include "taler_extensions.h" -#include "../../exchange/taler-exchange-httpd.h" -#include "taler_mhd_lib.h" -#include "stdint.h" -#include - -#define AUCTION_BRANDT "auction_brandt" -#define LOG_PREFIX "[auction_brandt] " -#define MAX_RESULT_SIZE 10 * 1024 - -/* Path to the replay program. */ -static char *replay_program; - -/* supported currency */ -static char *currency; - -/* This is basically BRANDT_Result with an extra string field */ -struct result -{ - uint16_t bidder; - uint16_t price_idx; - const char *price; -}; - -/* - * @brief Transcript information - * - */ -struct transcript -{ - /* - * The first couple of fields are from a JSON transcript - */ - - /* Public key of seller */ - struct GNUNET_CRYPTO_EddsaPublicKey seller_pub; - - /* Payto URL */ - const char *payto; - - /* Number of bidders + 1 (for seller) */ - uint16_t n; - - /* (n-1) public keys of bidders */ - struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub; - - /* Type of auction, see libbrandt */ - uint16_t m; - - /* Auction public outcome? */ - bool public; - - /* Start date of the auction */ - struct GNUNET_TIME_Timestamp time_start; - - /* End date of the auction */ - struct GNUNET_TIME_Relative time_round; - - /* Number of prices */ - uint16_t k; - - /* Prices, must be length k */ - struct TALER_Amount *prices; - - /* Expected winner(s), maybe NULL */ - struct result *expected; - size_t expected_len; - - /* - * These are the results from the replay via the external program. - */ - struct result *results; - size_t results_len; -}; - -/** - * @brief returns an JSON with the error - */ -static enum GNUNET_GenericReturnValue -json_error (json_t **output, - char *error, ...) -{ - va_list ap; - int n = 0; - char buf[4096]; - GNUNET_assert (error); - - va_start (ap, error); - n = vsprintf (buf, error, ap); - va_end (ap); - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - LOG_PREFIX "got error: %s\n", - n < 0 ? error: buf); - - *output = json_pack ("{s:s}", - "error", - n < 0 ? error : buf); - GNUNET_assert (*output); - - return GNUNET_SYSERR; -}; - -/** - * @brief returns an JSON with the result - */ -#if 0 -static enum GNUNET_GenericReturnValue -json_result (json_t **output, - const struct transcript *tr) -{ - json_t *results; - - GNUNET_assert (NULL != tr); - - *output = json_object (); - results = json_array (); - GNUNET_assert (*output); - GNUNET_assert (results); - - for (size_t i = 0; i < tr->results_len; i++) - { - json_t *result = json_pack ("{s:i, s:s}", - "bidder", tr->results[i].bidder, - "price", tr->results[i].price); - GNUNET_assert (result); - - GNUNET_assert (-1 != - json_array_append_new (results, result)); - } - - GNUNET_assert (-1 != - json_object_set_new (*output, - "winners", - results)); - - return GNUNET_OK; -} - - -#endif - - -/* - * @brief Parses a given json as transcript. - * - * @param[in] jtr JSON input - * @param[out] tr Parsed transcript data - * @param[out] jerror JSON output for errors - * @return GNUNET_OK on succes - * - * TODO: - * - parse and verify signatures - */ -static enum GNUNET_GenericReturnValue -parse_transcript (const json_t *jtr, - struct transcript *tr, - json_t **jerror) -{ - json_t *auc; - - // TODO: struct GNUNET_CRYPTO_EddsaSignature sig; - - GNUNET_assert (jtr); - GNUNET_assert (tr); - - // Parse auction - { - 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_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), - GNUNET_JSON_spec_string ("payto", &tr->payto), - GNUNET_JSON_spec_end () - }; - - auc = json_object_get (jtr, "auction"); - if (NULL == auc) - return json_error (jerror, - "no auction found in transcript"); - - if (GNUNET_OK != - GNUNET_JSON_parse (auc, - au_spec, - (const char **) &perr, - &eline)) - return json_error (jerror, - perr); - - // Prices... - { - size_t idx; - json_t *val; - json_t *prices; - - prices = json_object_get (auc, "prices"); - if (! json_is_array (prices)) - return json_error (jerror, - "no prices found"); - - tr->k = json_array_size (prices); - - tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount); - json_array_foreach (prices, idx, val) - { - struct GNUNET_JSON_Specification spec[] = { - TALER_JSON_spec_amount (NULL, - currency, - &(tr->prices[idx])), - GNUNET_JSON_spec_end (), - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (val, - spec, - NULL, - NULL)) - return json_error (jerror, - "price no. %ld couldn't be parsed", - idx + 1); - } - } - } - - // Bidders - { - size_t idx; - json_t *val; - json_t *bidders; - - bidders = json_object_get (jtr, "bidders"); - if (! bidders || ! json_is_array (bidders)) - return json_error (jerror, - "no bidders found"); - - tr->n = json_array_size (bidders); - - tr->bidder_pub = GNUNET_new_array (tr->n, struct - GNUNET_CRYPTO_EddsaPublicKey); - json_array_foreach (bidders, idx, val) - { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_fixed_auto (NULL, - &(tr->bidder_pub[idx])), - GNUNET_JSON_spec_end (), - }; - if (GNUNET_OK != - GNUNET_JSON_parse (val, - spec, - NULL, - NULL)) - return json_error (jerror, - "bidder no %ld public key couldn't be parsed", - idx + 1); - } - } - - // TODO: parse and verify signatures from bidders of the auction - - - // Messages - { - size_t nm; - json_t *messages = json_object_get (jtr, "transcript"); - - if (! json_is_array (messages)) - return json_error (jerror, - "no messages found"); - - - nm = json_array_size (messages); - - if (nm != (4 * tr->n)) - return json_error (jerror, - "not the right no. of messages found"); - - /* TODO: parse and evaluate signatures */ - } - - // Winners - { - size_t idx; - json_t *val; - json_t *winners = json_object_get (jtr, "winners"); - - if (! json_is_array (winners)) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - LOG_PREFIX "winners not provided, continuing without\n"); - goto DONE; - } - - tr->expected_len = json_array_size (winners); - tr->expected = GNUNET_new_array (tr->expected_len, - struct result); - - json_array_foreach (winners, idx, val) { - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_uint16 ("bidder", - &(tr->expected[idx].bidder)), - GNUNET_JSON_spec_uint16 ("price_idx", - &(tr->expected[idx].price_idx)), - GNUNET_JSON_spec_string ("price", - &(tr->expected[idx].price)), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (val, - spec, - NULL, - NULL)) - return json_error (jerror, - "couldn't parse winner no. %ld", - idx + 1); - } - } - - // TODO: parse and evalue sig of seller - -// TODO: check for max values - -DONE: - - *jerror = NULL; - return GNUNET_OK; -} - - -/** - * @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[out] result The JSON result from the program - * @return GNUNET_OK on success - * - * TODO: Make this resumable - */ -static enum GNUNET_GenericReturnValue -replay_transcript (const json_t*root, - struct transcript *tr, - json_t **result) -{ - struct GNUNET_DISK_PipeHandle *pi; - struct GNUNET_DISK_PipeHandle *po; - const struct GNUNET_DISK_FileHandle *fd; - struct GNUNET_OS_Process *proc; - - pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); - po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); - proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR, - pi, po, NULL, - replay_program, - replay_program, - NULL); - if (NULL == proc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX "couldn't create auction replay program '%s'\n", - replay_program); - - return json_error (result, "internal error"); - } - - // Write original transcript JSON to stdin - { - ssize_t sz; - char *str; - size_t str_len; - - - fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE); - str = json_dumps (root, JSON_COMPACT); - str_len = strlen (str); - sz = GNUNET_DISK_file_write (fd, - str, - str_len); - free (str); - if (sz != str_len) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - LOG_PREFIX "couldn't write all data to replay_program\n"); - } - } - - // Read output from stdout - { - ssize_t sz; - char buf[MAX_RESULT_SIZE]; - json_error_t error; - json_t *res; - - fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ); - - sz = GNUNET_DISK_file_read (fd, - buf, - sizeof(buf)); - if (GNUNET_SYSERR == sz) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - LOG_PREFIX "couldn't read data from replay_program\n"); - return json_error (result, "internal error"); - } - - buf[sz] = 0; - res = json_loads (buf, - JSON_DECODE_ANY, - &error); - - if (! res) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - LOG_PREFIX - "couldn't parse response from replay_program: %s (for '%s')\n", - error.text, - buf); - return json_error (result, error.text); - } - - // Handle error case first - { - json_t *err = json_object_get (res, - "error"); - if (NULL != err) - { - *result = json_copy (res); - json_decref (res); - return GNUNET_SYSERR; - } - } - - // Parse the result - { - json_t *winners = json_object_get (res, - "winners"); - if ((NULL == winners) || - (! json_is_array (winners))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX - "replay program didn't return a known result type, instead: '%s'\n", - json_dumps (res, JSON_INDENT (2))); - return json_error (result, "internal error"); - } - - { - // TODO: check each winner with tr->expected, if applicable - json_object_set (res, "exchange_sig", json_string ( - "sig(priv_E, winners)")); - - } - - // TODO: return own result object. - *result = json_copy (res); - json_decref (res); - } - - } - - if (GNUNET_OK != GNUNET_OS_process_wait (proc)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX "error while launching auction replay program '%s'\n", - replay_program); - - json_object_clear (*result); - return json_error (result, "internal error"); - } - - - return GNUNET_OK; -} - - -/** - * @brief implements the TALER_Extension.disable interface. - * - * @param ext Pointer to the current extension - */ -static void -auction_disable ( - struct TALER_Extension *ext) -{ - /* TODO: cleanup configuration */ - ext->enabled = false; -} - - -/** - * @brief implements the TALER_Extension.test_json_config interface. - * - * @param config configuration as json_t* to test - * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise. - */ -static enum GNUNET_GenericReturnValue -auction_test_json_config ( - const json_t *config) -{ - /* This extension has no configuration */ - return GNUNET_OK; -} - - -/** - * @brief implements the TALER_Extension.config_to_json interface. - * - * @param ext if NULL, only tests the configuration - * @return configuration as json_t* object, maybe NULL - */ -static json_t * -auction_config_to_json ( - const struct TALER_Extension *ext) -{ - /* TODO: add configuration */ - return GNUNET_JSON_PACK ( - GNUNET_JSON_pack_bool ("critical", ext->critical), - GNUNET_JSON_pack_string ("version", ext->version)); -} - - -/** - * @brief implements the TALER_Extension.load_json_config interface. - * - * @param ext if NULL, only tests the configuration - * @param jconfig the configuration as json - */ -static enum GNUNET_GenericReturnValue -auction_load_json_config ( - struct TALER_Extension *ext, - json_t *jconfig) -{ - /* TODO: add configuration */ - ext->enabled = true; - return GNUNET_OK; -} - - -/** - * @brief implements the TALER_Extension.http_get_handler - */ -static MHD_RESULT -auction_http_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"); - - 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"); - -} - - -/** - * @brief implements the TALER_Extension.http_post_handler - * - * TODO: make this non-blocking - */ -static MHD_RESULT -auction_http_post_handler ( - struct MHD_Connection *connection, - const json_t *root, - const char *const args[]) -{ - struct transcript tr = {}; - enum GNUNET_GenericReturnValue ret; - json_t *result1; - json_t *result2; - - ret = parse_transcript (root, - &tr, - &result1); - if (GNUNET_OK != ret) - return TALER_MHD_reply_json_steal (connection, - result1, - MHD_HTTP_BAD_REQUEST); - GNUNET_assert (NULL == result1); - - ret = replay_transcript (root, - &tr, - &result2); - - return TALER_MHD_reply_json_steal (connection, - result2, - GNUNET_OK == ret? - MHD_HTTP_OK : - MHD_HTTP_BAD_REQUEST); -} - - -/* The extension struct for auctions of brandt-style */ -struct TALER_Extension TE_auction_brandt = { - .type = TALER_Extension_AuctionBrandt, - .name = AUCTION_BRANDT, - .critical = false, - .version = "0", - .enabled = false, /* disabled per default */ - .has_config = true, - .config = NULL, - .config_json = NULL, - .disable = &auction_disable, - .test_json_config = &auction_test_json_config, - .load_json_config = &auction_load_json_config, - .config_to_json = &auction_config_to_json, - .http_get_handler = &auction_http_get_handler, - .http_post_handler = &auction_http_post_handler, -}; - - -/* TODO: sql handler */ - -/** - * =========================================== - * Handler for GNUNET_PLUGIN_load and _unload - * =========================================== - */ - -/** - * @brief Initialization function for the extension. - * Will be called by GNUNET_PLUGIN_load. - * - * @param arg Configuration - ptr to GNUNET_CONFIGURATION_Handle - * @return Pointer to TE_auction_brandt - */ -struct TALER_Extension * -libtaler_extension_auction_brandt_init (void *arg) -{ - const struct GNUNET_CONFIGURATION_Handle *cfg = arg; - - - if (GNUNET_OK != - TALER_config_get_currency (cfg, - ¤cy)) - return NULL; - - if (GNUNET_SYSERR == - GNUNET_CONFIGURATION_get_value_string (cfg, - TALER_EXTENSION_SECTION_PREFIX - AUCTION_BRANDT, - "REPLAY_PROGRAM", - &replay_program)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - TALER_EXTENSION_SECTION_PREFIX AUCTION_BRANDT, - "REPLAY_PROGRAM"); - return NULL; - } - - /* check if replay_program is actually an executable */ - { - struct stat sb; - - if (0 != stat (replay_program, &sb)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX "replay_program '%s' not found\n", - replay_program); - return NULL; - } - - if ( (sb.st_mode & S_IFDIR) || - ! (sb.st_mode & S_IXUSR)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - LOG_PREFIX "replay_program '%s' is not an executable\n", - replay_program); - return NULL; - } - - } - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - LOG_PREFIX "loading... using replay_program '%s'\n", - replay_program); - - /* TODO: read other config parameters and generate configuration */ - - - return &TE_auction_brandt; -} - - -/** - * @brief Tear-down function for the extension. - * Will be called by GNUNET_PLUGIN_unload. - * - * @param ignored - * @return null - */ -void * -libtaler_extension_auction_brandt_done (void *arg) -{ - auction_disable (&TE_auction_brandt); - GNUNET_free (replay_program); - replay_program = NULL; - - return NULL; -} - - -/* end of extension_auction_brandt.c */ diff --git a/src/extensions/policy_auction/Makefile.am b/src/extensions/policy_auction/Makefile.am new file mode 100644 index 00000000..cf44b95e --- /dev/null +++ b/src/extensions/policy_auction/Makefile.am @@ -0,0 +1,34 @@ +# This Makefile.am is in the public domain + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/include \ + $(LIBGCRYPT_CFLAGS) \ + $(POSTGRESQL_CPPFLAGS) + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + + +# Auction of Brandt type as an extension library + +plugindir = $(libdir)/taler + +plugin_LTLIBRARIES = \ + libtaler_extension_policy_auction.la + +libtaler_extension_policy_auction_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined + +libtaler_extension_policy_auction_la_SOURCES = \ + policy_auction.c + +libtaler_extension_policy_auction_la_LIBADD = \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + -lgnunetjson \ + -lgnunetutil \ + -ljansson \ + $(XLIB) diff --git a/src/extensions/policy_auction/policy_auction.c b/src/extensions/policy_auction/policy_auction.c new file mode 100644 index 00000000..d1c3237c --- /dev/null +++ b/src/extensions/policy_auction/policy_auction.c @@ -0,0 +1,731 @@ +/* + This file is part of TALER + Copyright (C) 2021-2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see + */ +/** + * @file policy_auction.c + * @brief Extension for replay of auctions of type Brandt + * @author Özgür Kesim + */ +#include "platform.h" +#include "taler_util.h" +#include "taler_extensions.h" +#include "../../exchange/taler-exchange-httpd.h" +#include "taler_mhd_lib.h" +#include "stdint.h" +#include + +#define POLICY_AUCTION "policy_auction" +#define LOG_PREFIX "[policy_auction] " +#define MAX_RESULT_SIZE 10 * 1024 + +/* Path to the replay program. */ +static char *replay_program; + +/* supported currency */ +static char *currency; + +/* This is basically BRANDT_Result with an extra string field */ +struct result +{ + uint16_t bidder; + uint16_t price_idx; + const char *price; +}; + +/* + * @brief Transcript information + * + */ +struct transcript +{ + /* + * The first couple of fields are from a JSON transcript + */ + + /* Public key of seller */ + struct GNUNET_CRYPTO_EddsaPublicKey seller_pub; + + /* Payto URL */ + const char *payto; + + /* Number of bidders + 1 (for seller) */ + uint16_t n; + + /* (n-1) public keys of bidders */ + struct GNUNET_CRYPTO_EddsaPublicKey *bidder_pub; + + /* Type of auction, see libbrandt */ + uint16_t m; + + /* Auction public outcome? */ + bool public; + + /* Start date of the auction */ + struct GNUNET_TIME_Timestamp time_start; + + /* End date of the auction */ + struct GNUNET_TIME_Relative time_round; + + /* Number of prices */ + uint16_t k; + + /* Prices, must be length k */ + struct TALER_Amount *prices; + + /* Expected winner(s), maybe NULL */ + struct result *expected; + size_t expected_len; + + /* + * These are the results from the replay via the external program. + */ + struct result *results; + size_t results_len; +}; + +/** + * @brief returns an JSON with the error + */ +static enum GNUNET_GenericReturnValue +json_error (json_t **output, + char *error, ...) +{ + va_list ap; + int n = 0; + char buf[4096]; + GNUNET_assert (error); + + va_start (ap, error); + n = vsprintf (buf, error, ap); + va_end (ap); + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + LOG_PREFIX "got error: %s\n", + n < 0 ? error: buf); + + *output = json_pack ("{s:s}", + "error", + n < 0 ? error : buf); + GNUNET_assert (*output); + + return GNUNET_SYSERR; +}; + +/** + * @brief returns an JSON with the result + */ +#if 0 +static enum GNUNET_GenericReturnValue +json_result (json_t **output, + const struct transcript *tr) +{ + json_t *results; + + GNUNET_assert (NULL != tr); + + *output = json_object (); + results = json_array (); + GNUNET_assert (*output); + GNUNET_assert (results); + + for (size_t i = 0; i < tr->results_len; i++) + { + json_t *result = json_pack ("{s:i, s:s}", + "bidder", tr->results[i].bidder, + "price", tr->results[i].price); + GNUNET_assert (result); + + GNUNET_assert (-1 != + json_array_append_new (results, result)); + } + + GNUNET_assert (-1 != + json_object_set_new (*output, + "winners", + results)); + + return GNUNET_OK; +} + + +#endif + + +/* + * @brief Parses a given json as transcript. + * + * @param[in] jtr JSON input + * @param[out] tr Parsed transcript data + * @param[out] jerror JSON output for errors + * @return GNUNET_OK on succes + * + * TODO: + * - parse and verify signatures + */ +static enum GNUNET_GenericReturnValue +parse_transcript (const json_t *jtr, + struct transcript *tr, + json_t **jerror) +{ + json_t *auc; + + // TODO: struct GNUNET_CRYPTO_EddsaSignature sig; + + GNUNET_assert (jtr); + GNUNET_assert (tr); + + // Parse auction + { + 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_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), + GNUNET_JSON_spec_string ("payto", &tr->payto), + GNUNET_JSON_spec_end () + }; + + auc = json_object_get (jtr, "auction"); + if (NULL == auc) + return json_error (jerror, + "no auction found in transcript"); + + if (GNUNET_OK != + GNUNET_JSON_parse (auc, + au_spec, + (const char **) &perr, + &eline)) + return json_error (jerror, + perr); + + // Prices... + { + size_t idx; + json_t *val; + json_t *prices; + + prices = json_object_get (auc, "prices"); + if (! json_is_array (prices)) + return json_error (jerror, + "no prices found"); + + tr->k = json_array_size (prices); + + tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount); + json_array_foreach (prices, idx, val) + { + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount (NULL, + currency, + &(tr->prices[idx])), + GNUNET_JSON_spec_end (), + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (val, + spec, + NULL, + NULL)) + return json_error (jerror, + "price no. %ld couldn't be parsed", + idx + 1); + } + } + } + + // Bidders + { + size_t idx; + json_t *val; + json_t *bidders; + + bidders = json_object_get (jtr, "bidders"); + if (! bidders || ! json_is_array (bidders)) + return json_error (jerror, + "no bidders found"); + + tr->n = json_array_size (bidders); + + tr->bidder_pub = GNUNET_new_array (tr->n, struct + GNUNET_CRYPTO_EddsaPublicKey); + json_array_foreach (bidders, idx, val) + { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto (NULL, + &(tr->bidder_pub[idx])), + GNUNET_JSON_spec_end (), + }; + if (GNUNET_OK != + GNUNET_JSON_parse (val, + spec, + NULL, + NULL)) + return json_error (jerror, + "bidder no %ld public key couldn't be parsed", + idx + 1); + } + } + + // TODO: parse and verify signatures from bidders of the auction + + + // Messages + { + size_t nm; + json_t *messages = json_object_get (jtr, "transcript"); + + if (! json_is_array (messages)) + return json_error (jerror, + "no messages found"); + + + nm = json_array_size (messages); + + if (nm != (4 * tr->n)) + return json_error (jerror, + "not the right no. of messages found"); + + /* TODO: parse and evaluate signatures */ + } + + // Winners + { + size_t idx; + json_t *val; + json_t *winners = json_object_get (jtr, "winners"); + + if (! json_is_array (winners)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + LOG_PREFIX "winners not provided, continuing without\n"); + goto DONE; + } + + tr->expected_len = json_array_size (winners); + tr->expected = GNUNET_new_array (tr->expected_len, + struct result); + + json_array_foreach (winners, idx, val) { + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_uint16 ("bidder", + &(tr->expected[idx].bidder)), + GNUNET_JSON_spec_uint16 ("price_idx", + &(tr->expected[idx].price_idx)), + GNUNET_JSON_spec_string ("price", + &(tr->expected[idx].price)), + GNUNET_JSON_spec_end () + }; + + if (GNUNET_OK != + GNUNET_JSON_parse (val, + spec, + NULL, + NULL)) + return json_error (jerror, + "couldn't parse winner no. %ld", + idx + 1); + } + } + + // TODO: parse and evalue sig of seller + +// TODO: check for max values + +DONE: + + *jerror = NULL; + return GNUNET_OK; +} + + +/** + * @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[out] result The JSON result from the program + * @return GNUNET_OK on success + * + * TODO: Make this resumable + */ +static enum GNUNET_GenericReturnValue +replay_transcript (const json_t*root, + struct transcript *tr, + json_t **result) +{ + struct GNUNET_DISK_PipeHandle *pi; + struct GNUNET_DISK_PipeHandle *po; + const struct GNUNET_DISK_FileHandle *fd; + struct GNUNET_OS_Process *proc; + + pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE); + po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ); + proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR, + pi, po, NULL, + replay_program, + replay_program, + NULL); + if (NULL == proc) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX "couldn't create auction replay program '%s'\n", + replay_program); + + return json_error (result, "internal error"); + } + + // Write original transcript JSON to stdin + { + ssize_t sz; + char *str; + size_t str_len; + + + fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE); + str = json_dumps (root, JSON_COMPACT); + str_len = strlen (str); + sz = GNUNET_DISK_file_write (fd, + str, + str_len); + free (str); + if (sz != str_len) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + LOG_PREFIX "couldn't write all data to replay_program\n"); + } + } + + // Read output from stdout + { + ssize_t sz; + char buf[MAX_RESULT_SIZE]; + json_error_t error; + json_t *res; + + fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ); + + sz = GNUNET_DISK_file_read (fd, + buf, + sizeof(buf)); + if (GNUNET_SYSERR == sz) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + LOG_PREFIX "couldn't read data from replay_program\n"); + return json_error (result, "internal error"); + } + + buf[sz] = 0; + res = json_loads (buf, + JSON_DECODE_ANY, + &error); + + if (! res) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + LOG_PREFIX + "couldn't parse response from replay_program: %s (for '%s')\n", + error.text, + buf); + return json_error (result, error.text); + } + + // Handle error case first + { + json_t *err = json_object_get (res, + "error"); + if (NULL != err) + { + *result = json_copy (res); + json_decref (res); + return GNUNET_SYSERR; + } + } + + // Parse the result + { + json_t *winners = json_object_get (res, + "winners"); + if ((NULL == winners) || + (! json_is_array (winners))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX + "replay program didn't return a known result type, instead: '%s'\n", + json_dumps (res, JSON_INDENT (2))); + return json_error (result, "internal error"); + } + + { + // TODO: check each winner with tr->expected, if applicable + json_object_set (res, "exchange_sig", json_string ( + "sig(priv_E, winners)")); + + } + + // TODO: return own result object. + *result = json_copy (res); + json_decref (res); + } + + } + + if (GNUNET_OK != GNUNET_OS_process_wait (proc)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX "error while launching auction replay program '%s'\n", + replay_program); + + json_object_clear (*result); + return json_error (result, "internal error"); + } + + + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.disable interface. + * + * @param ext Pointer to the current extension + */ +static void +auction_disable ( + struct TALER_Extension *ext) +{ + /* TODO: cleanup configuration */ + ext->enabled = false; +} + + +/** + * @brief implements the TALER_Extension.test_json_config interface. + * + * @param config configuration as json_t* to test + * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise. + */ +static enum GNUNET_GenericReturnValue +auction_test_json_config ( + const json_t *config) +{ + /* This extension has no configuration */ + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.config_to_json interface. + * + * @param ext if NULL, only tests the configuration + * @return configuration as json_t* object, maybe NULL + */ +static json_t * +auction_config_to_json ( + const struct TALER_Extension *ext) +{ + /* TODO: add configuration */ + return GNUNET_JSON_PACK ( + GNUNET_JSON_pack_bool ("critical", ext->critical), + GNUNET_JSON_pack_string ("version", ext->version)); +} + + +/** + * @brief implements the TALER_Extension.load_json_config interface. + * + * @param ext if NULL, only tests the configuration + * @param jconfig the configuration as json + */ +static enum GNUNET_GenericReturnValue +auction_load_json_config ( + struct TALER_Extension *ext, + json_t *jconfig) +{ + /* TODO: add configuration */ + ext->enabled = true; + return GNUNET_OK; +} + + +/** + * @brief implements the TALER_Extension.http_get_handler + */ +static MHD_RESULT +auction_http_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"); + + 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"); + +} + + +/** + * @brief implements the TALER_Extension.http_post_handler + * + * TODO: make this non-blocking + */ +static MHD_RESULT +auction_http_post_handler ( + struct MHD_Connection *connection, + const json_t *root, + const char *const args[]) +{ + struct transcript tr = {}; + enum GNUNET_GenericReturnValue ret; + json_t *result1; + json_t *result2; + + ret = parse_transcript (root, + &tr, + &result1); + if (GNUNET_OK != ret) + return TALER_MHD_reply_json_steal (connection, + result1, + MHD_HTTP_BAD_REQUEST); + GNUNET_assert (NULL == result1); + + ret = replay_transcript (root, + &tr, + &result2); + + return TALER_MHD_reply_json_steal (connection, + result2, + GNUNET_OK == ret? + MHD_HTTP_OK : + MHD_HTTP_BAD_REQUEST); +} + + +/* The extension struct for auctions of brandt-style */ +struct TALER_Extension TE_auction_brandt = { + .type = TALER_Extension_PolicyAuction, + .name = POLICY_AUCTION, + .critical = false, + .version = "0", + .enabled = false, /* disabled per default */ + .has_config = true, + .config = NULL, + .config_json = NULL, + .disable = &auction_disable, + .test_json_config = &auction_test_json_config, + .load_json_config = &auction_load_json_config, + .config_to_json = &auction_config_to_json, + .http_get_handler = &auction_http_get_handler, + .http_post_handler = &auction_http_post_handler, +}; + + +/** + * =========================================== + * Handler for GNUNET_PLUGIN_load and _unload + * =========================================== + */ + +/** + * @brief Initialization function for the extension. + * Will be called by GNUNET_PLUGIN_load. + * + * @param arg Configuration - ptr to GNUNET_CONFIGURATION_Handle + * @return Pointer to TE_auction_brandt + */ +struct TALER_Extension * +libtaler_extension_policy_auction_init (void *arg) +{ + const struct GNUNET_CONFIGURATION_Handle *cfg = arg; + + + if (GNUNET_OK != + TALER_config_get_currency (cfg, + ¤cy)) + return NULL; + + if (GNUNET_SYSERR == + GNUNET_CONFIGURATION_get_value_string (cfg, + TALER_EXTENSION_SECTION_PREFIX + POLICY_AUCTION, + "REPLAY_PROGRAM", + &replay_program)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + TALER_EXTENSION_SECTION_PREFIX POLICY_AUCTION, + "REPLAY_PROGRAM"); + return NULL; + } + + /* check if replay_program is actually an executable */ + { + struct stat sb; + + if (0 != stat (replay_program, &sb)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX "replay_program '%s' not found\n", + replay_program); + return NULL; + } + + if ( (sb.st_mode & S_IFDIR) || + ! (sb.st_mode & S_IXUSR)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + LOG_PREFIX "replay_program '%s' is not an executable\n", + replay_program); + return NULL; + } + + } + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + LOG_PREFIX "loading... using replay_program '%s'\n", + replay_program); + + /* TODO: read other config parameters and generate configuration */ + + + return &TE_auction_brandt; +} + + +/** + * @brief Tear-down function for the extension. + * Will be called by GNUNET_PLUGIN_unload. + * + * @param ignored + * @return null + */ +void * +libtaler_extension_policy_auction_done (void *arg) +{ + auction_disable (&TE_auction_brandt); + GNUNET_free (replay_program); + replay_program = NULL; + + return NULL; +} + + +/* end of policy_auction.c */ diff --git a/src/include/taler_extensions.h b/src/include/taler_extensions.h index 1ae8ad1e..f81b9653 100644 --- a/src/include/taler_extensions.h +++ b/src/include/taler_extensions.h @@ -31,12 +31,11 @@ enum TALER_Extension_Type { - TALER_Extension_Refund = 0, - TALER_Extension_AgeRestriction = 1, - TALER_Extension_P2P = 2, - TALER_Extension_AuctionBrandt = 3, - TALER_Extension_Escrow = 4, - TALER_Extension_MaxPredefined = 5 // Must be last of the predefined + TALER_Extension_AgeRestriction = 0, + TALER_Extension_PolicyRefund = 1, + TALER_Extension_PolicyAuction = 2, + TALER_Extension_PolicyEscrow = 3, + TALER_Extension_MaxPredefined = 4 // Must be last of the predefined }; diff --git a/src/testing/test_exchange_auction.conf b/src/testing/test_exchange_auction.conf index ee5ef7bc..89345d97 100644 --- a/src/testing/test_exchange_auction.conf +++ b/src/testing/test_exchange_auction.conf @@ -92,7 +92,8 @@ HTTP_PORT = 9081 # default age groups: #AGE_GROUPS = "8:10:12:14:16:18:21" -[exchange-extension-auction_brandt] +# Enable policy of type auction for deposits. +[exchange-extension-policy_auction] ENABLED = YES REPLAY_PROGRAM = "/usr/local/bin/taler-exchange-auction_brandt-replay" -- cgit v1.2.3