Merge branch 'master' of ssh://taler.net:/var/git/mint

This commit is contained in:
Christian Grothoff 2016-01-30 13:49:29 +01:00
commit a396f4e7fa
94 changed files with 9008 additions and 439 deletions

View File

@ -199,6 +199,10 @@ TALER_PLUGIN_LDFLAGS="-export-dynamic -avoid-version -module -no-undefined"
AC_SUBST(TALER_LIB_LDFLAGS)
AC_SUBST(TALER_PLUGIN_LDFLAGS)
CFLAGS_SAVE=$CFLAGS
LDFLAGS_SAVE=$LDFLAGS
LIBS_SAVE="$LIBS"
AM_CONDITIONAL(HAVE_POSTGRESQL, test x$postgres = xtrue)
@ -249,6 +253,10 @@ then
fi
fi
CFLAGS=$CFLAGS_SAVE
LDFLAGS=$LDFLAGS_SAVE
LIBS=$LIBS_SAVE
# should developer logic be compiled (not-for-production code)?
AC_MSG_CHECKING(whether to compile developer logic)
@ -349,6 +357,8 @@ AC_CONFIG_FILES([Makefile
src/include/Makefile
src/util/Makefile
src/pq/Makefile
src/bank-lib/Makefile
src/wire/Makefile
src/mintdb/Makefile
src/mint/Makefile
src/mint-tools/Makefile

View File

@ -7,10 +7,10 @@ if WALLET_ONLY
SUBDIRS = include util
else
SUBDIRS = include util $(PQ_DIR) mintdb mint mint-tools
SUBDIRS = include util $(PQ_DIR) bank-lib wire mintdb mint mint-tools
if HAVE_LIBCURL
SUBDIRS += mint-lib
else
else
if HAVE_LIBGNURL
SUBDIRS += mint-lib
endif

45
src/bank-lib/Makefile.am Normal file
View File

@ -0,0 +1,45 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
lib_LTLIBRARIES = \
libtalerbank.la
libtalerbank_la_LDFLAGS = \
-version-info 0:0:0 \
-no-undefined
libtalerbank_la_SOURCES = \
bank_api_context.c bank_api_context.h \
bank_api_json.c bank_api_json.h \
bank_api_admin.c
libtalerbank_la_LIBADD = \
-lgnunetutil \
-ljansson \
$(XLIB)
if HAVE_LIBCURL
libtalerbank_la_LIBADD += -lcurl
else
if HAVE_LIBGNURL
libtalerbank_la_LIBADD += -lgnurl
endif
endif
check_PROGRAMS = \
test_bank_api
TESTS = \
$(check_PROGRAMS)
test_bank_api_SOURCES = \
test_bank_api.c
test_bank_api_LDADD = \
libtalerbank.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil

View File

@ -0,0 +1,240 @@
/*
This file is part of TALER
Copyright (C) 2015, 2016 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/bank_api_admin.c
* @brief Implementation of the /admin/ requests of the bank's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <jansson.h>
#include <microhttpd.h> /* just for HTTP status codes */
#include <gnunet/gnunet_util_lib.h>
#include "taler_bank_service.h"
#include "bank_api_json.h"
#include "bank_api_context.h"
#include "taler_signatures.h"
/**
* @brief An admin/add/incoming Handle
*/
struct TALER_BANK_AdminAddIncomingHandle
{
/**
* The connection to bank this request handle will use
*/
struct TALER_BANK_Context *bank;
/**
* The url for this request.
*/
char *url;
/**
* JSON encoding of the request to POST.
*/
char *json_enc;
/**
* Handle for the request.
*/
struct BAC_Job *job;
/**
* HTTP headers for the request.
*/
struct curl_slist *headers;
/**
* Function to call with the result.
*/
TALER_BANK_AdminAddIncomingResultCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Download buffer
*/
struct BAC_DownloadBuffer db;
};
/**
* Function called when we're done processing the
* HTTP /admin/add/incoming request.
*
* @param cls the `struct TALER_BANK_AdminAddIncomingHandle`
* @param eh the curl request handle
*/
static void
handle_admin_add_incoming_finished (void *cls,
CURL *eh)
{
struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
long response_code;
json_t *json;
aai->job = NULL;
json = BAC_download_get_result (&aai->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the bank is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_FORBIDDEN:
/* Access denied */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, bank says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Nothing really to verify, this should never
happen, we should pass the JSON reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
aai->cb (aai->cb_cls,
response_code);
json_decref (json);
TALER_BANK_admin_add_incoming_cancel (aai);
}
/**
* Notify the bank that we have received an incoming transaction
* which fills a reserve. Note that this API is an administrative
* API and thus not accessible to typical bank clients, but only
* to the operators of the bank.
*
* @param bank the bank handle; the bank must be ready to operate
* @param reserve_pub public key of the reserve
* @param amount amount that was deposited
* @param execution_date when did we receive the amount
* @param account_no account number (53 bits at most)
* @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback
* @return NULL
* if the inputs are invalid (i.e. invalid amount).
* In this case, the callback is not called.
*/
struct TALER_BANK_AdminAddIncomingHandle *
TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *amount,
uint64_t account_no,
TALER_BANK_AdminAddIncomingResultCallback res_cb,
void *res_cb_cls)
{
struct TALER_BANK_AdminAddIncomingHandle *aai;
json_t *admin_obj;
CURL *eh;
admin_obj = json_pack ("{s:o, s:o," /* reserve_pub/amount */
" s:I}", /* execution_Date/wire */
"wtid", TALER_json_from_data (wtid,
sizeof (*wtid)),
"amount", TALER_json_from_amount (amount),
"account", (json_int_t) account_no);
aai = GNUNET_new (struct TALER_BANK_AdminAddIncomingHandle);
aai->bank = bank;
aai->cb = res_cb;
aai->cb_cls = res_cb_cls;
aai->url = BAC_path_to_url (bank, "/admin/add/incoming");
eh = curl_easy_init ();
GNUNET_assert (NULL != (aai->json_enc =
json_dumps (admin_obj,
JSON_COMPACT)));
json_decref (admin_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
aai->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDS,
aai->json_enc));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDSIZE,
strlen (aai->json_enc)));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&BAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&aai->db));
aai->job = BAC_job_add (bank,
eh,
GNUNET_YES,
&handle_admin_add_incoming_finished,
aai);
return aai;
}
/**
* Cancel an add incoming. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param aai the admin add incoming request handle
*/
void
TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai)
{
if (NULL != aai->job)
{
BAC_job_cancel (aai->job);
aai->job = NULL;
}
curl_slist_free_all (aai->headers);
GNUNET_free_non_null (aai->db.buf);
GNUNET_free (aai->url);
GNUNET_free (aai->json_enc);
GNUNET_free (aai);
}
/* end of bank_api_admin.c */

View File

@ -0,0 +1,570 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 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/bank_api_context.c
* @brief Implementation of the context part of the bank's HTTP API
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include "taler_bank_service.h"
#include "bank_api_context.h"
/**
* Log error related to CURL operations.
*
* @param type log level
* @param function which function failed to run
* @param code what was the curl error code
*/
#define CURL_STRERROR(type, function, code) \
GNUNET_log (type, \
"Curl function `%s' has failed at `%s:%d' with error: %s\n", \
function, __FILE__, __LINE__, curl_easy_strerror (code));
/**
* Print JSON parsing related error information
*/
#define JSON_WARN(error) \
GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
"JSON parsing failed at %s:%u: %s (%s)\n", \
__FILE__, __LINE__, error.text, error.source)
/**
* Failsafe flag. Raised if our constructor fails to initialize
* the Curl library.
*/
static int TALER_BANK_curl_fail;
/**
* Jobs are CURL requests running within a `struct TALER_BANK_Context`.
*/
struct BAC_Job
{
/**
* We keep jobs in a DLL.
*/
struct BAC_Job *next;
/**
* We keep jobs in a DLL.
*/
struct BAC_Job *prev;
/**
* Easy handle of the job.
*/
CURL *easy_handle;
/**
* Context this job runs in.
*/
struct TALER_BANK_Context *ctx;
/**
* Function to call upon completion.
*/
BAC_JobCompletionCallback jcc;
/**
* Closure for @e jcc.
*/
void *jcc_cls;
};
/**
* Context
*/
struct TALER_BANK_Context
{
/**
* Curl multi handle
*/
CURLM *multi;
/**
* Curl share handle
*/
CURLSH *share;
/**
* We keep jobs in a DLL.
*/
struct BAC_Job *jobs_head;
/**
* We keep jobs in a DLL.
*/
struct BAC_Job *jobs_tail;
/**
* HTTP header "application/json", created once and used
* for all requests that need it.
*/
struct curl_slist *json_header;
/**
* Base URL of the bank.
*/
char *url;
};
/**
* Initialise this library. This function should be called before using any of
* the following functions.
*
* @param url HTTP base URL for the bank
* @return library context
*/
struct TALER_BANK_Context *
TALER_BANK_init (const char *url)
{
struct TALER_BANK_Context *ctx;
CURLM *multi;
CURLSH *share;
if (TALER_BANK_curl_fail)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Curl was not initialised properly\n");
return NULL;
}
if (NULL == (multi = curl_multi_init ()))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create a Curl multi handle\n");
return NULL;
}
if (NULL == (share = curl_share_init ()))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create a Curl share handle\n");
return NULL;
}
ctx = GNUNET_new (struct TALER_BANK_Context);
ctx->multi = multi;
ctx->share = share;
ctx->url = GNUNET_strdup (url);
GNUNET_assert (NULL != (ctx->json_header =
curl_slist_append (NULL,
"Content-Type: application/json")));
return ctx;
}
/**
* Schedule a CURL request to be executed and call the given @a jcc
* upon its completion. Note that the context will make use of the
* CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
* instead use #BAC_easy_to_closure to extract the @a jcc_cls argument
* from a valid @a eh afterwards.
*
* This function modifies the CURL handle to add the
* "Content-Type: application/json" header if @a add_json is set.
*
* @param ctx context to execute the job in
* @param eh curl easy handle for the request, will
* be executed AND cleaned up
* @param add_json add "application/json" content type header
* @param jcc callback to invoke upon completion
* @param jcc_cls closure for @a jcc
*/
struct BAC_Job *
BAC_job_add (struct TALER_BANK_Context *ctx,
CURL *eh,
int add_json,
BAC_JobCompletionCallback jcc,
void *jcc_cls)
{
struct BAC_Job *job;
if (GNUNET_YES == add_json)
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_HTTPHEADER,
ctx->json_header));
job = GNUNET_new (struct BAC_Job);
job->easy_handle = eh;
job->ctx = ctx;
job->jcc = jcc;
job->jcc_cls = jcc_cls;
GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
ctx->jobs_tail,
job);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_PRIVATE,
job));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_SHARE,
ctx->share));
GNUNET_assert (CURLM_OK ==
curl_multi_add_handle (ctx->multi,
eh));
return job;
}
/**
* Obtain the `jcc_cls` argument from an `eh` that was
* given to #BAC_job_add().
*
* @param eh easy handle that was used
* @return the `jcc_cls` that was given to #BAC_job_add().
*/
void *
BAC_easy_to_closure (CURL *eh)
{
struct BAC_Job *job;
GNUNET_assert (CURLE_OK ==
curl_easy_getinfo (eh,
CURLINFO_PRIVATE,
(char **) &job));
return job->jcc_cls;
}
/**
* Cancel a job. Must only be called before the job completion
* callback is called for the respective job.
*
* @param job job to cancel
*/
void
BAC_job_cancel (struct BAC_Job *job)
{
struct TALER_BANK_Context *ctx = job->ctx;
GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
ctx->jobs_tail,
job);
GNUNET_assert (CURLM_OK ==
curl_multi_remove_handle (ctx->multi,
job->easy_handle));
curl_easy_cleanup (job->easy_handle);
GNUNET_free (job);
}
/**
* Run the main event loop for the Taler interaction.
*
* @param ctx the library context
*/
void
TALER_BANK_perform (struct TALER_BANK_Context *ctx)
{
CURLMsg *cmsg;
struct BAC_Job *job;
int n_running;
int n_completed;
(void) curl_multi_perform (ctx->multi,
&n_running);
while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
&n_completed)))
{
/* Only documented return value is CURLMSG_DONE */
GNUNET_break (CURLMSG_DONE == cmsg->msg);
GNUNET_assert (CURLE_OK ==
curl_easy_getinfo (cmsg->easy_handle,
CURLINFO_PRIVATE,
(char **) &job));
GNUNET_assert (job->ctx == ctx);
job->jcc (job->jcc_cls,
cmsg->easy_handle);
BAC_job_cancel (job);
}
}
/**
* Obtain the information for a select() call to wait until
* #TALER_BANK_perform() is ready again. Note that calling
* any other TALER_BANK-API may also imply that the library
* is again ready for #TALER_BANK_perform().
*
* Basically, a client should use this API to prepare for select(),
* then block on select(), then call #TALER_BANK_perform() and then
* start again until the work with the context is done.
*
* This function will NOT zero out the sets and assumes that @a max_fd
* and @a timeout are already set to minimal applicable values. It is
* safe to give this API FD-sets and @a max_fd and @a timeout that are
* already initialized to some other descriptors that need to go into
* the select() call.
*
* @param ctx context to get the event loop information for
* @param read_fd_set will be set for any pending read operations
* @param write_fd_set will be set for any pending write operations
* @param except_fd_set is here because curl_multi_fdset() has this argument
* @param max_fd set to the highest FD included in any set;
* if the existing sets have no FDs in it, the initial
* value should be "-1". (Note that `max_fd + 1` will need
* to be passed to select().)
* @param timeout set to the timeout in milliseconds (!); -1 means
* no timeout (NULL, blocking forever is OK), 0 means to
* proceed immediately with #TALER_BANK_perform().
*/
void
TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
int *max_fd,
long *timeout)
{
long to;
int m;
m = -1;
GNUNET_assert (CURLM_OK ==
curl_multi_fdset (ctx->multi,
read_fd_set,
write_fd_set,
except_fd_set,
&m));
to = *timeout;
*max_fd = GNUNET_MAX (m, *max_fd);
GNUNET_assert (CURLM_OK ==
curl_multi_timeout (ctx->multi,
&to));
/* Only if what we got back from curl is smaller than what we
already had (-1 == infinity!), then update timeout */
if ( (to < *timeout) &&
(-1 != to) )
*timeout = to;
if ( (-1 == (*timeout)) &&
(NULL != ctx->jobs_head) )
*timeout = to;
}
/**
* Cleanup library initialisation resources. This function should be called
* after using this library to cleanup the resources occupied during library's
* initialisation.
*
* @param ctx the library context
*/
void
TALER_BANK_fini (struct TALER_BANK_Context *ctx)
{
/* all jobs must have been cancelled at this time, assert this */
GNUNET_assert (NULL == ctx->jobs_head);
curl_share_cleanup (ctx->share);
curl_multi_cleanup (ctx->multi);
curl_slist_free_all (ctx->json_header);
GNUNET_free (ctx->url);
GNUNET_free (ctx);
}
/**
* Obtain the URL to use for an API request.
*
* @param h the mint handle to query
* @param path Taler API path (i.e. "/reserve/withdraw")
* @return the full URI to use with cURL
*/
char *
BAC_path_to_url (struct TALER_BANK_Context *h,
const char *path)
{
char *url;
if ( ('/' == path[0]) &&
(0 < strlen (h->url)) &&
('/' == h->url[strlen (h->url) - 1]) )
path++; /* avoid generating URL with "//" from concat */
GNUNET_asprintf (&url,
"%s%s",
h->url,
path);
return url;
}
/**
* Callback used when downloading the reply to an HTTP request.
* Just appends all of the data to the `buf` in the
* `struct BAC_DownloadBuffer` for further processing. The size of
* the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
* the download exceeds this size, we abort with an error.
*
* @param bufptr data downloaded via HTTP
* @param size size of an item in @a bufptr
* @param nitems number of items in @a bufptr
* @param cls the `struct KeysRequest`
* @return number of bytes processed from @a bufptr
*/
size_t
BAC_download_cb (char *bufptr,
size_t size,
size_t nitems,
void *cls)
{
struct BAC_DownloadBuffer *db = cls;
size_t msize;
void *buf;
if (0 == size * nitems)
{
/* Nothing (left) to do */
return 0;
}
msize = size * nitems;
if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
{
db->eno = ENOMEM;
return 0; /* signals an error to curl */
}
db->buf = GNUNET_realloc (db->buf,
db->buf_size + msize);
buf = db->buf + db->buf_size;
memcpy (buf, bufptr, msize);
db->buf_size += msize;
return msize;
}
/**
* Obtain information about the final result about the
* HTTP download. If the download was successful, parses
* the JSON in the @a db and returns it. Also returns
* the HTTP @a response_code. If the download failed,
* the return value is NULL. The response code is set
* in any case, on download errors to zero.
*
* Calling this function also cleans up @a db.
*
* @param db download buffer
* @param eh CURL handle (to get the response code)
* @param[out] response_code set to the HTTP response code
* (or zero if we aborted the download, i.e.
* because the response was too big, or if
* the JSON we received was malformed).
* @return NULL if downloading a JSON reply failed
*/
json_t *
BAC_download_get_result (struct BAC_DownloadBuffer *db,
CURL *eh,
long *response_code)
{
json_t *json;
json_error_t error;
char *ct;
if ( (CURLE_OK !=
curl_easy_getinfo (eh,
CURLINFO_CONTENT_TYPE,
&ct)) ||
(NULL == ct) ||
(0 != strcasecmp (ct,
"application/json")) )
{
/* No content type or explicitly not JSON, refuse to parse
(but keep response code) */
if (CURLE_OK !=
curl_easy_getinfo (eh,
CURLINFO_RESPONSE_CODE,
response_code))
{
/* unexpected error... */
GNUNET_break (0);
*response_code = 0;
}
return NULL;
}
json = NULL;
if (0 == db->eno)
{
json = json_loadb (db->buf,
db->buf_size,
JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
&error);
if (NULL == json)
{
JSON_WARN (error);
*response_code = 0;
}
}
GNUNET_free_non_null (db->buf);
db->buf = NULL;
db->buf_size = 0;
if (NULL != json)
{
if (CURLE_OK !=
curl_easy_getinfo (eh,
CURLINFO_RESPONSE_CODE,
response_code))
{
/* unexpected error... */
GNUNET_break (0);
*response_code = 0;
}
}
return json;
}
/**
* Initial global setup logic, specifically runs the Curl setup.
*/
__attribute__ ((constructor))
void
TALER_BANK_constructor__ (void)
{
CURLcode ret;
if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
{
CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
"curl_global_init",
ret);
TALER_BANK_curl_fail = 1;
}
}
/**
* Cleans up after us, specifically runs the Curl cleanup.
*/
__attribute__ ((destructor))
void
TALER_BANK_destructor__ (void)
{
if (TALER_BANK_curl_fail)
return;
curl_global_cleanup ();
}
/* end of bank_api_context.c */

View File

@ -0,0 +1,181 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 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/bank_api_context.h
* @brief Internal interface to the context part of the bank's HTTP API
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_bank_service.h"
#include "taler_signatures.h"
/**
* Entry in the context's job queue.
*/
struct BAC_Job;
/**
* Function to call upon completion of a job.
*
* @param cls closure
* @param eh original easy handle (for inspection)
*/
typedef void
(*BAC_JobCompletionCallback)(void *cls,
CURL *eh);
/**
* Schedule a CURL request to be executed and call the given @a jcc
* upon its completion. Note that the context will make use of the
* CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
* instead use #BAC_easy_to_closure to extract the @a jcc_cls argument
* from a valid @a eh afterwards.
*
* This function modifies the CURL handle to add the
* "Content-Type: application/json" header if @a add_json is set.
*
* @param ctx context to execute the job in
* @param eh curl easy handle for the request, will
* be executed AND cleaned up
* @param add_json add "application/json" content type header
* @param jcc callback to invoke upon completion
* @param jcc_cls closure for @a jcc
*/
struct BAC_Job *
BAC_job_add (struct TALER_BANK_Context *ctx,
CURL *eh,
int add_json,
BAC_JobCompletionCallback jcc,
void *jcc_cls);
/**
* Obtain the `jcc_cls` argument from an `eh` that was
* given to #BAC_job_add().
*
* @param eh easy handle that was used
* @return the `jcc_cls` that was given to #BAC_job_add().
*/
void *
BAC_easy_to_closure (CURL *eh);
/**
* Cancel a job. Must only be called before the job completion
* callback is called for the respective job.
*
* @param job job to cancel
*/
void
BAC_job_cancel (struct BAC_Job *job);
/**
* @brief Buffer data structure we use to buffer the HTTP download
* before giving it to the JSON parser.
*/
struct BAC_DownloadBuffer
{
/**
* Download buffer
*/
void *buf;
/**
* The size of the download buffer
*/
size_t buf_size;
/**
* Error code (based on libc errno) if we failed to download
* (i.e. response too large).
*/
int eno;
};
/**
* Callback used when downloading the reply to an HTTP request.
* Just appends all of the data to the `buf` in the
* `struct BAC_DownloadBuffer` for further processing. The size of
* the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
* the download exceeds this size, we abort with an error.
*
* Should be used by the various routines as the
* CURLOPT_WRITEFUNCTION. A `struct BAC_DownloadBuffer` needs to be
* passed to the CURLOPT_WRITEDATA.
*
* Afterwards, `eno` needs to be checked to ensure that the download
* completed correctly.
*
* @param bufptr data downloaded via HTTP
* @param size size of an item in @a bufptr
* @param nitems number of items in @a bufptr
* @param cls the `struct KeysRequest`
* @return number of bytes processed from @a bufptr
*/
size_t
BAC_download_cb (char *bufptr,
size_t size,
size_t nitems,
void *cls);
/**
* Obtain information about the final result about the
* HTTP download. If the download was successful, parses
* the JSON in the @a db and returns it. Also returns
* the HTTP @a response_code. If the download failed,
* the return value is NULL. The response code is set
* in any case, on download errors to zero.
*
* Calling this function also cleans up @a db.
*
* @param db download buffer
* @param eh CURL handle (to get the response code)
* @param[out] response_code set to the HTTP response code
* (or zero if we aborted the download, i.e.
* because the response was too big, or if
* the JSON we received was malformed).
* @return NULL if downloading a JSON reply failed
*/
json_t *
BAC_download_get_result (struct BAC_DownloadBuffer *db,
CURL *eh,
long *response_code);
/**
* Obtain the URL to use for an API request.
*
* @param h the bank handle to query
* @param path Taler API path (i.e. "/reserve/withdraw")
* @return the full URI to use with cURL
*/
char *
BAC_path_to_url (struct TALER_BANK_Context *h,
const char *path);
/* end of bank_api_context.h */

View File

