457 lines
10 KiB
C
457 lines
10 KiB
C
/* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* @file replay.c
|
|
* @brief Replay a auction, given as transcript in json form on stdin
|
|
* @author Özgür Kesim
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include <gnunet/gnunet_util_lib.h>
|
|
#include <gnunet/gnunet_json_lib.h>
|
|
#include <taler/taler_amount_lib.h>
|
|
#include <jansson.h>
|
|
|
|
#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 *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));
|
|
|
|
json_dumpfd(output, 1, JSON_INDENT(2));
|
|
}
|
|
|
|
static void
|
|
tr_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);
|
|
}
|
|
|
|
// TODO: compare computed and expected results!
|
|
|
|
print_result(tr, NULL);
|
|
}
|
|
|
|
|
|
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)
|
|
{
|
|
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);
|
|
|
|
return ret;
|
|
}
|