diff --git a/src/extensions/auction_brandt/extension_auction_brandt.c b/src/extensions/auction_brandt/extension_auction_brandt.c index 24ac452bc..358c6b7c6 100644 --- a/src/extensions/auction_brandt/extension_auction_brandt.c +++ b/src/extensions/auction_brandt/extension_auction_brandt.c @@ -33,6 +33,9 @@ /* 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 { @@ -42,28 +45,54 @@ struct result }; /* - * TODO: Transcript information + * @brief 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; + /* + * 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; - 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; + /* Start date of the auction */ + struct GNUNET_TIME_Timestamp time_start; - struct result *results; - size_t results_len; + /* 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; - uint16_t id; - struct GNUNET_CRYPTO_EccDlogContext *edc; + + /* + * These are the results from the replay via the external program. + */ + struct result *results; + size_t results_len; }; /** @@ -71,16 +100,25 @@ struct transcript */ static enum GNUNET_GenericReturnValue json_error (json_t **output, - char *error) + char *error, ...) { + va_list ap; + int n = 0; + char buf[4096]; GNUNET_assert (error); - *output = json_pack ("{s:s}", "error", error); - GNUNET_assert (*output); + va_start (ap, error); + n = vsprintf (buf, error, ap); + va_end (ap); GNUNET_log (GNUNET_ERROR_TYPE_INFO, LOG_PREFIX "got error: %s\n", - error); + n < 0 ? error: buf); + + *output = json_pack ("{s:s}", + "error", + n < 0 ? error : buf); + GNUNET_assert (*output); return GNUNET_SYSERR; }; @@ -129,118 +167,153 @@ json_result (json_t **output, * @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 + * @param[out] tr Parsed transcript data + * @param[out] jerror JSON output for errors * @return GNUNET_OK on succes * * TODO: - * - fix leakages * - parse and verify signatures */ static enum GNUNET_GenericReturnValue parse_transcript (const json_t *jtr, struct transcript *tr, - json_t **output) + json_t **jerror) { + json_t *auc; + // TODO: struct GNUNET_CRYPTO_EddsaSignature sig; GNUNET_assert (jtr); GNUNET_assert (tr); + // Parse auction { - 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_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 (output, "no auction found in transcript"); + 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 (output, perr); + return json_error (jerror, + perr); // Prices... { - json_t *prices = json_object_get (auc, "prices"); size_t idx; json_t *val; + json_t *prices; - + prices = json_object_get (auc, "prices"); if (! json_is_array (prices)) - // TODO: fix leak!? - return json_error (output, "no prices found"); + return json_error (jerror, + "no prices found"); tr->k = json_array_size (prices); - tr->prices = GNUNET_new_array (tr->k, char *); + tr->prices = GNUNET_new_array (tr->k, struct TALER_Amount); json_array_foreach (prices, idx, val) { - if (! json_is_string (val)) - // TODO: fix leak!_ - return json_error (output, "prices not strings"); + struct GNUNET_JSON_Specification spec[] = { + TALER_JSON_spec_amount (NULL, + currency, + &(tr->prices[idx])), + GNUNET_JSON_spec_end (), + }; - tr->prices[idx] = (char *) json_string_value (val); - - // TODO: parse prices + 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)) - // TODO: fix leak!_ - return json_error (output, "bidders not found"); + return json_error (jerror, + "no bidders found"); - // TODO: parse bidders as pub keys; 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 { - json_t *messages; size_t nm; + json_t *messages = json_object_get (jtr, "transcript"); - messages = json_object_get (jtr, "transcript"); if (! json_is_array (messages)) - // TODO: fix leak! - return json_error (output, "no messages found"); + return json_error (jerror, + "no messages found"); nm = json_array_size (messages); if (nm != (4 * tr->n)) - // TODO: fix leak! - return json_error (output, "not the right no. of messages found"); + return json_error (jerror, + "not the right no. of messages found"); + + /* TODO: parse and evaluate signatures */ } // Winners { - json_t *winners; size_t idx; json_t *val; - - winners = json_object_get (jtr, "winners"); + 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"); - // TODO: fix leakage - goto CONT; + goto DONE; } tr->expected_len = json_array_size (winners); @@ -248,8 +321,6 @@ parse_transcript (const json_t *jtr, struct result); json_array_foreach (winners, idx, val) { - char *error; - struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_uint16 ("bidder", &(tr->expected[idx].bidder)), @@ -263,16 +334,21 @@ parse_transcript (const json_t *jtr, if (GNUNET_OK != GNUNET_JSON_parse (val, spec, - (const char**) &error, + NULL, NULL)) - // TODO: fix leak! - return json_error (output, "couldn't parse winners"); + return json_error (jerror, + "couldn't parse winner no. %ld", + idx + 1); } - -CONT: - // TODO: fix leakages } - *output = NULL; + + // TODO: parse and evalue sig of seller + +// TODO: check for max values + +DONE: + + *jerror = NULL; return GNUNET_OK; } @@ -285,7 +361,7 @@ CONT: * @param[out] result The JSON result from the program * @return GNUNET_OK on success * - * TODO: Make this asynchronous (giving out a replay-ID to poll from)? + * TODO: Make this resumable */ static enum GNUNET_GenericReturnValue replay_transcript (const json_t*root, @@ -338,6 +414,9 @@ replay_transcript (const json_t*root, { 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, @@ -347,21 +426,62 @@ replay_transcript (const json_t*root, { GNUNET_log (GNUNET_ERROR_TYPE_INFO, LOG_PREFIX "couldn't read data from replay_program\n"); + return json_error (result, "internal error"); } - else + + buf[sz] = 0; + res = json_loads (buf, + JSON_DECODE_ANY, + &error); + + if (! res) { - json_error_t error; - json_t *res; - res = json_loads (buf, - JSON_DECODE_ANY, - &error); + 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); + } - if (! res) - 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)) @@ -375,7 +495,6 @@ replay_transcript (const json_t*root, } - // TODO: parse result return GNUNET_OK; } @@ -389,6 +508,7 @@ static void auction_disable ( struct TALER_Extension *ext) { + /* TODO: cleanup configuration */ ext->enabled = false; } @@ -418,7 +538,7 @@ static json_t * auction_config_to_json ( const struct TALER_Extension *ext) { - /* This extension has no configuration */ + /* TODO: add configuration */ return GNUNET_JSON_PACK ( GNUNET_JSON_pack_bool ("critical", ext->critical), GNUNET_JSON_pack_string ("version", ext->version)); @@ -436,7 +556,7 @@ auction_load_json_config ( struct TALER_Extension *ext, json_t *jconfig) { - /* This extension has no configuration */ + /* TODO: add configuration */ ext->enabled = true; return GNUNET_OK; } @@ -465,6 +585,8 @@ auction_http_get_handler ( /** * @brief implements the TALER_Extension.http_post_handler + * + * TODO: make this non-blocking */ static MHD_RESULT auction_http_post_handler ( @@ -505,7 +627,7 @@ struct TALER_Extension TE_auction_brandt = { .critical = false, .version = "0", .enabled = false, /* disabled per default */ - .has_config = false, /* This extension has no configuration */ + .has_config = true, .config = NULL, .config_json = NULL, .disable = &auction_disable, @@ -537,6 +659,12 @@ 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 @@ -549,11 +677,37 @@ libtaler_extension_auction_brandt_init (void *arg) "REPLAY_PROGRAM"); return NULL; } - /* TODO: check if replay_program is actually executable */ + + /* 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; }