@ -0,0 +1,525 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file bank-lib/bank_api_json.c
* @brief functions to parse incoming requests (JSON snippets)
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
#include "platform.h"
#include "bank_api_json.h"
/**
* Navigate and parse data in a JSON tree.
*
* @param root the JSON node to start the navigation at.
* @param spec parse specification array
* @return offset in @a spec where parsing failed, -1 on success (!)
*/
static int
parse_json (json_t *root,
struct BAJ_Specification *spec)
{
int i;
json_t *pos; /* what's our current position? */
pos = root;
for (i=0;BAJ_CMD_END != spec[i].cmd;i++)
{
pos = json_object_get (root,
spec[i].field);
if (NULL == pos)
{
GNUNET_break_op (0);
return i;
}
switch (spec[i].cmd)
{
case BAJ_CMD_END:
GNUNET_assert (0);
return i;
case BAJ_CMD_AMOUNT:
if (GNUNET_OK !=
TALER_json_to_amount (pos,
spec[i].details.amount))
{
GNUNET_break_op (0);
return i;
}
break;
case BAJ_CMD_TIME_ABSOLUTE:
if (GNUNET_OK !=
TALER_json_to_abs (pos,
spec[i].details.abs_time))
{
GNUNET_break_op (0);
return i;
}
break;
case BAJ_CMD_STRING:
{
const char *str;
str = json_string_value (pos);
if (NULL == str)
{
GNUNET_break_op (0);
return i;
}
*spec[i].details.strptr = str;
}
break;
case BAJ_CMD_BINARY_FIXED:
{
const char *str;
int res;
str = json_string_value (pos);
if (NULL == str)
{
GNUNET_break_op (0);
return i;
}
res = GNUNET_STRINGS_string_to_data (str, strlen (str),
spec[i].details.fixed_data.dest,
spec[i].details.fixed_data.dest_size);
if (GNUNET_OK != res)
{
GNUNET_break_op (0);
return i;
}
}
break;
case BAJ_CMD_BINARY_VARIABLE:
{
const char *str;
size_t size;
void *data;
int res;
str = json_string_value (pos);
if (NULL == str)
{
GNUNET_break_op (0);
return i;
}
size = (strlen (str) * 5) / 8;
if (size >= 1024)
{
GNUNET_break_op (0);
return i;
}
data = GNUNET_malloc (size);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
data,
size);
if (GNUNET_OK != res)
{
GNUNET_break_op (0);
GNUNET_free (data);
return i;
}
*spec[i].details.variable_data.dest_p = data;
*spec[i].details.variable_data.dest_size_p = size;
}
break;
case BAJ_CMD_RSA_PUBLIC_KEY:
{
size_t size;
const char *str;
int res;
void *buf;
str = json_string_value (pos);
if (NULL == str)
{
GNUNET_break_op (0);
return i;
}
size = (strlen (str) * 5) / 8;
buf = GNUNET_malloc (size);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
buf,
size);
if (GNUNET_OK != res)
{
GNUNET_free (buf);
GNUNET_break_op (0);
return i;
}
*spec[i].details.rsa_public_key
= GNUNET_CRYPTO_rsa_public_key_decode (buf,
size);
GNUNET_free (buf);
if (NULL == spec[i].details.rsa_public_key)
{
GNUNET_break_op (0);
return i;
}
}
break;
case BAJ_CMD_RSA_SIGNATURE:
{
size_t size;
const char *str;
int res;
void *buf;
str = json_string_value (pos);
if (NULL == str)
{
GNUNET_break_op (0);
return i;
}
size = (strlen (str) * 5) / 8;
buf = GNUNET_malloc (size);
res = GNUNET_STRINGS_string_to_data (str,
strlen (str),
buf,
size);
if (GNUNET_OK != res)
{
GNUNET_free (buf);
GNUNET_break_op (0);
return i;
}
*spec[i].details.rsa_signature
= GNUNET_CRYPTO_rsa_signature_decode (buf,
size);
GNUNET_free (buf);
if (NULL == spec[i].details.rsa_signature)
return i;
}
break;
case BAJ_CMD_UINT16:
{
json_int_t val;
if (! json_is_integer (pos))
{
GNUNET_break_op (0);
return i;
}
val = json_integer_value (pos);
if ( (0 > val) || (val > UINT16_MAX) )
{
GNUNET_break_op (0);
return i;
}
*spec[i].details.u16 = (uint16_t) val;
}
break;
case BAJ_CMD_UINT64:
{
json_int_t val;
if (! json_is_integer (pos))
{
GNUNET_break_op (0);
return i;
}
val = json_integer_value (pos);
*spec[i].details.u64 = (uint64_t) val;
}
break;
case BAJ_CMD_JSON_OBJECT:
{
if (! (json_is_object (pos) || json_is_array (pos)) )
{
GNUNET_break_op (0);
return i;
}
json_incref (pos);
*spec[i].details.obj = pos;
}
break;
default:
GNUNET_break (0);
return i;
}
}
return -1; /* all OK! */
}
/**
* Free all elements allocated during a
* #BAJ_parse_json() operation.
*
* @param spec specification of the parse operation
* @param end number of elements in @a spec to process
*/
static void
parse_free (struct BAJ_Specification *spec,
int end)
{
int i;
for (i=0;i<end;i++)
{
switch (spec[i].cmd)
{
case BAJ_CMD_END:
GNUNET_assert (0);
return;
case BAJ_CMD_AMOUNT:
break;
case BAJ_CMD_TIME_ABSOLUTE:
break;
case BAJ_CMD_BINARY_FIXED:
break;
case BAJ_CMD_STRING:
break;
case BAJ_CMD_BINARY_VARIABLE:
GNUNET_free (*spec[i].details.variable_data.dest_p);
*spec[i].details.variable_data.dest_p = NULL;
*spec[i].details.variable_data.dest_size_p = 0;
break;
case BAJ_CMD_RSA_PUBLIC_KEY:
GNUNET_CRYPTO_rsa_public_key_free (*spec[i].details.rsa_public_key);
*spec[i].details.rsa_public_key = NULL;
break;
case BAJ_CMD_RSA_SIGNATURE:
GNUNET_CRYPTO_rsa_signature_free (*spec[i].details.rsa_signature);
*spec[i].details.rsa_signature = NULL;
break;
case BAJ_CMD_JSON_OBJECT:
json_decref (*spec[i].details.obj);
*spec[i].details.obj = NULL;
break;
default:
GNUNET_break (0);
break;
}
}
}
/**
* Navigate and parse data in a JSON tree.
*
* @param root the JSON node to start the navigation at.
* @param spec parse specification array
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
int
BAJ_parse_json (const json_t *root,
struct BAJ_Specification *spec)
{
int ret;
ret = parse_json ((json_t *) root,
spec);
if (-1 == ret)
return GNUNET_OK;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"JSON field `%s` (%d) had unexpected value\n",
spec[ret].field,
ret);
parse_free (spec, ret);
return GNUNET_SYSERR;
}
/**
* Free all elements allocated during a
* #BAJ_parse_json() operation.
*
* @param spec specification of the parse operation
*/
void
BAJ_parse_free (struct BAJ_Specification *spec)
{
int i;
for (i=0;BAJ_CMD_END != spec[i].cmd;i++) ;
parse_free (spec, i);
}
/**
* The expected field stores a string.
*
* @param name name of the JSON field
* @param strptr where to store a pointer to the field
*/
struct BAJ_Specification
BAJ_spec_string (const char *name,
const char **strptr)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_STRING,
.field = name,
.details.strptr = strptr
};
return ret;
}
/**
* Specification for parsing an absolute time value.
*
* @param name name of the JSON field
* @param at where to store the absolute time found under @a name
*/
struct BAJ_Specification
BAJ_spec_absolute_time (const char *name,
struct GNUNET_TIME_Absolute *at)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_TIME_ABSOLUTE,
.field = name,
.details.abs_time = at
};
return ret;
}
/**
* Specification for parsing an amount value.
*
* @param name name of the JSON field
* @param amount where to store the amount found under @a name
*/
struct BAJ_Specification
BAJ_spec_amount (const char *name,
struct TALER_Amount *amount)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_AMOUNT,
.field = name,
.details.amount = amount
};
return ret;
}
/**
* 16-bit integer.
*
* @param name name of the JSON field
* @param[out] u16 where to store the integer found under @a name
*/
struct BAJ_Specification
BAJ_spec_uint16 (const char *name,
uint16_t *u16)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_UINT16,
.field = name,
.details.u16 = u16
};
return ret;
}
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct BAJ_Specification
BAJ_spec_uint64 (const char *name,
uint64_t *u64)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_UINT64,
.field = name,
.details.u64 = u64
};
return ret;
}
/**
* JSON object.
*
* @param name name of the JSON field
* @param[out] jsonp where to store the JSON found under @a name
*/
struct BAJ_Specification
BAJ_spec_json (const char *name,
json_t **jsonp)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_JSON_OBJECT,
.field = name,
.details.obj = jsonp
};
return ret;
}
/**
* Specification for parsing an RSA public key.
*
* @param name name of the JSON field
* @param pk where to store the RSA key found under @a name
*/
struct BAJ_Specification
BAJ_spec_rsa_public_key (const char *name,
struct GNUNET_CRYPTO_rsa_PublicKey **pk)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_RSA_PUBLIC_KEY,
.field = name,
.details.rsa_public_key = pk
};
return ret;
}
/**
* Specification for parsing an RSA signature.
*
* @param name name of the JSON field
* @param sig where to store the RSA signature found under @a name
*/
struct BAJ_Specification
BAJ_spec_rsa_signature (const char *name,
struct GNUNET_CRYPTO_rsa_Signature **sig)
{
struct BAJ_Specification ret =
{
.cmd = BAJ_CMD_RSA_SIGNATURE,
.field = name,
.details.rsa_signature = sig
};
return ret;
}
/* end of bank_api_json.c */

View File

@ -0,0 +1,352 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file mint-lib/mint_api_json.h
* @brief functions to parse incoming requests (JSON snippets)
* @author Florian Dold
* @author Benedikt Mueller
* @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include "taler_util.h"
#include <jansson.h>
/**
* Enumeration with the various commands for the
* #BAJ_parse_json interpreter.
*/
enum BAJ_Command
{
/**
* End of command list.
*/
BAJ_CMD_END,
/**
* Parse amount at current position.
*/
BAJ_CMD_AMOUNT,
/**
* Parse absolute time at current position.
*/
BAJ_CMD_TIME_ABSOLUTE,
/**
* Parse fixed binary value at current position.
*/
BAJ_CMD_BINARY_FIXED,
/**
* Parse variable-size binary value at current position.
*/
BAJ_CMD_BINARY_VARIABLE,
/**
* Parse RSA public key at current position.
*/
BAJ_CMD_RSA_PUBLIC_KEY,
/**
* Parse RSA signature at current position.
*/
BAJ_CMD_RSA_SIGNATURE,
/**
* Parse `const char *` JSON string at current position.
*/
BAJ_CMD_STRING,
/**
* Parse `uint16_t` integer at the current position.
*/
BAJ_CMD_UINT16,
/**
* Parse `uint64_t` integer at the current position.
*/
BAJ_CMD_UINT64,
/**
* Parse JSON object at the current position.
*/
BAJ_CMD_JSON_OBJECT,
/**
* Parse ??? at current position.
*/
BAJ_CMD_C
};
/**
* @brief Entry in parser specification for #BAJ_parse_json.
*/
struct BAJ_Specification
{
/**
* Command to execute.
*/
enum BAJ_Command cmd;
/**
* Name of the field to access.
*/
const char *field;
/**
* Further details for the command.
*/
union {
/**
* Where to store amount for #BAJ_CMD_AMOUNT.
*/
struct TALER_Amount *amount;
/**
* Where to store time, for #BAJ_CMD_TIME_ABSOLUTE.
*/
struct GNUNET_TIME_Absolute *abs_time;
/**
* Where to write binary data, for #BAJ_CMD_BINARY_FIXED.
*/
struct {
/**
* Where to write the data.
*/
void *dest;
/**
* How many bytes to write to @e dest.
*/
size_t dest_size;
} fixed_data;
/**
* Where to write binary data, for #BAJ_CMD_BINARY_VARIABLE.
*/
struct {
/**
* Where to store the pointer with the data (is allocated).
*/
void **dest_p;
/**
* Where to store the number of bytes allocated at `*dest`.
*/
size_t *dest_size_p;
} variable_data;
/**
* Where to store the RSA public key for #BAJ_CMD_RSA_PUBLIC_KEY
*/
struct GNUNET_CRYPTO_rsa_PublicKey **rsa_public_key;
/**
* Where to store the RSA signature for #BAJ_CMD_RSA_SIGNATURE
*/
struct GNUNET_CRYPTO_rsa_Signature **rsa_signature;
/**
* Details for #BAJ_CMD_EDDSA_SIGNATURE
*/
struct {
/**
* Where to store the purpose.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose **purpose_p;
/**
* Key to verify the signature against.
*/
const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key;
} eddsa_signature;
/**
* Where to store a pointer to the string.
*/
const char **strptr;
/**
* Where to store 16-bit integer.
*/
uint16_t *u16;
/**
* Where to store 64-bit integer.
*/
uint64_t *u64;
/**
* Where to store a JSON object.
*/
json_t **obj;
} details;
};
/**
* Navigate and parse data in a JSON tree.
*
* @param root the JSON node to start the navigation at.
* @param spec parse specification array
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
int
BAJ_parse_json (const json_t *root,
struct BAJ_Specification *spec);
/**
* Free all elements allocated during a
* #BAJ_parse_json() operation.
*
* @param spec specification of the parse operation
*/
void
BAJ_parse_free (struct BAJ_Specification *spec);
/**
* End of a parser specification.
*/
#define BAJ_spec_end { .cmd = BAJ_CMD_END }
/**
* Fixed size object (in network byte order, encoded using Crockford
* Base32hex encoding).
*
* @param name name of the JSON field
* @param obj pointer where to write the data (type of `*obj` will determine size)
*/
#define BAJ_spec_fixed_auto(name,obj) { .cmd = BAJ_CMD_BINARY_FIXED, .field = name, .details.fixed_data.dest = obj, .details.fixed_data.dest_size = sizeof (*obj) }
/**
* Variable size object (in network byte order, encoded using Crockford
* Base32hex encoding).
*
* @param name name of the JSON field
* @param obj pointer where to write the data (a `void **`)
* @param size where to store the number of bytes allocated for @a obj (of type `size_t *`
*/
#define BAJ_spec_varsize(name,obj,size) { .cmd = BAJ_CMD_BINARY_VARIABLE, .field = name, .details.variable_data.dest_p = obj, .details.variable_data.dest_size_p = size }
/**
* The expected field stores a string.
*
* @param name name of the JSON field
* @param strptr where to store a pointer to the field
*/
struct BAJ_Specification
BAJ_spec_string (const char *name,
const char **strptr);
/**
* Absolute time.
*
* @param name name of the JSON field
* @param[out] at where to store the absolute time found under @a name
*/
struct BAJ_Specification
BAJ_spec_absolute_time (const char *name,
struct GNUNET_TIME_Absolute *at);
/**
* 16-bit integer.
*
* @param name name of the JSON field
* @param[out] u16 where to store the integer found under @a name
*/
struct BAJ_Specification
BAJ_spec_uint16 (const char *name,
uint16_t *u16);
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct BAJ_Specification
BAJ_spec_uint64 (const char *name,
uint64_t *u64);
/**
* JSON object.
*
* @param name name of the JSON field
* @param[out] jsonp where to store the JSON found under @a name
*/
struct BAJ_Specification
BAJ_spec_json (const char *name,
json_t **jsonp);
/**
* Specification for parsing an amount value.
*
* @param name name of the JSON field
* @param amount where to store the amount under @a name
*/
struct BAJ_Specification
BAJ_spec_amount (const char *name,
struct TALER_Amount *amount);
/**
* Specification for parsing an RSA public key.
*
* @param name name of the JSON field
* @param pk where to store the RSA key found under @a name
*/
struct BAJ_Specification
BAJ_spec_rsa_public_key (const char *name,
struct GNUNET_CRYPTO_rsa_PublicKey **pk);
/**
* Specification for parsing an RSA signature.
*
* @param name name of the JSON field
* @param sig where to store the RSA signature found under @a name
*/
struct BAJ_Specification
BAJ_spec_rsa_signature (const char *name,
struct GNUNET_CRYPTO_rsa_Signature **sig);
/* end of mint_api_json.h */

View File

@ -0,0 +1,542 @@
/*
This file is part of TALER
Copyright (C) 2016 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/test_bank_api.c
* @brief testcase to test bank's HTTP API interface
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_signatures.h"
#include "taler_bank_service.h"
#include <gnunet/gnunet_util_lib.h>
#include <microhttpd.h>
/**
* Main execution context for the main loop.
*/
static struct TALER_BANK_Context *ctx;
/**
* Task run on shutdown.
*/
static struct GNUNET_SCHEDULER_Task *shutdown_task;
/**
* Task that runs the main event loop.
*/
static struct GNUNET_SCHEDULER_Task *ctx_task;
/**
* Result of the testcases, #GNUNET_OK on success
*/
static int result;
/**
* Opcodes for the interpreter.
*/
enum OpCode
{
/**
* Termination code, stops the interpreter loop (with success).
*/
OC_END = 0,
/**
* Add funds to a reserve by (faking) incoming wire transfer.
*/
OC_ADMIN_ADD_INCOMING
};
/**
* Details for a bank operation to execute.
*/
struct Command
{
/**
* Opcode of the command.
*/
enum OpCode oc;
/**
* Label for the command, can be NULL.
*/
const char *label;
/**
* Which response code do we expect for this command?
*/
unsigned int expected_response_code;
/**
* Details about the command.
*/
union
{
/**
* Information for a #OC_ADMIN_ADD_INCOMING command.
*/
struct
{
/**
* String describing the amount to add to the reserve.
*/
const char *amount;
/**
* Account number.
*/
uint64_t account_no;
/**
* Wire transfer identifier to use. Initialized to
* a random value.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Set to the API's handle during the operation.
*/
struct TALER_BANK_AdminAddIncomingHandle *aih;
} admin_add_incoming;
} details;
};
/**
* State of the interpreter loop.
*/
struct InterpreterState
{
/**
* Keys from the bank.
*/
const struct TALER_BANK_Keys *keys;
/**
* Commands the interpreter will run.
*/
struct Command *commands;
/**
* Interpreter task (if one is scheduled).
*/
struct GNUNET_SCHEDULER_Task *task;
/**
* Instruction pointer. Tells #interpreter_run() which
* instruction to run next.
*/
unsigned int ip;
};
/**
* Task that runs the context's event loop with the GNUnet scheduler.
*
* @param cls unused
* @param tc scheduler context (unused)
*/
static void
context_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc);
/**
* Run the context task, the working set has changed.
*/
static void
trigger_context_task ()
{
GNUNET_SCHEDULER_cancel (ctx_task);
ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
NULL);
}
/**
* The testcase failed, return with an error code.
*
* @param is interpreter state to clean up
*/
static void
fail (struct InterpreterState *is)
{
result = GNUNET_SYSERR;
GNUNET_SCHEDULER_shutdown ();
}
#if 0
/**
* Find a command by label.
*
* @param is interpreter state to search
* @param label label to look for
* @return NULL if command was not found
*/
static const struct Command *
find_command (const struct InterpreterState *is,
const char *label)
{
unsigned int i;
const struct Command *cmd;
if (NULL == label)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Attempt to lookup command for empty label\n");
return NULL;
}
for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
if ( (NULL != cmd->label) &&
(0 == strcmp (cmd->label,
label)) )
return cmd;
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command not found: %s\n",
label);
return NULL;
}
#endif
/**
* Run the main interpreter loop that performs bank operations.
*
* @param cls contains the `struct InterpreterState`
* @param tc scheduler context
*/
static void
interpreter_run (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc);
/**
* Function called upon completion of our /admin/add/incoming request.
*
* @param cls closure with the interpreter state
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol)
*/
static void
add_incoming_cb (void *cls,
unsigned int http_status)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
cmd->details.admin_add_incoming.aih = NULL;
if (cmd->expected_response_code != http_status)
{
GNUNET_break (0);
fail (is);
return;
}
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/**
* Run the main interpreter loop that performs bank operations.
*
* @param cls contains the `struct InterpreterState`
* @param tc scheduler context
*/
static void
interpreter_run (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
struct TALER_Amount amount;
is->task = NULL;
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
{
fprintf (stderr,
"Test aborted by shutdown request\n");
fail (is);
return;
}
switch (cmd->oc)
{
case OC_END:
result = GNUNET_OK;
GNUNET_SCHEDULER_shutdown ();
return;
case OC_ADMIN_ADD_INCOMING:
if (GNUNET_OK !=
TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
&amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u\n",
cmd->details.admin_add_incoming.amount,
is->ip);
fail (is);
return;
}
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&cmd->details.admin_add_incoming.wtid,
sizeof (cmd->details.admin_add_incoming.wtid));
cmd->details.admin_add_incoming.aih
= TALER_BANK_admin_add_incoming (ctx,
&cmd->details.admin_add_incoming.wtid,
&amount,
cmd->details.admin_add_incoming.account_no,
&add_incoming_cb,
is);
if (NULL == cmd->details.admin_add_incoming.aih)
{
GNUNET_break (0);
fail (is);
return;
}
trigger_context_task ();
return;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
cmd->oc,
is->ip,
cmd->label);
fail (is);
return;
}
}
/**
* Function run when the test terminates (good or bad).
* Cleans up our state.
*
* @param cls the interpreter state.
* @param tc unused
*/
static void
do_shutdown (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct InterpreterState *is = cls;
struct Command *cmd;
unsigned int i;
shutdown_task = NULL;
for (i=0;OC_END != (cmd = &is->commands[i])->oc;i++)
{
switch (cmd->oc)
{
case OC_END:
GNUNET_assert (0);
break;
case OC_ADMIN_ADD_INCOMING:
if (NULL != cmd->details.admin_add_incoming.aih)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_BANK_admin_add_incoming_cancel (cmd->details.admin_add_incoming.aih);
cmd->details.admin_add_incoming.aih = NULL;
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
cmd->oc,
i,
cmd->label);
break;
}
}
if (NULL != is->task)
{
GNUNET_SCHEDULER_cancel (is->task);
is->task = NULL;
}
GNUNET_free (is);
if (NULL != ctx_task)
{
GNUNET_SCHEDULER_cancel (ctx_task);
ctx_task = NULL;
}
if (NULL != ctx)
{
TALER_BANK_fini (ctx);
ctx = NULL;
}
}
/**
* Task that runs the context's event loop with the GNUnet scheduler.
*
* @param cls unused
* @param tc scheduler context (unused)
*/
static void
context_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
long timeout;
int max_fd;
fd_set read_fd_set;
fd_set write_fd_set;
fd_set except_fd_set;
struct GNUNET_NETWORK_FDSet *rs;
struct GNUNET_NETWORK_FDSet *ws;
struct GNUNET_TIME_Relative delay;
ctx_task = NULL;
TALER_BANK_perform (ctx);
max_fd = -1;
timeout = -1;
FD_ZERO (&read_fd_set);
FD_ZERO (&write_fd_set);
FD_ZERO (&except_fd_set);
TALER_BANK_get_select_info (ctx,
&read_fd_set,
&write_fd_set,
&except_fd_set,
&max_fd,
&timeout);
if (timeout >= 0)
delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
timeout);
else
delay = GNUNET_TIME_UNIT_FOREVER_REL;
rs = GNUNET_NETWORK_fdset_create ();
GNUNET_NETWORK_fdset_copy_native (rs,
&read_fd_set,
max_fd + 1);
ws = GNUNET_NETWORK_fdset_create ();
GNUNET_NETWORK_fdset_copy_native (ws,
&write_fd_set,
max_fd + 1);
ctx_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
delay,
rs,
ws,
&context_task,
cls);
GNUNET_NETWORK_fdset_destroy (rs);
GNUNET_NETWORK_fdset_destroy (ws);
}
/**
* Main function that will be run by the scheduler.
*
* @param cls closure
* @param args remaining command-line arguments
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
* @param config configuration
*/
static void
run (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
struct InterpreterState *is;
static struct Command commands[] =
{
/* Add EUR:5.01 to account 42 */
{ .oc = OC_ADMIN_ADD_INCOMING,
.label = "deposit-1",
.expected_response_code = MHD_HTTP_OK,
.details.admin_add_incoming.account_no = 42,
.details.admin_add_incoming.amount = "EUR:5.01" },
{ .oc = OC_END }
};
is = GNUNET_new (struct InterpreterState);
is->commands = commands;
ctx = TALER_BANK_init ("http://localhost:8081");
GNUNET_assert (NULL != ctx);
ctx_task = GNUNET_SCHEDULER_add_now (&context_task,
ctx);
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
shutdown_task
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS, 150),
&do_shutdown, is);
}
/**
* Main function for the testcase for the bank API.
*
* @param argc expected to be 1
* @param argv expected to only contain the program name
*/
int
main (int argc,
char * const *argv)
{
struct GNUNET_OS_Process *bankd;
GNUNET_log_setup ("test-bank-api",
"WARNING",
NULL);
bankd = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ALL,
NULL, NULL, NULL,
"taler-bank-httpd",
"taler-bank-httpd",
"-d", "test-bank-home",
NULL);
if (NULL == bankd)
{
fprintf (stderr,
"taler-bank-httpd not found, skipping test\n");
return 77; /* report 'skip' */
}
/* give child time to start and bind against the socket */
fprintf (stderr,
"Waiting for taler-bank-httpd to be ready");
do
{
fprintf (stderr, ".");
sleep (1);
}
while (0 != system ("wget -q -t 1 -T 1 http://127.0.0.1:8081/keys -o /dev/null -O /dev/null"));
fprintf (stderr, "\n");
result = GNUNET_SYSERR;
GNUNET_SCHEDULER_run (&run, NULL);
GNUNET_OS_process_kill (bankd,
SIGTERM);
GNUNET_OS_process_wait (bankd);
GNUNET_OS_process_destroy (bankd);
return (GNUNET_OK == result) ? 0 : 1;
}
/* end of test_bank_api.c */

View File

