WiP: parsing and replay of auction transcripts

POST handler of auction_brandt extension now parses JSON transcript and
passes JSON via stdin to external program.  Reads result via stdout.

TODOs:
- check signatures in transcript
- parse JSON output of replay progam
- add configuration to to auction_brandt:
	- types of auctions
	- max. concurrent users
	- max. number of prices
	- maximum price
	- fees
- add timeout handler for replay program execution
- (maybe) sign output of exchange by master-key
- (maybe) add GET handler with information re: auction_brandt?
This commit is contained in:
Özgür Kesim 2022-10-03 08:15:07 +02:00
parent b2d68d4b2d
commit 12736abcf7
Signed by: oec
GPG Key ID: 3D76A56D79EDD9D7
2 changed files with 302 additions and 19 deletions

View File

@ -27,6 +27,8 @@
#include <microhttpd.h> #include <microhttpd.h>
#define AUCTION_BRANDT "auction_brandt" #define AUCTION_BRANDT "auction_brandt"
#define LOG_PREFIX "[auction_brandt] "
#define MAX_RESULT_SIZE 10 * 1024
/* Path to the replay program. */ /* Path to the replay program. */
static char *replay_program; static char *replay_program;
@ -77,7 +79,7 @@ json_error (json_t **output,
GNUNET_assert (*output); GNUNET_assert (*output);
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[auction_brandt] got error: %s\n", LOG_PREFIX "got error: %s\n",
error); error);
return GNUNET_SYSERR; return GNUNET_SYSERR;
@ -86,6 +88,7 @@ json_error (json_t **output,
/** /**
* @brief returns an JSON with the result * @brief returns an JSON with the result
*/ */
#if 0
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
json_result (json_t **output, json_result (json_t **output,
const struct transcript *tr) const struct transcript *tr)
@ -119,6 +122,9 @@ json_result (json_t **output,
} }
#endif
/* /*
* @brief Parses a given json as transcript. * @brief Parses a given json as transcript.
* *
@ -126,13 +132,16 @@ json_result (json_t **output,
* @param[out] transcript Parsed transcript data * @param[out] transcript Parsed transcript data
* @param[out] jresult JSON output, both, for results or errors * @param[out] jresult JSON output, both, for results or errors
* @return GNUNET_OK on succes * @return GNUNET_OK on succes
*
* TODO:
* - fix leakages
* - parse and verify signatures
*/ */
static enum GNUNET_GenericReturnValue static enum GNUNET_GenericReturnValue
parse_transcript (const json_t *jtr, parse_transcript (const json_t *jtr,
struct transcript *tr, struct transcript *tr,
json_t **output) json_t **output)
{ {
// TODO: json_error_t jerror;
// TODO: struct GNUNET_CRYPTO_EddsaSignature sig; // TODO: struct GNUNET_CRYPTO_EddsaSignature sig;
GNUNET_assert (jtr); GNUNET_assert (jtr);
@ -167,7 +176,7 @@ parse_transcript (const json_t *jtr,
if (! json_is_array (prices)) if (! json_is_array (prices))
// TODO: leak!? // TODO: fix leak!?
return json_error (output, "no prices found"); return json_error (output, "no prices found");
tr->k = json_array_size (prices); tr->k = json_array_size (prices);
@ -176,7 +185,7 @@ parse_transcript (const json_t *jtr,
json_array_foreach (prices, idx, val) json_array_foreach (prices, idx, val)
{ {
if (! json_is_string (val)) if (! json_is_string (val))
// TODO: leak!_ // TODO: fix leak!_
return json_error (output, "prices not strings"); return json_error (output, "prices not strings");
tr->prices[idx] = (char *) json_string_value (val); tr->prices[idx] = (char *) json_string_value (val);
@ -192,7 +201,7 @@ parse_transcript (const json_t *jtr,
bidders = json_object_get (jtr, "bidders"); bidders = json_object_get (jtr, "bidders");
if (! bidders || ! json_is_array (bidders)) if (! bidders || ! json_is_array (bidders))
// TODO: leak!_ // TODO: fix leak!_
return json_error (output, "bidders not found"); return json_error (output, "bidders not found");
// TODO: parse bidders as pub keys; // TODO: parse bidders as pub keys;
@ -207,14 +216,14 @@ parse_transcript (const json_t *jtr,
messages = json_object_get (jtr, "transcript"); messages = json_object_get (jtr, "transcript");
if (! json_is_array (messages)) if (! json_is_array (messages))
// TODO: leak! // TODO: fix leak!
return json_error (output, "no messages found"); return json_error (output, "no messages found");
nm = json_array_size (messages); nm = json_array_size (messages);
if (nm != (4 * tr->n)) if (nm != (4 * tr->n))
// TODO: leak! // TODO: fix leak!
return json_error (output, "not the right no. of messages found"); return json_error (output, "not the right no. of messages found");
} }
@ -229,7 +238,8 @@ parse_transcript (const json_t *jtr,
if (! json_is_array (winners)) if (! json_is_array (winners))
{ {
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"winners not provided, continuing without\n"); LOG_PREFIX "winners not provided, continuing without\n");
// TODO: fix leakage
goto CONT; goto CONT;
} }
@ -255,13 +265,118 @@ parse_transcript (const json_t *jtr,
spec, spec,
(const char**) &error, (const char**) &error,
NULL)) NULL))
// TODO: leak! // TODO: fix leak!
return json_error (output, "couldn't parse winners"); return json_error (output, "couldn't parse winners");
} }
CONT: CONT:
// TODO: fix leakages
} }
return json_result (output, tr); *output = NULL;
return GNUNET_OK;
}
/**
* @brief replay an auction using the external program
*
* @param[in] root The original JSON transcript
* @param[in] transcript The transcript object parsed so far
* @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)?
*/
static enum GNUNET_GenericReturnValue
replay_transcript (const json_t*root,
struct transcript *tr,
json_t **result)
{
struct GNUNET_DISK_PipeHandle *pi;
struct GNUNET_DISK_PipeHandle *po;
const struct GNUNET_DISK_FileHandle *fd;
struct GNUNET_OS_Process *proc;
pi = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_WRITE);
po = GNUNET_DISK_pipe (GNUNET_DISK_PF_BLOCKING_READ);
proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ERR,
pi, po, NULL,
replay_program,
replay_program,
NULL);
if (NULL == proc)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
LOG_PREFIX "couldn't create auction replay program '%s'\n",
replay_program);
return json_error (result, "internal error");
}
// Write original transcript JSON to stdin
{
ssize_t sz;
char *str;
size_t str_len;
fd = GNUNET_DISK_pipe_handle (pi, GNUNET_DISK_PIPE_END_WRITE);
str = json_dumps (root, JSON_COMPACT);
str_len = strlen (str);
sz = GNUNET_DISK_file_write (fd,
str,
str_len);
free (str);
if (sz != str_len)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
LOG_PREFIX "couldn't write all data to replay_program\n");
}
}
// Read output from stdout
{
ssize_t sz;
char buf[MAX_RESULT_SIZE];
fd = GNUNET_DISK_pipe_handle (po, GNUNET_DISK_PIPE_END_READ);
sz = GNUNET_DISK_file_read (fd,
buf,
sizeof(buf));
if (GNUNET_SYSERR == sz)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
LOG_PREFIX "couldn't read data from replay_program\n");
}
else
{
json_error_t error;
json_t *res;
res = json_loads (buf,
JSON_DECODE_ANY,
&error);
if (! res)
return json_error (result, error.text);
*result = json_copy (res);
json_decref (res);
}
}
if (GNUNET_OK != GNUNET_OS_process_wait (proc))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
LOG_PREFIX "error while launching auction replay program '%s'\n",
replay_program);
json_object_clear (*result);
return json_error (result, "internal error");
}
// TODO: parse result
return GNUNET_OK;
} }
@ -338,7 +453,7 @@ auction_http_get_handler (
/* TODO: return some meta-data about supported version, limits, etc.*/ /* TODO: return some meta-data about supported version, limits, etc.*/
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"auction_http_get_handler not implemented yet\n"); LOG_PREFIX "auction_http_get_handler not implemented yet\n");
return TALER_MHD_reply_with_error (connection, return TALER_MHD_reply_with_error (connection,
MHD_HTTP_NOT_IMPLEMENTED, MHD_HTTP_NOT_IMPLEMENTED,
@ -359,17 +474,26 @@ auction_http_post_handler (
{ {
struct transcript tr = {}; struct transcript tr = {};
enum GNUNET_GenericReturnValue ret; enum GNUNET_GenericReturnValue ret;
json_t *result; json_t *result1;
json_t *result2;
ret = parse_transcript (root, &tr, &result); ret = parse_transcript (root,
&tr,
&result1);
if (GNUNET_OK != ret)
return TALER_MHD_reply_json_steal (connection,
result1,
MHD_HTTP_BAD_REQUEST);
GNUNET_assert (NULL == result1);
/* TODO */ ret = replay_transcript (root,
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, &tr,
"auction_http_post_handler not implemented yet\n"); &result2);
return TALER_MHD_reply_json_steal (connection, return TALER_MHD_reply_json_steal (connection,
result, result2,
GNUNET_OK == ret? MHD_HTTP_OK : GNUNET_OK == ret?
MHD_HTTP_OK :
MHD_HTTP_BAD_REQUEST); MHD_HTTP_BAD_REQUEST);
} }
@ -427,7 +551,7 @@ libtaler_extension_auction_brandt_init (void *arg)
} }
/* TODO: check if replay_program is actually executable */ /* TODO: check if replay_program is actually executable */
GNUNET_log (GNUNET_ERROR_TYPE_INFO, GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"[auction_brandt] loading... using replay_program '%s'\n", LOG_PREFIX "loading... using replay_program '%s'\n",
replay_program); replay_program);
return &TE_auction_brandt; return &TE_auction_brandt;

