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>
|
||||
|
||||
#define AUCTION_BRANDT "auction_brandt"
|
||||
#define LOG_PREFIX "[auction_brandt] "
|
||||
#define MAX_RESULT_SIZE 10 * 1024
|
||||
|
||||
/* Path to the replay program. */
|
||||
static char *replay_program;
|
||||
@ -77,7 +79,7 @@ json_error (json_t **output,
|
||||
GNUNET_assert (*output);
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"[auction_brandt] got error: %s\n",
|
||||
LOG_PREFIX "got error: %s\n",
|
||||
error);
|
||||
|
||||
return GNUNET_SYSERR;
|
||||
@ -86,6 +88,7 @@ json_error (json_t **output,
|
||||
/**
|
||||
* @brief returns an JSON with the result
|
||||
*/
|
||||
#if 0
|
||||
static enum GNUNET_GenericReturnValue
|
||||
json_result (json_t **output,
|
||||
const struct transcript *tr)
|
||||
@ -119,6 +122,9 @@ json_result (json_t **output,
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* @brief Parses a given json as transcript.
|
||||
*
|
||||
@ -126,13 +132,16 @@ json_result (json_t **output,
|
||||
* @param[out] transcript Parsed transcript data
|
||||
* @param[out] jresult JSON output, both, for results or errors
|
||||
* @return GNUNET_OK on succes
|
||||
*
|
||||
* TODO:
|
||||
* - fix leakages
|
||||
* - parse and verify signatures
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
parse_transcript (const json_t *jtr,
|
||||
struct transcript *tr,
|
||||
json_t **output)
|
||||
{
|
||||
// TODO: json_error_t jerror;
|
||||
// TODO: struct GNUNET_CRYPTO_EddsaSignature sig;
|
||||
|
||||
GNUNET_assert (jtr);
|
||||
@ -167,7 +176,7 @@ parse_transcript (const json_t *jtr,
|
||||
|
||||
|
||||
if (! json_is_array (prices))
|
||||
// TODO: leak!?
|
||||
// TODO: fix leak!?
|
||||
return json_error (output, "no prices found");
|
||||
|
||||
tr->k = json_array_size (prices);
|
||||
@ -176,7 +185,7 @@ parse_transcript (const json_t *jtr,
|
||||
json_array_foreach (prices, idx, val)
|
||||
{
|
||||
if (! json_is_string (val))
|
||||
// TODO: leak!_
|
||||
// TODO: fix leak!_
|
||||
return json_error (output, "prices not strings");
|
||||
|
||||
tr->prices[idx] = (char *) json_string_value (val);
|
||||
@ -192,7 +201,7 @@ parse_transcript (const json_t *jtr,
|
||||
|
||||
bidders = json_object_get (jtr, "bidders");
|
||||
if (! bidders || ! json_is_array (bidders))
|
||||
// TODO: leak!_
|
||||
// TODO: fix leak!_
|
||||
return json_error (output, "bidders not found");
|
||||
|
||||
// TODO: parse bidders as pub keys;
|
||||
@ -207,14 +216,14 @@ parse_transcript (const json_t *jtr,
|
||||
|
||||
messages = json_object_get (jtr, "transcript");
|
||||
if (! json_is_array (messages))
|
||||
// TODO: leak!
|
||||
// TODO: fix leak!
|
||||
return json_error (output, "no messages found");
|
||||
|
||||
|
||||
nm = json_array_size (messages);
|
||||
|
||||
if (nm != (4 * tr->n))
|
||||
// TODO: leak!
|
||||
// TODO: fix leak!
|
||||
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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -255,13 +265,118 @@ parse_transcript (const json_t *jtr,
|
||||
spec,
|
||||
(const char**) &error,
|
||||
NULL))
|
||||
// TODO: leak!
|
||||
// TODO: fix leak!
|
||||
return json_error (output, "couldn't parse winners");
|
||||
}
|
||||
|
||||
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.*/
|
||||
|
||||
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,
|
||||
MHD_HTTP_NOT_IMPLEMENTED,
|
||||
@ -359,17 +474,26 @@ auction_http_post_handler (
|
||||
{
|
||||
struct transcript tr = {};
|
||||
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 */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"auction_http_post_handler not implemented yet\n");
|
||||
ret = replay_transcript (root,
|
||||
&tr,
|
||||
&result2);
|
||||
|
||||
return TALER_MHD_reply_json_steal (connection,
|
||||
result,
|
||||
GNUNET_OK == ret? MHD_HTTP_OK :
|
||||
result2,
|
||||
GNUNET_OK == ret?
|
||||
MHD_HTTP_OK :
|
||||
MHD_HTTP_BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ -427,7 +551,7 @@ libtaler_extension_auction_brandt_init (void *arg)
|
||||
}
|
||||
/* TODO: check if replay_program is actually executable */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"[auction_brandt] loading... using replay_program '%s'\n",
|
||||
LOG_PREFIX "loading... using replay_program '%s'\n",
|
||||
replay_program);
|
||||
|
||||
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