aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorÖzgür Kesim <oec-taler@kesim.org>2022-10-03 08:15:07 +0200
committerÖzgür Kesim <oec-taler@kesim.org>2022-10-03 08:15:07 +0200
commit12736abcf7c8a63665e45a8d1691bd52bf940e93 (patch)
treee2997ff04272e2a2114ad7730228993951c1c0fd
parentb2d68d4b2d70974abca41ee6331169e63a916add (diff)
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?
-rw-r--r--src/extensions/auction_brandt/extension_auction_brandt.c166
-rw-r--r--src/testing/test_exchange_auction.conf159
2 files changed, 304 insertions, 21 deletions
diff --git a/src/extensions/auction_brandt/extension_auction_brandt.c b/src/extensions/auction_brandt/extension_auction_brandt.c
index a5f3af8c..24ac452b 100644
--- a/src/extensions/auction_brandt/extension_auction_brandt.c
+++ b/src/extensions/auction_brandt/extension_auction_brandt.c
@@ -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
+ }
+ *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);
+ }
}
- return json_result (output, tr);
+
+ 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;
-
- ret = parse_transcript (root, &tr, &result);
-
- /* TODO */
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "auction_http_post_handler not implemented yet\n");
+ json_t *result1;
+ json_t *result2;
+
+ 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);
+
+ 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;
diff --git a/src/testing/test_exchange_auction.conf b/src/testing/test_exchange_auction.conf
new file mode 100644
index 00000000..ee5ef7bc
--- /dev/null
+++ b/src/testing/test_exchange_auction.conf
@@ -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