move fake bank implementation to new library to re-use the code in upcoming testcases in preparation of fixing #4399
This commit is contained in:
parent
4dcd70ac2b
commit
8d503b17e3
@ -9,6 +9,9 @@ endif
|
|||||||
lib_LTLIBRARIES = \
|
lib_LTLIBRARIES = \
|
||||||
libtalerbank.la
|
libtalerbank.la
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = \
|
||||||
|
libfakebank.la
|
||||||
|
|
||||||
libtalerbank_la_LDFLAGS = \
|
libtalerbank_la_LDFLAGS = \
|
||||||
-version-info 0:0:0 \
|
-version-info 0:0:0 \
|
||||||
-no-undefined
|
-no-undefined
|
||||||
@ -24,6 +27,17 @@ libtalerbank_la_LIBADD = \
|
|||||||
-ljansson \
|
-ljansson \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
|
libfakebank_la_SOURCES = \
|
||||||
|
fakebank.c
|
||||||
|
|
||||||
|
libfakebank_la_LIBADD = \
|
||||||
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
|
-lgnunetjson \
|
||||||
|
-lgnunetutil \
|
||||||
|
-ljansson \
|
||||||
|
$(XLIB)
|
||||||
|
|
||||||
|
|
||||||
if HAVE_LIBCURL
|
if HAVE_LIBCURL
|
||||||
libtalerbank_la_LIBADD += -lcurl
|
libtalerbank_la_LIBADD += -lcurl
|
||||||
else
|
else
|
||||||
|
441
src/bank-lib/fakebank.c
Normal file
441
src/bank-lib/fakebank.c
Normal file
@ -0,0 +1,441 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
(C) 2016 Inria and GNUnet e.V.
|
||||||
|
|
||||||
|
TALER 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, or (at your option) any later version.
|
||||||
|
|
||||||
|
TALER 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
|
||||||
|
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file bank-lib/fakebank.c
|
||||||
|
* @brief library that fakes being a Taler bank for testcases
|
||||||
|
* @author Christian Grothoff <christian@grothoff.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
#include "fakebank.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum POST request size (for /admin/add/incoming)
|
||||||
|
*/
|
||||||
|
#define REQUEST_BUFFER_MAX (4*1024)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Details about a transcation we (as the simulated bank) received.
|
||||||
|
*/
|
||||||
|
struct Transaction
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We store transactions in a DLL.
|
||||||
|
*/
|
||||||
|
struct Transaction *next;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We store transactions in a DLL.
|
||||||
|
*/
|
||||||
|
struct Transaction *prev;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Amount to be transferred.
|
||||||
|
*/
|
||||||
|
struct TALER_Amount amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account to debit.
|
||||||
|
*/
|
||||||
|
uint64_t debit_account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account to credit.
|
||||||
|
*/
|
||||||
|
uint64_t credit_account;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject of the transfer.
|
||||||
|
*/
|
||||||
|
struct TALER_WireTransferIdentifierRawP wtid;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the fake bank.
|
||||||
|
*/
|
||||||
|
struct FAKEBANK_Handle
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* We store transactions in a DLL.
|
||||||
|
*/
|
||||||
|
struct Transaction *transactions_head;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We store transactions in a DLL.
|
||||||
|
*/
|
||||||
|
struct Transaction *transactions_tail;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP server we run to pretend to be the "test" bank.
|
||||||
|
*/
|
||||||
|
struct MHD_Daemon *mhd_bank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task running HTTP server for the "test" bank.
|
||||||
|
*/
|
||||||
|
struct GNUNET_SCHEDULER_Task *mhd_task;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the @a want_amount was transferred from
|
||||||
|
* the @a want_debit to the @a want_credit account. If
|
||||||
|
* so, set the @a wtid to the transfer identifier.
|
||||||
|
* If not, return #GNUNET_SYSERR.
|
||||||
|
*
|
||||||
|
* @param h bank instance
|
||||||
|
* @param want_amount transfer amount desired
|
||||||
|
* @param want_debit account that should have been debited
|
||||||
|
* @param want_debit account that should have been credited
|
||||||
|
* @param[out] wtid set to the wire transfer identifier
|
||||||
|
* @return #GNUNET_OK on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
FAKEBANK_check (struct FAKEBANK_Handle *h,
|
||||||
|
const struct TALER_Amount *want_amount,
|
||||||
|
uint64_t want_debit,
|
||||||
|
uint64_t want_credit,
|
||||||
|
struct TALER_WireTransferIdentifierRawP *wtid)
|
||||||
|
{
|
||||||
|
struct Transaction *t;
|
||||||
|
|
||||||
|
for (t = h->transactions_head; NULL != t; t = t->next)
|
||||||
|
{
|
||||||
|
if ( (want_debit == t->debit_account) &&
|
||||||
|
(want_credit == t->credit_account) &&
|
||||||
|
(0 == TALER_amount_cmp (want_amount,
|
||||||
|
&t->amount)) )
|
||||||
|
{
|
||||||
|
GNUNET_CONTAINER_DLL_remove (h->transactions_head,
|
||||||
|
h->transactions_tail,
|
||||||
|
t);
|
||||||
|
*wtid = t->wtid;
|
||||||
|
GNUNET_free (t);
|
||||||
|
return GNUNET_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf (stderr,
|
||||||
|
"Did not find matching transaction!\nI have:\n");
|
||||||
|
for (t = h->transactions_head; NULL != t; t = t->next)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = TALER_amount_to_string (&t->amount);
|
||||||
|
fprintf (stderr,
|
||||||
|
"%llu -> %llu (%s)\n",
|
||||||
|
(unsigned long long) t->debit_account,
|
||||||
|
(unsigned long long) t->credit_account,
|
||||||
|
s);
|
||||||
|
GNUNET_free (s);
|
||||||
|
}
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that no wire transfers were ordered (or at least none
|
||||||
|
* that have not been taken care of via #FAKEBANK_check()).
|
||||||
|
* If any transactions are onrecord, return #GNUNET_SYSERR.
|
||||||
|
*
|
||||||
|
* @param h bank instance
|
||||||
|
* @return #GNUNET_OK on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
FAKEBANK_check_empty (struct FAKEBANK_Handle *h)
|
||||||
|
{
|
||||||
|
struct Transaction *t;
|
||||||
|
|
||||||
|
if (NULL == h->transactions_head)
|
||||||
|
return GNUNET_OK;
|
||||||
|
|
||||||
|
fprintf (stderr,
|
||||||
|
"Expected empty transaction set, but I have:\n");
|
||||||
|
for (t = h->transactions_head; NULL != t; t = t->next)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
s = TALER_amount_to_string (&t->amount);
|
||||||
|
fprintf (stderr,
|
||||||
|
"%llu -> %llu (%s)\n",
|
||||||
|
(unsigned long long) t->debit_account,
|
||||||
|
(unsigned long long) t->credit_account,
|
||||||
|
s);
|
||||||
|
GNUNET_free (s);
|
||||||
|
}
|
||||||
|
return GNUNET_SYSERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop running the fake bank.
|
||||||
|
*
|
||||||
|
* @param h bank to stop
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
FAKEBANK_stop (struct FAKEBANK_Handle *h)
|
||||||
|
{
|
||||||
|
if (NULL != h->mhd_task)
|
||||||
|
{
|
||||||
|
GNUNET_SCHEDULER_cancel (h->mhd_task);
|
||||||
|
h->mhd_task = NULL;
|
||||||
|
}
|
||||||
|
if (NULL != h->mhd_bank)
|
||||||
|
{
|
||||||
|
MHD_stop_daemon (h->mhd_bank);
|
||||||
|
h->mhd_bank = NULL;
|
||||||
|
}
|
||||||
|
GNUNET_free (h);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function called whenever MHD is done with a request. If the
|
||||||
|
* request was a POST, we may have stored a `struct Buffer *` in the
|
||||||
|
* @a con_cls that might still need to be cleaned up. Call the
|
||||||
|
* respective function to free the memory.
|
||||||
|
*
|
||||||
|
* @param cls client-defined closure
|
||||||
|
* @param connection connection handle
|
||||||
|
* @param con_cls value as set by the last call to
|
||||||
|
* the #MHD_AccessHandlerCallback
|
||||||
|
* @param toe reason for request termination
|
||||||
|
* @see #MHD_OPTION_NOTIFY_COMPLETED
|
||||||
|
* @ingroup request
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
handle_mhd_completion_callback (void *cls,
|
||||||
|
struct MHD_Connection *connection,
|
||||||
|
void **con_cls,
|
||||||
|
enum MHD_RequestTerminationCode toe)
|
||||||
|
{
|
||||||
|
/* struct FAKEBANK_Handle *h = cls; */
|
||||||
|
|
||||||
|
GNUNET_JSON_post_parser_cleanup (*con_cls);
|
||||||
|
*con_cls = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle incoming HTTP request.
|
||||||
|
*
|
||||||
|
* @param cls closure for MHD daemon (unused)
|
||||||
|
* @param connection the connection
|
||||||
|
* @param url the requested url
|
||||||
|
* @param method the method (POST, GET, ...)
|
||||||
|
* @param version HTTP version (ignored)
|
||||||
|
* @param upload_data request data
|
||||||
|
* @param upload_data_size size of @a upload_data in bytes
|
||||||
|
* @param con_cls closure for request (a `struct Buffer *`)
|
||||||
|
* @return MHD result code
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
handle_mhd_request (void *cls,
|
||||||
|
struct MHD_Connection *connection,
|
||||||
|
const char *url,
|
||||||
|
const char *method,
|
||||||
|
const char *version,
|
||||||
|
const char *upload_data,
|
||||||
|
size_t *upload_data_size,
|
||||||
|
void **con_cls)
|
||||||
|
{
|
||||||
|
struct FAKEBANK_Handle *h = cls;
|
||||||
|
enum GNUNET_JSON_PostResult pr;
|
||||||
|
json_t *json;
|
||||||
|
struct Transaction *t;
|
||||||
|
struct MHD_Response *resp;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (0 != strcasecmp (url,
|
||||||
|
"/admin/add/incoming"))
|
||||||
|
{
|
||||||
|
/* Unexpected URI path, just close the connection. */
|
||||||
|
/* we're rather impolite here, but it's a testcase. */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
return MHD_NO;
|
||||||
|
}
|
||||||
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
|
con_cls,
|
||||||
|
upload_data,
|
||||||
|
upload_data_size,
|
||||||
|
&json);
|
||||||
|
switch (pr)
|
||||||
|
{
|
||||||
|
case GNUNET_JSON_PR_OUT_OF_MEMORY:
|
||||||
|
GNUNET_break (0);
|
||||||
|
return MHD_NO;
|
||||||
|
case GNUNET_JSON_PR_CONTINUE:
|
||||||
|
return MHD_YES;
|
||||||
|
case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
|
||||||
|
GNUNET_break (0);
|
||||||
|
return MHD_NO;
|
||||||
|
case GNUNET_JSON_PR_JSON_INVALID:
|
||||||
|
GNUNET_break (0);
|
||||||
|
return MHD_NO;
|
||||||
|
case GNUNET_JSON_PR_SUCCESS:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t = GNUNET_new (struct Transaction);
|
||||||
|
{
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("wtid", &t->wtid),
|
||||||
|
GNUNET_JSON_spec_uint64 ("debit_account", &t->debit_account),
|
||||||
|
GNUNET_JSON_spec_uint64 ("credit_account", &t->credit_account),
|
||||||
|
TALER_JSON_spec_amount ("amount", &t->amount),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (json,
|
||||||
|
spec,
|
||||||
|
NULL, NULL))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
json_decref (json);
|
||||||
|
return MHD_NO;
|
||||||
|
}
|
||||||
|
GNUNET_CONTAINER_DLL_insert (h->transactions_head,
|
||||||
|
h->transactions_tail,
|
||||||
|
t);
|
||||||
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Receiving incoming wire transfer: %llu->%llu\n",
|
||||||
|
(unsigned long long) t->debit_account,
|
||||||
|
(unsigned long long) t->credit_account);
|
||||||
|
json_decref (json);
|
||||||
|
resp = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT);
|
||||||
|
ret = MHD_queue_response (connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
resp);
|
||||||
|
MHD_destroy_response (resp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task run whenever HTTP server operations are pending.
|
||||||
|
*
|
||||||
|
* @param cls the `struct FAKEBANK_Handle`
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
run_mhd (void *cls);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schedule MHD. This function should be called initially when an
|
||||||
|
* MHD is first getting its client socket, and will then automatically
|
||||||
|
* always be called later whenever there is work to be done.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
schedule_httpd (struct FAKEBANK_Handle *h)
|
||||||
|
{
|
||||||
|
fd_set rs;
|
||||||
|
fd_set ws;
|
||||||
|
fd_set es;
|
||||||
|
struct GNUNET_NETWORK_FDSet *wrs;
|
||||||
|
struct GNUNET_NETWORK_FDSet *wws;
|
||||||
|
int max;
|
||||||
|
int haveto;
|
||||||
|
MHD_UNSIGNED_LONG_LONG timeout;
|
||||||
|
struct GNUNET_TIME_Relative tv;
|
||||||
|
|
||||||
|
FD_ZERO (&rs);
|
||||||
|
FD_ZERO (&ws);
|
||||||
|
FD_ZERO (&es);
|
||||||
|
max = -1;
|
||||||
|
if (MHD_YES != MHD_get_fdset (h->mhd_bank, &rs, &ws, &es, &max))
|
||||||
|
{
|
||||||
|
GNUNET_assert (0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
haveto = MHD_get_timeout (h->mhd_bank, &timeout);
|
||||||
|
if (MHD_YES == haveto)
|
||||||
|
tv.rel_value_us = (uint64_t) timeout * 1000LL;
|
||||||
|
else
|
||||||
|
tv = GNUNET_TIME_UNIT_FOREVER_REL;
|
||||||
|
if (-1 != max)
|
||||||
|
{
|
||||||
|
wrs = GNUNET_NETWORK_fdset_create ();
|
||||||
|
wws = GNUNET_NETWORK_fdset_create ();
|
||||||
|
GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
|
||||||
|
GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wrs = NULL;
|
||||||
|
wws = NULL;
|
||||||
|
}
|
||||||
|
if (NULL != h->mhd_task)
|
||||||
|
GNUNET_SCHEDULER_cancel (h->mhd_task);
|
||||||
|
h->mhd_task =
|
||||||
|
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
|
||||||
|
tv,
|
||||||
|
wrs,
|
||||||
|
wws,
|
||||||
|
&run_mhd, h);
|
||||||
|
if (NULL != wrs)
|
||||||
|
GNUNET_NETWORK_fdset_destroy (wrs);
|
||||||
|
if (NULL != wws)
|
||||||
|
GNUNET_NETWORK_fdset_destroy (wws);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task run whenever HTTP server operations are pending.
|
||||||
|
*
|
||||||
|
* @param cls the `struct FAKEBANK_Handle`
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
run_mhd (void *cls)
|
||||||
|
{
|
||||||
|
struct FAKEBANK_Handle *h = cls;
|
||||||
|
|
||||||
|
h->mhd_task = NULL;
|
||||||
|
MHD_run (h->mhd_bank);
|
||||||
|
schedule_httpd (h);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the fake bank.
|
||||||
|
*
|
||||||
|
* @param port port to listen to
|
||||||
|
* @return NULL on error
|
||||||
|
*/
|
||||||
|
struct FAKEBANK_Handle *
|
||||||
|
FAKEBANK_start (uint16_t port)
|
||||||
|
{
|
||||||
|
struct FAKEBANK_Handle *h;
|
||||||
|
|
||||||
|
h = GNUNET_new (struct FAKEBANK_Handle);
|
||||||
|
h->mhd_bank = MHD_start_daemon (MHD_USE_DEBUG,
|
||||||
|
port,
|
||||||
|
NULL, NULL,
|
||||||
|
&handle_mhd_request, h,
|
||||||
|
MHD_OPTION_NOTIFY_COMPLETED,
|
||||||
|
&handle_mhd_completion_callback, h,
|
||||||
|
MHD_OPTION_END);
|
||||||
|
if (NULL == h->mhd_bank)
|
||||||
|
{
|
||||||
|
GNUNET_free (h);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
schedule_httpd (h);
|
||||||
|
return h;
|
||||||
|
}
|
94
src/bank-lib/fakebank.h
Normal file
94
src/bank-lib/fakebank.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
(C) 2016 Inria and GNUnet e.V.
|
||||||
|
|
||||||
|
TALER 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, or (at your option) any later version.
|
||||||
|
|
||||||
|
TALER 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
|
||||||
|
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file bank-lib/fakebank.h
|
||||||
|
* @brief API for a library that fakes being a Taler bank
|
||||||
|
* @author Christian Grothoff <christian@grothoff.org>
|
||||||
|
*/
|
||||||
|
#ifndef FAKEBANK_H
|
||||||
|
#define FAKEBANK_H
|
||||||
|
|
||||||
|
#include "taler_util.h"
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include <microhttpd.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for the fake bank.
|
||||||
|
*/
|
||||||
|
struct FAKEBANK_Handle;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the fake bank. The fake bank will, like the normal bank,
|
||||||
|
* listen for requests for /admin/add/incoming. However, instead of
|
||||||
|
* executing or storing those requests, it will simply allow querying
|
||||||
|
* whether such a request has been made via #FAKEBANK_check().
|
||||||
|
*
|
||||||
|
* This is useful for writing testcases to check whether the exchange
|
||||||
|
* would have issued the correct wire transfer orders.
|
||||||
|
*
|
||||||
|
* @param port port to listen to
|
||||||
|
* @return NULL on error
|
||||||
|
*/
|
||||||
|
struct FAKEBANK_Handle *
|
||||||
|
FAKEBANK_start (uint16_t port);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that no wire transfers were ordered (or at least none
|
||||||
|
* that have not been taken care of via #FAKEBANK_check()).
|
||||||
|
* If any transactions are onrecord, return #GNUNET_SYSERR.
|
||||||
|
*
|
||||||
|
* @param h bank instance
|
||||||
|
* @return #GNUNET_OK on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
FAKEBANK_check_empty (struct FAKEBANK_Handle *h);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the @a want_amount was transferred from the @a
|
||||||
|
* want_debit to the @a want_credit account. If so, set the @a wtid
|
||||||
|
* to the transfer identifier and remove the transaction from the
|
||||||
|
* list. If the transaction was not recorded, return #GNUNET_SYSERR.
|
||||||
|
*
|
||||||
|
* @param h bank instance
|
||||||
|
* @param want_amount transfer amount desired
|
||||||
|
* @param want_debit account that should have been debited
|
||||||
|
* @param want_debit account that should have been credited
|
||||||
|
* @param[out] wtid set to the wire transfer identifier
|
||||||
|
* @return #GNUNET_OK on success
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
FAKEBANK_check (struct FAKEBANK_Handle *h,
|
||||||
|
const struct TALER_Amount *want_amount,
|
||||||
|
uint64_t want_debit,
|
||||||
|
uint64_t want_credit,
|
||||||
|
struct TALER_WireTransferIdentifierRawP *wtid);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop running the fake bank.
|
||||||
|
*
|
||||||
|
* @param h bank to stop
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
FAKEBANK_stop (struct FAKEBANK_Handle *h);
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -1,5 +1,5 @@
|
|||||||
# This Makefile.am is in the public domain
|
# This Makefile.am is in the public domain
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/src/include
|
AM_CPPFLAGS = -I$(top_srcdir)/src/include -I$(top_srcdir)/src/bank-lib/
|
||||||
|
|
||||||
if USE_COVERAGE
|
if USE_COVERAGE
|
||||||
AM_CFLAGS = --coverage -O0
|
AM_CFLAGS = --coverage -O0
|
||||||
@ -70,6 +70,7 @@ test_taler_exchange_aggregator_postgres_SOURCES = \
|
|||||||
test_taler_exchange_aggregator_postgres_LDADD = \
|
test_taler_exchange_aggregator_postgres_LDADD = \
|
||||||
$(LIBGCRYPT_LIBS) \
|
$(LIBGCRYPT_LIBS) \
|
||||||
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
|
$(top_builddir)/src/exchangedb/libtalerexchangedb.la \
|
||||||
|
$(top_builddir)/src/bank-lib/libfakebank.la \
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
-lmicrohttpd \
|
-lmicrohttpd \
|
||||||
|
@ -25,50 +25,9 @@
|
|||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_exchangedb_plugin.h"
|
#include "taler_exchangedb_plugin.h"
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
|
#include "fakebank.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum POST request size (for /admin/add/incoming)
|
|
||||||
*/
|
|
||||||
#define REQUEST_BUFFER_MAX (4*1024)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Details about a transcation we (as the simulated bank) received.
|
|
||||||
*/
|
|
||||||
struct Transaction
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We store transactions in a DLL.
|
|
||||||
*/
|
|
||||||
struct Transaction *next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We store transactions in a DLL.
|
|
||||||
*/
|
|
||||||
struct Transaction *prev;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Amount to be transferred.
|
|
||||||
*/
|
|
||||||
struct TALER_Amount amount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account to debit.
|
|
||||||
*/
|
|
||||||
uint64_t debit_account;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account to credit.
|
|
||||||
*/
|
|
||||||
uint64_t credit_account;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subject of the transfer.
|
|
||||||
*/
|
|
||||||
struct TALER_WireTransferIdentifierRawP wtid;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commands for the interpreter.
|
* Commands for the interpreter.
|
||||||
@ -270,31 +229,11 @@ static struct GNUNET_OS_Process *aggregator_proc;
|
|||||||
*/
|
*/
|
||||||
static struct State *aggregator_state;
|
static struct State *aggregator_state;
|
||||||
|
|
||||||
/**
|
|
||||||
* HTTP server we run to pretend to be the "test" bank.
|
|
||||||
*/
|
|
||||||
static struct MHD_Daemon *mhd_bank;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Task running HTTP server for the "test" bank.
|
|
||||||
*/
|
|
||||||
static struct GNUNET_SCHEDULER_Task *mhd_task;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task running the interpreter().
|
* Task running the interpreter().
|
||||||
*/
|
*/
|
||||||
static struct GNUNET_SCHEDULER_Task *int_task;
|
static struct GNUNET_SCHEDULER_Task *int_task;
|
||||||
|
|
||||||
/**
|
|
||||||
* We store transactions in a DLL.
|
|
||||||
*/
|
|
||||||
static struct Transaction *transactions_head;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We store transactions in a DLL.
|
|
||||||
*/
|
|
||||||
static struct Transaction *transactions_tail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private key we use for fake coins.
|
* Private key we use for fake coins.
|
||||||
*/
|
*/
|
||||||
@ -305,6 +244,11 @@ static struct GNUNET_CRYPTO_RsaPrivateKey *coin_pk;
|
|||||||
*/
|
*/
|
||||||
static struct GNUNET_CRYPTO_RsaPublicKey *coin_pub;
|
static struct GNUNET_CRYPTO_RsaPublicKey *coin_pub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle for our fake bank.
|
||||||
|
*/
|
||||||
|
static struct FAKEBANK_Handle *fb;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interprets the commands from the test program.
|
* Interprets the commands from the test program.
|
||||||
@ -324,20 +268,15 @@ static void
|
|||||||
shutdown_action (void *cls)
|
shutdown_action (void *cls)
|
||||||
{
|
{
|
||||||
shutdown_task = NULL;
|
shutdown_task = NULL;
|
||||||
if (NULL != mhd_task)
|
|
||||||
{
|
|
||||||
GNUNET_SCHEDULER_cancel (mhd_task);
|
|
||||||
mhd_task = NULL;
|
|
||||||
}
|
|
||||||
if (NULL != int_task)
|
if (NULL != int_task)
|
||||||
{
|
{
|
||||||
GNUNET_SCHEDULER_cancel (int_task);
|
GNUNET_SCHEDULER_cancel (int_task);
|
||||||
int_task = NULL;
|
int_task = NULL;
|
||||||
}
|
}
|
||||||
if (NULL != mhd_bank)
|
if (NULL != fb)
|
||||||
{
|
{
|
||||||
MHD_stop_daemon (mhd_bank);
|
FAKEBANK_stop (fb);
|
||||||
mhd_bank = NULL;
|
fb = NULL;
|
||||||
}
|
}
|
||||||
if (NULL == aggregator_proc)
|
if (NULL == aggregator_proc)
|
||||||
{
|
{
|
||||||
@ -573,24 +512,8 @@ interpreter (void *cls)
|
|||||||
NULL);
|
NULL);
|
||||||
return;
|
return;
|
||||||
case OPCODE_EXPECT_TRANSACTIONS_EMPTY:
|
case OPCODE_EXPECT_TRANSACTIONS_EMPTY:
|
||||||
if (NULL != transactions_head)
|
if (GNUNET_OK != FAKEBANK_check_empty (fb))
|
||||||
{
|
{
|
||||||
struct Transaction *t;
|
|
||||||
|
|
||||||
fprintf (stderr,
|
|
||||||
"Expected empty transaction set, but I have:\n");
|
|
||||||
for (t = transactions_head; NULL != t; t = t->next)
|
|
||||||
{
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
s = TALER_amount_to_string (&t->amount);
|
|
||||||
fprintf (stderr,
|
|
||||||
"%llu -> %llu (%s)\n",
|
|
||||||
(unsigned long long) t->debit_account,
|
|
||||||
(unsigned long long) t->credit_account,
|
|
||||||
s);
|
|
||||||
GNUNET_free (s);
|
|
||||||
}
|
|
||||||
fail (cmd);
|
fail (cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -608,8 +531,6 @@ interpreter (void *cls)
|
|||||||
case OPCODE_EXPECT_TRANSACTION:
|
case OPCODE_EXPECT_TRANSACTION:
|
||||||
{
|
{
|
||||||
struct TALER_Amount want_amount;
|
struct TALER_Amount want_amount;
|
||||||
struct Transaction *t;
|
|
||||||
int found;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_string_to_amount (cmd->details.expect_transaction.amount,
|
TALER_string_to_amount (cmd->details.expect_transaction.amount,
|
||||||
@ -619,39 +540,13 @@ interpreter (void *cls)
|
|||||||
fail (cmd);
|
fail (cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
found = GNUNET_NO;
|
if (GNUNET_OK !=
|
||||||
for (t = transactions_head; NULL != t; t = t->next)
|
FAKEBANK_check (fb,
|
||||||
|
&want_amount,
|
||||||
|
cmd->details.expect_transaction.debit_account,
|
||||||
|
cmd->details.expect_transaction.credit_account,
|
||||||
|
&cmd->details.expect_transaction.wtid))
|
||||||
{
|
{
|
||||||
if ( (cmd->details.expect_transaction.debit_account == t->debit_account) &&
|
|
||||||
(cmd->details.expect_transaction.credit_account == t->credit_account) &&
|
|
||||||
(0 == TALER_amount_cmp (&want_amount,
|
|
||||||
&t->amount)) )
|
|
||||||
{
|
|
||||||
GNUNET_CONTAINER_DLL_remove (transactions_head,
|
|
||||||
transactions_tail,
|
|
||||||
t);
|
|
||||||
cmd->details.expect_transaction.wtid = t->wtid;
|
|
||||||
GNUNET_free (t);
|
|
||||||
found = GNUNET_YES;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (GNUNET_NO == found)
|
|
||||||
{
|
|
||||||
fprintf (stderr,
|
|
||||||
"Did not find matching transaction!\nI have:\n");
|
|
||||||
for (t = transactions_head; NULL != t; t = t->next)
|
|
||||||
{
|
|
||||||
char *s;
|
|
||||||
|
|
||||||
s = TALER_amount_to_string (&t->amount);
|
|
||||||
fprintf (stderr,
|
|
||||||
"%llu -> %llu (%s)\n",
|
|
||||||
(unsigned long long) t->debit_account,
|
|
||||||
(unsigned long long) t->credit_account,
|
|
||||||
s);
|
|
||||||
GNUNET_free (s);
|
|
||||||
}
|
|
||||||
fail (cmd);
|
fail (cmd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1209,205 +1104,6 @@ run_test ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called whenever MHD is done with a request. If the
|
|
||||||
* request was a POST, we may have stored a `struct Buffer *` in the
|
|
||||||
* @a con_cls that might still need to be cleaned up. Call the
|
|
||||||
* respective function to free the memory.
|
|
||||||
*
|
|
||||||
* @param cls client-defined closure
|
|
||||||
* @param connection connection handle
|
|
||||||
* @param con_cls value as set by the last call to
|
|
||||||
* the #MHD_AccessHandlerCallback
|
|
||||||
* @param toe reason for request termination
|
|
||||||
* @see #MHD_OPTION_NOTIFY_COMPLETED
|
|
||||||
* @ingroup request
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
handle_mhd_completion_callback (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
void **con_cls,
|
|
||||||
enum MHD_RequestTerminationCode toe)
|
|
||||||
{
|
|
||||||
GNUNET_JSON_post_parser_cleanup (*con_cls);
|
|
||||||
*con_cls = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle incoming HTTP request.
|
|
||||||
*
|
|
||||||
* @param cls closure for MHD daemon (unused)
|
|
||||||
* @param connection the connection
|
|
||||||
* @param url the requested url
|
|
||||||
* @param method the method (POST, GET, ...)
|
|
||||||
* @param version HTTP version (ignored)
|
|
||||||
* @param upload_data request data
|
|
||||||
* @param upload_data_size size of @a upload_data in bytes
|
|
||||||
* @param con_cls closure for request (a `struct Buffer *`)
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
handle_mhd_request (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const char *url,
|
|
||||||
const char *method,
|
|
||||||
const char *version,
|
|
||||||
const char *upload_data,
|
|
||||||
size_t *upload_data_size,
|
|
||||||
void **con_cls)
|
|
||||||
{
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
|
||||||
json_t *json;
|
|
||||||
struct Transaction *t;
|
|
||||||
struct MHD_Response *resp;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (0 != strcasecmp (url,
|
|
||||||
"/admin/add/incoming"))
|
|
||||||
{
|
|
||||||
/* Unexpected URI path, just close the connection. */
|
|
||||||
/* we're rather impolite here, but it's a testcase. */
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return MHD_NO;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
|
||||||
con_cls,
|
|
||||||
upload_data,
|
|
||||||
upload_data_size,
|
|
||||||
&json);
|
|
||||||
switch (pr)
|
|
||||||
{
|
|
||||||
case GNUNET_JSON_PR_OUT_OF_MEMORY:
|
|
||||||
GNUNET_break (0);
|
|
||||||
return MHD_NO;
|
|
||||||
case GNUNET_JSON_PR_CONTINUE:
|
|
||||||
return MHD_YES;
|
|
||||||
case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
|
|
||||||
GNUNET_break (0);
|
|
||||||
return MHD_NO;
|
|
||||||
case GNUNET_JSON_PR_JSON_INVALID:
|
|
||||||
GNUNET_break (0);
|
|
||||||
return MHD_NO;
|
|
||||||
case GNUNET_JSON_PR_SUCCESS:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
t = GNUNET_new (struct Transaction);
|
|
||||||
{
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("wtid", &t->wtid),
|
|
||||||
GNUNET_JSON_spec_uint64 ("debit_account", &t->debit_account),
|
|
||||||
GNUNET_JSON_spec_uint64 ("credit_account", &t->credit_account),
|
|
||||||
TALER_JSON_spec_amount ("amount", &t->amount),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_JSON_parse (json,
|
|
||||||
spec,
|
|
||||||
NULL, NULL))
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
json_decref (json);
|
|
||||||
return MHD_NO;
|
|
||||||
}
|
|
||||||
GNUNET_CONTAINER_DLL_insert (transactions_head,
|
|
||||||
transactions_tail,
|
|
||||||
t);
|
|
||||||
}
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Receiving incoming wire transfer: %llu->%llu\n",
|
|
||||||
(unsigned long long) t->debit_account,
|
|
||||||
(unsigned long long) t->credit_account);
|
|
||||||
json_decref (json);
|
|
||||||
resp = MHD_create_response_from_buffer (0, "", MHD_RESPMEM_PERSISTENT);
|
|
||||||
ret = MHD_queue_response (connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
resp);
|
|
||||||
MHD_destroy_response (resp);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Task run whenever HTTP server operations are pending.
|
|
||||||
*
|
|
||||||
* @param cls NULL
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
run_mhd (void *cls);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Schedule MHD. This function should be called initially when an
|
|
||||||
* MHD is first getting its client socket, and will then automatically
|
|
||||||
* always be called later whenever there is work to be done.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
schedule_httpd ()
|
|
||||||
{
|
|
||||||
fd_set rs;
|
|
||||||
fd_set ws;
|
|
||||||
fd_set es;
|
|
||||||
struct GNUNET_NETWORK_FDSet *wrs;
|
|
||||||
struct GNUNET_NETWORK_FDSet *wws;
|
|
||||||
int max;
|
|
||||||
int haveto;
|
|
||||||
MHD_UNSIGNED_LONG_LONG timeout;
|
|
||||||
struct GNUNET_TIME_Relative tv;
|
|
||||||
|
|
||||||
FD_ZERO (&rs);
|
|
||||||
FD_ZERO (&ws);
|
|
||||||
FD_ZERO (&es);
|
|
||||||
max = -1;
|
|
||||||
if (MHD_YES != MHD_get_fdset (mhd_bank, &rs, &ws, &es, &max))
|
|
||||||
{
|
|
||||||
GNUNET_assert (0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
haveto = MHD_get_timeout (mhd_bank, &timeout);
|
|
||||||
if (MHD_YES == haveto)
|
|
||||||
tv.rel_value_us = (uint64_t) timeout * 1000LL;
|
|
||||||
else
|
|
||||||
tv = GNUNET_TIME_UNIT_FOREVER_REL;
|
|
||||||
if (-1 != max)
|
|
||||||
{
|
|
||||||
wrs = GNUNET_NETWORK_fdset_create ();
|
|
||||||
wws = GNUNET_NETWORK_fdset_create ();
|
|
||||||
GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
|
|
||||||
GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wrs = NULL;
|
|
||||||
wws = NULL;
|
|
||||||
}
|
|
||||||
if (NULL != mhd_task)
|
|
||||||
GNUNET_SCHEDULER_cancel (mhd_task);
|
|
||||||
mhd_task =
|
|
||||||
GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
|
|
||||||
tv,
|
|
||||||
wrs,
|
|
||||||
wws,
|
|
||||||
&run_mhd, NULL);
|
|
||||||
if (NULL != wrs)
|
|
||||||
GNUNET_NETWORK_fdset_destroy (wrs);
|
|
||||||
if (NULL != wws)
|
|
||||||
GNUNET_NETWORK_fdset_destroy (wws);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Task run whenever HTTP server operations are pending.
|
|
||||||
*
|
|
||||||
* @param cls NULL
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
run_mhd (void *cls)
|
|
||||||
{
|
|
||||||
mhd_task = NULL;
|
|
||||||
MHD_run (mhd_bank);
|
|
||||||
schedule_httpd ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1466,18 +1162,13 @@ run (void *cls)
|
|||||||
&shutdown_action,
|
&shutdown_action,
|
||||||
NULL);
|
NULL);
|
||||||
result = 1; /* test failed for undefined reason */
|
result = 1; /* test failed for undefined reason */
|
||||||
mhd_bank = MHD_start_daemon (MHD_USE_DEBUG,
|
fb = FAKEBANK_start (8082);
|
||||||
8082,
|
if (NULL == fb)
|
||||||
NULL, NULL,
|
|
||||||
&handle_mhd_request, NULL,
|
|
||||||
MHD_OPTION_NOTIFY_COMPLETED, &handle_mhd_completion_callback, NULL,
|
|
||||||
MHD_OPTION_END);
|
|
||||||
if (NULL == mhd_bank)
|
|
||||||
{
|
{
|
||||||
GNUNET_SCHEDULER_shutdown ();
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
result = 77;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
schedule_httpd ();
|
|
||||||
run_test ();
|
run_test ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user