/* 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: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));
{
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!
}
static uint16_t
cb_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 (&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 ("public", &tr->public),
GNUNET_JSON_spec_uint16 ("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;
}