@ -20,7 +20,9 @@ talerinclude_HEADERS = \
taler_mintdb_lib.h \
taler_mintdb_plugin.h \
taler_pq_lib.h \
taler_signatures.h
taler_signatures.h \
taler_wire_lib.h \
taler_wire_plugin.h
endif

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -0,0 +1,161 @@
/*
This file is part of TALER
Copyright (C) 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file include/taler_bank_service.h
* @brief C interface of libtalerbank, a C library to use the Taler bank's HTTP API
* This is currently ONLY used to provide the "test" wire transfer protocol.
* @author Christian Grothoff
*/
#ifndef _TALER_BANK_SERVICE_H
#define _TALER_BANK_SERVICE_H
#include "taler_util.h"
/* ********************* event loop *********************** */
/**
* @brief Handle to this library context. This is where the
* main event loop logic lives.
*/
struct TALER_BANK_Context;
/**
* Initialise a context. A context should be used for each thread and should
* not be shared among multiple threads.
*
* @param url HTTP base URL for the bank
* @return the context, NULL on error (failure to initialize)
*/
struct TALER_BANK_Context *
TALER_BANK_init (const char *url);
/**
* Obtain the information for a select() call to wait until
* #TALER_BANK_perform() is ready again. Note that calling
* any other TALER_BANK-API may also imply that the library
* is again ready for #TALER_BANK_perform().
*
* Basically, a client should use this API to prepare for select(),
* then block on select(), then call #TALER_BANK_perform() and then
* start again until the work with the context is done.
*
* This function will NOT zero out the sets and assumes that @a max_fd
* and @a timeout are already set to minimal applicable values. It is
* safe to give this API FD-sets and @a max_fd and @a timeout that are
* already initialized to some other descriptors that need to go into
* the select() call.
*
* @param ctx context to get the event loop information for
* @param read_fd_set will be set for any pending read operations
* @param write_fd_set will be set for any pending write operations
* @param except_fd_set is here because curl_multi_fdset() has this argument
* @param max_fd set to the highest FD included in any set;
* if the existing sets have no FDs in it, the initial
* value should be "-1". (Note that `max_fd + 1` will need
* to be passed to select().)
* @param timeout set to the timeout in milliseconds (!); -1 means
* no timeout (NULL, blocking forever is OK), 0 means to
* proceed immediately with #TALER_BANK_perform().
*/
void
TALER_BANK_get_select_info (struct TALER_BANK_Context *ctx,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *except_fd_set,
int *max_fd,
long *timeout);
/**
* Run the main event loop for the Taler interaction.
*
* @param ctx the library context
*/
void
TALER_BANK_perform (struct TALER_BANK_Context *ctx);
/**
* Cleanup library initialisation resources. This function should be called
* after using this library to cleanup the resources occupied during library's
* initialisation.
*
* @param ctx the library context
*/
void
TALER_BANK_fini (struct TALER_BANK_Context *ctx);
/* ********************* /admin/add/incoming *********************** */
/**
* @brief A /admin/add/incoming Handle
*/
struct TALER_BANK_AdminAddIncomingHandle;
/**
* Callbacks of this type are used to serve the result of submitting
* information about an incoming transaction to a bank.
*
* @param cls closure
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol)
*/
typedef void
(*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
unsigned int http_status);
/**
* Notify the bank that we have received an incoming transaction
* which fills a reserve. Note that this API is an administrative
* API and thus not accessible to typical bank clients, but only
* to the operators of the bank.
*
* @param bank the bank handle; the bank must be ready to operate
* @param reserve_pub public key of the reserve
* @param amount amount that was deposited
* @param execution_date when did we receive the amount
* @param account_no account number (53 bits at most)
* @param res_cb the callback to call when the final result for this request is available
* @param res_cb_cls closure for the above callback
* @return NULL
* if the inputs are invalid (i.e. invalid amount).
* In this case, the callback is not called.
*/
struct TALER_BANK_AdminAddIncomingHandle *
TALER_BANK_admin_add_incoming (struct TALER_BANK_Context *bank,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *amount,
uint64_t account_no,
TALER_BANK_AdminAddIncomingResultCallback res_cb,
void *res_cb_cls);
/**
* Cancel an add incoming. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param aai the admin add incoming request handle
*/
void
TALER_BANK_admin_add_incoming_cancel (struct TALER_BANK_AdminAddIncomingHandle *aai);
#endif /* _TALER_BANK_SERVICE_H */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -428,6 +428,61 @@ struct TALER_RefreshLinkDecrypted
};
/**
* Length of the raw value in the Taler wire transfer identifier
* (in binary representation).
*/
#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN 32
/**
* #TALER_WIRE_TRANSFER_IDENTIFIER_LEN as a string.
*/
#define TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR "32"
/**
* Raw value of a wire transfer subjects, without the checksum.
*/
struct TALER_WireTransferIdentifierRawP
{
/**
* Raw value. Note that typical payment systems (SEPA, ACH) support
* at least two lines of 27 ASCII characters to encode a transaction
* subject or "details", for a total of 54 characters. (The payment
* system protocols often support more lines, but the forms presented
* to customers are usually limited to 54 characters.)
*
* With a Base32-encoding of 5 bit per character, this gives us 270
* bits or (rounded down) 33 bytes. So we use the first 32 bytes to
* encode the actual value (i.e. a 256-bit / 32-byte public key or
* a hash code), and the last byte for a minimalistic checksum.
*/
uint8_t raw[TALER_WIRE_TRANSFER_IDENTIFIER_LEN];
};
/**
* Binary information encoded in Crockford's Base32 in wire transfer
* subjects of transfers from Taler to a merchant. The actual value
* is chosen by the mint and has no particular semantics, other than
* being unique so that the mint can lookup details about the wire
* transfer when needed.
*/
struct TALER_WireTransferIdentifierP
{
/**
* Raw value.
*/
struct TALER_WireTransferIdentifierRawP raw;
/**
* Checksum using CRC8 over the @e raw data.
*/
uint8_t crc8;
};
GNUNET_NETWORK_STRUCT_END

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -1059,5 +1059,162 @@ void
TALER_MINT_admin_add_incoming_cancel (struct TALER_MINT_AdminAddIncomingHandle *aai);
/* ********************* /wire/deposits *********************** */
/**
* @brief A /wire/deposits Handle
*/
struct TALER_MINT_WireDepositsHandle;
/**
* Details for one of the /deposit operations that the
* mint combined into a single wire transfer.
*/
struct TALER_WireDepositDetails
{
/**
* Hash of the contract.
*/
struct GNUNET_HashCode h_contract;
/**
* Which coin was deposited?
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Value of the deposit (including fee).
*/
struct TALER_Amount coin_value;
/**
* Fee charged by the mint for the deposit.
*/
struct TALER_Amount coin_fee;
/**
* Merchant's transaction identifier.
*/
uint64_t transaction_id;
};
/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid extracted wire transfer identifier, or NULL if the mint could
* not provide any (set only if @a http_status is #MHD_HTTP_OK)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param details_length length of the @a details array
* @param details array with details about the combined transactions
*/
typedef void
(*TALER_MINT_WireDepositsCallback)(void *cls,
unsigned int http_status,
json_t *json,
const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *total_amount,
unsigned int details_length,
const struct TALER_WireDepositDetails *details);
/**
* Query the mint about which transactions were combined
* to create a wire transfer.
*
* @param mint mint to query
* @param wtid raw wire transfer identifier to get information about
* @param cb callback to call
* @param cb_cls closure for @a cb
* @return handle to cancel operation
*/
struct TALER_MINT_WireDepositsHandle *
TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINT_WireDepositsCallback cb,
void *cb_cls);
/**
* Cancel wire deposits request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param wdh the wire deposits request handle
*/
void
TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh);
/* ********************* /deposit/wtid *********************** */
/**
* @brief A /deposit/wtid Handle
*/
struct TALER_MINT_DepositWtidHandle;
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid wire transfer identifier used by the mint, NULL if mint did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
* @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
*/
typedef void
(*TALER_MINT_DepositWtidCallback)(void *cls,
unsigned int http_status,
json_t *json,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution);
/**
* Obtain the wire transfer details for a given deposit.
*
* @param mint the mint to query
* @param merchant_priv the merchant's private key
* @param h_wire hash of merchant's wire transfer details
* @param h_contract hash of the contract
* @param coin_pub public key of the coin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
*/
struct TALER_MINT_DepositWtidHandle *
TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t transaction_id,
TALER_MINT_DepositWtidCallback cb,
void *cb_cls);
/**
* Cancel deposit wtid request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param dwh the wire deposits request handle
*/
void
TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh);
#endif /* _TALER_MINT_SERVICE_H */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015, 2016 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
@ -530,20 +530,24 @@ struct TALER_MINTDB_Session;
* corresponding wire transaction.
*
* @param cls closure
* @param id transaction ID (used as future `min_id` to avoid
* iterating over transactions more than once)
* @param rowid unique ID for the deposit in our DB, used for marking
* it as 'tiny' or 'done'
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the mint gets to keep as transaction fees
* @param transaction_id unique transaction ID chosen by the merchant
* @param h_contract hash of the contract between merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant
* @param wire wire details for the merchant, NULL from iterate_matching_deposits()
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
typedef int
(*TALER_MINTDB_DepositIterator)(void *cls,
uint64_t id,
unsigned long long rowid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
uint64_t transaction_id,
@ -570,21 +574,67 @@ typedef void
/**
* Function called with the results of the lookup of the
* wire transfer identifier information.
*
* wire transfer identifier information. Only called if
* we are at least aware of the transaction existing.
*
* @param cls closure
* @param wtid base32-encoded wire transfer identifier, NULL
* @param wtid wire transfer identifier, NULL
* if the transaction was not yet done
* @param coin_contribution how much did the coin we asked about
* contribute to the total transfer value? (deposit value including fee)
* @param coin_fee how much did the mint charge for the deposit fee
* @param execution_time when was the transaction done, or
* when we expect it to be done (if @a wtid was NULL);
* #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
* to the mint
* when we expect it to be done (if @a wtid was NULL)
*/
typedef void
(*TALER_MINTDB_DepositWtidCallback)(void *cls,
const char *wtid,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
struct GNUNET_TIME_Absolute execution_time);
/**
* Function called with the results of the lookup of the
* transaction data associated with a wire transfer identifier.
*
* @param cls closure
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @param coin_pub which public key was this payment about
* @param coin_value amount contributed by this coin in total (with fee)
* @param coin_fee applicable fee for this coin
* @param transfer_value total amount of the wire transfer
*/
typedef void
(*TALER_MINTDB_WireTransferDataCallback)(void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value);
/**
* Callback with data about a prepared transaction.
*
* @param cls closure
* @param rowid row identifier used to mark prepared transaction as done
* @param buf transaction data that was persisted, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
typedef void
(*TALER_MINTDB_WirePreparationCallback) (void *cls,
unsigned long long rowid,
const char *buf,
size_t buf_size);
/**
* @brief The plugin API, returned from the plugin's "init" function.
* The argument given to "init" is simply a configuration handle.
@ -848,27 +898,78 @@ struct TALER_MINTDB_Plugin
/**
* Obtain information about deposits. Iterates over all deposits
* above a certain ID. Use a @a min_id of 0 to start at the beginning.
* This operation is executed in its own transaction in transaction
* mode "REPEATABLE READ", i.e. we should only see valid deposits.
* Mark a deposit as tiny, thereby declaring that it cannot be
* executed by itself and should no longer be returned by
* @e iterate_ready_deposits()
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param min_id deposit to start at
* @param limit maximum number of transactions to fetch
* @param deposit_cb function to call for each deposit
* @param deposit_rowid identifies the deposit row to modify
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
int
(*mark_deposit_tiny) (void *cls,
struct TALER_MINTDB_Session *session,
unsigned long long rowid);
/**
* Mark a deposit as done, thereby declaring that it cannot be
* executed at all anymore, and should no longer be returned by
* @e iterate_ready_deposits() or @e iterate_matching_deposits().
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit_rowid identifies the deposit row to modify
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error
*/
int
(*mark_deposit_done) (void *cls,
struct TALER_MINTDB_Session *session,
unsigned long long rowid);
/**
* Obtain information about deposits that are ready to be executed.
* Such deposits must not be marked as "tiny" or "done", and the
* execution time must be in the past.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param deposit_cb function to call for ONE such deposit
* @param deposit_cb_cls closure for @a deposit_cb
* @return number of rows processed, 0 if none exist,
* #GNUNET_SYSERR on error
*/
int
(*iterate_deposits) (void *cls,
struct TALER_MINTDB_Session *session,
uint64_t min_id,
uint32_t limit,
TALER_MINTDB_DepositIterator deposit_cb,
void *deposit_cb_cls);
(*get_ready_deposit) (void *cls,
struct TALER_MINTDB_Session *session,
TALER_MINTDB_DepositIterator deposit_cb,
void *deposit_cb_cls);
/**
* Obtain information about other pending deposits for the same
* destination. Those deposits must not already be "done".
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param h_wire destination of the wire transfer
* @param merchant_pub public key of the merchant
* @param deposit_cb function to call for each deposit
* @param deposit_cb_cls closure for @a deposit_cb
* @param limit maximum number of matching deposits to return
* @return number of rows processed, 0 if none exist,
* #GNUNET_SYSERR on error
*/
int
(*iterate_matching_deposits) (void *cls,
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_wire,
const struct TALER_MerchantPublicKeyP *merchant_pub,
TALER_MINTDB_DepositIterator deposit_cb,
void *deposit_cb_cls,
uint32_t limit);
/**
@ -1112,10 +1213,10 @@ struct TALER_MINTDB_Plugin
*/
int
(*insert_refresh_out) (void *cls,
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *session_hash,
uint16_t newcoin_index,
const struct TALER_DenominationSignature *ev_sig);
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *session_hash,
uint16_t newcoin_index,
const struct TALER_DenominationSignature *ev_sig);
/**
@ -1194,12 +1295,33 @@ struct TALER_MINTDB_Plugin
struct TALER_MINTDB_TransactionList *list);
/**
* Lookup the list of Taler transactions that was aggregated
* into a wire transfer by the respective @a raw_wtid.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param cb function to call on each transaction found
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
* #GNUNET_NO if we found no results
*/
int
(*lookup_wire_transfer) (void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINTDB_WireTransferDataCallback cb,
void *cb_cls);
/**
* Try to find the wire transfer details for a deposit operation.
* If we did not execute the deposit yet, return when it is supposed
* to be executed.
*
*
* @param cls closure
* @param session database connection
* @param h_contract hash of the contract
* @param h_wire hash of merchant wire details
* @param coin_pub public key of deposited coin
@ -1207,10 +1329,12 @@ struct TALER_MINTDB_Plugin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure to pass to @a cb
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
* #GNUNET_NO if nothing was found
*/
int
(*wire_lookup_deposit_wtid)(void *cls,
struct TALER_MINTDB_Session *session,
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -1219,7 +1343,91 @@ struct TALER_MINTDB_Plugin
TALER_MINTDB_DepositWtidCallback cb,
void *cb_cls);
/**
* Function called to insert aggregation information into the DB.
*
* @param cls closure
* @param session database connection
* @param wtid the raw wire transfer identifier we used
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @param execution_time when did we execute the transaction
* @param coin_pub which public key was this payment about
* @param coin_value amount contributed by this coin in total
* @param coin_fee deposit fee charged by mint for this coin
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
*/
int
(*insert_aggregation_tracking)(void *cls,
struct TALER_MINTDB_Session *session,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee);
/**
* Function called to insert wire transfer commit data into the DB.
*
* @param cls closure
* @param session database connection
* @param type type of the wire transfer (i.e. "sepa")
* @param buf buffer with wire transfer preparation data
* @param buf_size number of bytes in @a buf
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
*/
int
(*wire_prepare_data_insert)(void *cls,
struct TALER_MINTDB_Session *session,
const char *type,
const char *buf,
size_t buf_size);
/**
* Function called to mark wire transfer commit data as finished.
*
* @param cls closure
* @param session database connection
* @param rowid which entry to mark as finished
* @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
*/
int
(*wire_prepare_data_mark_finished)(void *cls,
struct TALER_MINTDB_Session *session,
unsigned long long rowid);
/**
* Function called to get an unfinished wire transfer
* preparation data. Fetches at most one item.
*
* @param cls closure
* @param session database connection
* @param type type fo the wire transfer (i.e. "sepa")
* @param cb function to call for ONE unfinished item
* @param cb_cls closure for @a cb
* @return #GNUNET_OK on success,
* #GNUNET_NO if there are no entries,
* #GNUNET_SYSERR on DB errors
*/
int
(*wire_prepare_data_get)(void *cls,
struct TALER_MINTDB_Session *session,
const char *type,
TALER_MINTDB_WirePreparationCallback cb,
void *cb_cls);
};
#endif /* _NEURO_MINT_DB_H */
#endif /* _TALER_MINT_DB_H */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -156,7 +156,7 @@ struct TALER_PQ_QueryParam
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x);
TALER_PQ_query_param_amount_nbo (const struct TALER_AmountNBO *x);
/**
@ -168,7 +168,7 @@ TALER_PQ_query_param_amount_nbo(const struct TALER_AmountNBO *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_amount(const struct TALER_Amount *x);
TALER_PQ_query_param_amount (const struct TALER_Amount *x);
/**
@ -178,7 +178,7 @@ TALER_PQ_query_param_amount(const struct TALER_Amount *x);
* @param x the query parameter to pass.
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x);
TALER_PQ_query_param_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *x);
/**
@ -188,7 +188,7 @@ TALER_PQ_query_param_rsa_public_key(const struct GNUNET_CRYPTO_rsa_PublicKey *x)
* @param x the query parameter to pass
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x);
TALER_PQ_query_param_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *x);
/**
@ -198,7 +198,7 @@ TALER_PQ_query_param_rsa_signature(const struct GNUNET_CRYPTO_rsa_Signature *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x);
TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x);
/**
@ -208,7 +208,7 @@ TALER_PQ_query_param_absolute_time(const struct GNUNET_TIME_Absolute *x);
* @param x pointer to the query parameter to pass
*/
struct TALER_PQ_QueryParam
TALER_PQ_query_param_absolute_time_nbo(const struct GNUNET_TIME_AbsoluteNBO *x);
TALER_PQ_query_param_absolute_time_nbo (const struct GNUNET_TIME_AbsoluteNBO *x);
/**

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015, 2016 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
@ -106,6 +106,11 @@
*/
#define TALER_SIGNATURE_MINT_WIRE_TYPES 1036
/**
* Signature where the Mint confirms the /deposit/wtid response.
*/
#define TALER_SIGNATURE_MINT_CONFIRM_WIRE 1036
/*********************/
/* Wallet signatures */
@ -854,6 +859,33 @@ struct TALER_ContractPS
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Merchant-generated transaction ID to detect duplicate
* transactions, in big endian. The merchant must communicate a
* merchant-unique ID to the customer for each transaction. Note
* that different coins that are part of the same transaction can
* use the same transaction ID. The transaction ID is useful for
* later disputes, and the merchant's contract offer (@e h_contract)
* with the customer should include the offer's term and transaction
* ID signed with a key from the merchant. This field must match
* the corresponding field in the JSON contract.
*/
uint64_t transaction_id GNUNET_PACKED;
/**
* The total amount to be paid to the merchant. Note that if deposit
* fees are higher than @e max_fee, the actual total must be higher
* to cover the additional fees. This field must match the
* corresponding field in the JSON contract.
*/
struct TALER_AmountNBO total_amount;
/**
* The maximum fee the merchant is willing to cover. This field
* must match the corresponding field in the JSON contract.
*/
struct TALER_AmountNBO max_fee;
/**
* Hash of the JSON contract in UTF-8 including 0-termination,
* using JSON_COMPACT | JSON_SORT_KEYS
@ -863,6 +895,65 @@ struct TALER_ContractPS
};
/**
* Details affirmed by the mint about a wire transfer the mint
* claims to have done with respect to a deposit operation.
*/
struct TALER_ConfirmWirePS
{
/**
* Purpose header for the signature over the contract with
* purpose #TALER_SIGNATURE_MINT_CONFIRM_WIRE.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
/**
* Hash over the wiring information of the merchant.
*/
struct GNUNET_HashCode h_wire GNUNET_PACKED;
/**
* Hash over the contract for which this deposit is made.
*/
struct GNUNET_HashCode h_contract GNUNET_PACKED;
/**
* Raw value (binary encoding) of the wire transfer subject.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* The coin's public key. This is the value that must have been
* signed (blindly) by the Mint.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Merchant-generated transaction ID to detect duplicate
* transactions, in big endian. The merchant must communicate a
* merchant-unique ID to the customer for each transaction. Note
* that different coins that are part of the same transaction can
* use the same transaction ID. The transaction ID is useful for
* later disputes, and the merchant's contract offer (@e h_contract)
* with the customer should include the offer's term and transaction
* ID signed with a key from the merchant.
*/
uint64_t transaction_id GNUNET_PACKED;
/**
* When did the mint execute this transfer? Note that the
* timestamp may not be exactly the same on the wire, i.e.
* because the wire has a different timezone or resolution.
*/
struct GNUNET_TIME_AbsoluteNBO execution_time;
/**
* The contribution of @e coin_pub to the total transfer volume.
* This is the value of the deposit minus the fee.
*/
struct TALER_AmountNBO coin_contribution;
};
GNUNET_NETWORK_STRUCT_END

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -0,0 +1,48 @@
/*
This file is part of TALER
Copyright (C) 2016 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 include/taler_wire_lib.h
* @brief Interface for loading and unloading wire plugins
* @author Christian Grothoff <christian@grothoff.org>
*/
#ifndef TALER_WIRE_H
#define TALER_WIRE_H
#include <gnunet/gnunet_util_lib.h>
#include "taler_wire_plugin.h"
/**
* Load a WIRE plugin.
*
* @param cfg configuration to use
* @param plugin_name name of the plugin to load
* @return #GNUNET_OK on success
*/
struct TALER_WIRE_Plugin *
TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *plugin_name);
/**
* Unload a WIRE plugin.
*
* @param plugin the plugin to unload
*/
void
TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin);
#endif

View File

@ -0,0 +1,180 @@
/*
This file is part of TALER
Copyright (C) 2016 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 include/taler_wire_plugin.h
* @brief Plugin API for the handling of wire transactions
* @author Christian Grothoff
*/
#ifndef TALER_WIRE_PLUGIN_H
#define TALER_WIRE_PLUGIN_H
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler_util.h"
/**
* Callback with prepared transaction.
*
* @param cls closure
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
typedef void
(*TALER_WIRE_PrepareTransactionCallback) (void *cls,
const char *buf,
size_t buf_size);
/**
* Handle returned for cancelling a preparation step.
*/
struct TALER_WIRE_PrepareHandle;
/**
* Handle returned for cancelling an execution step.
*/
struct TALER_WIRE_ExecuteHandle;
/**
* Function called with the result from the execute step.
*
* @param cls closure
* @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure
* @param emsg NULL on success, otherwise an error message
*/
typedef void
(*TALER_WIRE_ConfirmationCallback)(void *cls,
int success,
const char *emsg);
/**
* @brief The plugin API, returned from the plugin's "init" function.
* The argument given to "init" is simply a configuration handle.
*/
struct TALER_WIRE_Plugin
{
/**
* Closure for all callbacks.
*/
void *cls;
/**
* Name of the library which generated this plugin. Set by the
* plugin loader.
*/
char *library_name;
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
* payment, but SEPA only supports 0.01 EUR. This function would
* round 0.125 EUR to 0.12 EUR in this case.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
int
(*amount_round) (void *cls,
struct TALER_Amount *amount);
/**
* Check if the given wire format JSON object is correctly formatted
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
int
(*wire_validate) (const json_t *wire);
/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param amount amount to transfer, already rounded
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
* @return NULL on failure
*/
struct TALER_WIRE_PrepareHandle *
(*prepare_wire_transfer) (void *cls,
const json_t *wire,
const struct TALER_Amount *amount,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls);
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
void
(*prepare_wire_transfer_cancel) (void *cls,
struct TALER_WIRE_PrepareHandle *pth);
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
struct TALER_WIRE_ExecuteHandle *
(*execute_wire_transfer) (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls);
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
void
(*execute_wire_transfer_cancel) (void *cls,
struct TALER_WIRE_ExecuteHandle *eh);
};
#endif /* TALER_WIRE_PLUGIN_H */

View File

