WiP: added auction_brandt as extension

- added an extension to handle auctions via libbrandt
  - /extensions/auction_brandt GET and POST handler defined
  - initial parsing of transcript.json implemented, yet WiP

- multiple cleanups and changes to extension handling
This commit is contained in:
Özgür Kesim 2022-10-02 22:51:54 +02:00
parent 04c7e0bb33
commit 72cbf66395
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
10 changed files with 380 additions and 37 deletions

View File

@ -4255,13 +4255,17 @@ do_extensions_show (char *const *args)
GNUNET_assert (NULL != exts); GNUNET_assert (NULL != exts);
for (it = TALER_extensions_get_head (); for (it = TALER_extensions_get_head ();
NULL != it; NULL != it && NULL != it->extension;
it = it->next) it = it->next)
{
const struct TALER_Extension *extension = it->extension;
GNUNET_assert (0 == GNUNET_assert (0 ==
json_object_set_new (exts, json_object_set_new (exts,
it->extension->name, it->extension->name,
it->extension->config_to_json ( it->extension->config_to_json (
it->extension))); it->extension)));
}
obj = GNUNET_JSON_PACK ( obj = GNUNET_JSON_PACK (
GNUNET_JSON_pack_object_steal ("extensions", GNUNET_JSON_pack_object_steal ("extensions",
exts)); exts));

View File

@ -1040,6 +1040,42 @@ handle_post_auditors (struct TEH_RequestContext *rc,
root); root);
} }
/**
* Handle GET "/extensions/..." requests.
*
* @param rc request context
* @param args array of additional options
* @return MHD result code
*/
static MHD_RESULT
handle_get_extensions (struct TEH_RequestContext *rc,
const char *const args[])
{
const struct TALER_Extension *ext = NULL;
if (NULL == args[0])
{
GNUNET_break_op (0);
return r404 (rc->connection,
"/extensions/$EXTENSION");
}
ext = TALER_extensions_get_by_name (args[0]);
if (NULL == ext)
{
GNUNET_break_op (0);
return r404 (rc->connection,
"/extensions/$EXTENSION unknown");
}
if (NULL == ext->http_get_handler)
return MHD_HTTP_NOT_IMPLEMENTED;
return ext->http_get_handler (
rc->connection,
&args[1]);
}
/** /**
* Handle POST "/extensions/..." requests. * Handle POST "/extensions/..." requests.
@ -1299,6 +1335,13 @@ handle_mhd_request (void *cls,
.nargs_is_upper_bound = true .nargs_is_upper_bound = true
}, },
/* extensions endpoints */ /* extensions endpoints */
{
.url = "extensions",
.method = MHD_HTTP_METHOD_GET,
.handler.get = &handle_get_extensions,
.nargs = 4, /* Arbitrary upper bound */
.nargs_is_upper_bound = true,
},
{ {
.url = "extensions", .url = "extensions",
.method = MHD_HTTP_METHOD_POST, .method = MHD_HTTP_METHOD_POST,

View File

@ -78,6 +78,7 @@ extension_update_event_cb (void *cls,
} }
// Get the config from the database as string // Get the config from the database as string
if (extension->has_config)
{ {
char *config_str = NULL; char *config_str = NULL;
enum GNUNET_DB_QueryStatus qs; enum GNUNET_DB_QueryStatus qs;
@ -117,14 +118,14 @@ extension_update_event_cb (void *cls,
err.text, err.text,
err.source); err.source);
GNUNET_break (0); GNUNET_break (0);
free(config_str); free (config_str);
return; return;
} }
// Call the parser for the extension // Call the parser for the extension
ret = extension->load_json_config ( ret = extension->load_json_config (
(struct TALER_Extension *) extension, (struct TALER_Extension *) extension,
json_object_get(config, "config")); json_object_get (config, "config"));
if (GNUNET_OK != ret) if (GNUNET_OK != ret)
{ {
@ -135,8 +136,8 @@ extension_update_event_cb (void *cls,
GNUNET_break (0); GNUNET_break (0);
} }
free(config_str); free (config_str);
json_decref(config); json_decref (config);
} }
/* Special case age restriction: Update global flag and mask */ /* Special case age restriction: Update global flag and mask */
@ -190,12 +191,16 @@ TEH_extensions_init ()
it = it->next) it = it->next)
{ {
const struct TALER_Extension *ext = it->extension; const struct TALER_Extension *ext = it->extension;
uint32_t typ = htonl (ext->type);
char *conf = json_dumps (ext->config_to_json (ext), JSON_COMPACT); char *conf = json_dumps (ext->config_to_json (ext), JSON_COMPACT);
TEH_plugin->set_extension_config (TEH_plugin->cls, TEH_plugin->set_extension_config (TEH_plugin->cls,
ext->name, ext->name,
conf); conf);
extension_update_event_cb (NULL, &ext->type, sizeof(ext->type));
extension_update_event_cb (NULL,
&typ,
sizeof(typ));
free (conf); free (conf);
} }

