/* This file is part of libbrandt. * Copyright (C) 2022 GNUnet e.V. * * libbrandt 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 of the License, or (at your option) any later * version. * * libbrandt 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 * libbrandt. If not, see . */ /** * @file replay.c * @brief Replay a auction, given as transcript in json form on stdin * @author Özgür Kesim */ #include "platform.h" #include #include #include #include #include "brandt.h" #include "crypto.h" #include "util.h" struct msg { uint16_t sender; void *buf; size_t buf_len; }; /* This is basically BRANDT_Result with an extra string field */ struct result { uint16_t bidder; uint16_t price_idx; const char *price; }; 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; }; /* static struct transcript tr; */ static void print_result (struct transcript *tr, char *error) { json_t *output; json_t *results; if (NULL != error) { output = json_pack ("{s:s}", "error", error); GNUNET_assert (output); json_dumpfd (output, 1, JSON_INDENT (2)); return; } 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:i, s:s}", "bidder", tr->results[i].bidder, "price_idx", tr->results[i].price_idx, "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)); { FILE *f = fdopen (1, "w"); GNUNET_assert (f); json_dumpf (output, f, JSON_INDENT (2)); fflush (f); } json_decref (output); } static void cb_result (void *arg, struct BRANDT_Result results[], uint16_t results_len) { struct transcript *tr = (struct transcript *) arg; tr->results = GNUNET_new_array (results_len, struct result); tr->results_len = results_len; for (uint16_t i = 0; i < results_len; i++) { GNUNET_log ( GNUNET_ERROR_TYPE_INFO, "REPLAY [seller] computed result is: bidder %d got status %d with price %d (%s)\n", results[i].bidder, results[i].status, results[i].price, tr->prices[results[i].price]); tr->results[i].bidder = results[i].bidder; tr->results[i].price_idx = results[i].price; tr->results[i].price = tr->prices[results[i].price]; } for (uint16_t i = 0; i < tr->expected_len; i++) { GNUNET_log ( GNUNET_ERROR_TYPE_INFO, "REPLAY [seller] expected result is: bidder %d wins with price %d (%s)\n", tr->expected[i].bidder, tr->expected[i].price_idx, tr->expected[i].price); } print_result (tr, NULL); // TODO: compare computed and expected results! } struct cls { size_t i; struct transcript *tr; }; static void resend (void *x) { struct cls c = *(struct cls *) x; struct msg m = c.tr->msgs[c.i]; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "REPLAY sending msg no. %ld, length %ld\n", c.i, m.buf_len); BRANDT_got_message (c.tr->auction, m.sender, m.buf, m.buf_len); } static uint16_t cb_start (void *auction_closure) { struct transcript *tr = (struct transcript *) auction_closure; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "REPLAY start replay auction\n"); for (size_t i = 0; i < 4 * tr->n; i++) { struct cls *c = GNUNET_new (struct cls); c->i = i; c->tr = tr; GNUNET_SCHEDULER_add_now (&resend, (void *) c); } return tr->n; } static void replay_transcript (void *arg) { struct transcript *tr = (struct transcript *) arg; void *desc; size_t desc_len; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "REPLAY calling BRANDT_new with %s outcome.\n", tr->public ? "public" : "private"); tr->auction = BRANDT_new (&cb_result, NULL, &cb_start, tr, &desc, &desc_len, GNUNET_TIME_absolute_get (), tr->time_round, tr->k, /* number of prizes */ tr->m, /* m */ tr->public, /* outcome public */ tr->public ? tr->edc : NULL); if (! tr->auction) { print_result (NULL, "REPLAY BRANDT_new() failed."); _exit (1); } } void parse_json_stdin (struct transcript *tr) { json_error_t jerror; json_t *jtr; /* struct GNUNET_CRYPTO_EddsaSignature sig; */ jtr = json_loadfd (0, JSON_REJECT_DUPLICATES, &jerror); if (! jtr) { char err[4096]; snprintf (err, sizeof(err), "failed to parse json: %s in line %d, column %d (pos %d)", jerror.text, jerror.line, jerror.column, jerror.position); print_result (NULL, err); _exit (1); } { json_t *auc; char *perr; unsigned int eline; struct GNUNET_JSON_Specification au_spec[] = { GNUNET_JSON_spec_bool ("outcome_public", &tr->public), GNUNET_JSON_spec_uint16 ("auction_type", &tr->m), GNUNET_JSON_spec_end () }; auc = json_object_get (jtr, "auction"); if (NULL == auc) { print_result (NULL, "no auction found in input"); _exit (1); } GNUNET_assert (GNUNET_OK == GNUNET_JSON_parse (auc, au_spec, (const char **) &perr, &eline)); // Prices... { json_t *prices = json_object_get (auc, "prices"); size_t idx; json_t *val; if (! json_is_array (prices)) { print_result (NULL, "no prices found in input"); _exit (1); } 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)) { char err[256]; snprintf (err, sizeof(err), "price %ld is not a string\n", idx); print_result (NULL, err); _exit (1); } tr->prices[idx] = (char *) json_string_value (val); } } } // Bidders { json_t *bidders; bidders = json_object_get (jtr, "bidders"); if (! bidders || ! json_is_array (bidders)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "bidders missing or not an array\n"); print_result (NULL, "bidders missing or not an array"); _exit (1); } tr->n = json_array_size (bidders); } // Messages { json_t *messages; size_t nm; size_t idx; json_t *val; messages = json_object_get (jtr, "transcript"); if (! json_is_array (messages)) { print_result (NULL, "transcript missing or not an array"); _exit (1); } nm = json_array_size (messages); if (nm != (4 * tr->n)) { print_result (NULL, "wrong number of messages in transript"); _exit (1); } tr->msgs = GNUNET_new_array (nm, struct msg); json_array_foreach (messages, idx, val) { char *error; uint16_t sender; void *msg; size_t size; struct GNUNET_JSON_Specification spec[] = { GNUNET_JSON_spec_uint16 ("bidder", &sender), GNUNET_JSON_spec_varsize ("msg", &msg, &size), GNUNET_JSON_spec_end () }; if (GNUNET_OK != GNUNET_JSON_parse (val, spec, (const char**) &error, NULL)) { char err[4096]; snprintf (err, sizeof(err), "error parsing message[%ld] in transcript: %s", idx, error); print_result (NULL, err); _exit (1); } tr->msgs[idx].sender = sender; tr->msgs[idx].buf = msg; tr->msgs[idx].buf_len = size; } } // 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)) { char err[4096]; snprintf (err, sizeof(err), "error parsing winners[%ld] in transcript: %s", idx, error); _exit (1); } } CONT: } } int main (int argc, char *argv[]) { int ret = 0; struct transcript tr = {0}; if (GNUNET_OK != GNUNET_log_setup ("replay", "INFO", NULL)) return 1; BRANDT_init (); parse_json_stdin (&tr); tr.edc = GNUNET_CRYPTO_ecc_dlog_prepare (1024 * 1024 * 40, 1024); GNUNET_SCHEDULER_run (&replay_transcript, &tr); GNUNET_CRYPTO_ecc_dlog_release (tr.edc); GNUNET_free (tr.msgs); return ret; }