@ -20,10 +20,12 @@ libtalermint_la_SOURCES = \
mint_api_handle.c mint_api_handle.h \
mint_api_admin.c \
mint_api_deposit.c \
mint_api_deposit_wtid.c \
mint_api_refresh.c \
mint_api_refresh_link.c \
mint_api_reserve.c \
mint_api_wire.c
mint_api_wire.c \
mint_api_wire_deposits.c
libtalermint_la_LIBADD = \
-lgnunetutil \

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -288,7 +288,7 @@ TALER_MINT_perform (struct TALER_MINT_Context *ctx)
GNUNET_assert (CURLE_OK ==
curl_easy_getinfo (cmsg->easy_handle,
CURLINFO_PRIVATE,
(char *) &job));
(char **) &job));
GNUNET_assert (job->ctx == ctx);
job->jcc (job->jcc_cls,
cmsg->easy_handle);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -434,6 +434,14 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint,
TALER_LOG_WARNING ("Denomination key unknown to mint\n");
return NULL;
}
if (GNUNET_SYSERR ==
TALER_amount_subtract (&amount_without_fee,
amount,
&dki->fee_deposit))
{
GNUNET_break (0);
return NULL;
}
if (GNUNET_OK !=
verify_signatures (dki,
@ -492,9 +500,6 @@ TALER_MINT_deposit (struct TALER_MINT_Handle *mint,
dh->depconf.transaction_id = GNUNET_htonll (transaction_id);
dh->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp);
dh->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
TALER_amount_subtract (&amount_without_fee,
amount,
&dki->fee_deposit);
TALER_amount_hton (&dh->depconf.amount_without_fee,
&amount_without_fee);
dh->depconf.coin_pub = *coin_pub;

View File

@ -0,0 +1,379 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 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 mint-lib/mint_api_deposit_wtid.c
* @brief Implementation of the /deposit/wtid request of the mint's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <jansson.h>
#include <microhttpd.h> /* just for HTTP status codes */
#include <gnunet/gnunet_util_lib.h>
#include "taler_mint_service.h"
#include "mint_api_common.h"
#include "mint_api_json.h"
#include "mint_api_context.h"
#include "mint_api_handle.h"
#include "taler_signatures.h"
/**
* @brief A Deposit Wtid Handle
*/
struct TALER_MINT_DepositWtidHandle
{
/**
* The connection to mint this request handle will use
*/
struct TALER_MINT_Handle *mint;
/**
* The url for this request.
*/
char *url;
/**
* JSON encoding of the request to POST.
*/
char *json_enc;
/**
* Handle for the request.
*/
struct MAC_Job *job;
/**
* Function to call with the result.
*/
TALER_MINT_DepositWtidCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Download buffer
*/
struct MAC_DownloadBuffer db;
/**
* Information the mint should sign in response.
* (with pre-filled fields from the request).
*/
struct TALER_ConfirmWirePS depconf;
};
/**
* Verify that the signature on the "200 OK" response
* from the mint is valid.
*
* @param dwh deposit wtid handle
* @param json json reply with the signature
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static int
verify_deposit_wtid_signature_ok (const struct TALER_MINT_DepositWtidHandle *dwh,
json_t *json)
{
struct TALER_MintSignatureP mint_sig;
struct TALER_MintPublicKeyP mint_pub;
const struct TALER_MINT_Keys *key_state;
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("mint_sig", &mint_sig),
MAJ_spec_fixed_auto ("mint_pub", &mint_pub),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
key_state = TALER_MINT_get_keys (dwh->mint);
if (GNUNET_OK !=
TALER_MINT_test_signing_key (key_state,
&mint_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MINT_CONFIRM_WIRE,
&dwh->depconf.purpose,
&mint_sig.eddsa_signature,
&mint_pub.eddsa_pub))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Function called when we're done processing the
* HTTP /deposit/wtid request.
*
* @param cls the `struct TALER_MINT_DepositWtidHandle`
* @param eh the curl request handle
*/
static void
handle_deposit_wtid_finished (void *cls,
CURL *eh)
{
struct TALER_MINT_DepositWtidHandle *dwh = cls;
long response_code;
json_t *json;
const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
const struct TALER_Amount *coin_contribution = NULL;
struct TALER_Amount coin_contribution_s;
dwh->job = NULL;
json = MAC_download_get_result (&dwh->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("wtid", &dwh->depconf.wtid),
MAJ_spec_absolute_time ("execution_time", &execution_time),
MAJ_spec_amount ("coin_contribution", &coin_contribution_s),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
wtid = &dwh->depconf.wtid;
dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time);
TALER_amount_hton (&dwh->depconf.coin_contribution,
&coin_contribution_s);
coin_contribution = &coin_contribution_s;
if (GNUNET_OK !=
verify_deposit_wtid_signature_ok (dwh,
json))
{
GNUNET_break_op (0);
response_code = 0;
}
}
break;
case MHD_HTTP_ACCEPTED:
{
/* Transaction known, but not executed yet */
struct MAJ_Specification spec[] = {
MAJ_spec_absolute_time ("execution_time", &execution_time),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the mint is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, mint says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Mint does not know about transaction;
we should pass the reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
dwh->cb (dwh->cb_cls,
response_code,
json,
wtid,
execution_time,
coin_contribution);
json_decref (json);
TALER_MINT_deposit_wtid_cancel (dwh);
}
/**
* Obtain wire transfer details about an existing deposit operation.
*
* @param mint the mint to query
* @param merchant_priv the merchant's private key
* @param h_wire hash of merchant's wire transfer details
* @param h_contract hash of the contract
* @param coin_pub public key of the coin
* @param transaction_id transaction identifier
* @param cb function to call with the result
* @param cb_cls closure for @a cb
* @return handle to abort request
*/
struct TALER_MINT_DepositWtidHandle *
TALER_MINT_deposit_wtid (struct TALER_MINT_Handle *mint,
const struct TALER_MerchantPrivateKeyP *merchant_priv,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t transaction_id,
TALER_MINT_DepositWtidCallback cb,
void *cb_cls)
{
struct TALER_DepositTrackPS dtp;
struct TALER_MerchantSignatureP merchant_sig;
struct TALER_MINT_DepositWtidHandle *dwh;
struct TALER_MINT_Context *ctx;
json_t *deposit_wtid_obj;
CURL *eh;
if (GNUNET_YES !=
MAH_handle_is_ready (mint))
{
GNUNET_break (0);
return NULL;
}
dtp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_DEPOSIT_WTID);
dtp.purpose.size = htonl (sizeof (dtp));
dtp.h_contract = *h_contract;
dtp.h_wire = *h_wire;
dtp.transaction_id = GNUNET_htonll (transaction_id);
GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
&dtp.merchant.eddsa_pub);
dtp.coin_pub = *coin_pub;
GNUNET_assert (GNUNET_OK ==
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&dtp.purpose,
&merchant_sig.eddsa_sig));
deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, H_contract */
" s:o, s:I," /* coin_pub, transaction_id */
" s:o, s:o}", /* merchant_pub, merchant_sig */
"H_wire", TALER_json_from_data (h_wire,
sizeof (struct GNUNET_HashCode)),
"H_contract", TALER_json_from_data (h_contract,
sizeof (struct GNUNET_HashCode)),
"coin_pub", TALER_json_from_data (coin_pub,
sizeof (*coin_pub)),
"transaction_id", (json_int_t) transaction_id,
"merchant_pub", TALER_json_from_data (&dtp.merchant,
sizeof (struct TALER_MerchantPublicKeyP)),
"merchant_sig", TALER_json_from_data (&merchant_sig,
sizeof (merchant_sig)));
dwh = GNUNET_new (struct TALER_MINT_DepositWtidHandle);
dwh->mint = mint;
dwh->cb = cb;
dwh->cb_cls = cb_cls;
dwh->url = MAH_path_to_url (mint, "/deposit/wtid");
dwh->depconf.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
dwh->depconf.h_wire = *h_wire;
dwh->depconf.h_contract = *h_contract;
dwh->depconf.coin_pub = *coin_pub;
dwh->depconf.transaction_id = GNUNET_htonll (transaction_id);
eh = curl_easy_init ();
GNUNET_assert (NULL != (dwh->json_enc =
json_dumps (deposit_wtid_obj,
JSON_COMPACT)));
json_decref (deposit_wtid_obj);
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
dwh->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDS,
dwh->json_enc));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_POSTFIELDSIZE,
strlen (dwh->json_enc)));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&MAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&dwh->db));
ctx = MAH_handle_to_context (mint);
dwh->job = MAC_job_add (ctx,
eh,
GNUNET_YES,
&handle_deposit_wtid_finished,
dwh);
return dwh;
}
/**
* Cancel deposit wtid request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param dwh the wire deposits request handle
*/
void
TALER_MINT_deposit_wtid_cancel (struct TALER_MINT_DepositWtidHandle *dwh)
{
if (NULL != dwh->job)
{
MAC_job_cancel (dwh->job);
dwh->job = NULL;
}
GNUNET_free_non_null (dwh->db.buf);
GNUNET_free (dwh->url);
GNUNET_free (dwh->json_enc);
GNUNET_free (dwh);
}
/* end of mint_api_deposit_wtid.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -755,7 +755,7 @@ TALER_MINT_connect (struct TALER_MINT_Context *ctx,
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_VERBOSE,
1));
0));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (c,
CURLOPT_STDERR,

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -232,6 +232,20 @@ parse_json (json_t *root,
}
break;
case MAJ_CMD_UINT64:
{
json_int_t val;
if (! json_is_integer (pos))
{
GNUNET_break_op (0);
return i;
}
val = json_integer_value (pos);
*spec[i].details.u64 = (uint64_t) val;
}
break;
case MAJ_CMD_JSON_OBJECT:
{
if (! (json_is_object (pos) || json_is_array (pos)) )
@ -428,6 +442,26 @@ MAJ_spec_uint16 (const char *name,
}
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct MAJ_Specification
MAJ_spec_uint64 (const char *name,
uint64_t *u64)
{
struct MAJ_Specification ret =
{
.cmd = MAJ_CMD_UINT64,
.field = name,
.details.u64 = u64
};
return ret;
}
/**
* JSON object.
*

View File

@ -78,6 +78,11 @@ enum MAJ_Command
*/
MAJ_CMD_UINT16,
/**
* Parse `uint64_t` integer at the current position.
*/
MAJ_CMD_UINT64,
/**
* Parse JSON object at the current position.
*/
@ -191,6 +196,11 @@ struct MAJ_Specification
*/
uint16_t *u16;
/**
* Where to store 64-bit integer.
*/
uint64_t *u64;
/**
* Where to store a JSON object.
*/
@ -282,6 +292,17 @@ MAJ_spec_uint16 (const char *name,
uint16_t *u16);
/**
* 64-bit integer.
*
* @param name name of the JSON field
* @param[out] u64 where to store the integer found under @a name
*/
struct MAJ_Specification
MAJ_spec_uint64 (const char *name,
uint64_t *u64);
/**
* JSON object.
*

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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
@ -334,12 +334,9 @@ free_melt_data (struct MeltData *md)
for (i=0;i<TALER_CNC_KAPPA;i++)
{
if (NULL != md->fresh_coins)
{
for (j=0;j<md->num_fresh_coins;j++)
free_fresh_coin (&md->fresh_coins[i][j]);
GNUNET_free (md->fresh_coins[i]);
}
for (j=0;j<md->num_fresh_coins;j++)
free_fresh_coin (&md->fresh_coins[i][j]);
GNUNET_free (md->fresh_coins[i]);
}
/* Finally, clean up a bit...
(NOTE: compilers might optimize this away, so this is
@ -774,7 +771,7 @@ deserialize_melt_data (const char *buf,
&buf[off],
buf_size - off,
&ok);
if (off != buf_size)
if (off != buf_size)
{
GNUNET_break (0);
ok = GNUNET_NO;

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015, 2016 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
@ -185,6 +185,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
return GNUNET_SYSERR;
}
num_coins = 0;
/* Theoretically, a coin may have been melted repeatedly
into different sessions; so the response is an array
which contains information by melting session. That
array contains another array. However, our API returns
a single 1d array, so we flatten the 2d array that is
returned into a single array. Note that usually a coin
is melted at most once, and so we'll only run this
loop once for 'session=0' in most cases.
num_coins tracks the size of the 1d array we return,
whilst 'i' and 'session' track the 2d array. */
for (session=0;session<json_array_size (json); session++)
{
json_t *jsona;
@ -194,7 +205,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
};
if (GNUNET_OK !=
MAJ_parse_json (json_array_get (json,
MAJ_parse_json (json_array_get (json,
session),
spec))
{
@ -212,13 +223,17 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
num_coins += json_array_size (jsona);
MAJ_parse_free (spec);
}
/* Now that we know how big the 1d array is, allocate
and fill it. */
{
unsigned int off_coin;
unsigned int off_coin; /* index into 1d array */
unsigned int i;
struct TALER_CoinSpendPrivateKeyP coin_privs[num_coins];
struct TALER_DenominationSignature sigs[num_coins];
struct TALER_DenominationPublicKey pubs[num_coins];
memset (sigs, 0, sizeof (sigs));
memset (pubs, 0, sizeof (pubs));
off_coin = 0;
for (session=0;session<json_array_size (json); session++)
{
@ -233,7 +248,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
};
if (GNUNET_OK !=
MAJ_parse_json (json_array_get (json,
MAJ_parse_json (json_array_get (json,
session),
spec))
{
@ -246,13 +261,13 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
MAJ_parse_free (spec);
return GNUNET_SYSERR;
}
/* decode all coins */
for (i=0;i<json_array_size (jsona);i++)
{
if (GNUNET_OK !=
parse_refresh_link_coin (rlh,
json_array_get (jsona,
json_array_get (jsona,
i),
&trans_pub,
&secret_enc,
@ -265,6 +280,7 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
}
/* check if we really got all, then invoke callback */
off_coin += i;
if (i != json_array_size (jsona))
{
GNUNET_break_op (0);
@ -272,9 +288,9 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
MAJ_parse_free (spec);
break;
}
off_coin += json_array_size (jsona);
MAJ_parse_free (spec);
}
} /* end of for (session) */
if (off_coin == num_coins)
{
rlh->link_cb (rlh->link_cb_cls,
@ -294,9 +310,13 @@ parse_refresh_link_ok (struct TALER_MINT_RefreshLinkHandle *rlh,
}
/* clean up */
for (i=0;i<num_coins;i++)
for (i=0;i<off_coin;i++)
{
if (NULL != sigs[i].rsa_signature)
GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
if (NULL != pubs[i].rsa_public_key)
GNUNET_CRYPTO_rsa_public_key_free (pubs[i].rsa_public_key);
}
}
return ret;
}

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -0,0 +1,284 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015, 2016 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 mint-lib/mint_api_wire_deposits.c
* @brief Implementation of the /wire/deposits request of the mint's HTTP API
* @author Christian Grothoff
*/
#include "platform.h"
#include <curl/curl.h>
#include <jansson.h>
#include <microhttpd.h> /* just for HTTP status codes */
#include <gnunet/gnunet_util_lib.h>
#include "taler_mint_service.h"
#include "mint_api_common.h"
#include "mint_api_json.h"
#include "mint_api_context.h"
#include "mint_api_handle.h"
#include "taler_signatures.h"
/**
* @brief A /wire/deposits Handle
*/
struct TALER_MINT_WireDepositsHandle
{
/**
* The connection to mint this request handle will use
*/
struct TALER_MINT_Handle *mint;
/**
* The url for this request.
*/
char *url;
/**
* Handle for the request.
*/
struct MAC_Job *job;
/**
* Function to call with the result.
*/
TALER_MINT_WireDepositsCallback cb;
/**
* Closure for @a cb.
*/
void *cb_cls;
/**
* Download buffer
*/
struct MAC_DownloadBuffer db;
};
/**
* Function called when we're done processing the
* HTTP /wire/deposits request.
*
* @param cls the `struct TALER_MINT_WireDepositsHandle`
* @param eh the curl request handle
*/
static void
handle_wire_deposits_finished (void *cls,
CURL *eh)
{
struct TALER_MINT_WireDepositsHandle *wdh = cls;
long response_code;
json_t *json;
wdh->job = NULL;
json = MAC_download_get_result (&wdh->db,
eh,
&response_code);
switch (response_code)
{
case 0:
break;
case MHD_HTTP_OK:
{
json_t *details_j;
struct GNUNET_HashCode h_wire;
struct TALER_Amount total_amount;
struct TALER_MerchantPublicKeyP merchant_pub;
unsigned int num_details;
struct MAJ_Specification spec[] = {
MAJ_spec_fixed_auto ("H_wire", &h_wire),
MAJ_spec_fixed_auto ("merchant_pub", &merchant_pub),
MAJ_spec_amount ("total_amount", &total_amount),
MAJ_spec_json ("details", &details_j),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (json,
spec))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
num_details = json_array_size (details_j);
{
struct TALER_WireDepositDetails details[num_details];
unsigned int i;
for (i=0;i<num_details;i++)
{
struct TALER_WireDepositDetails *detail = &details[i];
struct json_t *detail_j = json_array_get (details_j, i);
struct MAJ_Specification spec_detail[] = {
MAJ_spec_fixed_auto ("H_contract", &detail->h_contract),
MAJ_spec_amount ("deposit_value", &detail->coin_value),
MAJ_spec_amount ("deposit_fee", &detail->coin_fee),
MAJ_spec_uint64 ("transaction_id", &detail->transaction_id),
MAJ_spec_fixed_auto ("coin_pub", &detail->coin_pub),
MAJ_spec_end
};
if (GNUNET_OK !=
MAJ_parse_json (detail_j,
spec_detail))
{
GNUNET_break_op (0);
response_code = 0;
break;
}
}
if (0 == response_code)
break;
wdh->cb (wdh->cb_cls,
response_code,
json,
&h_wire,
&total_amount,
num_details,
details);
json_decref (json);
TALER_MINT_wire_deposits_cancel (wdh);
return;
}
}
break;
case MHD_HTTP_BAD_REQUEST:
/* This should never happen, either us or the mint is buggy
(or API version conflict); just pass JSON reply to the application */
break;
case MHD_HTTP_UNAUTHORIZED:
/* Nothing really to verify, mint says one of the signatures is
invalid; as we checked them, this should never happen, we
should pass the JSON reply to the application */
break;
case MHD_HTTP_NOT_FOUND:
/* Mint does not know about transaction;
we should pass the reply to the application */
break;
case MHD_HTTP_INTERNAL_SERVER_ERROR:
/* Server had an internal issue; we should retry, but this API
leaves this to the application */
break;
default:
/* unexpected response code */
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u\n",
response_code);
GNUNET_break (0);
response_code = 0;
break;
}
wdh->cb (wdh->cb_cls,
response_code,
json,
NULL, NULL, 0, NULL);
json_decref (json);
TALER_MINT_wire_deposits_cancel (wdh);
}
/**
* Query the mint about which transactions were combined
* to create a wire transfer.
*
* @param mint mint to query
* @param wtid raw wire transfer identifier to get information about
* @param cb callback to call
* @param cb_cls closure for @a cb
* @return handle to cancel operation
*/
struct TALER_MINT_WireDepositsHandle *
TALER_MINT_wire_deposits (struct TALER_MINT_Handle *mint,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_MINT_WireDepositsCallback cb,
void *cb_cls)
{
struct TALER_MINT_WireDepositsHandle *wdh;
struct TALER_MINT_Context *ctx;
char *buf;
char *path;
CURL *eh;
if (GNUNET_YES !=
MAH_handle_is_ready (mint))
{
GNUNET_break (0);
return NULL;
}
wdh = GNUNET_new (struct TALER_MINT_WireDepositsHandle);
wdh->mint = mint;
wdh->cb = cb;
wdh->cb_cls = cb_cls;
buf = GNUNET_STRINGS_data_to_string_alloc (wtid,
sizeof (struct TALER_WireTransferIdentifierRawP));
GNUNET_asprintf (&path,
"/wire/deposits?wtid=%s",
buf);
wdh->url = MAH_path_to_url (wdh->mint,
path);
GNUNET_free (buf);
GNUNET_free (path);
eh = curl_easy_init ();
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_URL,
wdh->url));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEFUNCTION,
&MAC_download_cb));
GNUNET_assert (CURLE_OK ==
curl_easy_setopt (eh,
CURLOPT_WRITEDATA,
&wdh->db));
ctx = MAH_handle_to_context (mint);
wdh->job = MAC_job_add (ctx,
eh,
GNUNET_YES,
&handle_wire_deposits_finished,
wdh);
return wdh;
}
/**
* Cancel wire deposits request. This function cannot be used on a request
* handle if a response is already served for it.
*
* @param wdh the wire deposits request handle
*/
void
TALER_MINT_wire_deposits_cancel (struct TALER_MINT_WireDepositsHandle *wdh)
{
if (NULL != wdh->job)
{
MAC_job_cancel (wdh->job);
wdh->job = NULL;
}
GNUNET_free_non_null (wdh->db.buf);
GNUNET_free (wdh->url);
GNUNET_free (wdh);
}
/* end of mint_api_wire_deposits.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015, 2016 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
@ -111,7 +111,17 @@ enum OpCode
/**
* Verify the mint's /wire-method.
*/
OC_WIRE
OC_WIRE,
/**
* Verify mint's /wire/deposits method.
*/
OC_WIRE_DEPOSITS,
/**
* Verify mint's /deposit/wtid method.
*/
OC_DEPOSIT_WTID
};
@ -470,6 +480,60 @@ struct Command
} wire;
/**
* Information for the /wire/deposits's command.
*/
struct {
/**
* Handle to the wire deposits request.
*/
struct TALER_MINT_WireDepositsHandle *wdh;
/**
* Reference to a /deposit/wtid command. If set, we use the
* WTID from that command.
*/
const char *wtid_ref;
/**
* WTID to use (used if @e wtid_ref is NULL).
*/
struct TALER_WireTransferIdentifierRawP wtid;
/* TODO: may want to add list of deposits we expected
to see aggregated here in the future. */
} wire_deposits;
/**
* Information for the /deposit/wtid command.
*/
struct {
/**
* Handle to the deposit wtid request.
*/
struct TALER_MINT_DepositWtidHandle *dwh;
/**
* Which /deposit operation should we obtain WTID data for?
*/
const char *deposit_ref;
/**
* What is the expected total amount? Only used if
* @e expected_response_code was #MHD_HTTP_OK.
*/
struct TALER_Amount total_amount_expected;
/**
* Wire transfer identifier, set if #MHD_HTTP_OK was the response code.
*/
struct TALER_WireTransferIdentifierRawP wtid;
} deposit_wtid;
} details;
};
@ -1219,6 +1283,158 @@ wire_cb (void *cls,
}
/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid extracted wire transfer identifier, or NULL if the mint could
* not provide any (set only if @a http_status is #MHD_HTTP_OK)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param details_length length of the @a details array
* @param details array with details about the combined transactions
*/
static void
wire_deposits_cb (void *cls,
unsigned int http_status,
json_t *json,
const struct GNUNET_HashCode *h_wire,
const struct TALER_Amount *total_amount,
unsigned int details_length,
const struct TALER_WireDepositDetails *details)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
const struct Command *ref;
cmd->details.wire_deposits.wdh = NULL;
ref = find_command (is,
cmd->details.wire_deposits.wtid_ref);
if (cmd->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
if (0 != TALER_amount_cmp (total_amount,
&ref->details.deposit_wtid.total_amount_expected))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
if (NULL != ref->details.deposit_wtid.deposit_ref)
{
const struct Command *dep;
struct GNUNET_HashCode hw;
dep = find_command (is,
ref->details.deposit_wtid.deposit_ref);
GNUNET_CRYPTO_hash (dep->details.deposit.wire_details,
strlen (dep->details.deposit.wire_details),
&hw);
if (0 != memcmp (&hw,
h_wire,
sizeof (struct GNUNET_HashCode)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire hash missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
}
break;
default:
break;
}
/* move to next command */
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param http_status HTTP status code we got, 0 on mint protocol violation
* @param json original json reply (may include signatures, those have then been
* validated already)
* @param wtid wire transfer identifier used by the mint, NULL if mint did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
* @param coin_contribution contribution to the @a total_amount of the deposited coin (may be NULL)
* @param total_amount total amount of the wire transfer, or NULL if the mint could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
*/
static void
deposit_wtid_cb (void *cls,
unsigned int http_status,
json_t *json,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *total_amount)
{
struct InterpreterState *is = cls;
struct Command *cmd = &is->commands[is->ip];
cmd->details.deposit_wtid.dwh = NULL;
if (cmd->expected_response_code != http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unexpected response code %u to command %s\n",
http_status,
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
switch (http_status)
{
case MHD_HTTP_OK:
cmd->details.deposit_wtid.wtid = *wtid;
if (0 != TALER_amount_cmp (total_amount,
&cmd->details.deposit_wtid.total_amount_expected))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount missmatch to command %s\n",
cmd->label);
json_dumpf (json, stderr, 0);
fail (is);
return;
}
break;
default:
break;
}
/* move to next command */
is->ip++;
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
/**
* Run the main interpreter loop that performs mint operations.
*
@ -1401,7 +1617,9 @@ interpreter_run (void *cls,
struct GNUNET_TIME_Absolute refund_deadline;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
struct TALER_MerchantPublicKeyP merchant_pub;
json_t *contract;
json_t *wire;
GNUNET_assert (NULL !=
@ -1444,37 +1662,51 @@ interpreter_run (void *cls,
fail (is);
return;
}
GNUNET_CRYPTO_hash (cmd->details.deposit.contract,
strlen (cmd->details.deposit.contract),
&h_contract);
contract = json_loads (cmd->details.deposit.contract,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == contract)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse contract details `%s' at %u/%s\n",
cmd->details.deposit.contract,
is->ip,
cmd->label);
fail (is);
return;
}
TALER_hash_json (contract,
&h_contract);
wire = json_loads (cmd->details.deposit.wire_details,
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse wire details `%s' at %u\n",
"Failed to parse wire details `%s' at %u/%s\n",
cmd->details.deposit.wire_details,
is->ip);
is->ip,
cmd->label);
fail (is);
return;
}
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
priv = GNUNET_CRYPTO_eddsa_key_create ();
cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
GNUNET_free (priv);
if (0 != cmd->details.deposit.refund_deadline.rel_value_us)
{
struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
priv = GNUNET_CRYPTO_eddsa_key_create ();
cmd->details.deposit.merchant_priv.eddsa_priv = *priv;
GNUNET_free (priv);
refund_deadline = GNUNET_TIME_relative_to_absolute (cmd->details.deposit.refund_deadline);
}
else
{
refund_deadline = GNUNET_TIME_UNIT_ZERO_ABS;
}
GNUNET_CRYPTO_eddsa_key_get_public (&cmd->details.deposit.merchant_priv.eddsa_priv,
&merchant_pub.eddsa_pub);
wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
timestamp = GNUNET_TIME_absolute_get ();
TALER_round_abs_time (&timestamp);
@ -1686,6 +1918,85 @@ interpreter_run (void *cls,
is);
trigger_context_task ();
return;
case OC_WIRE_DEPOSITS:
if (NULL != cmd->details.wire_deposits.wtid_ref)
{
ref = find_command (is,
cmd->details.wire_deposits.wtid_ref);
GNUNET_assert (NULL != ref);
cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
}
cmd->details.wire_deposits.wdh
= TALER_MINT_wire_deposits (mint,
&cmd->details.wire_deposits.wtid,
&wire_deposits_cb,
is);
trigger_context_task ();
return;
case OC_DEPOSIT_WTID:
{
struct GNUNET_HashCode h_wire;
struct GNUNET_HashCode h_contract;
json_t *wire;
json_t *contract;
const struct Command *coin;
struct TALER_CoinSpendPublicKeyP coin_pub;
ref = find_command (is,
cmd->details.deposit_wtid.deposit_ref);
GNUNET_assert (NULL != ref);
coin = find_command (is,
ref->details.deposit.coin_ref);
GNUNET_assert (NULL != coin);
switch (coin->oc)
{
case OC_WITHDRAW_SIGN:
GNUNET_CRYPTO_eddsa_key_get_public (&coin->details.reserve_withdraw.coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
break;
case OC_REFRESH_REVEAL:
{
const struct FreshCoin *fc;
unsigned int idx;
idx = ref->details.deposit.coin_idx;
GNUNET_assert (idx < coin->details.refresh_reveal.num_fresh_coins);
fc = &coin->details.refresh_reveal.fresh_coins[idx];
GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
&coin_pub.eddsa_pub);
}
break;
default:
GNUNET_assert (0);
}
wire = json_loads (ref->details.deposit.wire_details,
JSON_REJECT_DUPLICATES,
NULL);
GNUNET_assert (NULL != wire);
TALER_hash_json (wire,
&h_wire);
json_decref (wire);
contract = json_loads (ref->details.deposit.contract,
JSON_REJECT_DUPLICATES,
NULL);
GNUNET_assert (NULL != contract);
TALER_hash_json (contract,
&h_contract);
json_decref (contract);
cmd->details.deposit_wtid.dwh
= TALER_MINT_deposit_wtid (mint,
&ref->details.deposit.merchant_priv,
&h_wire,
&h_contract,
&coin_pub,
ref->details.deposit.transaction_id,
&deposit_wtid_cb,
is);
trigger_context_task ();
}
return;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@ -1695,8 +2006,6 @@ interpreter_run (void *cls,
fail (is);
return;
}
is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
is);
}
@ -1829,10 +2138,36 @@ do_shutdown (void *cls,
case OC_WIRE:
if (NULL != cmd->details.wire.wh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_wire_cancel (cmd->details.wire.wh);
cmd->details.wire.wh = NULL;
}
break;
case OC_WIRE_DEPOSITS:
if (NULL != cmd->details.wire_deposits.wdh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_wire_deposits_cancel (cmd->details.wire_deposits.wdh);
cmd->details.wire_deposits.wdh = NULL;
}
break;
case OC_DEPOSIT_WTID:
if (NULL != cmd->details.deposit_wtid.dwh)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Command %u (%s) did not complete\n",
i,
cmd->label);
TALER_MINT_deposit_wtid_cancel (cmd->details.deposit_wtid.dwh);
cmd->details.deposit_wtid.dwh = NULL;
}
break;
default:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Unknown instruction %d at %u (%s)\n",
@ -2047,7 +2382,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to overdraw funds ... */
@ -2064,7 +2399,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":43 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 1 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
transaction ID) */
@ -2074,7 +2409,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":1 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":1 } ] }",
.details.deposit.transaction_id = 2 },
/* Try to double-spend the 5 EUR coin at the same merchant (but different
contract) */
@ -2084,7 +2419,7 @@ run (void *cls,
.details.deposit.amount = "EUR:5",
.details.deposit.coin_ref = "withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":2 } }",
.details.deposit.contract = "{ \"items\":[{ \"name\":\"ice cream\", \"value\":2 } ] }",
.details.deposit.transaction_id = 1 },
/* ***************** /refresh testing ******************** */
@ -2109,7 +2444,7 @@ run (void *cls,
.details.deposit.amount = "EUR:1",
.details.deposit.coin_ref = "refresh-withdraw-coin-1",
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\"EUR:1 } }",
.details.deposit.contract = "{ \"items\" : [ { \"name\":\"ice cream\", \"value\":\"EUR:1\" } ] }",
.details.deposit.transaction_id = 42421 },
/* Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
@ -2143,7 +2478,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 0,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test successfully spending coins from the refresh operation:
@ -2155,7 +2490,7 @@ run (void *cls,
.details.deposit.coin_ref = "refresh-reveal-1",
.details.deposit.coin_idx = 4,
.details.deposit.wire_details = "{ \"type\":\"TEST\", \"bank\":\"dest bank\", \"account\":42 }",
.details.deposit.contract = "{ \"items\"={ \"name\":\"ice cream\", \"value\":3 } }",
.details.deposit.contract = "{ \"items\": [ { \"name\":\"ice cream\", \"value\":3 } ] }",
.details.deposit.transaction_id = 2 },
/* Test running a failing melt operation (same operation again must fail) */
@ -2168,6 +2503,35 @@ run (void *cls,
// FIXME: also test with coin that was already melted
// (signature differs from coin that was deposited...)
/* *************** end of /refresh testing ************** */
/* ************** Test tracking API ******************** */
/* Try resolving a deposit's WTID, as we never triggered
execution of transactions, the answer should be that
the mint knows about the deposit, but has no WTID yet. */
{ .oc = OC_DEPOSIT_WTID,
.label = "deposit-wtid-found",
.expected_response_code = MHD_HTTP_ACCEPTED,
.details.deposit_wtid.deposit_ref = "deposit-simple" },
/* Try resolving a deposit's WTID for a failed deposit.
As the deposit failed, the answer should be that
the mint does NOT know about the deposit. */
{ .oc = OC_DEPOSIT_WTID,
.label = "deposit-wtid-failing",
.expected_response_code = MHD_HTTP_NOT_FOUND,
.details.deposit_wtid.deposit_ref = "deposit-double-2" },
/* Try resolving an undefined (all zeros) WTID; this
should fail as obviously the mint didn't use that
WTID value for any transaction. */
{ .oc = OC_WIRE_DEPOSITS,
.label = "wire-deposit-failing",
.expected_response_code = MHD_HTTP_NOT_FOUND },
/* TODO: trigger aggregation logic and then check the
cases where tracking succeeds! */
/* ************** End of tracking API testing************* */
#endif
{ .oc = OC_END }

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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

View File

@ -7,21 +7,33 @@ if USE_COVERAGE
endif
bin_PROGRAMS = \
taler-mint-aggregator \
taler-mint-httpd
taler_mint_aggregator_SOURCES = \
taler-mint-aggregator.c
taler_mint_aggregator_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \
$(top_builddir)/src/wire/libtalerwire.la \
$(top_builddir)/src/mintdb/libtalermintdb.la \
-ljansson \
-lgnunetutil
taler_mint_httpd_SOURCES = \
taler-mint-httpd.c taler-mint-httpd.h \
taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
taler-mint-httpd_db.c taler-mint-httpd_db.h \
taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
taler-mint-httpd_admin.c taler-mint-httpd_admin.h \
taler-mint-httpd_db.c taler-mint-httpd_db.h \
taler-mint-httpd_deposit.c taler-mint-httpd_deposit.h \
taler-mint-httpd_keystate.c taler-mint-httpd_keystate.h \
taler-mint-httpd_mhd.c taler-mint-httpd_mhd.h \
taler-mint-httpd_parsing.c taler-mint-httpd_parsing.h \
taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h \
taler-mint-httpd_reserve.c taler-mint-httpd_reserve.h \
taler-mint-httpd_responses.c taler-mint-httpd_responses.h \
taler-mint-httpd_tracking.c taler-mint-httpd_tracking.h \
taler-mint-httpd_wire.c taler-mint-httpd_wire.h \
taler-mint-httpd_refresh.c taler-mint-httpd_refresh.h
taler-mint-httpd_validation.c taler-mint-httpd_validation.h
taler_mint_httpd_LDADD = \
$(LIBGCRYPT_LIBS) \
$(top_builddir)/src/util/libtalerutil.la \

View File

@ -0,0 +1,914 @@
/*
This file is part of TALER
Copyright (C) 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-mint-aggregator.c
* @brief Process that aggregates outgoing transactions and executes them
* @author Christian Grothoff
*
* TODO:
* - simplify global_ret: make it a global!
* - handle shutdown more nicely (call 'cancel' method on wire transfers)
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include <pthread.h>
#include "taler_mintdb_lib.h"
#include "taler_mintdb_plugin.h"
#include "taler_wire_lib.h"
/**
* Which currency is used by this mint?
*/
static char *mint_currency_string;
/**
* Which wireformat should be supported by this aggregator?
*/
static char *mint_wireformat;
/**
* Base directory of the mint (global)
*/
static char *mint_directory;
/**
* The mint's configuration (global)
*/
static struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Our DB plugin.
*/
static struct TALER_MINTDB_Plugin *db_plugin;
/**
* Our wire plugin.
*/
static struct TALER_WIRE_Plugin *wire_plugin;
/**
* Task for the main #run() function.
*/
static struct GNUNET_SCHEDULER_Task *task;
/**
* Limit on the number of transactions we aggregate at once. Note
* that the limit must be big enough to ensure that when transactions
* of the smallest possible unit are aggregated, they do surpass the
* "tiny" threshold beyond which we never trigger a wire transaction!
*
* TODO: make configurable (via config file or command line option)
*/
static unsigned int aggregation_limit = 10000;
/**
* Load configuration parameters for the mint
* server into the corresponding global variables.
*
* @param mint_directory the mint's directory
* @return #GNUNET_OK on success
*/
static int
mint_serve_process_config (const char *mint_directory)
{
char *type;
cfg = TALER_config_load (mint_directory);
if (NULL == cfg)
{
fprintf (stderr,
"Failed to load mint configuration\n");
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"currency",
&mint_currency_string))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"currency");
return GNUNET_SYSERR;
}
if (strlen (mint_currency_string) >= TALER_CURRENCY_LEN)
{
fprintf (stderr,
"Currency `%s' longer than the allowed limit of %u characters.",
mint_currency_string,
(unsigned int) TALER_CURRENCY_LEN);
return GNUNET_SYSERR;
}
if (NULL != mint_wireformat)
GNUNET_CONFIGURATION_set_value_string (cfg,
"mint",
"wireformat",
mint_wireformat);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"wireformat",
&type))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"wireformat");
return GNUNET_SYSERR;
}
if (NULL ==
(db_plugin = TALER_MINTDB_plugin_load (cfg)))
{
fprintf (stderr,
"Failed to initialize DB subsystem\n");
GNUNET_free (type);
return GNUNET_SYSERR;
}
if (NULL ==
(wire_plugin = TALER_WIRE_plugin_load (cfg,
type)))
{
fprintf (stderr,
"Failed to load wire plugin for `%s'\n",
type);
GNUNET_free (type);
return GNUNET_SYSERR;
}
GNUNET_free (type);
return GNUNET_OK;
}
/**
* Information about one aggregation process to
* be executed.
*/
struct AggregationUnit
{
/**
* Public key of the merchant.
*/
struct TALER_MerchantPublicKeyP merchant_pub;
/**
* Total amount to be transferred.
*/
struct TALER_Amount total_amount;
/**
* Hash of @e wire.
*/
struct GNUNET_HashCode h_wire;
/**
* Wire transfer identifier we use.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* Row ID of the transaction that started it all.
*/
unsigned long long row_id;
/**
* The current time.
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* Wire details of the merchant.
*/
json_t *wire;
/**
* Database session for all of our transactions.
*/
struct TALER_MINTDB_Session *session;
/**
* Wire preparation handle.
*/
struct TALER_WIRE_PrepareHandle *ph;
/**
* Array of #aggregation_limit row_ids from the
* aggregation.
*/
unsigned long long *additional_rows;
/**
* Pointer to global return value. Closure for #run().
*/
int *global_ret;
/**
* Offset specifying how many #additional_rows are in use.
*/
unsigned int rows_offset;
/**
* Set to #GNUNET_YES if we have to abort due to failure.
*/
int failed;
};
/**
* Function called with details about deposits that have been made,
* with the goal of executing the corresponding wire transaction.
*
* @param cls closure with the `struct AggregationUnit`
* @param row_id identifies database entry
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the mint gets to keep as transaction fees
* @param transaction_id unique transaction ID chosen by the merchant
* @param h_contract hash of the contract between merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
deposit_cb (void *cls,
unsigned long long row_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
uint64_t transaction_id,
const struct GNUNET_HashCode *h_contract,
struct GNUNET_TIME_Absolute wire_deadline,
const json_t *wire)
{
struct AggregationUnit *au = cls;
au->merchant_pub = *merchant_pub;
if (GNUNET_OK !=
TALER_amount_subtract (&au->total_amount,
amount_with_fee,
deposit_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Fatally malformed record at %llu\n",
row_id);
return GNUNET_SYSERR;
}
au->row_id = row_id;
au->wire = (json_t *) wire;
au->execution_time = GNUNET_TIME_absolute_get ();
TALER_hash_json (au->wire,
&au->h_wire);
json_incref (au->wire);
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
&au->wtid,
sizeof (au->wtid));
if (GNUNET_OK !=
db_plugin->insert_aggregation_tracking (db_plugin->cls,
au->session,
&au->wtid,
merchant_pub,
&au->h_wire,
h_contract,
transaction_id,
au->execution_time,
coin_pub,
amount_with_fee,
deposit_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
db_plugin->mark_deposit_done (db_plugin->cls,
au->session,
row_id))
{
GNUNET_break (0);
au->failed = GNUNET_YES;
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Function called with details about another deposit we
* can aggregate into an existing aggregation unit.
*
* @param cls closure with the `struct AggregationUnit`
* @param row_id identifies database entry
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
* @param deposit_fee amount the mint gets to keep as transaction fees
* @param transaction_id unique transaction ID chosen by the merchant
* @param h_contract hash of the contract between merchant and customer
* @param wire_deadline by which the merchant adviced that he would like the
* wire transfer to be executed
* @param wire wire details for the merchant
* @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
*/
static int
aggregate_cb (void *cls,
unsigned long long row_id,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
const struct TALER_Amount *deposit_fee,
uint64_t transaction_id,
const struct GNUNET_HashCode *h_contract,
struct GNUNET_TIME_Absolute wire_deadline,
const json_t *wire)
{
struct AggregationUnit *au = cls;
struct TALER_Amount delta;
GNUNET_break (0 ==
memcmp (&au->merchant_pub,
merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP)));
/* compute contribution of this coin after fees */
if (GNUNET_OK !=
TALER_amount_subtract (&delta,
amount_with_fee,
deposit_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Fatally malformed record at %llu\n",
row_id);
return GNUNET_SYSERR;
}
/* add to total */
if (GNUNET_OK !=
TALER_amount_add (&au->total_amount,
&au->total_amount,
&delta))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Overflow or currency incompatibility during aggregation at %llu\n",
row_id);
/* Skip this one, but keep going! */
return GNUNET_OK;
}
if (au->rows_offset >= aggregation_limit)
{
/* Bug: we asked for at most #aggregation_limit results! */
GNUNET_break (0);
/* Skip this one, but keep going. */
return GNUNET_OK;
}
if (NULL == au->additional_rows)
au->additional_rows = GNUNET_new_array (aggregation_limit,
unsigned long long);
/* "append" to our list of rows */
au->additional_rows[au->rows_offset++] = row_id;
/* insert into aggregation tracking table */
if (GNUNET_OK !=
db_plugin->insert_aggregation_tracking (db_plugin->cls,
au->session,
&au->wtid,
merchant_pub,
&au->h_wire,
h_contract,
transaction_id,
au->execution_time,
coin_pub,
amount_with_fee,
deposit_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
db_plugin->mark_deposit_done (db_plugin->cls,
au->session,
row_id))
{
GNUNET_break (0);
au->failed = GNUNET_YES;
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Function to be called with the prepared transfer data.
*
* @param cls closure with the `struct AggregationUnit`
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size);
/**
* Main work function that queries the DB and aggregates transactions
* into larger wire transfers.
*
* @param cls pointer to an `int` which we will return from main()
* @param tc scheduler context
*/
static void
run_aggregation (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
int *global_ret = cls;
struct TALER_MINTDB_Session *session;
struct AggregationUnit *au;
unsigned int i;
int ret;
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
return;
if (NULL == (session = db_plugin->get_session (db_plugin->cls,
GNUNET_NO)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to obtain database session!\n");
*global_ret = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
db_plugin->start (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
*global_ret = GNUNET_SYSERR;
return;
}
au = GNUNET_new (struct AggregationUnit);
au->session = session;
ret = db_plugin->get_ready_deposit (db_plugin->cls,
session,
&deposit_cb,
au);
if (GNUNET_OK != ret)
{
GNUNET_free (au);
db_plugin->rollback (db_plugin->cls,
session);
if (0 != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to execute deposit iteration!\n");
*global_ret = GNUNET_SYSERR;
return;
}
/* nothing to do, sleep for a minute and try again */
task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
&run_aggregation,
global_ret);
return;
}
/* Now try to find other deposits to aggregate */
ret = db_plugin->iterate_matching_deposits (db_plugin->cls,
session,
&au->h_wire,
&au->merchant_pub,
&aggregate_cb,
au,
aggregation_limit);
if ( (GNUNET_SYSERR == ret) ||
(GNUNET_YES == au->failed) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to execute deposit iteration!\n");
GNUNET_free_non_null (au->additional_rows);
GNUNET_free (au);
db_plugin->rollback (db_plugin->cls,
session);
*global_ret = GNUNET_SYSERR;
return;
}
/* Round to the unit supported by the wire transfer method */
GNUNET_assert (GNUNET_SYSERR !=
wire_plugin->amount_round (wire_plugin->cls,
&au->total_amount));
/* Check if after rounding down, we still have an amount to transfer */
if ( (0 == au->total_amount.value) &&
(0 == au->total_amount.fraction) )
{
/* Rollback ongoing transaction, as we will not use the respective
WTID and thus need to remove the tracking data */
db_plugin->rollback (db_plugin->cls,
session);
/* Start another transaction to mark all* of the selected deposits
*as minor! */
if (GNUNET_OK !=
db_plugin->start (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
*global_ret = GNUNET_SYSERR;
GNUNET_free_non_null (au->additional_rows);
GNUNET_free (au);
return;
}
/* Mark transactions by row_id as minor */
ret = GNUNET_OK;
if (GNUNET_OK !=
db_plugin->mark_deposit_tiny (db_plugin->cls,
session,
au->row_id))
ret = GNUNET_SYSERR;
else
for (i=0;i<au->rows_offset;i++)
if (GNUNET_OK !=
db_plugin->mark_deposit_tiny (db_plugin->cls,
session,
au->additional_rows[i]))
ret = GNUNET_SYSERR;
/* commit */
if (GNUNET_OK !=
db_plugin->commit (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Failed to commit database transaction!\n");
}
GNUNET_free_non_null (au->additional_rows);
GNUNET_free (au);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
au->global_ret = global_ret;
au->ph = wire_plugin->prepare_wire_transfer (wire_plugin->cls,
au->wire,
&au->total_amount,
&au->wtid,
&prepare_cb,
au);
/* FIXME: currently we have no clean-up plan on
shutdown to call prepare_wire_transfer_cancel!
Maybe make 'au' global? */
if (NULL == au->ph)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
GNUNET_free_non_null (au->additional_rows);
GNUNET_free (au);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
/* otherwise we continue with #prepare_cb(), see below */
}
/**
* Execute the wire transfers that we have committed to
* do.
*
* @param cls pointer to an `int` which we will return from main()
* @param tc scheduler context
*/
static void
run_transfers (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc);
/**
* Function to be called with the prepared transfer data.
*
* @param cls closure with the `struct AggregationUnit`
* @param buf transaction data to persist, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
prepare_cb (void *cls,
const char *buf,
size_t buf_size)
{
struct AggregationUnit *au = cls;
int *global_ret = au->global_ret;
struct TALER_MINTDB_Session *session = au->session;
GNUNET_free_non_null (au->additional_rows);
GNUNET_free (au);
if (NULL == buf)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
/* Commit our intention to execute the wire transfer! */
if (GNUNET_OK !=
db_plugin->wire_prepare_data_insert (db_plugin->cls,
session,
mint_wireformat,
buf,
buf_size))
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
/* start again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
/* Now we can finally commit the overall transaction, as we are
again consistent if all of this passes. */
if (GNUNET_OK !=
db_plugin->commit (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Failed to commit database transaction!\n");
/* try again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
/* run alternative task: actually do wire transfer! */
task = GNUNET_SCHEDULER_add_now (&run_transfers,
&global_ret);
}
/**
* Data we keep to #run_transfers().
*/
struct WirePrepareData
{
/**
* Database session for all of our transactions.
*/
struct TALER_MINTDB_Session *session;
/**
* Wire execution handle.
*/
struct TALER_WIRE_ExecuteHandle *eh;
/**
* Pointer to global return value. Closure for #run().
*/
int *global_ret;
/**
* Row ID of the transfer.
*/
unsigned long long row_id;
};
/**
* Function called with the result from the execute step.
*
* @param cls closure with the `struct WirePrepareData`
* @param success #GNUNET_OK on success, #GNUNET_SYSERR on failure
* @param emsg NULL on success, otherwise an error message
*/
static void
wire_confirm_cb (void *cls,
int success,
const char *emsg)
{
struct WirePrepareData *wpd = cls;
int *global_ret = wpd->global_ret;
struct TALER_MINTDB_Session *session = wpd->session;
wpd->eh = NULL;
if (GNUNET_SYSERR == success)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire transaction failed: %s\n",
emsg);
db_plugin->rollback (db_plugin->cls,
session);
*global_ret = GNUNET_SYSERR;
GNUNET_free (wpd);
return;
}
if (GNUNET_OK !=
db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
session,
wpd->row_id))
{
GNUNET_break (0); /* why!? */
db_plugin->rollback (db_plugin->cls,
session);
*global_ret = GNUNET_SYSERR;
GNUNET_free (wpd);
return;
}
GNUNET_free (wpd);
if (GNUNET_OK !=
db_plugin->commit (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Failed to commit database transaction!\n");
/* try again */
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
return;
}
/* continue with #run_transfers(), just to guard
against the unlikely case that there are more. */
task = GNUNET_SCHEDULER_add_now (&run_transfers,
&global_ret);
}
/**
* Callback with data about a prepared transaction.
*
* @param cls closure with the `struct WirePrepareData`
* @param rowid row identifier used to mark prepared transaction as done
* @param buf transaction data that was persisted, NULL on error
* @param buf_size number of bytes in @a buf, 0 on error
*/
static void
wire_prepare_cb (void *cls,
unsigned long long rowid,
const char *buf,
size_t buf_size)
{
struct WirePrepareData *wpd = cls;
int *global_ret = wpd->global_ret;
wpd->row_id = rowid;
wpd->eh = wire_plugin->execute_wire_transfer (wire_plugin->cls,
buf,
buf_size,
&wire_confirm_cb,
wpd);
/* FIXME: currently we have no clean-up plan on
shutdown to call execute_wire_transfer_cancel!
Maybe make 'wpd' global? */
if (NULL == wpd->eh)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
wpd->session);
*global_ret = GNUNET_SYSERR;
GNUNET_free (wpd);
return;
}
}
/**
* Execute the wire transfers that we have committed to
* do.
*
* @param cls pointer to an `int` which we will return from main()
* @param tc scheduler context
*/
static void
run_transfers (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
int *global_ret = cls;
int ret;
struct WirePrepareData *wpd;
struct TALER_MINTDB_Session *session;
if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
return;
if (NULL == (session = db_plugin->get_session (db_plugin->cls,
GNUNET_NO)))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to obtain database session!\n");
*global_ret = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
db_plugin->start (db_plugin->cls,
session))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to start database transaction!\n");
*global_ret = GNUNET_SYSERR;
return;
}
wpd = GNUNET_new (struct WirePrepareData);
wpd->session = session;
wpd->global_ret = global_ret;
ret = db_plugin->wire_prepare_data_get (db_plugin->cls,
session,
mint_wireformat,
&wire_prepare_cb,
wpd);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0); /* why? how to best recover? */
db_plugin->rollback (db_plugin->cls,
session);
*global_ret = GNUNET_SYSERR;
GNUNET_free (wpd);
return;
}
if (GNUNET_NO == ret)
{
/* no more prepared wire transfers, go back to aggregation! */
db_plugin->rollback (db_plugin->cls,
session);
task = GNUNET_SCHEDULER_add_now (&run_aggregation,
global_ret);
GNUNET_free (wpd);
return;
}
/* otherwise, continues in #wire_prepare_cb() */
}
/**
* The main function of the taler-mint-httpd server ("the mint").
*
* @param argc number of arguments from the command line
* @param argv command line arguments
* @return 0 ok, 1 on error
*/
int
main (int argc,
char *const *argv)
{
static const struct GNUNET_GETOPT_CommandLineOption options[] = {
{'d', "mint-dir", "DIR",
"mint directory with configuration and keys for operating the mint", 1,
&GNUNET_GETOPT_set_filename, &mint_directory},
{'f', "format", "WIREFORMAT",
"wireformat to use, overrides WIREFORMAT option in [mint] section", 1,
&GNUNET_GETOPT_set_filename, &mint_wireformat},
TALER_GETOPT_OPTION_HELP ("background process that aggregates and executes wire transfers to merchants"),
GNUNET_GETOPT_OPTION_VERSION (VERSION "-" VCS_VERSION),
GNUNET_GETOPT_OPTION_END
};
int ret = GNUNET_OK;
GNUNET_assert (GNUNET_OK ==
GNUNET_log_setup ("taler-mint-aggregator",
"INFO",
NULL));
if (0 >=
GNUNET_GETOPT_run ("taler-mint-aggregator",
options,
argc, argv))
return 1;
if (NULL == mint_directory)
{
fprintf (stderr,
"Mint directory not specified\n");
return 1;
}
if (GNUNET_OK !=
mint_serve_process_config (mint_directory))
{
return 1;
}
GNUNET_SCHEDULER_run (&run_transfers, &ret);
TALER_MINTDB_plugin_unload (db_plugin);
TALER_WIRE_plugin_unload (wire_plugin);
return (GNUNET_SYSERR == ret) ? 1 : 0;
}
/* end of taler-mint-aggregator.c */