View File

@ -0,0 +1,159 @@
# This file is in the public domain.
#
[PATHS]
# Persistent data storage for the testcase
TALER_TEST_HOME = test_exchange_api_home/
TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
[taler-exchange-secmod-rsa]
# Reduce from 1 year to speed up test
LOOKAHEAD_SIGN = 24 days
[taler-exchange-secmod-eddsa]
# Reduce from 1 year to speed up test
LOOKAHEAD_SIGN = 24 days
# Reduce from 12 weeks to ensure we have multiple
DURATION = 14 days
[taler]
# Currency supported by the exchange (can only be one)
CURRENCY = EUR
CURRENCY_ROUND_UNIT = EUR:0.01
[auditor]
BASE_URL = "http://localhost:8083/"
# HTTP port the auditor listens to
PORT = 8083
[exchange]
TERMS_ETAG = 0
PRIVACY_ETAG = 0
# HTTP port the exchange listens to
PORT = 8081
# Master public key used to sign the exchange's various keys
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
# How to access our database
DB = postgres
# Base URL of the exchange. Must be set to a URL where the
# exchange (or the twister) is actually listening.
BASE_URL = "http://localhost:8081/"
# How big is an individual shard to be processed
# by taler-exchange-expire (in time). It may take
# this much time for an expired purse to be really
# cleaned up and the coins refunded.
EXPIRE_SHARD_SIZE = 300 ms
EXPIRE_IDLE_SLEEP_INTERVAL = 1 s
[exchangedb-postgres]
CONFIG = "postgres:///talercheck"
[auditordb-postgres]
CONFIG = "postgres:///talercheck"
# Sections starting with "exchange-account-" configure the bank accounts
# of the exchange. The "URL" specifies the account in
# payto://-format.
[exchange-account-1]
# What is the URL of our account?
PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42"
# ENABLE_CREDIT = YES
[exchange-accountcredentials-1]
WIRE_GATEWAY_URL = "http://localhost:9081/42/"
[exchange-account-2]
# What is the bank account (with the "Taler Bank" demo system)?
PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2"
ENABLE_DEBIT = YES
ENABLE_CREDIT = YES
[exchange-accountcredentials-2]
WIRE_GATEWAY_AUTH_METHOD = basic
USERNAME = Exchange
PASSWORD = x
WIRE_GATEWAY_URL = "http://localhost:9081/2/"
[bank]
HTTP_PORT = 9081
# Enabled extensions
#[exchange-extension-age_restriction]
#ENABLED = YES
# default age groups:
#AGE_GROUPS = "8:10:12:14:16:18:21"
[exchange-extension-auction_brandt]
ENABLED = YES
REPLAY_PROGRAM = "/usr/local/bin/taler-exchange-auction_brandt-replay"
# Sections starting with "coin_" specify which denominations
# the exchange should support (and their respective fee structure)
[coin_eur_ct_1]
value = EUR:0.01
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.00
fee_deposit = EUR:0.00
fee_refresh = EUR:0.01
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 1024
[coin_eur_ct_10]
value = EUR:0.10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 1024
[coin_eur_1]
value = EUR:1
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 1024
[coin_eur_5]
value = EUR:5
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 1024
[coin_eur_10]
value = EUR:10
duration_withdraw = 7 days
duration_spend = 2 years
duration_legal = 3 years
fee_withdraw = EUR:0.01
fee_deposit = EUR:0.01
fee_refresh = EUR:0.03
fee_refund = EUR:0.01
CIPHER = RSA
rsa_keysize = 1024