taler-mdb

GNU Taler Extensions and Integrations
Log | Files | Refs | Submodules | README | LICENSE

commit 2881c2e6fb5286eea3d452417700d9fae18a83ae
parent aa66904d79bb3ce13e32689b45dbd416d4a73fc7
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Sat, 23 Jul 2022 12:22:45 +0200

-work on coin acceptor

Diffstat:
Mconfigure.ac | 6------
Msrc/Makefile.am | 3+++
Asrc/acceptor.conf | 14++++++++++++++
Msrc/taler-coin-acceptor.c | 291+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 303 insertions(+), 11 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -13,15 +13,9 @@ AC_PROG_CC # Checks for libraries. AC_CHECK_LIB([nfc], [nfc_open]) -# Checks for libraries. -AC_CHECK_LIB([usb], [libusb_init]) - # Checks for header files. AC_CHECK_HEADERS([stdlib.h string.h unistd.h sys/un.h netinet/in.h netinet/ip.h]) -# Checks for header files. -AC_CHECK_HEADERS([libusb-1.0/libusb.h]) - # Checks for typedefs, structures, and compiler characteristics. AC_CHECK_HEADER_STDBOOL AC_TYPE_SIZE_T diff --git a/src/Makefile.am b/src/Makefile.am @@ -15,6 +15,9 @@ endif taler_coin_acceptor_SOURCES = \ taler-coin-acceptor.c taler_coin_acceptor_LDADD = \ + -ltalermerchant \ + -ltalerutil \ + -lgnunetcurl \ -lgnunetutil taler_mdb_SOURCES = \ diff --git a/src/acceptor.conf b/src/acceptor.conf @@ -0,0 +1,14 @@ +# This file is in the public domain. + +# sample configuration for the taler-coin-acceptor + +[tca-map] +1 = CHF:0.1 +2 = CHF:0.2 +5 = CHF:0.5 +10 = CHF:1 +20 = CHF:2 +50 = CHF:5 + +[tca] +BACKEND_URL = https://backend.chf.taler.net/ diff --git a/src/taler-coin-acceptor.c b/src/taler-coin-acceptor.c @@ -33,6 +33,8 @@ along with #include <fcntl.h> #include <termios.h> #include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include <taler/taler_merchant_service.h> /** @@ -61,6 +63,46 @@ static struct GNUNET_SCHEDULER_Task *tt; static int global_ret; /** + * Array mapping numbers to the value of the respective coin. + */ +static struct TALER_Amount coin_value[256]; + +/** + * What is our backend? + */ +static char *merchant_url; + +/** + * Context for making CURL requests. + */ +static struct GNUNET_CURL_Context *ctx; + +/** + * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule(). + */ +static struct GNUNET_CURL_RescheduleContext *rc; + +/** + * Handle used to issue the tip. + */ +static struct TALER_MERCHANT_TipAuthorizeHandle *tah; + +/** + * Handle to watch for tip being picked up. + */ +static struct TALER_MERCHANT_TipMerchantGetHandle *tmgh; + +/** + * Current sum. + */ +static struct TALER_Amount sum; + +/** + * Tip ID of the currently active tip. + */ +static struct TALER_TipIdentifierP tip_id; + +/** * Function run on shutdown. * * @param cls NULL @@ -87,10 +129,140 @@ do_shutdown (void *cls) GNUNET_SCHEDULER_cancel (tt); tt = NULL; } + if (NULL != ctx) + { + GNUNET_CURL_fini (ctx); + ctx = NULL; + } + if (NULL != rc) + { + GNUNET_CURL_gnunet_rc_destroy (rc); + rc = NULL; + } + if (NULL != tah) + { + TALER_MERCHANT_tip_authorize_cancel (tah); + tah = NULL; + } + if (NULL != tmgh) + { + TALER_MERCHANT_merchant_tip_get_cancel (tmgh); + tmgh = NULL; + } + if (GNUNET_OK == + TALER_amount_is_valid (&sum)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "%s left in coin acceptor at shutdown\n", + TALER_amount2s (&sum)); + memset (&sum, 0, sizeof (sum)); + } + GNUNET_free (merchant_url); GNUNET_free (rf); } + +/** + * With result of a GET /private/tips/$TIP_ID request + * + * @param cls closure + * @param hr HTTP response details + * @param total_authorized how many tips were authorized under this tip + * @param total_picked_up how many tips have been picked up + * @param reason what was the reason given for the tip + * @param expiration when the tip will expire + * @param reserve_pub which reserve is funding this tip + * @param pickups_length length of the @a pickups array + * @param pickups array of pickup operations performed for this tip + */ +static void +pickup_cb ( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + const struct TALER_Amount *total_authorized, + const struct TALER_Amount *total_picked_up, + const char *reason, + struct GNUNET_TIME_Timestamp expiration, + const struct TALER_ReservePublicKeyP *reserve_pub, + unsigned int pickups_length, + const struct TALER_MERCHANT_PickupDetail pickups[]) +{ + tmgh = NULL; + switch (hr->http_status) + { + case MHD_HTTP_OK: + if (0 == TALER_amount_cmp (&sum, + total_picked_up)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Process completed\n"); + memset (&sum, + 0, + sizeof (sum)); + return; + } + /* Keep checking */ + tmgh = TALER_MERCHANT_merchant_tip_get (ctx, + merchant_url, + &tip_id, + false, + &pickup_cb, + NULL); + GNUNET_assert (NULL != tmgh); + return; + default: + GNUNET_break (0); /* FIXME: handle failures... */ + } +} + + +/** + * Callback for a /reserves/$RESERVE_PUB/tip-authorize request. Returns the result of + * the operation. + * + * @param cls closure, NULL + * @param hr HTTP response details + * @param tip_idp which tip ID should be used to pickup the tip + * @param tip_uri URI for the tip + * @param tip_expiration when does the tip expire + */ +static void +authorize_cb ( + void *cls, + const struct TALER_MERCHANT_HttpResponse *hr, + struct TALER_TipIdentifierP *tip_idp, + const char *tip_uri, + struct GNUNET_TIME_Timestamp tip_expiration) +{ + (void) cls; + tah = NULL; + switch (hr->http_status) + { + case MHD_HTTP_OK: + break; /* handled below */ + default: + GNUNET_break (0); // FIXME: handle cases! + return; + } + + // FIXME: show tip_uri on display! + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Tip pickup uri: %s\n", + tip_uri); + /* FIXME: update API to enable long-polling here! */ + tip_id = *tip_idp; + tmgh = TALER_MERCHANT_merchant_tip_get (ctx, + merchant_url, + &tip_id, + false, + &pickup_cb, + NULL); + GNUNET_assert (NULL != tmgh); +} + + + /** * Function run on coin insert. * @@ -103,13 +275,51 @@ do_read (void *cls) int ret; (void) cls; - ret = read (fd, &c, 1); - if ( (0 != ret) && + tt = NULL; + ret = read (fd, + &c, + sizeof (c)); + if ( (1 == ret) && (-1 != (int) c) ) { - fprintf (stdout, - "%d\n", - (int) c); + struct TALER_Amount *value = &coin_value[(unsigned char) c]; + + if (GNUNET_OK != + TALER_amount_is_valid (value)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Coin %d accepted by acceptor, but not configured!\n", + (int) c); + } + else + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Coin %d of value %s accepted\n", + (int) c, + TALER_amount2s (value)); + if (GNUNET_OK != + TALER_amount_is_valid (&sum)) + sum = *value; + else + GNUNET_assert (0 <= + TALER_amount_add (&sum, + &sum, + value)); + if (NULL != tah) + { + TALER_MERCHANT_tip_authorize_cancel (tah); + tah = NULL; + } + /* FIXME: handle case where we INCREASE an + existing tip! (needs new backend API!) */ + tah = TALER_MERCHANT_tip_authorize (ctx, + merchant_url, + "taler://FIXME", + &sum, + "coin acceptor", + &authorize_cb, + NULL); + } } tt = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, rf, @@ -119,6 +329,55 @@ do_read (void *cls) /** + * Function to iterate over options in the "tca-map" section. + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +static void +map_builder (void *cls, + const char *section, + const char *option, + const char *value) +{ + char dummy; + unsigned int num; + + if (1 != sscanf (option, + "%u%c", + &num, + &dummy)) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + section, + option, + "option name must be a number"); + return; + } + if (num > 255) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + section, + option, + "option number must be below 256"); + return; + } + if (GNUNET_OK != + TALER_string_to_amount (value, + &coin_value[num])) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, + section, + option, + "option value must be an amount"); + return; + } +} + + +/** * Function run on startup. * * @param cls NULL @@ -165,6 +424,26 @@ run (void *cls, global_ret = 1; return; } + GNUNET_CONFIGURATION_iterate_section_values (cfg, + "tca-map", + &map_builder, + NULL); + + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "tca", + "BACKEND_URL", + &merchant_url)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, + "tca", + "BACKEND_URL"); + return; + } + ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, + &rc); + rc = GNUNET_CURL_gnunet_rc_create (ctx); + // FIXME: setup authentication based on cfg for ctx! fd = open (argv[0], O_RDONLY); if (-1 == fd) @@ -172,6 +451,7 @@ run (void *cls, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", argv[0]); + GNUNET_free (merchant_url); global_ret = 1; return; } @@ -183,6 +463,7 @@ run (void *cls, GNUNET_break (0 == close (fd)); fd = -1; global_ret = 1; + GNUNET_free (merchant_url); return; } GNUNET_SCHEDULER_add_shutdown (&do_shutdown,