View File

@ -39,6 +39,7 @@
#include "taler-mint-httpd_test.h"
#endif
#include "taler_mintdb_plugin.h"
#include "taler-mint-httpd_validation.h"
/**
* Which currency is used by this mint?
@ -66,13 +67,6 @@ struct GNUNET_CONFIGURATION_Handle *cfg;
*/
struct GNUNET_CRYPTO_EddsaPublicKey TMH_master_public_key;
/**
* In which format does this MINT expect wiring instructions?
* NULL-terminated array of 0-terminated wire format types,
* suitable for passing to #TALER_json_validate_wireformat().
*/
const char **TMH_expected_wire_formats;
/**
* Our DB plugin.
*/
@ -384,9 +378,6 @@ mint_serve_process_config (const char *mint_directory)
{
unsigned long long port;
char *TMH_master_public_key_str;
char *wireformats;
const char *token;
unsigned int len;
cfg = TALER_config_load (mint_directory);
if (NULL == cfg)
@ -414,35 +405,9 @@ mint_serve_process_config (const char *mint_directory)
(unsigned int) TALER_CURRENCY_LEN);
return GNUNET_SYSERR;
}
/* Find out list of supported wire formats */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"wireformat",
&wireformats))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"wireformat");
TMH_VALIDATION_init (cfg))
return GNUNET_SYSERR;
}
/* build NULL-terminated array of TMH_expected_wire_formats */
TMH_expected_wire_formats = GNUNET_new_array (1,
const char *);
len = 1;
for (token = strtok (wireformats,
" ");
NULL != token;
token = strtok (NULL,
" "))
{
/* Grow by 1, appending NULL-terminator */
GNUNET_array_append (TMH_expected_wire_formats,
len,
NULL);
TMH_expected_wire_formats[len - 2] = GNUNET_strdup (token);
}
GNUNET_free (wireformats);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
@ -453,6 +418,7 @@ mint_serve_process_config (const char *mint_directory)
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"master_public_key");
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
@ -463,6 +429,7 @@ mint_serve_process_config (const char *mint_directory)
fprintf (stderr,
"Invalid master public key given in mint configuration.");
GNUNET_free (TMH_master_public_key_str);
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
GNUNET_free (TMH_master_public_key_str);
@ -472,6 +439,7 @@ mint_serve_process_config (const char *mint_directory)
{
fprintf (stderr,
"Failed to initialize DB subsystem\n");
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
if (GNUNET_YES ==
@ -496,6 +464,7 @@ mint_serve_process_config (const char *mint_directory)
"mint",
"port",
"port number required");
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
@ -505,6 +474,7 @@ mint_serve_process_config (const char *mint_directory)
fprintf (stderr,
"Invalid configuration (value out of range): %llu is not a valid port\n",
port);
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
serve_port = (uint16_t) port;
@ -772,6 +742,7 @@ main (int argc,
session);
}
TALER_MINTDB_plugin_unload (TMH_plugin);
TMH_VALIDATION_done ();
return (GNUNET_SYSERR == ret) ? 1 : 0;
}