View File

@ -1916,18 +1916,23 @@ create_krd (struct TEH_KeyStateHandle *ksh,
/* flag our findings so far */ /* flag our findings so far */
has_extensions = true; has_extensions = true;
GNUNET_assert (NULL != extension->config_json);
ext = GNUNET_JSON_PACK ( ext = GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", GNUNET_JSON_pack_bool ("critical",
extension->critical), extension->critical),
GNUNET_JSON_pack_string ("version", GNUNET_JSON_pack_string ("version",
extension->version), extension->version)
GNUNET_JSON_pack_object_incref ("config",
extension->config_json)
); );
GNUNET_assert (NULL != ext); GNUNET_assert (NULL != ext);
if (extension->has_config)
{
GNUNET_assert (extension->config_json);
json_object_set_new (ext,
"config",
extension->config_json);
}
r = json_object_set_new ( r = json_object_set_new (
extensions, extensions,
extension->name, extension->name,

View File

@ -105,8 +105,8 @@ age_restriction_load_json_config (
json_decref (ext->config_json); json_decref (ext->config_json);
ext->enabled = true; ext->enabled = true;
ext->config_json = json_copy(jconfig); ext->config_json = json_copy (jconfig);
json_decref(jconfig); json_decref (jconfig);
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"loaded new age restriction config with age groups: %s\n", "loaded new age restriction config with age groups: %s\n",
@ -155,7 +155,7 @@ age_restriction_config_to_json (
GNUNET_JSON_pack_string ("age_groups", mask_str) GNUNET_JSON_pack_string ("age_groups", mask_str)
); );
free(mask_str); free (mask_str);
return GNUNET_JSON_PACK ( return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical), GNUNET_JSON_pack_bool ("critical", ext->critical),
@ -188,6 +188,7 @@ struct TALER_Extension TE_extension_age_restriction = {
.critical = false, .critical = false,
.version = "1", .version = "1",
.enabled = false, /* disabled per default */ .enabled = false, /* disabled per default */
.has_config = true, /* we need to store configuration */
.config = NULL, .config = NULL,
.config_json = NULL, .config_json = NULL,
.disable = &age_restriction_disable, .disable = &age_restriction_disable,
@ -264,7 +265,7 @@ libtaler_extension_age_restriction_init (void *arg)
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[age restriction] setting age mask to %s with #groups: %d\n", "[age restriction] setting age mask to %s with #groups: %d\n",
TALER_age_mask_to_string(&AR_config.mask), TALER_age_mask_to_string (&AR_config.mask),
__builtin_popcount (AR_config.mask.bits) - 1); __builtin_popcount (AR_config.mask.bits) - 1);
TE_extension_age_restriction.config = &AR_config; TE_extension_age_restriction.config = &AR_config;

View File

@ -26,11 +26,245 @@
#include "stdint.h" #include "stdint.h"
#include <microhttpd.h> #include <microhttpd.h>
#define EXTENSION_NAME "auction_brandt" #define AUCTION_BRANDT "auction_brandt"
/* Path to the replay program. */ /* Path to the replay program. */
static char *replay_program; static char *replay_program;
/* This is basically BRANDT_Result with an extra string field */
struct result
{
uint16_t bidder;
uint16_t price_idx;
const char *price;
};
/*
* TODO: Transcript information
*/
struct transcript
{
// All fields from json come here.
uint16_t n; // #bidders + 1
uint16_t k; // #prices
uint16_t m; // type of auction
struct GNUNET_TIME_Absolute time_start;
struct GNUNET_TIME_Relative time_round;
bool public;
char **prices; // Must be of length k. We do not parse those
// struct msg *msgs; // Array must be of length 4*n
// struct BRANDT_Auction *auction;
struct result *results;
size_t results_len;
struct result *expected;
size_t expected_len;
uint16_t id;
struct GNUNET_CRYPTO_EccDlogContext *edc;
};
/**
* @brief returns an JSON with the error
*/
static enum GNUNET_GenericReturnValue
json_error (json_t **output,
char *error)
{
GNUNET_assert (error);
*output = json_pack ("{s:s}", "error", error);
GNUNET_assert (*output);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[auction_brandt] got error: %s\n",
error);
return GNUNET_SYSERR;
};
/**
* @brief returns an JSON with the result
*/
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;
}
/*
* @brief Parses a given json as transcript.
*
* @param[in] jtr JSON input
* @param[out] transcript Parsed transcript data
* @param[out] jresult JSON output, both, for results or errors
* @return GNUNET_OK on succes
*/
static enum GNUNET_GenericReturnValue
parse_transcript (const json_t *jtr,
struct transcript *tr,
json_t **output)
{
// TODO: json_error_t jerror;
// TODO: struct GNUNET_CRYPTO_EddsaSignature sig;
GNUNET_assert (jtr);
GNUNET_assert (tr);
{
json_t *auc;
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_end ()
};
auc = json_object_get (jtr, "auction");
if (NULL == auc)
return json_error (output, "no auction found in transcript");
if (GNUNET_OK !=
GNUNET_JSON_parse (auc,
au_spec,
(const char **) &perr,
&eline))
return json_error (output, perr);
// Prices...
{
json_t *prices = json_object_get (auc, "prices");
size_t idx;
json_t *val;
if (! json_is_array (prices))
// TODO: leak!?
return json_error (output, "no prices found");
tr->k = json_array_size (prices);
tr->prices = GNUNET_new_array (tr->k, char *);
json_array_foreach (prices, idx, val)
{
if (! json_is_string (val))
// TODO: leak!_
return json_error (output, "prices not strings");
tr->prices[idx] = (char *) json_string_value (val);
// TODO: parse prices
}
}
}
// Bidders
{
json_t *bidders;
bidders = json_object_get (jtr, "bidders");
if (! bidders || ! json_is_array (bidders))
// TODO: leak!_
return json_error (output, "bidders not found");
// TODO: parse bidders as pub keys;
tr->n = json_array_size (bidders);
}
// Messages
{
json_t *messages;
size_t nm;
messages = json_object_get (jtr, "transcript");
if (! json_is_array (messages))
// TODO: leak!
return json_error (output, "no messages found");
nm = json_array_size (messages);
if (nm != (4 * tr->n))
// TODO: leak!
return json_error (output, "not the right no. of messages found");
}
// Winners
{
json_t *winners;
size_t idx;
json_t *val;
winners = json_object_get (jtr, "winners");
if (! json_is_array (winners))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"winners not provided, continuing without\n");
goto CONT;
}
tr->expected_len = json_array_size (winners);
tr->expected = GNUNET_new_array (tr->expected_len,
struct result);
json_array_foreach (winners, idx, val) {
char *error;
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,
(const char**) &error,
NULL))
// TODO: leak!
return json_error (output, "couldn't parse winners");
}
CONT:
}
return json_result (output, tr);
}
/** /**
* @brief implements the TALER_Extension.disable interface. * @brief implements the TALER_Extension.disable interface.
* *
@ -70,7 +304,9 @@ auction_config_to_json (
const struct TALER_Extension *ext) const struct TALER_Extension *ext)
{ {
/* This extension has no configuration */ /* This extension has no configuration */
return json_null (); return GNUNET_JSON_PACK (
GNUNET_JSON_pack_bool ("critical", ext->critical),
GNUNET_JSON_pack_string ("version", ext->version));
} }
@ -91,36 +327,68 @@ auction_load_json_config (
} }
/**
* @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,
"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 * @brief implements the TALER_Extension.http_post_handler
*/ */
static MHD_RESULT static MHD_RESULT
auction_http_post_handler ( auction_http_post_handler (
struct MHD_Connection *connection, struct MHD_Connection *connection,
const json_t *root, const json_t *root,
const char *const args[]) const char *const args[])
{ {
struct transcript tr = {};
enum GNUNET_GenericReturnValue ret;
json_t *result;
ret = parse_transcript (root, &tr, &result);
/* TODO */ /* TODO */
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"auction_http_post_handler not implemented yet"); "auction_http_post_handler not implemented yet\n");
return MHD_HTTP_NOT_IMPLEMENTED;
return TALER_MHD_reply_json_steal (connection,
result,
GNUNET_OK == ret? MHD_HTTP_OK :
MHD_HTTP_BAD_REQUEST);
} }
/* The extension struct for auctions of brandt-style */ /* The extension struct for auctions of brandt-style */
struct TALER_Extension TE_auction_brandt = { struct TALER_Extension TE_auction_brandt = {
.type = 0x0815, /* TODO: where do we get this from? */ .type = TALER_Extension_AuctionBrandt,
.name = EXTENSION_NAME, .name = AUCTION_BRANDT,
.critical = false, .critical = false,
.version = "0", .version = "0",
.enabled = false, /* disabled per default */ .enabled = false, /* disabled per default */
.has_config = false, /* This extension has no configuration */
.config = NULL, .config = NULL,
.config_json = NULL, .config_json = NULL,
.disable = &auction_disable, .disable = &auction_disable,
.test_json_config = &auction_test_json_config, .test_json_config = &auction_test_json_config,
.load_json_config = &auction_load_json_config, .load_json_config = &auction_load_json_config,
.config_to_json = &auction_config_to_json, .config_to_json = &auction_config_to_json,
.http_get_handler = &auction_http_get_handler,
.http_post_handler = &auction_http_post_handler, .http_post_handler = &auction_http_post_handler,
}; };
@ -141,25 +409,31 @@ struct TALER_Extension TE_auction_brandt = {
* @return Pointer to TE_auction_brandt * @return Pointer to TE_auction_brandt
*/ */
struct TALER_Extension * struct TALER_Extension *
init (void *arg) libtaler_extension_auction_brandt_init (void *arg)
{ {
const struct GNUNET_CONFIGURATION_Handle *cfg = arg; const struct GNUNET_CONFIGURATION_Handle *cfg = arg;
if (GNUNET_SYSERR == if (GNUNET_SYSERR ==
GNUNET_CONFIGURATION_get_value_string (cfg, GNUNET_CONFIGURATION_get_value_string (cfg,
"extension_" EXTENSION_NAME, TALER_EXTENSION_SECTION_PREFIX
"replay_program", AUCTION_BRANDT,
"REPLAY_PROGRAM",
&replay_program)) &replay_program))
{ {
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"extension_" EXTENSION_NAME, TALER_EXTENSION_SECTION_PREFIX AUCTION_BRANDT,
"replay_program"); "REPLAY_PROGRAM");
return NULL; return NULL;
} }
/* TODO: check if replay_program is actually executable */
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[auction_brandt] loading... using replay_program '%s'\n",
replay_program);
return &TE_auction_brandt; return &TE_auction_brandt;
} }
/** /**
* @brief Tear-down function for the extension. * @brief Tear-down function for the extension.
* Will be called by GNUNET_PLUGIN_unload. * Will be called by GNUNET_PLUGIN_unload.
@ -168,15 +442,14 @@ init (void *arg)
* @return null * @return null
*/ */
void * void *
done (void *arg) libtaler_extension_auction_brandt_done (void *arg)
{ {
auction_disable(&TE_auction_brandt); auction_disable (&TE_auction_brandt);
GNUNET_free(replay_program); GNUNET_free (replay_program);
replay_program=NULL; replay_program = NULL;
return NULL; return NULL;
} }
/* end of extension_auction_brandt.c */ /* end of extension_auction_brandt.c */

