/* 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" #define MIN(A, B) ((A) < (B) ? (A) : (B)) 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 *expected; size_t expected_len; uint16_t id; struct GNUNET_CRYPTO_EccDlogContext *edc; }; static struct transcript tr; static void tr_result (void *arg, struct BRANDT_Result results[], uint16_t results_len) { struct transcript *tr = (struct transcript *) arg; 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]); } 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); } // TODO: generate JSON-output } static uint16_t tr_start (void *auction_closure) { struct transcript *tr = (struct transcript *) auction_closure; struct cls { size_t i; struct transcript *tr; }; 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); } 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 (&tr_result, NULL, &tr_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) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "REPLAY BRANDT_new() failed.\n"); _exit (1); // TODO: generate JSON error response } } enum GNUNET_GenericReturnValue 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) { // TODO: generate JSON error response GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "REPLAY failed to parse json: %s in line %d, column %d (pos %d)\n", jerror.text, jerror.line, jerror.column, jerror.position); _exit (1); } { 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) { // TODO error message as json GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no auction found in JSON"); _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)) { // TODO error message as json GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "no prices found in JSON\n"); _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)) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "price %ld is not a string\n", idx); _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)) { // TODO: json-error GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "bidders missing or not an array\n"); _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)) { // TODO: json-error GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "transcript missing or not an array\n"); _exit (1); } nm = json_array_size (messages); if (nm != (4 * tr->n)) { // TODO: json-error GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "wrong number of messages in transcript\n"); _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)) { // TODO: json-error GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "error parsing message[%ld] in transcript: %s\njson:\n%s\n", idx, error, json_dumps (val, JSON_INDENT (2))); _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)) { // TODO: json-warning 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: json-error GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "error parsing winners[%ld] in transcript: %s\njson:\n%s\n", idx, error, json_dumps (val, JSON_INDENT (2))); _exit (1); } } CONT: } return GNUNET_OK; } 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 (); GNUNET_assert (GNUNET_OK == parse_json_stdin (&tr)); // TODO: error as json-output if failure // tr.edc = GNUNET_CRYPTO_ecc_dlog_prepare (1024 * 1024 * 40, 1024); GNUNET_SCHEDULER_run (&replay_transcript, &tr); GNUNET_CRYPTO_ecc_dlog_release (tr.edc); return ret; }