View File

@ -27,6 +27,7 @@
#include <microhttpd.h>
/**
* Which currency is used by this mint?
*/
@ -52,13 +53,6 @@ extern int TMH_test_mode;
*/
extern char *TMH_mint_directory;
/**
* In which formats does this MINT expect wiring instructions?
* NULL-terminated array of 0-terminated wire format types,
* suitable for passing to #TALER_json_validate_wireformat().
*/
extern const char **TMH_expected_wire_formats;
/**
* Master public key (according to the
* configuration in the mint directory).

View File

@ -24,6 +24,7 @@
#include "taler-mint-httpd_admin.h"
#include "taler-mint-httpd_parsing.h"
#include "taler-mint-httpd_responses.h"
#include "taler-mint-httpd_validation.h"
/**
@ -144,8 +145,7 @@ TMH_ADMIN_handler_admin_add_incoming (struct TMH_RequestHandler *rh,
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
if (GNUNET_YES !=
TALER_json_validate_wireformat (TMH_expected_wire_formats,
wire))
TMH_json_validate_wireformat (wire))
{
GNUNET_break_op (0);
TMH_PARSE_release_data (spec);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015, 2016 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
@ -1551,10 +1551,215 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
}
/**
* Closure for #handle_transaction_data.
*/
struct WtidTransactionContext
{
/**
* Total amount of the wire transfer, as calculated by
* summing up the individual amounts. To be rounded down
* to calculate the real transfer amount at the end.
* Only valid if @e is_valid is #GNUNET_YES.
*/
struct TALER_Amount total;
/**
* Value we find in the DB for the @e total; only valid if @e is_valid
* is #GNUNET_YES.
*/
struct TALER_Amount db_transaction_value;
/**
* Public key of the merchant, only valid if @e is_valid
* is #GNUNET_YES.
*/
struct TALER_MerchantPublicKeyP merchant_pub;
/**
* Hash of the wire details of the merchant (identical for all
* deposits), only valid if @e is_valid is #GNUNET_YES.
*/
struct GNUNET_HashCode h_wire;
/**
* JSON array with details about the individual deposits.
*/
json_t *deposits;
/**
* Initially #GNUNET_NO, if we found no deposits so far. Set to
* #GNUNET_YES if we got transaction data, and the database replies
* remained consistent with respect to @e merchant_pub and @e h_wire
* (as they should). Set to #GNUNET_SYSERR if we encountered an
* internal error.
*/
int is_valid;
};
/**
* Function called with the results of the lookup of the
* transaction data for the given wire transfer identifier.
*
* @param cls our context for transmission
* @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
* @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
* @param h_contract which contract was this payment about
* @param transaction_id merchant's transaction ID for the payment
* @param coin_pub which public key was this payment about
* @param deposit_value amount contributed by this coin in total
* @param deposit_fee deposit fee charged by mint for this coin
* @param transaction_value total value of the wire transaction
*/
static void
handle_transaction_data (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *deposit_value,
const struct TALER_Amount *deposit_fee,
const struct TALER_Amount *transaction_value)
{
struct WtidTransactionContext *ctx = cls;
struct TALER_Amount delta;
if (GNUNET_SYSERR == ctx->is_valid)
return;
if (GNUNET_NO == ctx->is_valid)
{
ctx->merchant_pub = *merchant_pub;
ctx->h_wire = *h_wire;
ctx->db_transaction_value = *transaction_value;
ctx->is_valid = GNUNET_YES;
if (GNUNET_OK !=
TALER_amount_subtract (&ctx->total,
deposit_value,
deposit_fee))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
return;
}
}
else
{
if ( (0 != memcmp (&ctx->merchant_pub,
merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP))) ||
(0 != memcmp (&ctx->h_wire,
h_wire,
sizeof (struct GNUNET_HashCode))) ||
(0 != TALER_amount_cmp (transaction_value,
&ctx->db_transaction_value)) )
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
TALER_amount_subtract (&delta,
deposit_value,
deposit_fee))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
return;
}
if (GNUNET_OK !=
TALER_amount_add (&ctx->total,
&ctx->total,
&delta))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
return;
}
}
/* NOTE: We usually keep JSON stuff out of the _DB file, and this
is also ugly if we ever add signatures over this data. (#4135) */
json_array_append (ctx->deposits,
json_pack ("{s:o, s:o, s:o, s:I, s:o}",
"deposit_value", TALER_json_from_amount (deposit_value),
"deposit_fee", TALER_json_from_amount (deposit_fee),
"H_contract", TALER_json_from_data (h_contract,
sizeof (struct GNUNET_HashCode)),
"transaction_id", (json_int_t) transaction_id,
"coin_pub", TALER_json_from_data (coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP))));
}
/**
* Execute a "/wire/deposits". Returns the transaction information
* associated with the given wire transfer identifier.
*
* @param connection the MHD connection to handle
* @param wtid wire transfer identifier to resolve
* @return MHD result code
*/
int
TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
const struct TALER_WireTransferIdentifierRawP *wtid)
{
int ret;
struct WtidTransactionContext ctx;
struct TALER_MINTDB_Session *session;
if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
TMH_test_mode)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
ctx.is_valid = GNUNET_NO;
ctx.deposits = json_array ();
ret = TMH_plugin->lookup_wire_transfer (TMH_plugin->cls,
session,
wtid,
&handle_transaction_data,
&ctx);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
json_decref (ctx.deposits);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_SYSERR == ctx.is_valid)
{
GNUNET_break (0);
json_decref (ctx.deposits);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_NO == ctx.is_valid)
{
json_decref (ctx.deposits);
return TMH_RESPONSE_reply_arg_unknown (connection,
"wtid");
}
if (0 != TALER_amount_cmp (&ctx.total,
&ctx.db_transaction_value))
{
/* FIXME: this CAN actually differ, due to rounding
down. But we should still check that the values
do match after rounding 'total' down! */
}
return TMH_RESPONSE_reply_wire_deposit_details (connection,
&ctx.db_transaction_value,
&ctx.merchant_pub,
&ctx.h_wire,
ctx.deposits);
}
/**
* Closure for #handle_wtid_data.
*/
struct DepositWtidContext
struct DepositWtidContext
{
/**
@ -1562,6 +1767,26 @@ struct DepositWtidContext
*/
struct MHD_Connection *connection;
/**
* Hash of the contract we are looking up.
*/
struct GNUNET_HashCode h_contract;
/**
* Hash of the wire transfer details we are looking up.
*/
struct GNUNET_HashCode h_wire;
/**
* Public key we are looking up.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
* Transaction ID we are looking up.
*/
uint64_t transaction_id;
/**
* MHD result code to return.
*/
@ -1572,10 +1797,13 @@ struct DepositWtidContext
/**
* Function called with the results of the lookup of the
* wire transfer identifier information.
*
*
* @param cls our context for transmission
* @param wtid base32-encoded wire transfer identifier, NULL
* @param wtid raw wire transfer identifier, NULL
* if the transaction was not yet done
* @param coin_contribution how much did the coin we asked about
* contribute to the total transfer value? (deposit value including fee)
* @param coin_fee how much did the mint charge for the deposit fee
* @param execution_time when was the transaction done, or
* when we expect it to be done (if @a wtid was NULL);
* #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
@ -1583,23 +1811,41 @@ struct DepositWtidContext
*/
static void
handle_wtid_data (void *cls,
const char *wtid,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
struct GNUNET_TIME_Absolute execution_time)
{
struct DepositWtidContext *ctx = cls;
struct TALER_Amount coin_delta;
if (NULL == wtid)
{
if (GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us ==
execution_time.abs_value_us)
ctx->res = TMH_RESPONSE_reply_deposit_unknown (ctx->connection);
else
ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection);
ctx->res = TMH_RESPONSE_reply_deposit_pending (ctx->connection,
execution_time);
}
else
{
ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection);
}
if (GNUNET_SYSERR ==
TALER_amount_subtract (&coin_delta,
coin_contribution,
coin_fee))
{
GNUNET_break (0);
ctx->res = TMH_RESPONSE_reply_internal_db_error (ctx->connection);
}
else
{
ctx->res = TMH_RESPONSE_reply_deposit_wtid (ctx->connection,
&ctx->h_contract,
&ctx->h_wire,
&ctx->coin_pub,
&coin_delta,
ctx->transaction_id,
wtid,
execution_time);
}
}
}
@ -1625,21 +1871,46 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
{
int ret;
struct DepositWtidContext ctx;
struct TALER_MINTDB_Session *session;
if (NULL == (session = TMH_plugin->get_session (TMH_plugin->cls,
TMH_test_mode)))
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
ctx.connection = connection;
ctx.h_contract = *h_contract;
ctx.h_wire = *h_wire;
ctx.coin_pub = *coin_pub;
ctx.transaction_id = transaction_id;
ctx.res = GNUNET_SYSERR;
ret = TMH_plugin->wire_lookup_deposit_wtid (TMH_plugin->cls,
session,
h_contract,
h_wire,
coin_pub,
merchant_pub,
transaction_id,
&handle_wtid_data,
connection);
&ctx);
if (GNUNET_SYSERR == ret)
{
GNUNET_break (0);
GNUNET_break (GNUNET_SYSERR == ctx.res);
return TMH_RESPONSE_reply_internal_db_error (connection);
}
if (GNUNET_NO == ret)
{
GNUNET_break (GNUNET_SYSERR == ctx.res);
return TMH_RESPONSE_reply_deposit_unknown (connection);
}
if (GNUNET_SYSERR == ctx.res)
{
GNUNET_break (0);
return TMH_RESPONSE_reply_internal_error (connection,
"bug resolving deposit wtid");
}
return ctx.res;
}

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -192,6 +192,19 @@ TMH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
json_t *wire);
/**
* Execute a "/wire/deposits". Returns the transaction information
* associated with the given wire transfer identifier.
*
* @param connection the MHD connection to handle
* @param wtid wire transfer identifier to resolve
* @return MHD result code
*/
int
TMH_DB_execute_wire_deposits (struct MHD_Connection *connection,
const struct TALER_WireTransferIdentifierRawP *wtid);
/**
* Execute a "/deposit/wtid". Returns the transfer information
* associated with the given deposit.
@ -212,5 +225,6 @@ TMH_DB_execute_deposit_wtid (struct MHD_Connection *connection,
const struct TALER_MerchantPublicKeyP *merchant_pub,
uint64_t transaction_id);
#endif
/* TALER_MINT_HTTPD_DB_H */

View File

@ -34,6 +34,7 @@
#include "taler-mint-httpd_deposit.h"
#include "taler-mint-httpd_responses.h"
#include "taler-mint-httpd_keystate.h"
#include "taler-mint-httpd_validation.h"
/**
@ -162,8 +163,7 @@ parse_and_handle_deposit_request (struct MHD_Connection *connection,
return MHD_YES; /* failure */
if (GNUNET_YES !=
TALER_json_validate_wireformat (TMH_expected_wire_formats,
wire))
TMH_json_validate_wireformat (wire))
{
TMH_PARSE_release_data (spec);
return TMH_RESPONSE_reply_arg_unknown (connection,

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -1056,15 +1056,15 @@ TMH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
* 404 reply.
*
* @param connection connection to the client
* @param
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
...)
TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection)
{
GNUNET_break (0); // FIXME: not implemented
return MHD_NO;
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_NOT_FOUND,
"{s:s}",
"error", "Deposit unknown");
}
@ -1073,15 +1073,17 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
* we did not execute the deposit yet. Generate a 202 reply.
*
* @param connection connection to the client
* @param
* @param planned_exec_time planned execution time
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
...)
struct GNUNET_TIME_Absolute planned_exec_time)
{
GNUNET_break (0); // FIXME: not implemented
return MHD_NO;
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_ACCEPTED,
"{s:o}",
"execution_time", TALER_json_from_abs (planned_exec_time));
}
@ -1090,15 +1092,86 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
* them. Generates the 200 reply.
*
* @param connection connection to the client
* @param
* @param h_contract hash of the contract
* @param h_wire hash of wire account details
* @param coin_pub public key of the coin
* @param coin_contribution how much did the coin we asked about
* contribute to the total transfer value? (deposit value minus fee)
* @param transaction_id merchant transaction identifier
* @param wtid raw wire transfer identifier
* @param exec_time execution time of the wire transfer
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
...)
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_contribution,
uint64_t transaction_id,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute exec_time)
{
GNUNET_break (0); // FIXME: not implemented
return MHD_NO;
struct TALER_ConfirmWirePS cw;
struct TALER_MintPublicKeyP pub;
struct TALER_MintSignatureP sig;
cw.purpose.purpose = htonl (TALER_SIGNATURE_MINT_CONFIRM_WIRE);
cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
cw.h_wire = *h_wire;
cw.h_contract = *h_contract;
cw.wtid = *wtid;
cw.coin_pub = *coin_pub;
cw.transaction_id = GNUNET_htonll (transaction_id);
cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
TALER_amount_hton (&cw.coin_contribution,
coin_contribution);
TMH_KS_sign (&cw.purpose,
&pub,
&sig);
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o, s:o, s:o, s:o}",
"wtid", TALER_json_from_data (wtid,
sizeof (*wtid)),
"execution_time", TALER_json_from_abs (exec_time),
"coin_contribution", TALER_json_from_amount (coin_contribution),
"mint_sig", TALER_json_from_data (&sig,
sizeof (sig)),
"mint_pub", TALER_json_from_data (&pub,
sizeof (pub)));
}
/**
* A merchant asked for transaction details about a wire transfer.
* Provide them. Generates the 200 reply.
*
* @param connection connection to the client
* @param total total amount that was transferred
* @param merchant_pub public key of the merchant
* @param h_wire destination account
* @param deposits details about the combined deposits
* @return MHD result code
*/
int
TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection,
const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
json_t *deposits)
{
/* FIXME: #4135: signing not implemented here */
return TMH_RESPONSE_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o, s:o}",
"total", TALER_json_from_amount (total),
"merchant_pub", TALER_json_from_data (merchant_pub,
sizeof (struct TALER_MerchantPublicKeyP)),
"h_wire", TALER_json_from_data (h_wire,
sizeof (struct GNUNET_HashCode)),
"deposits", deposits);
}
/* end of taler-mint-httpd_responses.c */

View File

@ -253,12 +253,10 @@ TMH_RESPONSE_reply_deposit_insufficient_funds (struct MHD_Connection *connection
* 404 reply.
*
* @param connection connection to the client
* @param
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
...);
TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection);
/**
@ -266,12 +264,12 @@ TMH_RESPONSE_reply_deposit_unknown (struct MHD_Connection *connection,
* we did not execute the deposit yet. Generate a 202 reply.
*
* @param connection connection to the client
* @param
* @param planned_exec_time planned execution time
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
...);
struct GNUNET_TIME_Absolute planned_exec_time);
/**
@ -279,12 +277,43 @@ TMH_RESPONSE_reply_deposit_pending (struct MHD_Connection *connection,
* them. Generates the 200 reply.
*
* @param connection connection to the client
* @param
* @param h_contract hash of the contract
* @param h_wire hash of wire account details
* @param coin_pub public key of the coin
* @param coin_contribution contribution of this coin to the total amount transferred
* @param transaction_id merchant transaction identifier
* @param wtid raw wire transfer identifier
* @param exec_time execution time of the wire transfer
* @return MHD result code
*/
int
TMH_RESPONSE_reply_deposit_wtid (struct MHD_Connection *connection,
...);
const struct GNUNET_HashCode *h_contract,
const struct GNUNET_HashCode *h_wire,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_contribution,
uint64_t transaction_id,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute exec_time);
/**
* A merchant asked for transaction details about a wire transfer.
* Provide them. Generates the 200 reply.
*
* @param connection connection to the client
* @param total total amount that was transferred
* @param merchant_pub public key of the merchant
* @param h_wire destination account
* @param deposits details about the combined deposits
* @return MHD result code
*/
int
TMH_RESPONSE_reply_wire_deposit_details (struct MHD_Connection *connection,
const struct TALER_Amount *total,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
json_t *deposits);
/**

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 GNUnet e.V.
Copyright (C) 2014, 2015, 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -46,8 +46,19 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
GNUNET_break (0); // not implemented
return MHD_NO;
struct TALER_WireTransferIdentifierRawP wtid;
int res;
res = TMH_PARSE_mhd_request_arg_data (connection,
"wtid",
&wtid,
sizeof (struct TALER_WireTransferIdentifierRawP));
if (GNUNET_SYSERR == res)
return MHD_NO; /* internal error */
if (GNUNET_NO == res)
return MHD_YES; /* parse error */
return TMH_DB_execute_wire_deposits (connection,
&wtid);
}
@ -57,7 +68,7 @@ TMH_TRACKING_handler_wire_deposits (struct TMH_RequestHandler *rh,
*
* @param connection the MHD connection to handle
* @param tps signed request to execute
* @param merchant_pub public key from the merchant
* @param merchant_pub public key from the merchant
* @param merchant_sig signature from the merchant (to be checked)
* @param transaction_id transaction ID (in host byte order)
* @return MHD result code
@ -110,13 +121,12 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
struct TALER_DepositTrackPS tps;
uint64_t transaction_id;
struct TALER_MerchantSignatureP merchant_sig;
struct TALER_MerchantPublicKeyP merchant_pub;
struct TMH_PARSE_FieldSpecification spec[] = {
TMH_PARSE_member_fixed ("H_wire", &tps.h_wire),
TMH_PARSE_member_fixed ("H_contract", &tps.h_contract),
TMH_PARSE_member_fixed ("coin_pub", &tps.coin_pub),
TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
TMH_PARSE_member_fixed ("merchant_pub", &merchant_pub),
TMH_PARSE_member_fixed ("merchant_pub", &tps.merchant),
TMH_PARSE_member_fixed ("merchant_sig", &merchant_sig),
TMH_PARSE_MEMBER_END
};
@ -143,7 +153,7 @@ TMH_TRACKING_handler_deposit_wtid (struct TMH_RequestHandler *rh,
tps.transaction_id = GNUNET_htonll (transaction_id);
res = check_and_handle_deposit_wtid_request (connection,
&tps,
&merchant_pub,
&tps.merchant,
&merchant_sig,
transaction_id);
TMH_PARSE_release_data (spec);

View File

@ -0,0 +1,231 @@
/*
This file is part of TALER
Copyright (C) 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-mint-httpd_validation.c
* @brief helpers for calling the wire plugins to validate addresses
* @author Christian Grothoff
*/
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include "taler-mint-httpd_validation.h"
#include "taler_wire_plugin.h"
/**
* Information we keep for each plugin.
*/
struct Plugin
{
/**
* We keep plugins in a DLL.
*/
struct Plugin *next;
/**
* We keep plugins in a DLL.
*/
struct Plugin *prev;
/**
* Type of the wireformat.
*/
char *type;
/**
* Pointer to the plugin.
*/
struct TALER_WIRE_Plugin *plugin;
};
/**
* Head of DLL of wire plugins.
*/
static struct Plugin *wire_head;
/**
* Tail of DLL of wire plugins.
*/
static struct Plugin *wire_tail;
/**
* Initialize validation subsystem.
*
* @param cfg configuration to use
* @return #GNUNET_OK on success
*/
int
TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
{
struct Plugin *p;
char *wireformats;
char *lib_name;
const char *token;
/* Find out list of supported wire formats */
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"wireformat",
&wireformats))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"wireformat");
return GNUNET_SYSERR;
}
for (token = strtok (wireformats,
" ");
NULL != token;
token = strtok (NULL,
" "))
{
(void) GNUNET_asprintf (&lib_name,
"libtaler_plugin_wire_%s",
lib_name);
p = GNUNET_new (struct Plugin);
p->type = GNUNET_strdup (token);
p->plugin = GNUNET_PLUGIN_load (lib_name,
(void *) cfg);
if (NULL == p->plugin)
{
GNUNET_free (p);
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load plugin %s\n",
lib_name);
GNUNET_free (lib_name);
TMH_VALIDATION_done ();
return GNUNET_SYSERR;
}
p->plugin->library_name = lib_name;
GNUNET_CONTAINER_DLL_insert (wire_head,
wire_tail,
p);
}
GNUNET_free (wireformats);
return GNUNET_OK;
}
/**
* Shutdown validation subsystem.
*/
void
TMH_VALIDATION_done ()
{
struct Plugin *p;
char *lib_name;
while (NULL != (p = wire_head))
{
GNUNET_CONTAINER_DLL_remove (wire_head,
wire_tail,
p);
lib_name = p->plugin->library_name;
GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
p->plugin));
GNUNET_free (lib_name);
GNUNET_free (p->type);
GNUNET_free (p);
}
}
/**
* Check if the given wire format JSON object is correctly formatted as
* a wire address.
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
int
TMH_json_validate_wireformat (const json_t *wire)
{
const char *stype;
json_error_t error;
struct Plugin *p;
if (0 != json_unpack_ex ((json_t *) wire,
&error, 0,
"{s:s}",
"type", &stype))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
for (p=wire_head; NULL != p; p = p->next)
if (0 == strcasecmp (p->type,
stype))
return p->plugin->wire_validate (wire);
return GNUNET_NO;
}
/**
* Check if we support the given wire method.
*
* @param type type of wire method to check
* @return #GNUNET_YES if the method is supported
*/
int
TMH_VALIDATION_test_method (const char *type)
{
struct Plugin *p;
for (p=wire_head;NULL != p;p = p->next)
if (0 == strcasecmp (type,
p->type))
return GNUNET_YES;
return GNUNET_NO;
}
/**
* Obtain supported validation methods as a JSON array,
* and as a hash.
*
* @param[out] h set to the hash of the JSON methods
* @return JSON array with the supported validation methods
*/
json_t *
TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h)
{
json_t *methods;
struct GNUNET_HashContext *hc;
const char *wf;
struct Plugin *p;
methods = json_array ();
hc = GNUNET_CRYPTO_hash_context_start ();
for (p=wire_head;NULL != p;p = p->next)
{
wf = p->type;
json_array_append_new (methods,
json_string (wf));
GNUNET_CRYPTO_hash_context_read (hc,
wf,
strlen (wf) + 1);
}
GNUNET_CRYPTO_hash_context_finish (hc,
h);
return methods;
}
/* end of taler-mint-httpd_validation.c */

View File

@ -0,0 +1,76 @@
/*
This file is part of TALER
Copyright (C) 2016 GNUnet e.V.
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file taler-mint-httpd_validation.h
* @brief helpers for calling the wire plugins to validate addresses
* @author Christian Grothoff
*/
#ifndef TALER_MINT_HTTPD_VALIDATION_H
#define TALER_MINT_HTTPD_VALIDATION_H
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
/**
* Initialize validation subsystem.
*
* @param cfg configuration to use
* @return #GNUNET_OK on success
*/
int
TMH_VALIDATION_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
/**
* Shutdown validation subsystem.
*/
void
TMH_VALIDATION_done (void);
/**
* Check if the given wire format JSON object is correctly formatted as
* a wire address.
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
int
TMH_json_validate_wireformat (const json_t *wire);
/**
* Check if we support the given wire method.
*
* @param type type of wire method to check
* @return #GNUNET_YES if the method is supported
*/
int
TMH_VALIDATION_test_method (const char *type);
/**
* Obtain supported validation methods as a JSON array,
* and as a hash.
*
* @param[out] h set to the hash of the JSON methods
* @return JSON array with the supported validation methods
*/
json_t *
TMH_VALIDATION_get_methods (struct GNUNET_HashCode *h);
#endif