View File

@ -31,8 +31,12 @@
enum TALER_Extension_Type enum TALER_Extension_Type
{ {
TALER_Extension_AgeRestriction = 0, TALER_Extension_Refund = 0,
TALER_Extension_MaxPredefined = 1 // Must be last of the predefined 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
}; };
@ -59,6 +63,7 @@ struct TALER_Extension
char *version; char *version;
void *config; void *config;
bool enabled; bool enabled;
bool has_config; /* some extension might not have a configuration */
json_t *config_json; json_t *config_json;
void (*disable)(struct TALER_Extension *ext); void (*disable)(struct TALER_Extension *ext);
@ -78,6 +83,10 @@ struct TALER_Extension
const json_t *root, const json_t *root,
const char *const args[]); const char *const args[]);
MHD_RESULT (*http_get_handler)(
struct MHD_Connection *connection,
const char *const args[]);
}; };
/** /**

View File

@ -931,6 +931,9 @@ decode_keys_json (const json_t *resp_obj,
if (! no_extensions && ! no_signature) if (! no_extensions && ! no_signature)
{ {
GNUNET_log(GNUNET_ERROR_TYPE_INFO,
"DEBUG: got extensions object %s\n",
json_dumps(extensions, JSON_INDENT(2)));
/* 2. We have an extensions object. Verify its signature. */ /* 2. We have an extensions object. Verify its signature. */
EXITIF (GNUNET_OK != EXITIF (GNUNET_OK !=
TALER_extensions_verify_json_config_signature ( TALER_extensions_verify_json_config_signature (

View File

@ -490,7 +490,7 @@ TALER_TESTING_cmd_batch_withdraw (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof); acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash); hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_get_age_restriction_mask(); mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed, &seed,
sizeof(seed)); sizeof(seed));

View File

@ -590,7 +590,7 @@ TALER_TESTING_cmd_withdraw_amount (const char *label,
acp = GNUNET_new (struct TALER_AgeCommitmentProof); acp = GNUNET_new (struct TALER_AgeCommitmentProof);
hac = GNUNET_new (struct TALER_AgeCommitmentHash); hac = GNUNET_new (struct TALER_AgeCommitmentHash);
mask = TALER_extensions_get_age_restriction_mask(); mask = TALER_extensions_get_age_restriction_mask ();
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
&seed, &seed,
sizeof(seed)); sizeof(seed));