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:
parent
b2d68d4b2d
commit
12736abcf7
@ -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;
|
||||||
|
159
src/testing/test_exchange_auction.conf
Normal file
159
src/testing/test_exchange_auction.conf
Normal 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
|
Loading…
Reference in New Issue
Block a user