View File

@ -21,6 +21,7 @@
#include "platform.h"
#include "taler-mint-httpd_keystate.h"
#include "taler-mint-httpd_responses.h"
#include "taler-mint-httpd_validation.h"
#include "taler-mint-httpd_wire.h"
#include <jansson.h>
@ -45,24 +46,10 @@ TMH_WIRE_handler_wire (struct TMH_RequestHandler *rh,
struct TALER_MintPublicKeyP pub;
struct TALER_MintSignatureP sig;
json_t *methods;
struct GNUNET_HashContext *hc;
unsigned int i;
const char *wf;
methods = json_array ();
hc = GNUNET_CRYPTO_hash_context_start ();
for (i=0;NULL != (wf = TMH_expected_wire_formats[i]); i++)
{
json_array_append_new (methods,
json_string (wf));
GNUNET_CRYPTO_hash_context_read (hc,
wf,
strlen (wf) + 1);
}
wsm.purpose.size = htonl (sizeof (wsm));
wsm.purpose.purpose = htonl (TALER_SIGNATURE_MINT_WIRE_TYPES);
GNUNET_CRYPTO_hash_context_finish (hc,
&wsm.h_wire_types);
methods = TMH_VALIDATION_get_methods (&wsm.h_wire_types);
TMH_KS_sign (&wsm.purpose,
&pub,
&sig);
@ -97,7 +84,6 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
struct MHD_Response *response;
int ret;
char *wire_test_redirect;
unsigned int i;
response = MHD_create_response_from_buffer (0, NULL,
MHD_RESPMEM_PERSISTENT);
@ -107,11 +93,7 @@ TMH_WIRE_handler_wire_test (struct TMH_RequestHandler *rh,
return MHD_NO;
}
TMH_RESPONSE_add_global_headers (response);
for (i=0;NULL != TMH_expected_wire_formats[i];i++)
if (0 == strcasecmp ("test",
TMH_expected_wire_formats[i]))
break;
if (NULL == TMH_expected_wire_formats[i])
if (GNUNET_NO == TMH_VALIDATION_test_method ("test"))
{
/* Return 501: not implemented */
ret = MHD_queue_response (connection,
@ -165,13 +147,8 @@ TMH_WIRE_handler_wire_sepa (struct TMH_RequestHandler *rh,
char *sepa_wire_file;
int fd;
struct stat sbuf;
unsigned int i;
for (i=0;NULL != TMH_expected_wire_formats[i];i++)
if (0 == strcasecmp ("sepa",
TMH_expected_wire_formats[i]))
break;
if (NULL == TMH_expected_wire_formats[i])
if (GNUNET_NO == TMH_VALIDATION_test_method ("sepa"))
{
/* Return 501: not implemented */
response = MHD_create_response_from_buffer (0, NULL,

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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
@ -83,67 +83,5 @@ TALER_MINTDB_plugin_unload (struct TALER_MINTDB_Plugin *plugin)
}
/**
* Libtool search path before we started.
*/
static char *old_dlsearchpath;
/**
* Setup libtool paths.
*/
void __attribute__ ((constructor))
plugin_init ()
{
int err;
const char *opath;
char *path;
char *cpath;
err = lt_dlinit ();
if (err > 0)
{
FPRINTF (stderr,
_("Initialization of plugin mechanism failed: %s!\n"),
lt_dlerror ());
return;
}
opath = lt_dlgetsearchpath ();
if (NULL != opath)
old_dlsearchpath = GNUNET_strdup (opath);
path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
if (NULL != path)
{
if (NULL != opath)
{
GNUNET_asprintf (&cpath, "%s:%s", opath, path);
lt_dlsetsearchpath (cpath);
GNUNET_free (path);
GNUNET_free (cpath);
}
else
{
lt_dlsetsearchpath (path);
GNUNET_free (path);
}
}
}
/**
* Shutdown libtool.
*/
void __attribute__ ((destructor))
plugin_fini ()
{
lt_dlsetsearchpath (old_dlsearchpath);
if (NULL != old_dlsearchpath)
{
GNUNET_free (old_dlsearchpath);
old_dlsearchpath = NULL;
}
lt_dlexit ();
}
/* end of mintdb_plugin.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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
@ -67,9 +67,11 @@ PERF_TALER_MINTDB_denomination_init ()
properties.expire_withdraw = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_spend = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
properties.expire_legal = GNUNET_TIME_absolute_hton (GNUNET_TIME_absolute_get_forever_());
TALER_string_to_amount (CURRENCY ":1.1", &amount);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.1", &amount));
TALER_amount_hton (&properties.value, &amount);
TALER_string_to_amount (CURRENCY ":0.1", &amount);
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.1", &amount));
TALER_amount_hton (&properties.fee_withdraw, &amount);
TALER_amount_hton (&properties.fee_deposit, &amount);
TALER_amount_hton (&properties.fee_refresh, &amount);
@ -467,8 +469,8 @@ PERF_TALER_MINTDB_refresh_session_free (struct TALER_MINTDB_RefreshSession *refr
{
if (NULL == refresh_session)
return GNUNET_OK;
return GNUNET_OK;
GNUNET_free (refresh_session);
return GNUNET_OK;
}
@ -502,10 +504,12 @@ PERF_TALER_MINTDB_refresh_melt_init (struct GNUNET_HashCode *session,
&to_sign.purpose,
&coin_sig.eddsa_signature);
}
GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":1.1",
&amount));
GNUNET_assert (GNUNET_OK == TALER_string_to_amount (CURRENCY ":0.1",
&amount_with_fee));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":1.1",
&amount));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY ":0.1",
&amount_with_fee));
melt = GNUNET_new (struct TALER_MINTDB_RefreshMelt);
melt->coin.coin_pub = coin->public_info.coin_pub;
melt->coin.denom_sig.rsa_signature =

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2015 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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015, 2016 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
@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/licenses/>
*/
/**
* @file mint/test_mintdb.c
* @file mintdb/test_mintdb.c
* @brief test cases for DB interaction functions
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
@ -305,8 +305,10 @@ test_melting (struct TALER_MINTDB_Session *session)
RND_BLK (&refresh_session);
RND_BLK (&session_hash);
melts = NULL;
dkp = NULL;
new_dkp = NULL;
new_denom_pubs = NULL;
ret_denom_pubs = NULL;
/* create and test a refresh session */
refresh_session.num_oldcoins = MELT_OLD_COINS;
refresh_session.num_newcoins = 1;
@ -324,11 +326,11 @@ test_melting (struct TALER_MINTDB_Session *session)
sizeof (refresh_session)));
/* create a denomination (value: 1; fraction: 100) */
dkp = create_denom_key_pair(512, session,
&value,
&fee_withdraw,
&fee_deposit,
&fee_refresh);
dkp = create_denom_key_pair (512, session,
&value,
&fee_withdraw,
&fee_deposit,
&fee_refresh);
/* create MELT_OLD_COINS number of refresh melts */
melts = GNUNET_new_array (MELT_OLD_COINS, struct TALER_MINTDB_RefreshMelt);
for (cnt=0; cnt < MELT_OLD_COINS; cnt++)
@ -416,7 +418,8 @@ test_melting (struct TALER_MINTDB_Session *session)
ret = GNUNET_OK;
drop:
destroy_denom_key_pair (dkp);
if (NULL != dkp)
destroy_denom_key_pair (dkp);
if (NULL != melts)
{
for (cnt = 0; cnt < MELT_OLD_COINS; cnt++)
@ -439,6 +442,114 @@ test_melting (struct TALER_MINTDB_Session *session)
}
/**
* Callback that should never be called.
*/
static void
cb_wt_never (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value)
{
GNUNET_assert (0); /* this statement should be unreachable */
}
/**
* Callback that should never be called.
*/
static void
cb_wtid_never (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time)
{
GNUNET_assert (0);
}
static struct TALER_MerchantPublicKeyP merchant_pub_wt;
static struct GNUNET_HashCode h_wire_wt;
static struct GNUNET_HashCode h_contract_wt;
static uint64_t transaction_id_wt;
static struct TALER_CoinSpendPublicKeyP coin_pub_wt;
static struct TALER_Amount coin_value_wt;
static struct TALER_Amount coin_fee_wt;
static struct TALER_Amount transfer_value_wt;
static struct GNUNET_TIME_Absolute execution_time_wt;
static struct TALER_WireTransferIdentifierRawP wtid_wt;
/**
* Callback that should be called with the WT data.
*/
static void
cb_wt_check (void *cls,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract,
uint64_t transaction_id,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *coin_value,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *transfer_value)
{
GNUNET_assert (cls == &cb_wt_never);
GNUNET_assert (0 == memcmp (merchant_pub,
&merchant_pub_wt,
sizeof (struct TALER_MerchantPublicKeyP)));
GNUNET_assert (0 == memcmp (h_wire,
&h_wire_wt,
sizeof (struct GNUNET_HashCode)));
GNUNET_assert (0 == memcmp (h_contract,
&h_contract_wt,
sizeof (struct GNUNET_HashCode)));
GNUNET_assert (transaction_id == transaction_id_wt);
GNUNET_assert (0 == memcmp (coin_pub,
&coin_pub_wt,
sizeof (struct TALER_CoinSpendPublicKeyP)));
GNUNET_assert (0 == TALER_amount_cmp (coin_value,
&coin_value_wt));
GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
&coin_fee_wt));
GNUNET_assert (0 == TALER_amount_cmp (transfer_value,
&transfer_value_wt));
}
/**
* Callback that should be called with the WT data.
*/
static void
cb_wtid_check (void *cls,
const struct TALER_WireTransferIdentifierRawP *wtid,
const struct TALER_Amount *coin_contribution,
const struct TALER_Amount *coin_fee,
const struct TALER_Amount *total_amount,
struct GNUNET_TIME_Absolute execution_time)
{
GNUNET_assert (cls == &cb_wtid_never);
GNUNET_assert (0 == memcmp (wtid,
&wtid_wt,
sizeof (struct TALER_WireTransferIdentifierRawP)));
GNUNET_assert (execution_time.abs_value_us ==
execution_time_wt.abs_value_us);
GNUNET_assert (0 == TALER_amount_cmp (coin_contribution,
&coin_value_wt));
GNUNET_assert (0 == TALER_amount_cmp (coin_fee,
&coin_fee_wt));
GNUNET_assert (0 == TALER_amount_cmp (total_amount,
&transfer_value_wt));
}
/**
* Main function that will be run by the scheduler.
*
@ -455,7 +566,6 @@ run (void *cls,
{
struct TALER_MINTDB_Session *session;
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_Amount amount;
struct DenomKeyPair *dkp;
struct TALER_MINTDB_CollectableBlindcoin cbc;
struct TALER_MINTDB_CollectableBlindcoin cbc2;
@ -465,6 +575,7 @@ run (void *cls,
struct TALER_MINTDB_CollectableBlindcoin *withdraw;
struct TALER_MINTDB_Deposit deposit;
struct TALER_MINTDB_Deposit deposit2;
struct TALER_WireTransferIdentifierRawP wtid;
json_t *wire;
json_t *just;
const char * const json_wire_str =
@ -563,11 +674,7 @@ run (void *cls,
= GNUNET_CRYPTO_rsa_sign (dkp->priv.rsa_private_key,
&cbc.h_coin_envelope,
sizeof (cbc.h_coin_envelope));
(void) memcpy (&cbc.reserve_pub,
&reserve_pub,
sizeof (reserve_pub));
amount.value--;
amount.fraction--;
cbc.reserve_pub = reserve_pub;
cbc.amount_with_fee = value;
GNUNET_assert (GNUNET_OK ==
TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
@ -652,9 +759,7 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit));
(void) memcpy (&deposit2,
&deposit,
sizeof (deposit));
deposit2 = deposit;
deposit2.transaction_id++; /* should fail if transaction id is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
@ -666,15 +771,79 @@ run (void *cls,
plugin->have_deposit (plugin->cls,
session,
&deposit2));
(void) memcpy (&deposit2.merchant_pub,
&deposit.merchant_pub,
sizeof (deposit.merchant_pub));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_NO !=
plugin->have_deposit (plugin->cls,
session,
&deposit2));
FAILIF (GNUNET_OK != test_melting (session));
/* setup values for wire transfer aggregation data */
memset (&wtid, 42, sizeof (wtid));
memset (&merchant_pub_wt, 43, sizeof (merchant_pub_wt));
memset (&h_wire_wt, 44, sizeof (h_wire_wt));
memset (&h_contract_wt, 45, sizeof (h_contract_wt));
memset (&coin_pub_wt, 46, sizeof (coin_pub_wt));
transaction_id_wt = 47;
execution_time_wt = GNUNET_TIME_absolute_get ();
memset (&merchant_pub_wt, 48, sizeof (merchant_pub_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:1.000010",
&coin_value_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:0.000010",
&coin_fee_wt));
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount (CURRENCY "KUDOS:1.000000",
&transfer_value_wt));
FAILIF (GNUNET_NO !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wtid_wt,
&cb_wt_never,
NULL));
FAILIF (GNUNET_NO !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_wt,
&h_wire_wt,
&coin_pub_wt,
&merchant_pub_wt,
transaction_id_wt,
&cb_wtid_never,
NULL));
/* insert WT data */
FAILIF (GNUNET_OK !=
plugin->insert_aggregation_tracking (plugin->cls,
session,
&wtid_wt,
&merchant_pub_wt,
&h_wire_wt,
&h_contract_wt,
transaction_id_wt,
execution_time_wt,
&coin_pub_wt,
&coin_value_wt,
&coin_fee_wt,
&transfer_value_wt));
FAILIF (GNUNET_OK !=
plugin->lookup_wire_transfer (plugin->cls,
session,
&wtid_wt,
&cb_wt_check,
&cb_wt_never));
FAILIF (GNUNET_OK !=
plugin->wire_lookup_deposit_wtid (plugin->cls,
session,
&h_contract_wt,
&h_wire_wt,
&coin_pub_wt,
&merchant_pub_wt,
transaction_id_wt,
&cb_wtid_check,
&cb_wtid_never));
result = 0;
drop:

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014 Christian Grothoff (and other contributing authors)
Copyright (C) 2014 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2015 Christian Grothoff (and other contributing authors)
(C) 2015 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

View File

@ -33,6 +33,7 @@ libtalerutil_la_SOURCES = \
util.c \
json.c \
os_installation.c \
plugin.c \
wireformats.c
libtalerutil_la_LIBADD = \
@ -48,14 +49,12 @@ libtalerutil_la_LDFLAGS = \
TESTS = \
test_amount \
test_crypto \
test_json \
test_wireformats
test_json
check_PROGRAMS= \
test_amount \
test_crypto \
test_json \
test_wireformats
test_json
test_amount_SOURCES = \
@ -76,10 +75,3 @@ test_json_LDADD = \
-lgnunetutil \
-ljansson \
libtalerutil.la
test_wireformats_SOURCES = \
test_wireformats.c
test_wireformats_LDADD = \
-lgnunetutil \
-ljansson \
libtalerutil.la

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014 Christian Grothoff (and other contributing authors)
Copyright (C) 2014 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of GNUnet.
Copyright (C) 2006-2014 Christian Grothoff (and other contributing authors)
Copyright (C) 2006-2014 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published

88
src/util/plugin.c Normal file
View File

@ -0,0 +1,88 @@
/*
This file is part of TALER
Copyright (C) 2015 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 util/plugin.c
* @brief Setup paths so that we can load Taler plugins
* @author Christian Grothoff
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#include "platform.h"
#include "taler_util.h"
#include <ltdl.h>
/**
* Libtool search path before we started.
*/
static char *old_dlsearchpath;
/**
* Setup libtool paths.
*/
void __attribute__ ((constructor))
plugin_init ()
{
int err;
const char *opath;
char *path;
char *cpath;
err = lt_dlinit ();
if (err > 0)
{
FPRINTF (stderr,
_("Initialization of plugin mechanism failed: %s!\n"),
lt_dlerror ());
return;
}
opath = lt_dlgetsearchpath ();
if (NULL != opath)
old_dlsearchpath = GNUNET_strdup (opath);
path = TALER_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
if (NULL != path)
{
if (NULL != opath)
{
GNUNET_asprintf (&cpath, "%s:%s", opath, path);
lt_dlsetsearchpath (cpath);
GNUNET_free (path);
GNUNET_free (cpath);
}
else
{
lt_dlsetsearchpath (path);
GNUNET_free (path);
}
}
}
/**
* Shutdown libtool.
*/
void __attribute__ ((destructor))
plugin_fini ()
{
lt_dlsetsearchpath (old_dlsearchpath);
if (NULL != old_dlsearchpath)
{
GNUNET_free (old_dlsearchpath);
old_dlsearchpath = NULL;
}
lt_dlexit ();
}
/* end of plugin.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2015 Christian Grothoff (and other contributing authors)
(C) 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2015 Christian Grothoff (and other contributing authors)
(C) 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2015 Christian Grothoff (and other contributing authors)
(C) 2015 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014 Christian Grothoff (and other contributing authors)
Copyright (C) 2014 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

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
Copyright (C) 2014, 2015 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

78
src/wire/Makefile.am Normal file
View File

@ -0,0 +1,78 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
plugindir = $(libdir)/taler
plugin_LTLIBRARIES = \
libtaler_plugin_wire_sepa.la \
libtaler_plugin_wire_test.la
noinst_LTLIBRARIES = \
libtaler_plugin_wire_template.la
lib_LTLIBRARIES = \
libtalerwire.la
libtaler_plugin_wire_test_la_SOURCES = \
plugin_wire_test.c
libtaler_plugin_wire_test_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_test_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/bank-lib/libtalerbank.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil $(XLIB)
libtaler_plugin_wire_sepa_la_SOURCES = \
plugin_wire_sepa.c
libtaler_plugin_wire_sepa_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_sepa_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil $(XLIB)
libtaler_plugin_wire_template_la_SOURCES = \
plugin_wire_template.c
libtaler_plugin_wire_template_la_LIBADD = \
$(LTLIBINTL)
libtaler_plugin_wire_template_la_LDFLAGS = \
$(TALER_PLUGIN_LDFLAGS) \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil $(XLIB)
libtalerwire_la_SOURCES = \
wire.c
libtalerwire_la_LIBADD = \
-lgnunetutil \
$(XLIB)
libtalerwire_la_LDFLAGS = \
-version-info 0:0:0 \
-export-dynamic -no-undefined
TESTS = \
test_sepa_wireformat
check_PROGRAMS= \
test_sepa_wireformat
test_sepa_wireformat_SOURCES = \
test_sepa_wireformat.c
test_sepa_wireformat_LDADD = \
-lgnunetutil \
-ljansson \
libtalerwire.la \
$(top_builddir)/src/util/libtalerutil.la

545
src/wire/plugin_wire_sepa.c Normal file
View File

@ -0,0 +1,545 @@
/*
This file is part of TALER
Copyright (C) 2016 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 plugin_wire_sepa.c
* @brief wire plugin for transfers using SEPA/EBICS
* @author Florian Dold
* @author Christian Grothoff
* @author Sree Harsha Totakura
*/
#include "platform.h"
#include "taler_wire_plugin.h"
/**
* Type of the "cls" argument given to each of the functions in
* our API.
*/
struct SepaClosure
{
/**
* Which currency do we support?
*/
char *currency;
};
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
* payment, but SEPA only supports 0.01 EUR. This function would
* round 0.125 EUR to 0.12 EUR in this case.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
static int
sepa_amount_round (void *cls,
struct TALER_Amount *amount)
{
struct SepaClosure *sc = cls;
uint32_t delta;
if (0 != strcasecmp (amount->currency,
sc->currency))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
if (0 == delta)
return GNUNET_NO;
amount->fraction -= delta;
return GNUNET_SYSERR;
}
/* Taken from GNU gettext */
/**
* Entry in the country table.
*/
struct table_entry
{
/**
* 2-Character international country code.
*/
const char *code;
/**
* Long English name of the country.
*/
const char *english;
};
/* Keep the following table in sync with gettext.
WARNING: the entries should stay sorted according to the code */
/**
* List of country codes.
*/
static const struct table_entry country_table[] =
{
{ "AE", "U.A.E." },
{ "AF", "Afghanistan" },
{ "AL", "Albania" },
{ "AM", "Armenia" },
{ "AN", "Netherlands Antilles" },
{ "AR", "Argentina" },
{ "AT", "Austria" },
{ "AU", "Australia" },
{ "AZ", "Azerbaijan" },
{ "BA", "Bosnia and Herzegovina" },
{ "BD", "Bangladesh" },
{ "BE", "Belgium" },
{ "BG", "Bulgaria" },
{ "BH", "Bahrain" },
{ "BN", "Brunei Darussalam" },
{ "BO", "Bolivia" },
{ "BR", "Brazil" },
{ "BT", "Bhutan" },
{ "BY", "Belarus" },
{ "BZ", "Belize" },
{ "CA", "Canada" },
{ "CG", "Congo" },
{ "CH", "Switzerland" },
{ "CI", "Cote d'Ivoire" },
{ "CL", "Chile" },
{ "CM", "Cameroon" },
{ "CN", "People's Republic of China" },
{ "CO", "Colombia" },
{ "CR", "Costa Rica" },
{ "CS", "Serbia and Montenegro" },
{ "CZ", "Czech Republic" },
{ "DE", "Germany" },
{ "DK", "Denmark" },
{ "DO", "Dominican Republic" },
{ "DZ", "Algeria" },
{ "EC", "Ecuador" },
{ "EE", "Estonia" },
{ "EG", "Egypt" },
{ "ER", "Eritrea" },
{ "ES", "Spain" },
{ "ET", "Ethiopia" },
{ "FI", "Finland" },
{ "FO", "Faroe Islands" },
{ "FR", "France" },
{ "GB", "United Kingdom" },
{ "GD", "Caribbean" },
{ "GE", "Georgia" },
{ "GL", "Greenland" },
{ "GR", "Greece" },
{ "GT", "Guatemala" },
{ "HK", "Hong Kong" },
{ "HK", "Hong Kong S.A.R." },
{ "HN", "Honduras" },
{ "HR", "Croatia" },
{ "HT", "Haiti" },
{ "HU", "Hungary" },
{ "ID", "Indonesia" },
{ "IE", "Ireland" },
{ "IL", "Israel" },
{ "IN", "India" },
{ "IQ", "Iraq" },
{ "IR", "Iran" },
{ "IS", "Iceland" },
{ "IT", "Italy" },
{ "JM", "Jamaica" },
{ "JO", "Jordan" },
{ "JP", "Japan" },
{ "KE", "Kenya" },
{ "KG", "Kyrgyzstan" },
{ "KH", "Cambodia" },
{ "KR", "South Korea" },
{ "KW", "Kuwait" },
{ "KZ", "Kazakhstan" },
{ "LA", "Laos" },
{ "LB", "Lebanon" },
{ "LI", "Liechtenstein" },
{ "LK", "Sri Lanka" },
{ "LT", "Lithuania" },
{ "LU", "Luxembourg" },
{ "LV", "Latvia" },
{ "LY", "Libya" },
{ "MA", "Morocco" },
{ "MC", "Principality of Monaco" },
{ "MD", "Moldava" },
{ "MD", "Moldova" },
{ "ME", "Montenegro" },
{ "MK", "Former Yugoslav Republic of Macedonia" },
{ "ML", "Mali" },
{ "MM", "Myanmar" },
{ "MN", "Mongolia" },
{ "MO", "Macau S.A.R." },
{ "MT", "Malta" },
{ "MV", "Maldives" },
{ "MX", "Mexico" },
{ "MY", "Malaysia" },
{ "NG", "Nigeria" },
{ "NI", "Nicaragua" },
{ "NL", "Netherlands" },
{ "NO", "Norway" },
{ "NP", "Nepal" },
{ "NZ", "New Zealand" },
{ "OM", "Oman" },
{ "PA", "Panama" },
{ "PE", "Peru" },
{ "PH", "Philippines" },
{ "PK", "Islamic Republic of Pakistan" },
{ "PL", "Poland" },
{ "PR", "Puerto Rico" },
{ "PT", "Portugal" },
{ "PY", "Paraguay" },
{ "QA", "Qatar" },
{ "RE", "Reunion" },
{ "RO", "Romania" },
{ "RS", "Serbia" },
{ "RU", "Russia" },
{ "RW", "Rwanda" },
{ "SA", "Saudi Arabia" },
{ "SE", "Sweden" },
{ "SG", "Singapore" },
{ "SI", "Slovenia" },
{ "SK", "Slovak" },
{ "SN", "Senegal" },
{ "SO", "Somalia" },
{ "SR", "Suriname" },
{ "SV", "El Salvador" },
{ "SY", "Syria" },
{ "TH", "Thailand" },
{ "TJ", "Tajikistan" },
{ "TM", "Turkmenistan" },
{ "TN", "Tunisia" },
{ "TR", "Turkey" },
{ "TT", "Trinidad and Tobago" },
{ "TW", "Taiwan" },
{ "TZ", "Tanzania" },
{ "UA", "Ukraine" },
{ "US", "United States" },
{ "UY", "Uruguay" },
{ "VA", "Vatican" },
{ "VE", "Venezuela" },
{ "VN", "Viet Nam" },
{ "YE", "Yemen" },
{ "ZA", "South Africa" },
{ "ZW", "Zimbabwe" }
};
/**
* Country code comparator function, for binary search with bsearch().
*
* @param ptr1 pointer to a `struct table_entry`
* @param ptr2 pointer to a `struct table_entry`
* @return result of strncmp()'ing the 2-digit country codes of the entries
*/
static int
cmp_country_code (const void *ptr1,
const void *ptr2)
{
const struct table_entry *cc1 = ptr1;
const struct table_entry *cc2 = ptr2;
return strncmp (cc1->code,
cc2->code,
2);
}
/**
* Validates given IBAN according to the European Banking Standards. See:
* http://www.europeanpaymentscouncil.eu/documents/ECBS%20IBAN%20standard%20EBS204_V3.2.pdf
*
* @param iban the IBAN number to validate
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
validate_iban (const char *iban)
{
char cc[2];
char ibancpy[35];
struct table_entry cc_entry;
unsigned int len;
char *nbuf;
unsigned int i;
unsigned int j;
unsigned long long dividend;
unsigned long long remainder;
int nread;
int ret;
len = strlen (iban);
if (len > 34)
return GNUNET_NO;
strncpy (cc, iban, 2);
strncpy (ibancpy, iban + 4, len - 4);
strncpy (ibancpy + len - 4, iban, 4);
ibancpy[len] = '\0';
cc_entry.code = cc;
cc_entry.english = NULL;
if (NULL ==
bsearch (&cc_entry,
country_table,
sizeof (country_table) / sizeof (struct table_entry),
sizeof (struct table_entry),
&cmp_country_code))
return GNUNET_NO;
nbuf = GNUNET_malloc ((len * 2) + 1);
for (i=0, j=0; i < len; i++)
{
if (isalpha ((int) ibancpy[i]))
{
if (2 != snprintf(&nbuf[j],
3,
"%2u",
(ibancpy[i] - 'A' + 10)))
{
GNUNET_free (nbuf);
return GNUNET_NO;
}
j += 2;
continue;
}
nbuf[j] = ibancpy[i];
j++;
}
for (j=0;'\0' != nbuf[j];j++)
GNUNET_assert (isdigit(nbuf[j]));
GNUNET_assert (sizeof(dividend) >= 8);
remainder = 0;
for (i=0; i<j; i+=16)
{
if (1 !=
(ret = sscanf (&nbuf[i],
"%16llu %n",
&dividend,
&nread)))
{
GNUNET_free (nbuf);
return GNUNET_NO;
}
if (0 != remainder)
dividend += remainder * (pow (10, nread));
remainder = dividend % 97;
}
GNUNET_free (nbuf);
if (1 == remainder)
return GNUNET_YES;
return GNUNET_NO;
}
/**
* Check if the given wire format JSON object is correctly formatted
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
sepa_wire_validate (const json_t *wire)
{
json_error_t error;
const char *type;
const char *iban;
const char *name;
const char *bic;
uint64_t r;
const char *address;
if (0 != json_unpack_ex
((json_t *) wire,
&error, JSON_STRICT,
"{"
"s:s," /* TYPE: sepa */
"s:s," /* IBAN: iban */
"s:s," /* name: beneficiary name */
"s:s," /* BIC: beneficiary bank's BIC */
"s:i," /* r: random 64-bit integer nounce */
"s:s" /* address: address of the beneficiary */
"}",
"type", &type,
"IBAN", &iban,
"name", &name,
"bic", &bic,
"r", &r,
"address", &address))
{
TALER_json_warn (error);
return GNUNET_SYSERR;
}
if (0 != strcasecmp (type,
"sepa"))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Transfer type `%s' invalid\n",
type);
return GNUNET_SYSERR;
}
if (1 != validate_iban (iban))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"IBAN `%s' invalid\n",
iban);
return GNUNET_NO;
}
return GNUNET_YES;
}
/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param amount amount to transfer, already rounded
* @param wtid wire transfer identifier to use
* @param psc function to call with the prepared data to persist
* @param psc_cls closure for @a psc
* @return NULL on failure
*/
static struct TALER_WIRE_PrepareHandle *
sepa_prepare_wire_transfer (void *cls,
const json_t *wire,
const struct TALER_Amount *amount,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback psc,
void *psc_cls)
{
GNUNET_break (0); // FIXME: not implemented
return NULL;
}
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
static void
sepa_prepare_wire_transfer_cancel (void *cls,
struct TALER_WIRE_PrepareHandle *pth)
{
GNUNET_break (0); // FIXME: not implemented
}
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
static struct TALER_WIRE_ExecuteHandle *
sepa_execute_wire_transfer (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls)
{
GNUNET_break (0); // FIXME: not implemented
return NULL;
}
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
static void
sepa_execute_wire_transfer_cancel (void *cls,
struct TALER_WIRE_ExecuteHandle *eh)
{
GNUNET_break (0); // FIXME: not implemented
}
/**
* Initialize sepa-wire subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
*/
void *
libtaler_plugin_wire_sepa_init (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct SepaClosure *sc;
struct TALER_WIRE_Plugin *plugin;
sc = GNUNET_new (struct SepaClosure);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"CURRENCY",
&sc->currency))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"CURRENCY");
GNUNET_free (sc);
return NULL;
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = sc;
plugin->amount_round = &sepa_amount_round;
plugin->wire_validate = &sepa_wire_validate;
plugin->prepare_wire_transfer = &sepa_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &sepa_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &sepa_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
return plugin;
}
/**
* Shutdown Sepa wire subsystem.
*
* @param cls a `struct TALER_WIRE_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_wire_sepa_done (void *cls)
{
struct TALER_WIRE_Plugin *plugin = cls;
struct SepaClosure *sc = plugin->cls;
GNUNET_free (sc->currency);
GNUNET_free (sc);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_wire_sepa.c */

