cash2ecash

cash2ecash: cash acceptor that issues digital cash (experimental)
Log | Files | Refs | README | LICENSE

commit f6533b68a9fc14936fa9f892b2fad47231febfe7
parent 285b884edfab8d63d9fd8ffd5a2118a9c3728ae0
Author: Tellenbach Reto <tellr1@bfh.ch>
Date:   Tue,  2 Jun 2026 16:03:25 +0200

[new] thinker_QRCode: draft show qrcode

Diffstat:
MREADME.md | 18++++++++++++++++++
Msrc/taler-digitizer.c | 29+++++++++++++++++------------
Mtaler-digitizer.conf | 18+++++++++---------
Athinker/qr_code/CMakeLists.txt | 17+++++++++++++++++
Athinker/qr_code/CMakePresets.json | 15+++++++++++++++
Athinker/qr_code/taler-digitizer.conf | 34++++++++++++++++++++++++++++++++++
Athinker/qr_code/taler-mdb-qr-show.c | 437+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Athinker/qr_code/taler_digitizer_util.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 603 insertions(+), 21 deletions(-)

diff --git a/README.md b/README.md @@ -4,6 +4,24 @@ This software is meant for use on a system which is designed, to allow users to **Note: This software is currently under developpement. Do not attempt to use it on a system with real customers!** +# Config +Get a Token for the bank account which transfers money to the User. +The following example is with the demo bank to get a valid token for one day (86400000000 qs). +One has to write its own username and password from the demobank account https://bank.demo.taler.net/webui/ instead of `$USERNAME` and `$PASSWORD`. +``` +curl -X POST https://bank.demo.taler.net/$USERNAME/bank_acc_tellr/token \\ + -H "Content-Type: application/json" \ + -H "Authorization: Basic $(echo -n '$USERNAME:$PASSWORD' | base64)" \ + -d '{ + "scope": "readwrite", + "duration": { "d_us": 86400000000 }, + "refreshable": false, + "description": "test token" + }' +``` +The recived token is the token for the config. +The token has an expiration date and needs to be renewed. + # Building `export TALER_DIGITIZER_PREFIX=<installation_path>` \ No newline at end of file diff --git a/src/taler-digitizer.c b/src/taler-digitizer.c @@ -31,7 +31,7 @@ /** * Time unit for PERSON_WITHDRAL_PERIOD config. - * Normal in Days but can be changed for testing + * Normaly in Days but, can be changed for testing */ #define DIGITIZER_PERSON_WITHDRAWL_PERIOD_TIME_UNIT GNUNET_TIME_UNIT_DAYS @@ -63,12 +63,12 @@ static unsigned long long cfg_backend_min_balance; /** * Per-person withdrawal limit read from configuration file */ -static unsigned long long cfg_person_withdrawllimit; +static unsigned long long cfg_user_withdrawllimit; /** * Per-person withdrawal period read from configuration file */ -static struct GNUNET_TIME_Relative cfg_person_withdrawl_period; +static struct GNUNET_TIME_Relative cfg_user_withdrawl_period; /** * KYC functionality flag read from configuration file @@ -91,6 +91,11 @@ static struct GNUNET_CURL_RescheduleContext *reschedule_ctx; static struct TALER_BANK_GetConfigHandle *get_config_handle; +static enum DIGITIZER_ + +typedef void (*DIGITIZER_State)(enum) + + static void on_config_received (void *cls, const struct TALER_BANK_ConfigResponse *vr) @@ -195,32 +200,32 @@ run (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, "taler-digitizer", - "PERSON_WITHDRAWLLIMIT", - &cfg_person_withdrawllimit)) + "USER_WITHDRAWLLIMIT", + &cfg_user_withdrawllimit)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-digitizer", - "PERSON_WITHDRAWLLIMIT"); + "USER_WITHDRAWLLIMIT"); global_ret = EXIT_FAILURE; return; } - unsigned long long person_withdrawl_period_number; + unsigned long long user_withdrawl_period_number; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, "taler-digitizer", - "PERSON_WITHDRAL_PERIOD", - &person_withdrawl_period_number)) + "USER_WITHDRAL_PERIOD", + &user_withdrawl_period_number)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "taler-digitizer", - "PERSON_WITHDRAL_PERIOD"); + "USER_WITHDRAL_PERIOD"); global_ret = EXIT_FAILURE; return; } - cfg_person_withdrawl_period = GNUNET_TIME_relative_multiply ( + cfg_user_withdrawl_period = GNUNET_TIME_relative_multiply ( DIGITIZER_PERSON_WITHDRAWL_PERIOD_TIME_UNIT, - person_withdrawl_period_number); + user_withdrawl_period_number); cfg_kyc_functionality = GNUNET_CONFIGURATION_get_value_yesno (cfg, "taler-digitizer", diff --git a/taler-digitizer.conf b/taler-digitizer.conf @@ -1,16 +1,14 @@ [taler-digitizer] -BACKEND_BASE_URL = https://exchange.demo.taler.net/ +BACKEND_BASE_URL = https://bank.demo.taler.net/ CURRENCY = KUDOS -#BANK_ACCOUNT = sdflksdflsdkjf - -# Balance required to start a Transaction -BANK_MIN_BALANCE = 1000 +BANK_ACCOUNT = bank_acc_tellr +BANK_TOKEN = 4NA4S6GKGCRBB65M99QKPKD29K2HTRBDZRJYTTVZ0K6QEYBQPHH0 # Withdrawl limitation per person, for a limited time period. # This can only be enforced with KYC functionality -PERSON_WITHDRAWLLIMIT = 200 -PERSON_WITHDRAL_PERIOD = 10 +USER_WITHDRAWLLIMIT = 200 +USER_WITHDRAL_PERIOD = 10 #BACKEND_AUTHORIZATION = Bearer secret-token:sandbox @@ -26,9 +24,11 @@ FRAMEBUFFER_BACKLIGHT = /sys/class/backlight/soc:backlight/brightness ENABLE = YES GPIO_CHIP = /dev/gpiochip0 GPIO_PIN = 16 -bigest_denomination = 5 +MAX_DENOMINATION = 5 +MIN_DENOMINATION = 0.1 [bill-acceptor] ENABLE = NO -bigest_denomination = 100 +MAX_DENOMINATION = 100 +MIN_DENOMINATION = 10 diff --git a/thinker/qr_code/CMakeLists.txt b/thinker/qr_code/CMakeLists.txt @@ -0,0 +1,17 @@ +#Project Config +cmake_minimum_required(VERSION 3.13) +project(QRshow LANGUAGES C ) + +#Compiler Settings +set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) + +# Librarys +## Defining Librarys +##add_library(gpiod gpiod_wrapper.c) +add_executable(QRshow taler-mdb-qr-show.c) + +## Linking Targets +target_link_libraries(QRshow PUBLIC gnunetutil talerutil + PRIVATE qrencode) diff --git a/thinker/qr_code/CMakePresets.json b/thinker/qr_code/CMakePresets.json @@ -0,0 +1,14 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "debug", + "binaryDir": "${sourceDir}/build/debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "CMAKE_C_FLAGS": "-Wall -Wextra -g" + } + } + ] +} +\ No newline at end of file diff --git a/thinker/qr_code/taler-digitizer.conf b/thinker/qr_code/taler-digitizer.conf @@ -0,0 +1,34 @@ +[taler-digitizer] +BACKEND_BASE_URL = https://exchange.demo.taler.net/ +CURRENCY = KUDOS + +#BANK_ACCOUNT = sdflksdflsdkjf + +# Balance required to start a Transaction +BANK_MIN_BALANCE = 1000 + +# Withdrawl limitation per person, for a limited time period. +# This can only be enforced with KYC functionality +PERSON_WITHDRAWLLIMIT = 200 +PERSON_WITHDRAL_PERIOD = 10 + + +#BACKEND_AUTHORIZATION = Bearer secret-token:sandbox +KYC_FUNCTIONALITY = NO + +# Name of the framebuffer to use for the QR code. +FRAMEBUFFER_DEVICE = /dev/fb0 + +# Name of the backlight file for the framebuffer +FRAMEBUFFER_BACKLIGHT = /sys/class/backlight/10-0045/brightness + +[coin-acceptor] +ENABLE = YES +GPIO_CHIP = /dev/gpiochip0 +GPIO_PIN = 16 +bigest_denomination = 5 + + +[bill-acceptor] +ENABLE = NO +bigest_denomination = 100 diff --git a/thinker/qr_code/taler-mdb-qr-show.c b/thinker/qr_code/taler-mdb-qr-show.c @@ -0,0 +1,437 @@ +/* + This file is part of TALER + Copyright (C) 2019 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, see <http://www.gnu.org/licenses/> +*/ +/** +* @file taler-mdb-qr-show.c +* @brief shows a QR code on the display for a defined amount of time +* @author Boss Marco +* @author Christian Grothoff +*/ + +#include <errno.h> +#include <qrencode.h> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <linux/fb.h> +#include "taler_digitizer_util.h" + + +/** + * Disable i18n support. + */ +#define _(s) (s) + +/** + * Handle for the Framebuffer device + */ +struct Display +{ + /** + * File descriptor for the screen + */ + int devicefd; + + /** + * File descriptor to set backlight information + */ + int backlightfd; + + /** + * The display memory to set the pixel information + */ + uint16_t *memory; + + /** + * Original screen information + */ + struct fb_var_screeninfo orig_vinfo; + + /** + * Variable screen information (color depth ...) + */ + struct fb_var_screeninfo var_info; + + /** + * Fixed screen informtaion + */ + struct fb_fix_screeninfo fix_info; +}; + + +/** + * Next program to launch. + */ +static char *const *arg_next; + +static struct GNUNET_TIME_Relative delay; + +/** + * Reference to the delay task + */ +static struct GNUNET_SCHEDULER_Task *delay_task; + +/** + * Name of the framebuffer device (i.e. /dev/fb1). + */ +static char *framebuffer_device_filename; + +/** + * Name of the backlight file of @e framebuffer_device_filename (i.e. /sys/class/backlight/soc:backlight/brightness). + */ +static char *framebuffer_backlight_filename; + +/** + * Global option '-i' to invert backlight on/off values + */ +static int backlight_invert; + +/** + * Standard backlight on value + */ +static char backlight_on = '1'; + +/** + * Standard backlight off value + */ +static char backlight_off = '0'; + +/** + * Handle for the framebuffer device + */ +static struct Display qrDisplay; + + +/** + * @brief Create the QR code to pay and display it on screen + * + * @param uri what text to show in the QR code + */ +static void +show_qrcode (const char *uri) +{ + QRinput *qri; + QRcode *qrc; + unsigned int size; + size_t xOff; + size_t yOff; + unsigned int nwidth; + + if (0 > qrDisplay.devicefd) + return; /* no display, no dice */ + qri = QRinput_new2 (0, QR_ECLEVEL_L); + if (NULL == qri) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "QRinput_new2"); + return; + } + /* first try encoding as uppercase-only alpha-numerical + QR code (much smaller encoding); if that fails, also + try using binary encoding (in case nick contains + special characters). */ + if ( (0 != + QRinput_append (qri, + QR_MODE_AN, + strlen (uri), + (unsigned char *) uri)) && + (0 != + QRinput_append (qri, + QR_MODE_8, + strlen (uri), + (unsigned char *) uri))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "QRinput_append"); + return; + } + qrc = QRcode_encodeInput (qri); + if (NULL == qrc) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "QRcode_encodeInput"); + QRinput_free (qri); + return; + } + + /* +8 for 4-pixels border */ + size = GNUNET_MIN (qrDisplay.var_info.xres, + qrDisplay.var_info.yres); + nwidth = qrc->width + 8; /* 4 pixel border */ + xOff = 4 * size / nwidth; + yOff = 4 * size / nwidth; + if (qrDisplay.var_info.xres < qrDisplay.var_info.yres) + yOff += (qrDisplay.var_info.yres - qrDisplay.var_info.xres) / 2; + else + xOff += (qrDisplay.var_info.xres - qrDisplay.var_info.yres) / 2; + for (unsigned int x = 0; x < qrDisplay.var_info.xres - 2 * xOff; x++) + for (unsigned int y = 0; y < qrDisplay.var_info.yres - 2 * yOff; y++) + { + unsigned int xoff = x * nwidth / size; + unsigned int yoff = y * nwidth / size; + unsigned int off = xoff + yoff * qrc->width; + if ( (xoff >= (unsigned) qrc->width) || + (yoff >= (unsigned) qrc->width) ) + continue; + qrDisplay.memory[(y + yOff) * qrDisplay.var_info.xres + (x + xOff)] = + (0 == (qrc->data[off] & 1)) ? 0xFFFF : 0x0000; + } + + QRcode_free (qrc); + QRinput_free (qri); + + if (0 < qrDisplay.backlightfd) + (void) ! write (qrDisplay.backlightfd, + &backlight_on, + 1); +} + + +static void +stop_task (void *cls) +{ + (void) cls; + delay_task = NULL; + GNUNET_SCHEDULER_shutdown (); +} + + +static void +shutdown_task (void *cls) +{ + (void) cls; + if (NULL != delay_task) + { + GNUNET_SCHEDULER_cancel (delay_task); + delay_task = NULL; + } + if (NULL != qrDisplay.memory) + memset (qrDisplay.memory, + 0xFF, + qrDisplay.var_info.xres + * qrDisplay.var_info.yres + * sizeof (uint16_t)); + if (0 < qrDisplay.backlightfd) + (void) ! write (qrDisplay.backlightfd, + &backlight_off, + 1); + if (NULL != qrDisplay.memory) + { + /* free the display data */ + munmap (qrDisplay.memory, + qrDisplay.fix_info.smem_len); + qrDisplay.memory = NULL; + /* reset original state */ + if (0 > ioctl (qrDisplay.devicefd, + FBIOPUT_VSCREENINFO, + &qrDisplay.orig_vinfo)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "failed to reset original display state\n"); + } + /* close device */ + GNUNET_break (0 == close (qrDisplay.devicefd)); + qrDisplay.devicefd = -1; + if (0 < qrDisplay.backlightfd) + GNUNET_break (0 == close (qrDisplay.backlightfd)); + qrDisplay.backlightfd = -1; + } +} + + +/** + * @brief Start the application + * + * @param cls closure + * @param args arguments left + * @param cfgfile config file name + * @param cfg handle for the configuration file + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) cfgfile; + + if (NULL == args[0]) + return; + arg_next = args + 1; + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename ( + cfg, + "taler-digitizer", + "FRAMEBUFFER_DEVICE", + &framebuffer_device_filename)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler-digitizer", + "FRAMEBUFFER_DEVICE"); + framebuffer_device_filename = GNUNET_strdup ("/dev/fb1"); + } + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename ( + cfg, + "taler-digitizer", + "FRAMEBUFFER_BACKLIGHT", + &framebuffer_backlight_filename)) + { + GNUNET_log_config_missing ( + GNUNET_ERROR_TYPE_ERROR, + "taler-digitizer", + "FRAMEBUFFER_BACKLIGHT"); + framebuffer_backlight_filename = GNUNET_strdup ( + "/sys/class/backlight/*/brightness"); + } + /* open the framebuffer device */ + qrDisplay.devicefd = open (framebuffer_device_filename, + O_RDWR); + if (0 < qrDisplay.devicefd) + { + /* read information about the screen */ + ioctl (qrDisplay.devicefd, + FBIOGET_VSCREENINFO, + &qrDisplay.var_info); + + /* store current screeninfo for reset */ + qrDisplay.orig_vinfo = qrDisplay.var_info; + + if (16 != qrDisplay.var_info.bits_per_pixel) + { + /* Change variable info to 16 bit per pixel */ + qrDisplay.var_info.bits_per_pixel = 16; + if (0 > ioctl (qrDisplay.devicefd, + FBIOPUT_VSCREENINFO, + &qrDisplay.var_info)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "ioctl(FBIOPUT_VSCREENINFO)"); + return; + } + } + + /* Get fixed screen information */ + if (0 > ioctl (qrDisplay.devicefd, + FBIOGET_FSCREENINFO, + &qrDisplay.fix_info)) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "ioctl(FBIOGET_FSCREENINFO)"); + return; + } + + /* get pointer onto frame buffer */ + qrDisplay.memory + = mmap (NULL, + qrDisplay.fix_info.smem_len, + PROT_READ | PROT_WRITE, MAP_SHARED, + qrDisplay.devicefd, + 0); + + /* open backlight file to turn display backlight on and off */ + if (0 > qrDisplay.devicefd) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, + "mmap"); + return; + } + + memset (qrDisplay.memory, + 0xFF, + qrDisplay.var_info.xres * qrDisplay.var_info.yres + * sizeof (uint16_t)); + + qrDisplay.backlightfd = open ( + framebuffer_backlight_filename, O_WRONLY); + if (0 > qrDisplay.backlightfd) + { + GNUNET_log_strerror_file ( + GNUNET_ERROR_TYPE_WARNING, + "open", + framebuffer_backlight_filename); + } + else + { + if (backlight_invert) + { + backlight_on = '0'; + backlight_off = '1'; + } + (void) ! write (qrDisplay.backlightfd, + &backlight_off, + 1); + } + } + else + { + GNUNET_log_strerror_file ( + GNUNET_ERROR_TYPE_WARNING, + "open", + framebuffer_device_filename); + } + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + NULL); + show_qrcode (args[0]); + delay_task = GNUNET_SCHEDULER_add_delayed (delay, + &stop_task, + NULL); +} + + +int +main (int argc, + char*const*argv) +{ + int ret; + /* the available command line options */ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_relative_time ('d', + "delay", + "DELAY", + "how long should we display the QR code before exiting", + &delay), + GNUNET_GETOPT_option_flag ('i', + "backlight-invert", + "invert the backlight on/off values (standard on = 1)", + &backlight_invert), + GNUNET_GETOPT_OPTION_END + }; + + ret = GNUNET_PROGRAM_run (TALER_DIGITIZER_project_data (), + argc, + argv, + "taler-digitizer-qr-show", + "This is an application to show a QR code for a defined period of time before starting another program.\n", + options, + &run, + NULL); + if (GNUNET_OK != ret) + return 1; + if ( (NULL == arg_next) || + (NULL == arg_next[0]) ) + return 0; + execvp (arg_next[0], + arg_next); + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "execvp", + arg_next[0]); + return 1; +} diff --git a/thinker/qr_code/taler_digitizer_util.h b/thinker/qr_code/taler_digitizer_util.h @@ -0,0 +1,56 @@ +/* + This file is part of TALER cash2ecash + Copyright (C) 2026 GNUnet e.V. + + This program 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 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + 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 this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +/** + * @file taler-digitizer_util.h + * @brief Interface for common utility functions, and base project settings + * @author Reto Tellenbach + */ +#ifndef TALER_DIGITIZER_UTIL_H +#define TALER_DIGITIZER_UTIL_H + +#include <gnunet/gnunet_util_lib.h> + +/** + * Default project data. When shipping use a seperate os_installation.c which sets values at installation. + */ +static const struct GNUNET_OS_ProjectData digitizer_pd = { + .base_config_varname = "TALER_DIGITIZER_PREFIX", + .bug_email = "taler@lists.gnu.org", + .homepage = "http://www.gnu.org/s/taler/", + .config_file = "taler-digitizer.conf", + .user_config_file = "~/.config/taler-digitizer.conf", + .version = "0.1.0", + .is_gnu = 0, + .gettext_domain = NULL, + .gettext_path = NULL, +}; + +/** + * Return default project data. + */ +const struct GNUNET_OS_ProjectData * +TALER_DIGITIZER_project_data (void) +{ + return &digitizer_pd; +} + + + +#endif