View File

@ -0,0 +1,243 @@
/*
This file is part of TALER
Copyright (C) 2016 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 plugin_wire_template.c
* @brief template for wire plugins; replace "template" with real plugin name!
* @author Florian Dold
* @author Christian Grothoff
* @author Sree Harsha Totakura
*/
#include "platform.h"
#include "taler_wire_plugin.h"
/**
* Type of the "cls" argument given to each of the functions in
* our API.
*/
struct TemplateClosure
{
/**
* URI of the bank for sending funds to the bank.
*/
char *bank_uri;
/**
* Which currency do we support?
*/
char *currency;
};
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
* payment, but SEPA only supports 0.01 EUR. This function would
* round 0.125 EUR to 0.12 EUR in this case.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
static int
template_amount_round (void *cls,
struct TALER_Amount *amount)
{
struct TemplateClosure *tc = cls;
if (0 != strcasecmp (amount->currency,
tc->currency))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_break (0); // not implemented
return GNUNET_SYSERR;
}
/**
* Check if the given wire format JSON object is correctly formatted
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
template_wire_validate (const json_t *wire)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/**
* Prepare for exeuction of a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param amount amount to transfer, already rounded
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
* @return NULL on failure
*/
static struct TALER_WIRE_PrepareHandle *
template_prepare_wire_transfer (void *cls,
const json_t *wire,
const struct TALER_Amount *amount,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls)
{
GNUNET_break (0);
return NULL;
}
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
static void
template_prepare_wire_transfer_cancel (void *cls,
struct TALER_WIRE_PrepareHandle *pth)
{
GNUNET_break (0);
}
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
static struct TALER_WIRE_ExecuteHandle *
template_execute_wire_transfer (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls)
{
GNUNET_break (0);
return NULL;
}
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
static void
template_execute_wire_transfer_cancel (void *cls,
struct TALER_WIRE_ExecuteHandle *eh)
{
GNUNET_break (0);
}
/**
* Initialize template-wire subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
*/
void *
libtaler_plugin_wire_template_init (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct TemplateClosure *tc;
struct TALER_WIRE_Plugin *plugin;
tc = GNUNET_new (struct TemplateClosure);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"wire-template",
"bank_uri",
&tc->bank_uri))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"wire-template",
"bank_uri");
GNUNET_free (tc);
return NULL;
}
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"CURRENCY",
&tc->currency))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"CURRENCY");
GNUNET_free (tc->bank_uri);
GNUNET_free (tc);
return NULL;
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->amount_round = &template_amount_round;
plugin->wire_validate = &template_wire_validate;
plugin->prepare_wire_transfer = &template_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &template_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &template_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &template_execute_wire_transfer_cancel;
return plugin;
}
/**
* Shutdown Template wire subsystem.
*
* @param cls a `struct TALER_WIRE_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_wire_template_done (void *cls)
{
struct TALER_WIRE_Plugin *plugin = cls;
struct TemplateClosure *tc = plugin->cls;
GNUNET_free (tc->bank_uri);
GNUNET_free (tc->currency);
GNUNET_free (tc);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_wire_template.c */

581
src/wire/plugin_wire_test.c Normal file
View File

@ -0,0 +1,581 @@
/*
This file is part of TALER
Copyright (C) 2016 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 plugin_wire_test.c
* @brief plugin for the "test" wire method
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler_wire_plugin.h"
#include "taler_bank_service.h"
/* only for HTTP status codes */
#include <microhttpd.h>
/**
* Type of the "cls" argument given to each of the functions in
* our API.
*/
struct TestClosure
{
/**
* Handle to the bank for sending funds to the bank.
*/
struct TALER_BANK_Context *bank;
/**
* Which currency do we support?
*/
char *currency;
/**
* Handle to the bank task, or NULL.
*/
struct GNUNET_SCHEDULER_Task *bt;
};
/**
* Handle returned by #test_prepare_wire_transfer.
*/
struct TALER_WIRE_PrepareHandle
{
/**
* Task we use for async execution.
*/
struct GNUNET_SCHEDULER_Task *task;
/**
* Test closure we run in.
*/
struct TestClosure *tc;
/**
* Wire data for the transfer.
*/
json_t *wire;
/**
* Function to call with the serialized data.
*/
TALER_WIRE_PrepareTransactionCallback ptc;
/**
* Closure for @e ptc.
*/
void *ptc_cls;
/**
* Amount to transfer.
*/
struct TALER_Amount amount;
/**
* Subject of the wire transfer.
*/
struct TALER_WireTransferIdentifierRawP wtid;
};
/**
* Handle returned by #test_execute_wire_transfer.
*/
struct TALER_WIRE_ExecuteHandle
{
/**
* Handle to the HTTP request to the bank.
*/
struct TALER_BANK_AdminAddIncomingHandle *aaih;
/**
* Function to call with the result.
*/
TALER_WIRE_ConfirmationCallback cc;
/**
* Closure for @e cc.
*/
void *cc_cls;
};
/**
* Task that runs the bank's context's event loop with the GNUnet
* scheduler.
*
* @param cls our `struct TestClosure`
* @param tc scheduler context (unused)
*/
static void
context_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *sct)
{
struct TestClosure *tc = cls;
long timeout;
int max_fd;
fd_set read_fd_set;
fd_set write_fd_set;
fd_set except_fd_set;
struct GNUNET_NETWORK_FDSet *rs;
struct GNUNET_NETWORK_FDSet *ws;
struct GNUNET_TIME_Relative delay;
tc->bt = NULL;
TALER_BANK_perform (tc->bank);
max_fd = -1;
timeout = -1;
FD_ZERO (&read_fd_set);
FD_ZERO (&write_fd_set);
FD_ZERO (&except_fd_set);
TALER_BANK_get_select_info (tc->bank,
&read_fd_set,
&write_fd_set,
&except_fd_set,
&max_fd,
&timeout);
if (timeout >= 0)
delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
timeout);
else
delay = GNUNET_TIME_UNIT_FOREVER_REL;
rs = GNUNET_NETWORK_fdset_create ();
GNUNET_NETWORK_fdset_copy_native (rs,
&read_fd_set,
max_fd + 1);
ws = GNUNET_NETWORK_fdset_create ();
GNUNET_NETWORK_fdset_copy_native (ws,
&write_fd_set,
max_fd + 1);
tc->bt = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
delay,
rs,
ws,
&context_task,
cls);
GNUNET_NETWORK_fdset_destroy (rs);
GNUNET_NETWORK_fdset_destroy (ws);
}
/**
* Run the bank task now.
*
* @param tc context for which we should initiate running the task
*/
static void
run_bt (struct TestClosure *tc)
{
if (NULL != tc->bt)
GNUNET_SCHEDULER_cancel (tc->bt);
tc->bt = GNUNET_SCHEDULER_add_now (&context_task,
tc);
}
/**
* Round amount DOWN to the amount that can be transferred via the wire
* method. For example, Taler may support 0.000001 EUR as a unit of
* payment, but SEPA only supports 0.01 EUR. This function would
* round 0.125 EUR to 0.12 EUR in this case.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param[in,out] amount amount to round down
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
* #GNUNET_SYSERR if the amount or currency was invalid
*/
static int
test_amount_round (void *cls,
struct TALER_Amount *amount)
{
struct TestClosure *tc = cls;
uint32_t delta;
if (0 != strcasecmp (amount->currency,
tc->currency))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
/* 'test' method supports 1/100 of the unit currency, i.e. 0.01 CUR */
delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
if (0 == delta)
return GNUNET_NO;
amount->fraction -= delta;
return GNUNET_OK;
}
/**
* Check if the given wire format JSON object is correctly formatted
*
* @param wire the JSON wire format object
* @return #GNUNET_YES if correctly formatted; #GNUNET_NO if not
*/
static int
test_wire_validate (const json_t *wire)
{
GNUNET_break (0); /* FIXME: we still need to define the
proper wire format for 'test' */
return GNUNET_YES;
}
GNUNET_NETWORK_STRUCT_BEGIN
/**
* Format we used for serialized transaction data.
*/
struct BufFormatP
{
/**
* The wire transfer identifier.
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* The amount.
*/
struct TALER_AmountNBO amount;
/* followed by serialized 'wire' JSON data */
};
GNUNET_NETWORK_STRUCT_END
/**
* Prepare for exeuction of a wire transfer. Calls the
* callback with the serialized state.
*
* @param cls the `struct TALER_WIRE_PrepareHandle`
* @param sct unused
*/
static void
do_prepare (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *sct)
{
struct TALER_WIRE_PrepareHandle *pth = cls;
char *wire_enc;
size_t len;
struct BufFormatP bf;
pth->task = NULL;
/* serialize the state into a 'buf' */
wire_enc = json_dumps (pth->wire,
JSON_COMPACT | JSON_SORT_KEYS);
if (NULL == wire_enc)
{
GNUNET_break (0);
pth->ptc (pth->ptc_cls,
NULL,
0);
GNUNET_free (pth);
return;
}
len = strlen (wire_enc) + 1;
bf.wtid = pth->wtid;
TALER_amount_hton (&bf.amount,
&pth->amount);
{
char buf[sizeof (struct BufFormatP) + len];
memcpy (buf,
&bf,
sizeof (struct BufFormatP));
memcpy (&buf[sizeof (struct BufFormatP)],
wire_enc,
len);
/* finally give the state back */
pth->ptc (pth->ptc_cls,
buf,
sizeof (buf));
}
free (wire_enc); /* not using GNUNET_free(),
as this one is allocated by libjansson */
GNUNET_free (pth);
}
/**
* Prepare for exeuction of a wire transfer. Note that we should call
* @a ptc asynchronously (as that is what the API requires, because
* some transfer methods need it). So while we could immediately call
* @a ptc, we first bundle up all the data and schedule a task to do
* the work.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param wire valid wire account information
* @param amount amount to transfer, already rounded
* @param wtid wire transfer identifier to use
* @param ptc function to call with the prepared data to persist
* @param ptc_cls closure for @a ptc
* @return NULL on failure
*/
static struct TALER_WIRE_PrepareHandle *
test_prepare_wire_transfer (void *cls,
const json_t *wire,
const struct TALER_Amount *amount,
const struct TALER_WireTransferIdentifierRawP *wtid,
TALER_WIRE_PrepareTransactionCallback ptc,
void *ptc_cls)
{
struct TestClosure *tc = cls;
struct TALER_WIRE_PrepareHandle *pth;
if (GNUNET_YES !=
test_wire_validate (wire))
{
GNUNET_break (0);
return NULL;
}
pth = GNUNET_new (struct TALER_WIRE_PrepareHandle);
pth->tc = tc;
pth->wire = (json_t *) wire;
json_incref (pth->wire);
pth->wtid = *wtid;
pth->ptc = ptc;
pth->ptc_cls = ptc_cls;
pth->amount = *amount;
pth->task = GNUNET_SCHEDULER_add_now (&do_prepare,
pth);
return pth;
}
/**
* Abort preparation of a wire transfer. For example,
* because we are shutting down.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param pth preparation to cancel
*/
static void
test_prepare_wire_transfer_cancel (void *cls,
struct TALER_WIRE_PrepareHandle *pth)
{
GNUNET_SCHEDULER_cancel (pth->task);
json_decref (pth->wire);
GNUNET_free (pth);
}
/**
* Called with the result of submitting information about an incoming
* transaction to a bank.
*
* @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
* @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
* 0 if the bank's reply is bogus (fails to follow the protocol)
*/
static void
execute_cb (void *cls,
unsigned int http_status)
{
struct TALER_WIRE_ExecuteHandle *eh = cls;
char s[14];
eh->aaih = NULL;
GNUNET_snprintf (s,
sizeof (s),
"%u",
http_status);
eh->cc (eh->cc_cls,
(MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
(MHD_HTTP_OK == http_status) ? NULL : s);
GNUNET_free (eh);
}
/**
* Execute a wire transfer.
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param buf buffer with the prepared execution details
* @param buf_size number of bytes in @a buf
* @param cc function to call upon success
* @param cc_cls closure for @a cc
* @return NULL on error
*/
static struct TALER_WIRE_ExecuteHandle *
test_execute_wire_transfer (void *cls,
const char *buf,
size_t buf_size,
TALER_WIRE_ConfirmationCallback cc,
void *cc_cls)
{
struct TestClosure *tc = cls;
struct TALER_WIRE_ExecuteHandle *eh;
json_t *wire;
struct TALER_Amount amount;
struct BufFormatP bf;
if ( (buf_size <= sizeof (struct BufFormatP)) ||
('\0' != buf[buf_size -1]) )
{
GNUNET_break (0);
return NULL;
}
memcpy (&bf,
buf,
sizeof (bf));
TALER_amount_ntoh (&amount,
&bf.amount);
wire = json_loads (&buf[sizeof (struct BufFormatP)],
JSON_REJECT_DUPLICATES,
NULL);
if (NULL == wire)
{
GNUNET_break (0);
return NULL;
}
GNUNET_assert (GNUNET_YES ==
test_wire_validate (wire));
eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
eh->cc = cc;
eh->cc_cls = cc_cls;
eh->aaih = TALER_BANK_admin_add_incoming (tc->bank,
&bf.wtid,
&amount,
wire,
&execute_cb,
eh);
json_decref (wire);
if (NULL == eh->aaih)
{
GNUNET_break (0);
GNUNET_free (eh);
return NULL;
}
run_bt (tc);
return eh;
}
/**
* Abort execution of a wire transfer. For example, because we are
* shutting down. Note that if an execution is aborted, it may or
* may not still succeed. The caller MUST run @e
* execute_wire_transfer again for the same request as soon as
* possilbe, to ensure that the request either ultimately succeeds
* or ultimately fails. Until this has been done, the transaction is
* in limbo (i.e. may or may not have been committed).
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param eh execution to cancel
*/
static void
test_execute_wire_transfer_cancel (void *cls,
struct TALER_WIRE_ExecuteHandle *eh)
{
TALER_BANK_admin_add_incoming_cancel (eh->aaih);
GNUNET_free (eh);
}
/**
* Initialize test-wire subsystem.
*
* @param cls a configuration instance
* @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
*/
void *
libtaler_plugin_wire_test_init (void *cls)
{
struct GNUNET_CONFIGURATION_Handle *cfg = cls;
struct TestClosure *tc;
struct TALER_WIRE_Plugin *plugin;
char *uri;
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"wire-test",
"bank_uri",
&uri))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"wire-test",
"bank_uri");
return NULL;
}
tc = GNUNET_new (struct TestClosure);
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (cfg,
"mint",
"CURRENCY",
&tc->currency))
{
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
"mint",
"CURRENCY");
GNUNET_free (uri);
GNUNET_free (tc);
return NULL;
}
tc->bank = TALER_BANK_init (uri);
if (NULL == tc->bank)
{
GNUNET_break (0);
GNUNET_free (tc->currency);
GNUNET_free (tc);
return NULL;
}
plugin = GNUNET_new (struct TALER_WIRE_Plugin);
plugin->cls = tc;
plugin->amount_round = &test_amount_round;
plugin->wire_validate = &test_wire_validate;
plugin->prepare_wire_transfer = &test_prepare_wire_transfer;
plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;
plugin->execute_wire_transfer = &test_execute_wire_transfer;
plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
return plugin;
}
/**
* Shutdown Test wire subsystem.
*
* @param cls a `struct TALER_WIRE_Plugin`
* @return NULL (always)
*/
void *
libtaler_plugin_wire_test_done (void *cls)
{
struct TALER_WIRE_Plugin *plugin = cls;
struct TestClosure *tc = plugin->cls;
if (NULL != tc->bt)
{
GNUNET_SCHEDULER_cancel (tc->bt);
tc->bt = NULL;
}
TALER_BANK_fini (tc->bank);
GNUNET_free (tc->currency);
GNUNET_free (tc);
GNUNET_free (plugin);
return NULL;
}
/* end of plugin_wire_test.c */

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
(C) 2014 Christian Grothoff (and other contributing authors)
(C) 2015, 2016 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
@ -15,14 +15,15 @@
*/
/**
* @file util/test_wireformats.c
* @brief Tests for JSON validations
* @file wire/test_sepa_wireformat.c
* @brief Tests for JSON SEPA format validation
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_json_lib.h"
#include "taler_wire_lib.h"
/* Valid SEPA data */
static const char * const valid_wire_str =
@ -65,33 +66,39 @@ int
main(int argc,
const char *const argv[])
{
const char *unsupported[] = {
"unsupported",
NULL
};
const char *sepa[] = {
"SEPA",
NULL
};
json_t *wire;
json_error_t error;
int ret;
struct GNUNET_CONFIGURATION_Handle *cfg;
struct TALER_WIRE_Plugin *plugin;
GNUNET_log_setup ("test-json-validations", "WARNING", NULL);
GNUNET_log_setup ("test-sepa-wireformats",
"WARNING",
NULL);
cfg = GNUNET_CONFIGURATION_create ();
GNUNET_CONFIGURATION_set_value_string (cfg,
"mint",
"currency",
"EUR");
plugin = TALER_WIRE_plugin_load (cfg,
"sepa");
GNUNET_assert (NULL != plugin);
(void) memset(&error, 0, sizeof(error));
GNUNET_assert (NULL != (wire = json_loads (unsupported_wire_str, 0, NULL)));
GNUNET_assert (1 != TALER_json_validate_wireformat (unsupported, wire));
GNUNET_assert (GNUNET_YES != plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str, 0, NULL)));
GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire));
GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (invalid_wire_str2, 0, NULL)));
GNUNET_assert (1 != TALER_json_validate_wireformat (sepa, wire));
GNUNET_assert (GNUNET_NO == plugin->wire_validate (wire));
json_decref (wire);
GNUNET_assert (NULL != (wire = json_loads (valid_wire_str, 0, &error)));
ret = TALER_json_validate_wireformat (sepa, wire);
ret = plugin->wire_validate (wire);
json_decref (wire);
if (1 == ret)
return 0;
return 1;
TALER_WIRE_plugin_unload (plugin);
GNUNET_CONFIGURATION_destroy (cfg);
if (GNUNET_NO == ret)
return 1;
return 0;
}

72
src/wire/wire.c Normal file
View File

@ -0,0 +1,72 @@
/*
This file is part of TALER
(C) 2015, 2016 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 wire/wire.c
* @brief Functions for loading wire plugins
* @author Christian Grothoff <christian@grothoff.org>
*/
#include "platform.h"
#include "taler_util.h"
#include "taler_wire_lib.h"
/**
* Load a WIRE plugin.
*
* @param cfg configuration to use
* @param plugin_name name of the plugin to load
* @return #GNUNET_OK on success
*/
struct TALER_WIRE_Plugin *
TALER_WIRE_plugin_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *plugin_name)
{
char *lib_name;
struct TALER_WIRE_Plugin *plugin;
(void) GNUNET_asprintf (&lib_name,
"libtaler_plugin_wire_%s",
plugin_name);
plugin = GNUNET_PLUGIN_load (lib_name,
(void *) cfg);
if (NULL != plugin)
plugin->library_name = lib_name;
else
GNUNET_free (lib_name);
return plugin;
}
/**
* Unload a WIRE plugin.
*
* @param plugin the plugin to unload
*/
void
TALER_WIRE_plugin_unload (struct TALER_WIRE_Plugin *plugin)
{
char *lib_name;
if (NULL == plugin)
return;
lib_name = plugin->library_name;
GNUNET_assert (NULL == GNUNET_PLUGIN_unload (lib_name,
plugin));
GNUNET_free (lib_name);
}
/* end of wire.c */