Compare commits
213 Commits
master
...
age-withdr
Author | SHA1 | Date | |
---|---|---|---|
8706a36c95 | |||
2eb3ff1f64 | |||
|
2ca7ce1b89 | ||
|
1e5976a3e9 | ||
|
0698835fed | ||
0a4422f5fa | |||
77f99b6363 | |||
|
3ce29a711b | ||
|
ec03f262b6 | ||
|
942dd73794 | ||
|
cd5fafffe2 | ||
|
5b14fd547e | ||
|
cd45ba408a | ||
|
ea47fb40c0 | ||
|
22ad41a2b4 | ||
|
3976729af1 | ||
c86a169fb5 | |||
3bebbfc2da | |||
7837015692 | |||
a3922302c7 | |||
|
d3607c286d | ||
|
49f8332ef9 | ||
ecea165db7 | |||
63efa1f135 | |||
34f44ccb27 | |||
|
de24415e17 | ||
|
e08fe4eff8 | ||
|
b60b339ee4 | ||
65c861027a | |||
c8250cae25 | |||
fde760aef0 | |||
|
a5451527cb | ||
|
e984dbd8f4 | ||
e0f78bc3c9 | |||
4ebdcc0247 | |||
8a7bfefa38 | |||
88ecba945e | |||
|
43d5e5707c | ||
|
1e572ebcab | ||
|
c6676f1aa5 | ||
|
6c44755458 | ||
|
6a483b51ec | ||
|
66f9a5b5e5 | ||
|
f4abc1c369 | ||
|
c57c3463d1 | ||
8d6b0944da | |||
6107e99559 | |||
3024dc9fa5 | |||
9d706a01a2 | |||
|
c05f832048 | ||
|
1026a32c71 | ||
7e9f5324b7 | |||
|
c02d88c8e3 | ||
|
11ea6fcfce | ||
|
f2c3443860 | ||
|
95c05a8827 | ||
|
40dfb94e0f | ||
|
1db17d43bd | ||
|
999db0fb80 | ||
|
ff8349e6e7 | ||
47620fa81b | |||
|
d3772a834f | ||
|
290268e9af | ||
|
75ea35722b | ||
f969bd3c5b | |||
40629e8992 | |||
|
2d4ebd3fc3 | ||
|
8d6bce26ad | ||
|
2d5f0a87e0 | ||
|
c6e3cba61d | ||
|
a8b3f0eb4e | ||
|
e961b29103 | ||
|
67262173a1 | ||
4d8d6d1222 | |||
|
78ed6228eb | ||
|
57527a5e8d | ||
9c3ddcbc18 | |||
537206e49f | |||
e889179bdd | |||
d3d744a444 | |||
1be14a3416 | |||
2f21fa24e4 | |||
89de1678ef | |||
f8536e8c14 | |||
145310e20e | |||
ddd0e0af13 | |||
ee42b70692 | |||
eeece1c96d | |||
17001e445d | |||
c5c3a44c25 | |||
b87d1112ea | |||
ddedf03a81 | |||
|
421129a32e | ||
|
75733ee00e | ||
|
e2f44ea7b3 | ||
|
fcd3948f3b | ||
|
d25dc8b0ad | ||
|
720783b66a | ||
|
999dae7c5d | ||
|
d4a65faad4 | ||
|
7bb9547599 | ||
|
22d5b9fc3a | ||
|
32d5b90827 | ||
|
ee2471a8c3 | ||
|
ef6496aba5 | ||
|
c512c8b101 | ||
|
f5ce22ddf6 | ||
|
af77a2a178 | ||
|
a37a8d34d5 | ||
|
17789253e9 | ||
|
d6838ed841 | ||
|
35bf856fcb | ||
|
8be960125f | ||
|
9b20c5047e | ||
70bfe0ed1b | |||
|
d8f8c550bd | ||
|
a2dde02b64 | ||
|
015b08b048 | ||
46188ae07e | |||
|
96cbda85e3 | ||
|
4355a08769 | ||
|
4a86d411de | ||
|
27f22ef1d2 | ||
98b51edf49 | |||
|
8cc1edfe0a | ||
|
82fd1a1164 | ||
|
9e7d3f9065 | ||
|
0ad3de938e | ||
|
809300158c | ||
|
9718bc4920 | ||
|
8f2c2766a4 | ||
|
f8fd492e9e | ||
fb5bc18c58 | |||
80a1b8f524 | |||
2ea3ae1008 | |||
|
04885a289a | ||
|
be40886515 | ||
|
ae6e62a0a3 | ||
|
89e2a02380 | ||
|
d36f1b3b6a | ||
|
aa8d44aab3 | ||
|
721cd047ff | ||
|
144b3a50a9 | ||
|
689fd46a60 | ||
|
bac7123763 | ||
|
9f081d28d7 | ||
|
0045eea277 | ||
|
6cc3846f4d | ||
|
b30952ed72 | ||
|
79671bba66 | ||
|
f7dc35e59e | ||
|
755955de28 | ||
|
e371d76cfe | ||
|
30b953ff0f | ||
|
29694be4b1 | ||
4a31a180a4 | |||
|
ef2059c9df | ||
|
4e5a9906c2 | ||
|
89ed38c03c | ||
|
40e45e7f5e | ||
|
bac4932cf5 | ||
|
b7d1ca4cd9 | ||
|
89e21002d6 | ||
|
90ca90b576 | ||
|
92df1cb1da | ||
|
c9ed524bc3 | ||
9f1f069cea | |||
a04425df34 | |||
4833234df6 | |||
|
ff1a28319f | ||
|
bc03a27cba | ||
|
7899bc5621 | ||
|
ec8ad2e3b3 | ||
|
0dd0fff17d | ||
|
b15713f42e | ||
|
4954963405 | ||
|
5259ea0532 | ||
|
cc34502ac1 | ||
|
5f9c3021db | ||
|
09f09a2104 | ||
|
c014acf3c4 | ||
|
3ebd0a70b2 | ||
|
aedd13a778 | ||
|
1cf58e8ff8 | ||
|
4e79967f9b | ||
dc5b0fb0d3 | |||
|
d1379e492d | ||
|
f009e0bd12 | ||
|
85f6c8cdcc | ||
f40932196e | |||
|
fddd06c152 | ||
|
6d363488a1 | ||
|
1639cefa61 | ||
|
404b2b78f1 | ||
|
1f9427e1d9 | ||
|
737b3338ed | ||
|
1e88796045 | ||
|
7c0de44a2b | ||
|
2de2b6e3cf | ||
|
4c1a2c0307 | ||
|
0b8752bb1b | ||
|
82bb911720 | ||
|
8e0f9b40c0 | ||
|
4267f1d762 | ||
|
ffd4057c61 | ||
|
41cb79c685 | ||
|
00021d7e83 | ||
|
8ce9433736 | ||
|
647ae694cc | ||
|
faca037018 | ||
|
75f75c4a51 | ||
9130cda9e7 | |||
3ec14744f0 |
@ -274,11 +274,11 @@ AS_CASE([$with_gnunet],
|
||||
CPPFLAGS="-I$with_gnunet/include ${CPPFLAGS}"])
|
||||
CPPFLAGS="${CPPFLAGS} ${POSTGRESQL_CPPFLAGS}"
|
||||
AC_CHECK_HEADERS([gnunet/gnunet_pq_lib.h],
|
||||
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_string], libgnunetpq=1)])
|
||||
[AC_CHECK_LIB([gnunetpq], [GNUNET_PQ_result_spec_array_string], libgnunetpq=1)])
|
||||
AS_IF([test $libgnunetpq != 1],
|
||||
[AC_MSG_ERROR([[
|
||||
***
|
||||
*** You need libgnunetpq to build this program.
|
||||
*** You need libgnunetpq version >= 4.0.0 to build this program.
|
||||
*** Make sure you have Postgres installed while
|
||||
*** building GNUnet (and that your GNUnet version
|
||||
*** is recent!)
|
||||
|
@ -70,7 +70,8 @@ EXTRA_DIST = \
|
||||
$(rdata_DATA) \
|
||||
coverage.sh \
|
||||
gnunet.tag \
|
||||
microhttpd.tag
|
||||
microhttpd.tag \
|
||||
packages
|
||||
|
||||
# Change the set of supported languages here. You should
|
||||
# also update tos'XX'data and EXTRA_DIST accordingly.
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit bd4e73b2ed06269fdee42eaad21acb5be8be9302
|
||||
Subproject commit 86b36917a59cc46961a9c9042b1af75a88545558
|
@ -31,7 +31,13 @@
|
||||
<member kind="define">
|
||||
<type>#define</type>
|
||||
<name>GNUNET_TIME_UNIT_FOREVER_ABS</name>
|
||||
<anchorfile>gnunet_util_lib.h</anchorfile>
|
||||
<anchorfile>gnunet_time_lib.h</anchorfile>
|
||||
<arglist></arglist>
|
||||
</member>
|
||||
<member kind="define">
|
||||
<type>#define</type>
|
||||
<name>GNUNET_TIME_UNIT_ZERO_ABS</name>
|
||||
<anchorfile>gnunet_time_lib.h</anchorfile>
|
||||
<arglist></arglist>
|
||||
</member>
|
||||
</compound>
|
||||
|
@ -0,0 +1 @@
|
||||
# This configuration will be changed by tooling. Do not touch it manually.
|
@ -0,0 +1,49 @@
|
||||
# Main entry point for the GNU Taler configuration.
|
||||
#
|
||||
# Structure:
|
||||
# - taler.conf is the main configuration entry point
|
||||
# used by all Taler components (the file you are currently
|
||||
# looking at.
|
||||
# - overrides.conf contains configuration overrides that are
|
||||
# set by some tools that help with the configuration,
|
||||
# and should not be edited by humans. Comments in this file
|
||||
# are not preserved.
|
||||
# - conf.d/ contains configuration files for
|
||||
# Taler components, which can be read by all
|
||||
# users of the system and are included by the main
|
||||
# configuration.
|
||||
# - secrets/ contains configuration snippets
|
||||
# with secrets for particular services.
|
||||
# These files should have restrictive permissions
|
||||
# so that only users of the relevant services
|
||||
# can read it. All files in it should end with
|
||||
# ".secret.conf".
|
||||
|
||||
[taler]
|
||||
|
||||
# Currency of the Taler deployment. This setting applies to all Taler
|
||||
# components that only support a single currency.
|
||||
#currency = KUDOS
|
||||
|
||||
# Smallest currency unit handled by the underlying bank system. Taler payments
|
||||
# can make payments smaller than this units, but interactions with external
|
||||
# systems is always rounded to this unit.
|
||||
#currency_round_unit = KUDOS:0.01
|
||||
|
||||
# Monthly amount that mandatorily triggers an AML check
|
||||
#AML_THRESHOLD = KUDOS:10000000
|
||||
|
||||
[paths]
|
||||
|
||||
TALER_HOME = /var/lib/taler
|
||||
TALER_RUNTIME_DIR = /run/taler
|
||||
TALER_CACHE_HOME = /var/cache/taler
|
||||
TALER_CONFIG_HOME = /etc/taler
|
||||
TALER_DATA_HOME = /var/lib/taler
|
||||
|
||||
|
||||
# Inline configurations from all Taler components.
|
||||
@inline-matching@ conf.d/*.conf
|
||||
|
||||
# Overrides from tools that help with configuration.
|
||||
@inline@ overrides.conf
|
@ -0,0 +1,4 @@
|
||||
<Location "/taler-auditor/">
|
||||
ProxyPass "unix:/var/lib/taler-auditor/auditor.sock|http://example.com/"
|
||||
RequestHeader add "X-Forwarded-Proto" "https"
|
||||
</Location>
|
@ -0,0 +1,18 @@
|
||||
server {
|
||||
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name localhost;
|
||||
|
||||
access_log /var/log/nginx/auditor.log;
|
||||
error_log /var/log/nginx/auditor.err;
|
||||
|
||||
location /taler-auditor/ {
|
||||
proxy_pass http://unix:/var/lib/taler-auditor/auditor.sock;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host "localhost";
|
||||
#proxy_set_header X-Forwarded-Proto "https";
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
# Read secret sections into configuration, but only
|
||||
# if we have permission to do so.
|
||||
@inline-secret@ auditordb-postgres ../secrets/auditor-db.secret.conf
|
||||
|
||||
[auditor]
|
||||
# Debian package is configured to use a reverse proxy with a UNIX
|
||||
# domain socket. See nginx/apache configuration files.
|
||||
SERVE = UNIX
|
||||
UNIXPATH = /var/lib/taler-auditor/auditor.sock
|
||||
|
||||
# Only supported database is Postgres right now.
|
||||
DATABASE = postgres
|
@ -0,0 +1,10 @@
|
||||
# Database configuration for the Taler auditor.
|
||||
|
||||
[auditordb-postgres]
|
||||
|
||||
# Typically, there should only be a single line here, of the form:
|
||||
|
||||
CONFIG=postgres:///DATABASE
|
||||
|
||||
# The details of the URI depend on where the database lives and how
|
||||
# access control was configured.
|
@ -0,0 +1,4 @@
|
||||
<Location "/taler-exchange/">
|
||||
ProxyPass "unix:/run/taler/exchange-httpd/exchange-http.sock|http://example.com/"
|
||||
RequestHeader add "X-Forwarded-Proto" "https"
|
||||
</Location>
|
@ -0,0 +1,17 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
|
||||
server_name localhost;
|
||||
|
||||
access_log /var/log/nginx/exchange.log;
|
||||
error_log /var/log/nginx/exchange.err;
|
||||
|
||||
location /taler-exchange/ {
|
||||
proxy_pass http://unix:/run/taler/exchange-httpd/exchange-http.sock:/;
|
||||
proxy_redirect off;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host "localhost";
|
||||
#proxy_set_header X-Forwarded-Proto "https";
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
# Configuration for business-level aspects of the exchange.
|
||||
|
||||
[exchange]
|
||||
|
||||
# Here you MUST add the master public key of the offline system
|
||||
# which you can get using `taler-exchange-offline setup`.
|
||||
# This is just an example, your key will be different!
|
||||
# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG
|
||||
# MASTER_PUBLIC_KEY =
|
||||
|
||||
# Publicly visible base URL of the exchange.
|
||||
# BASE_URL = https://example.com/
|
||||
# BASE_URL =
|
||||
|
||||
# Here you MUST configure the amount above which transactions are
|
||||
# always subject to manual AML review.
|
||||
# AML_THRESHOLD =
|
||||
|
||||
# Attribute encryption key for storing attributes encrypted
|
||||
# in the database. Should be a high-entropy nonce.
|
||||
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
|
||||
|
||||
# For your terms of service and privacy policy, you should specify
|
||||
# an Etag that must be updated whenever there are significant
|
||||
# changes to either document. The format is up to you, what matters
|
||||
# is that the value is updated and never re-used. See the HTTP
|
||||
# specification on Etags.
|
||||
# TERMS_ETAG =
|
||||
# PRIVACY_ETAG =
|
||||
|
||||
SERVE = unix
|
||||
UNIXPATH_MODE = 666
|
||||
|
||||
# Bank accounts used by the exchange should be specified here:
|
||||
[exchange-account-1]
|
||||
|
||||
ENABLE_CREDIT = NO
|
||||
ENABLE_DEBIT = NO
|
||||
|
||||
# Account identifier in the form of an RFC-8905 payto:// URI.
|
||||
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
|
||||
# Make sure to URL-encode spaces in $NAME!
|
||||
PAYTO_URI =
|
||||
|
||||
# Credentials to access the account are in a separate
|
||||
# config file with restricted permissions.
|
||||
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf
|
||||
|
||||
|
||||
|
@ -0,0 +1,33 @@
|
||||
#
|
||||
# This configuration file specifies the various denominations offered by your
|
||||
# exchange.
|
||||
#
|
||||
# Each denomination must be specified in a sections starting with
|
||||
# "coin_".
|
||||
#
|
||||
# What follows is an example.
|
||||
#
|
||||
|
||||
# [coin_FOO]
|
||||
## Actual value of the coin
|
||||
#VALUE = KUDOS:1
|
||||
|
||||
## How long will one key be used for withdrawals?
|
||||
#DURATION_WITHDRAW = 7 days
|
||||
|
||||
## How long do users have to spend their coins?
|
||||
#DURATION_SPEND = 2 years
|
||||
|
||||
## How long does the exchange keep the proofs around for legal disputes?
|
||||
#DURATION_LEGAL = 6 years
|
||||
|
||||
## Fees charged. Note that for the lowest denomination, the
|
||||
## fee must precisely be the lowest denomination, or zero.
|
||||
#FEE_WITHDRAW = KUDOS:0
|
||||
#FEE_DEPOSIT = KUDOS:0
|
||||
#FEE_REFRESH = KUDOS:0
|
||||
#FEE_REFUND = KUDOS:0
|
||||
|
||||
## How long should the RSA keys be. Do not change unless you really know
|
||||
## what you are doing (consult your local cryptographer first!).
|
||||
#RSA_KEYSIZE = 2048
|
@ -0,0 +1,13 @@
|
||||
# Configuration settings for system parameters of the exchange.
|
||||
|
||||
# Read secret sections into configuration, but only
|
||||
# if we have permission to do so.
|
||||
@inline-secret@ exchangedb-postgres ../secrets/exchange-db.secret.conf
|
||||
|
||||
[exchange]
|
||||
|
||||
# Only supported database is Postgres right now.
|
||||
DATABASE = postgres
|
||||
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
# This file contains the secret credentials
|
||||
# to access the Taler Wire Gateway API (usually
|
||||
# provided by LibEuFin) for the exchange accounts.
|
||||
#
|
||||
# Each exchange-account-* section should have a matching
|
||||
# exchange-accountcredentials-* section here.
|
||||
#
|
||||
# Each of those sections must be imported via @inline-secret@,
|
||||
# usually in conf.d/exchange-business.conf.
|
||||
|
||||
[exchange-accountcredentials-1]
|
||||
|
||||
wire_gateway_auth_method = basic
|
||||
password =
|
||||
username =
|
||||
wire_gateway_url =
|
||||
|
@ -0,0 +1,10 @@
|
||||
# Database configuration for the Taler exchange.
|
||||
|
||||
[exchangedb-postgres]
|
||||
|
||||
# Typically, there should only be a single line here, of the form:
|
||||
|
||||
# CONFIG=postgres:///DATABASE
|
||||
|
||||
# The details of the URI depend on where the database lives and how
|
||||
# access control was configured.
|
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system auditor REST API
|
||||
After=postgres.service network.target
|
||||
|
||||
[Service]
|
||||
User=taler-auditor-httpd
|
||||
Type=simple
|
||||
Restart=on-failure
|
||||
ExecStart=/usr/bin/taler-auditor-httpd -c /etc/taler/taler.conf
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange aggregator service
|
||||
PartOf=taler-exchange.target
|
||||
After=postgres.service
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-aggregator
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange aggregator service
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-aggregator
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange closer service
|
||||
PartOf=taler-exchange.target
|
||||
After=network.target postgres.service
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-closer
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange expire service
|
||||
PartOf=taler-exchange.target
|
||||
After=postgres.service
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-expire
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-expire -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -0,0 +1,33 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange REST API
|
||||
AssertPathExists=/run/taler/exchange-httpd
|
||||
Requires=taler-exchange-httpd.socket taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
|
||||
After=postgres.service network.target taler-exchange-secmod-cs.service taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-httpd
|
||||
Type=simple
|
||||
|
||||
# Depending on the configuration, the service process kills itself and then
|
||||
# needs to be restarted. Thus no significant delay on restarts.
|
||||
Restart=always
|
||||
RestartSec=1ms
|
||||
|
||||
# Disable the service if more than 5 restarts are encountered within 5s.
|
||||
# These are usually the systemd defaults, but can be overwritten, thus we set
|
||||
# them here explicitly, as the exchange code assumes StartLimitInterval
|
||||
# to be >=5s.
|
||||
StartLimitBurst=5
|
||||
StartLimitInterval=5s
|
||||
|
||||
ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=no
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,27 @@
|
||||
% This is a systemd service template.
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange REST API at %I
|
||||
AssertPathExists=/run/taler/exchange-httpd
|
||||
Requires=taler-exchange-httpd@%i.socket taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
|
||||
After=postgres.service network.target taler-exchange-secmod-rsa.service taler-exchange-secmod-eddsa.service
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-httpd
|
||||
Type=simple
|
||||
# Depending on the configuration, the service suicides and then
|
||||
# needs to be restarted.
|
||||
Restart=always
|
||||
# Do not dally on restarts.
|
||||
RestartSec=1ms
|
||||
EnvironmentFile=/etc/environment
|
||||
ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=no
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange CS security module
|
||||
AssertPathExists=/run/taler/exchange-secmod-cs
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-secmod-cs
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=100ms
|
||||
ExecStart=/usr/bin/taler-exchange-secmod-cs -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=no
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
IPAddressDeny=any
|
||||
Slice=taler-exchange.slice
|
@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange EdDSA security module
|
||||
AssertPathExists=/run/taler/exchange-secmod-eddsa
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-secmod-eddsa
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=100ms
|
||||
ExecStart=/usr/bin/taler-exchange-secmod-eddsa -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=no
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
IPAddressDeny=any
|
||||
Slice=taler-exchange.slice
|
||||
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange RSA security module
|
||||
AssertPathExists=/run/taler/exchange-secmod-rsa
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-secmod-rsa
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=100ms
|
||||
ExecStart=/usr/bin/taler-exchange-secmod-rsa -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=no
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
IPAddressDeny=any
|
||||
Slice=taler-exchange.slice
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=Taler Exchange Transfer Service
|
||||
After=network.target postgres.service
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-wire
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange wirewatch service
|
||||
After=network.target postgres.service
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-wire
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
RuntimeMaxSec=3600s
|
||||
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
@ -0,0 +1,18 @@
|
||||
[Unit]
|
||||
Description=GNU Taler payment system exchange wirewatch service
|
||||
After=network.target
|
||||
PartOf=taler-exchange.target
|
||||
|
||||
[Service]
|
||||
User=taler-exchange-wire
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1s
|
||||
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
PrivateTmp=yes
|
||||
PrivateDevices=yes
|
||||
ProtectSystem=full
|
||||
Slice=taler-exchange.slice
|
||||
RuntimeMaxSec=3600s
|
@ -4,7 +4,7 @@
|
||||
exec 1>&2
|
||||
|
||||
RET=0
|
||||
changed=$(git diff --cached --name-only | grep -v mustach)
|
||||
changed=$(git diff --cached --name-only | grep -v mustach | grep -v templating/test./)
|
||||
crustified=""
|
||||
|
||||
for f in $changed;
|
||||
@ -28,7 +28,7 @@ done
|
||||
if [ $RET = 1 ];
|
||||
then
|
||||
echo "Run"
|
||||
echo "uncrustify --no-backup -c uncrustify.cfg ${crustified}"
|
||||
echo "uncrustify --replace -c uncrustify.cfg ${crustified}"
|
||||
echo "before committing."
|
||||
fi
|
||||
exit $RET
|
||||
|
@ -12,6 +12,14 @@
|
||||
# BASE_URL = https://example.com/
|
||||
# BASE_URL =
|
||||
|
||||
# Here you MUST configure the amount above which transactions are
|
||||
# always subject to manual AML review.
|
||||
# AML_THRESHOLD =
|
||||
|
||||
# Attribute encryption key for storing attributes encrypted
|
||||
# in the database. Should be a high-entropy nonce.
|
||||
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
|
||||
|
||||
# For your terms of service and privacy policy, you should specify
|
||||
# an Etag that must be updated whenever there are significant
|
||||
# changes to either document. The format is up to you, what matters
|
||||
@ -26,14 +34,17 @@ UNIXPATH_MODE = 666
|
||||
# Bank accounts used by the exchange should be specified here:
|
||||
[exchange-account-1]
|
||||
|
||||
enable_credit = no
|
||||
enable_debit = no
|
||||
ENABLE_CREDIT = NO
|
||||
ENABLE_DEBIT = NO
|
||||
|
||||
# Account identifier in the form of an RFC-8905 payto:// URI.
|
||||
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
|
||||
# Make sure to URL-encode spaces in $NAME!
|
||||
payto_uri =
|
||||
PAYTO_URI =
|
||||
|
||||
# Credentials to access the account are in a separate
|
||||
# config file with restricted permissions.
|
||||
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf
|
||||
|
||||
|
||||
|
||||
|
@ -8,3 +8,6 @@
|
||||
|
||||
# Only supported database is Postgres right now.
|
||||
DATABASE = postgres
|
||||
|
||||
|
||||
|
||||
|
12
debian/libtalerexchange-dev.install
vendored
12
debian/libtalerexchange-dev.install
vendored
@ -1,17 +1,22 @@
|
||||
# Benchmarks, only install them for the dev package.
|
||||
usr/bin/taler-aggregator-benchmark
|
||||
usr/bin/taler-exchange-benchmark
|
||||
usr/bin/taler-fakebank-run
|
||||
usr/bin/taler-bank-benchmark
|
||||
usr/bin/taler-exchange-benchmark
|
||||
usr/bin/taler-exchange-kyc-tester
|
||||
usr/bin/taler-fakebank-run
|
||||
usr/bin/taler-unified-setup.sh
|
||||
|
||||
# Only used in test cases. Maybe these
|
||||
# shouldn't even be installed?
|
||||
usr/bin/taler-nexus-prepare
|
||||
usr/bin/taler-bank-manage-testing
|
||||
usr/bin/taler-nexus-prepare
|
||||
|
||||
# Man pages
|
||||
usr/share/man/man1/taler-exchange-kyc-tester*
|
||||
usr/share/man/man1/taler-aggregator-benchmark*
|
||||
usr/share/man/man1/taler-bank-benchmark*
|
||||
usr/share/man/man1/taler-exchange-benchmark*
|
||||
usr/share/man/man1/taler-unified-setup*
|
||||
|
||||
|
||||
# Headers
|
||||
@ -27,5 +32,4 @@ usr/lib/*/libtalertesting.so
|
||||
usr/lib/*/libtalerfakebank.so
|
||||
|
||||
# Documentation
|
||||
usr/share/man/man1/taler-exchange-benchmark*
|
||||
usr/share/info/taler-developer-manual*
|
||||
|
@ -9,6 +9,7 @@ infoimagedir = $(infodir)/images
|
||||
man_MANS = \
|
||||
prebuilt/man/taler.conf.5 \
|
||||
prebuilt/man/taler-config.1 \
|
||||
prebuilt/man/taler-aggregator-benchmark.1 \
|
||||
prebuilt/man/taler-auditor.1 \
|
||||
prebuilt/man/taler-auditor-dbinit.1 \
|
||||
prebuilt/man/taler-auditor-exchange.1 \
|
||||
@ -16,6 +17,7 @@ man_MANS = \
|
||||
prebuilt/man/taler-auditor-offline.1 \
|
||||
prebuilt/man/taler-auditor-sign.1 \
|
||||
prebuilt/man/taler-auditor-sync.1 \
|
||||
prebuilt/man/taler-bank-benchmark.1 \
|
||||
prebuilt/man/taler-bank-transfer.1 \
|
||||
prebuilt/man/taler-exchange-aggregator.1 \
|
||||
prebuilt/man/taler-exchange-benchmark.1 \
|
||||
@ -24,6 +26,7 @@ man_MANS = \
|
||||
prebuilt/man/taler-exchange-drain.1 \
|
||||
prebuilt/man/taler-exchange-expire.1 \
|
||||
prebuilt/man/taler-exchange-httpd.1 \
|
||||
prebuilt/man/taler-exchange-kyc-aml-pep-trigger.1 \
|
||||
prebuilt/man/taler-exchange-kyc-tester.1 \
|
||||
prebuilt/man/taler-exchange-offline.1 \
|
||||
prebuilt/man/taler-exchange-router.1\
|
||||
@ -38,7 +41,8 @@ man_MANS = \
|
||||
prebuilt/man/taler-helper-auditor-deposits.1\
|
||||
prebuilt/man/taler-helper-auditor-purses.1\
|
||||
prebuilt/man/taler-helper-auditor-reserves.1\
|
||||
prebuilt/man/taler-helper-auditor-wire.1
|
||||
prebuilt/man/taler-helper-auditor-wire.1 \
|
||||
prebuilt/man/taler-unified-setup.1
|
||||
|
||||
info_TEXINFOS = \
|
||||
prebuilt/texinfo/taler-auditor.texi \
|
||||
|
File diff suppressed because it is too large
Load Diff
3
doc/flows/Makefile
Normal file
3
doc/flows/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
all:
|
||||
pdflatex main.tex
|
||||
pdflatex main.tex
|
39
doc/flows/fees-coins.tex
Normal file
39
doc/flows/fees-coins.tex
Normal file
@ -0,0 +1,39 @@
|
||||
\section{Fees per coin} \label{sec:fees:coin}
|
||||
|
||||
Payments with Taler are always made using coins. Each coin has a specific
|
||||
denomination, and an exchange will issue coins in different denominations (in
|
||||
the same currency). The fees per coin depend on the operation and the
|
||||
denomination.
|
||||
|
||||
The primary fee to be paid is a {\bf deposit} fee that is
|
||||
charged whenever a coin is fully or partially deposited
|
||||
into a bank account or another wallet.
|
||||
|
||||
A secondary fee to be paid is a {\bf change} fee that is
|
||||
charged whenever a coin partially spent and change must
|
||||
be rendered.
|
||||
|
||||
Coins also have an {\bf expiration} date of approximately {\bf one year}.
|
||||
After the expiration date, coins become worthless. Wallets that are online
|
||||
{\bf three months} {\em before} a coin expires will automatically trade any
|
||||
such coins for one or more fresh coins with a later expiration date. This
|
||||
process is also subject to the {\bf change} fee.
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Fees per coin. Coin denomination values are given in units of CHF 0.01.}
|
||||
\label{table:fees:coins}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Denomination} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
|
||||
$2^{-4}-2^{ 0}$ & deposit & {\em CHF 0.00125} \\
|
||||
$2^{-4}-2^{ 0}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 0}-2^{ 3}$ & deposit & {\em CHF 0.00250} \\
|
||||
$2^{ 0}-2^{ 3}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 4}-2^{ 8}$ & deposit & {\em CHF 0.005} \\
|
||||
$2^{ 4}-2^{ 8}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 8}-2^{12}$ & deposit & {\em CHF 0.01} \\
|
||||
$2^{ 8}-2^{12}$ & change & {\em CHF 0.00125} \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
30
doc/flows/fees-wire.tex
Normal file
30
doc/flows/fees-wire.tex
Normal file
@ -0,0 +1,30 @@
|
||||
\section{Fees per wire} \label{sec:fees:wire}
|
||||
|
||||
Wire fees apply whenever an exchange needs to initiate a wire transfer to
|
||||
another bank account. Wire fees do not apply to every individual payment to a
|
||||
merchant, as merchants can choose to {\em aggregate} multiple micropayments
|
||||
into one large payment on the wire. Wire fees also do not apply to
|
||||
wallet-to-wallet payments within the Taler system.
|
||||
|
||||
A {\bf wire} fee is applied when a merchant receives
|
||||
an aggregated payment into their bank account.
|
||||
|
||||
A {\bf closing} fee is applied when a wallet fails to
|
||||
withdraw coins and money has to be sent back to the
|
||||
originating bank account.
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Table with wire fees. Wire fees are set annually.}
|
||||
\label{table:fees:wire}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Year} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
|
||||
2023 & wire & {\em CHF 0.05} \\
|
||||
2023 & closing & {\em CHF 0.10} \\
|
||||
2024 & wire & {\em CHF 0.05} \\
|
||||
2024 & closing & {\em CHF 0.10} \\
|
||||
2025 & wire & {\em CHF 0.05} \\
|
||||
2025 & closing & {\em CHF 0.10} \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
52
doc/flows/int-deposit.tex
Normal file
52
doc/flows/int-deposit.tex
Normal file
@ -0,0 +1,52 @@
|
||||
\section{Deposit} \label{sec:deposit}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Retail bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{wallet}{Review deposit fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{Deposit {(Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{wallet}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
% \prelevel
|
||||
% \prelevel
|
||||
% \begin{sdblock}{User abort?}{}
|
||||
% \mess[0]{wallet}{{Request abort}}{exchange}
|
||||
% \mess[0]{exchange}{{Abort confirmation}}{wallet}
|
||||
% \end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{A customer deposits the coins issued by a Taler exchange (payment
|
||||
service provider) into a bank account. Even if the
|
||||
bank account is owned by the same customer, the
|
||||
KYC checks from Section~\ref{sec:kyc:deposit} apply.}
|
||||
\label{fig:int:deposit}
|
||||
\end{figure}
|
||||
|
||||
We do {\bf not} permit the customer to regain control over their funds {\em
|
||||
unless} they pass the KYC/AML checks. The technical reason is simply that
|
||||
the KYC/AML checks happen {\em after} the aggregation logic and at this point
|
||||
refunds are no longer permitted. From a compliance perspective, this also
|
||||
prevents malicious customers from risk-free probing of the system.
|
60
doc/flows/int-pay.tex
Normal file
60
doc/flows/int-pay.tex
Normal file
@ -0,0 +1,60 @@
|
||||
\section{Pay} \label{sec:pay}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{merchant}{\shortstack{Merchant \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{bank}{\shortstack{Merchant bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Commercial \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{Browse catalog}{merchant}
|
||||
\mess[0]{merchant}{Commercial offer}{wallet}
|
||||
\begin{callself}{wallet}{Review offer}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{Pay {(Coins)}}{merchant}
|
||||
\mess[0]{merchant}{Deposit {(Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{merchant}
|
||||
\mess[0]{merchant}{{Refund purchase}}{wallet}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Confirm deposit}}{merchant}
|
||||
\mess[0]{merchant}{Fulfill order}{wallet}
|
||||
\begin{callself}{exchange}{Aggregate transactions}{}
|
||||
\end{callself}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
\end{sequencediagram}
|
||||
\caption{Payments from a customer to merchant result in
|
||||
depositing coins at the Taler exchange (payment service provider)
|
||||
which then credits the merchant's bank account.
|
||||
The KYC/AML checks are described in Section~\ref{sec:kyc:deposit}}
|
||||
\label{fig:int:pay}
|
||||
\end{figure}
|
||||
|
||||
{\bf Internal note:} The exchange refusing a deposit immediately based on
|
||||
unaccaptable merchant accounts may not be fully implemented (this is a very
|
||||
recent feature, after all); especially the merchant then automatically
|
||||
refunding the purchase to the customer is certainly missing. However,
|
||||
the entire situation only arises when a merchant is incorrectly configured
|
||||
and in violation of the terms of service.
|
56
doc/flows/int-pull.tex
Normal file
56
doc/flows/int-pull.tex
Normal file
@ -0,0 +1,56 @@
|
||||
\section{Pull payment (aka invoicing)} \label{sec:pull}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{payer}{\shortstack{Payer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{payee}{\shortstack{Payee \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{payee}{Review pull payment fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{payee}{{Create invoice (Wallet ID)}}{exchange}
|
||||
|
||||
\mess[0]{exchange}{{Invoice ready}}{payee}
|
||||
\mess[0]{payee}{{Send invoice (e.g. via QR code)}}{payer}
|
||||
|
||||
\begin{callself}{payer}{Review invoice}{}
|
||||
\end{callself}
|
||||
\mess[0]{payer}{{Make payment (Coins)}}{exchange}
|
||||
|
||||
\begin{sdblock}{Domestic wallet?}{}
|
||||
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
|
||||
\mess[0]{exchange}{{Distribute digital cash}}{payee}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{Interactions between wallets and Taler exchange
|
||||
in a pull payment. KYC/AML checks are described in
|
||||
Section~\ref{sec:kyc:pull}.}
|
||||
\label{fig:int:pull}
|
||||
\end{figure}
|
||||
|
||||
We do {\bf not} permit the payer to regain control over their funds, once the
|
||||
payment was made they are locked {\em until} the payee passes the KYC/AML
|
||||
checks. We only do the AML/KYC process once the funds are locked at the
|
||||
exchange. This ensures we know the actual transacted amounts (which may be
|
||||
lower than the total amounts requested) and prevents risk-free probing
|
||||
attacks.
|
48
doc/flows/int-push.tex
Normal file
48
doc/flows/int-push.tex
Normal file
@ -0,0 +1,48 @@
|
||||
\section{Push payment} \label{sec:push}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{payer}{\shortstack{Payer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{payee}{\shortstack{Payee \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{payer}{Review push payment fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{payer}{{Push funds (Coins)}}{exchange}
|
||||
\mess[0]{payer}{{Offer payment (e.g. via QR code)}}{payee}
|
||||
\begin{callself}{payee}{Review payment offer}{}
|
||||
\end{callself}
|
||||
\mess[0]{payee}{{Request funds (Wallet ID)}}{exchange}
|
||||
\begin{sdblock}{Domestic wallet?}{}
|
||||
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Distribute digital cash}}{payee}
|
||||
% \postlevel
|
||||
\begin{sdblock}{Payment offer expired?}{}
|
||||
\mess[0]{exchange}{{Return funds}}{payer}
|
||||
\end{sdblock}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{Interactions between wallets and Taler exchange
|
||||
in a push payment. KYC/AML checks are described
|
||||
in Section~\ref{sec:kyc:push}.}
|
||||
\label{fig:int:push}
|
||||
\end{figure}
|
39
doc/flows/int-refund.tex
Normal file
39
doc/flows/int-refund.tex
Normal file
@ -0,0 +1,39 @@
|
||||
\section{Refund}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{merchant}{\shortstack{Merchant \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{merchant}{Initiate refund}{}
|
||||
\end{callself}
|
||||
\mess[0]{merchant}{{Refund offer (QR code)}}{wallet}
|
||||
\mess[0]{wallet}{Download refund}{merchant}
|
||||
\mess[0]{merchant}{Approve refund}{exchange}
|
||||
\mess[0]{exchange}{Confirm refund}{merchant}
|
||||
\mess[0]{merchant}{Return refund confirmation}{wallet}
|
||||
\end{sequencediagram}
|
||||
\caption{Refund processing when a merchant is unable to fulfill
|
||||
a contract. Refunds must happen {\em before} the
|
||||
exchange has aggregated the original transaction for
|
||||
a bank transfer to the merchant. Furthermore, refunds
|
||||
can only go to the customer who made the original payment
|
||||
and the refund cannot exceed the amount of the original
|
||||
payment.}
|
||||
\label{fig:int:refund}
|
||||
\end{figure}
|
48
doc/flows/int-shutdown.tex
Normal file
48
doc/flows/int-shutdown.tex
Normal file
@ -0,0 +1,48 @@
|
||||
\section{Shutdown}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Customer bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
|
||||
\begin{callself}{exchange}{Operator initiates shutdown}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{{Shutdown alert}}{wallet}
|
||||
\begin{sdblock}{Bank account known?}{}
|
||||
\begin{callself}{wallet}{Designate bank account}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{wallet}{{Deposit (Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{wallet}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
\end{sequencediagram}
|
||||
\caption{Shutdown interactions between customer, Taler exchange (payment
|
||||
service provider) and bank.}
|
||||
\label{fig:int:shutdown}
|
||||
\end{figure}
|
||||
|
||||
KYC/AML requirements are relaxed in cases where the customer is able to
|
||||
cryptographically demonstrate that they previously withdrew these coins from
|
||||
the designated checking account. Thus, KYC/AML checks here primarily still
|
||||
apply if the customer received the funds via P2P transfers from other wallets.
|
49
doc/flows/int-withdraw.tex
Normal file
49
doc/flows/int-withdraw.tex
Normal file
@ -0,0 +1,49 @@
|
||||
\section{Withdraw}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Customer bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{Withdraw {(Amount)}}{exchange}
|
||||
\mess[0]{exchange}{{Configuration (ToS, Fees)}}{wallet}
|
||||
\begin{sdblock}{once}{}
|
||||
\begin{callself}{wallet}{Accept ToS}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{callself}{wallet}{Review withdraw fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{{Initiate transfer (Amount, Credit account, Wallet ID)}}{bank}
|
||||
\mess[0]{bank}{{Credit (Wallet ID)}}{exchange}
|
||||
|
||||
\begin{sdblock}{Acceptable transfer?}{}
|
||||
\mess[0]{exchange}{{Bounce funds}}{bank}
|
||||
\end{sdblock}
|
||||
\postlevel
|
||||
\mess[0]{exchange}{Confirm wire transfer}{wallet}
|
||||
\mess[0]{wallet}{Request digital cash}{exchange}
|
||||
\mess[0]{exchange}{Distribute digital cash}{wallet}
|
||||
\postlevel
|
||||
\begin{sdblock}{Withdraw period expired?}{}
|
||||
\mess[0]{exchange}{{Return remaining funds}}{bank}
|
||||
\end{sdblock}
|
||||
\end{sequencediagram}
|
||||
\caption{Withdraw interactions between customer, Taler exchange (payment
|
||||
service provider) and bank. The amount of digital cash distributed is
|
||||
subject to limits per origin account (see Section~\ref{sec:kyc:withdraw}).}
|
||||
\label{fig:int:withdraw}
|
||||
\end{figure}
|
58
doc/flows/kyc-balance.tex
Normal file
58
doc/flows/kyc-balance.tex
Normal file
@ -0,0 +1,58 @@
|
||||
\section{KYC: Balance}
|
||||
|
||||
Note: this process is not implemented and would require non-trivial extra work
|
||||
if required.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (balance) [decision,below=of start,text width=3cm] {Transaction leaves wallet balance below AML threshold?};
|
||||
\node (registered) [decision,below=of balance,text width=3cm] {Wallet has been subject to KYC?};
|
||||
\node (kyc) [process, below=of registered] {KYC process};
|
||||
\node (aml) [process, left=of kyc] {AML process};
|
||||
\node (allow) [end, right=of balance] {Allow};
|
||||
\node (deny) [failed, right=of registered] {Deny};
|
||||
\draw[arr] (start) -> (balance) {};
|
||||
\draw[arr] (balance) -> (registered);
|
||||
\draw (balance) edge["No"] (registered);
|
||||
\draw[arr] (balance) -> (allow);
|
||||
\draw (balance) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (registered) -> (kyc);
|
||||
\draw (registered) edge["No"] (kyc);
|
||||
\draw[arr] (registered) -> (deny);
|
||||
\draw (registered) edge["Yes"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
\draw[arr] (kyc) -> (aml);
|
||||
\draw (kyc) edge["Ok"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (balance.west);
|
||||
\draw (aml) edge["New threshold"] (balance.west);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when a wallet exceeds its AML threshold.
|
||||
When the transfer is denied the transaction (withdraw, P2P transfer)
|
||||
is refused by the wallet.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the balance trigger.}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
KYC threshold & Amount & {\em 5000 CHF} \\
|
||||
Default AML threshold & Amount & {\em 5000 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
80
doc/flows/kyc-deposit.tex
Normal file
80
doc/flows/kyc-deposit.tex
Normal file
@ -0,0 +1,80 @@
|
||||
\section{KYC: Deposit} \label{sec:kyc:deposit}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (country) [decision,below=of start,text width=2.5cm] {Target account in allowed country?};
|
||||
\node (amount) [decision, below=of country,text width=2.5cm] {Target account received less than KYB threshold?};
|
||||
\node (kyc) [process, right=of amount] {KYB process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Target account received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (country) {};
|
||||
|
||||
\draw[arr] (country) -> (amount);
|
||||
\draw (country) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (country.east) -> (deny);
|
||||
\draw (country.east) edge["No"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when depositing digital cash into a bank
|
||||
account. When the transfer is denied, the money is held in escrow
|
||||
until authorities authorize the transfer.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the deposit trigger. Note that the operation
|
||||
must satisfy all of the given rules.}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
|
||||
KYB deposit threshold & Amount/month & {\em 5000 CHF} \\
|
||||
KYB deposit threshold & Amount/year & {\em 25000 CHF} \\
|
||||
Default AML deposit threshold & Amount/month & {\em 2500 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The KYB deposit threshold of 5'000 \CURRENCY{} per month and than 25'000
|
||||
\CURRENCY{} per year ensure compliance with article 48-1b.
|
||||
|
||||
Additionally, our terms of service will prohibit businesses to receive
|
||||
amounts exceeding 1'000 \CURRENCY{} per transaction (well below the
|
||||
15'000 \CURRENCY{} threshold defined in article 24-1c).
|
86
doc/flows/kyc-pull.tex
Normal file
86
doc/flows/kyc-pull.tex
Normal file
@ -0,0 +1,86 @@
|
||||
\section{KYC/AML: Pull Payment} \label{sec:kyc:pull}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
|
||||
\node (domestic) [process, right=of wallet] {Validate phone number};
|
||||
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
|
||||
\node (kyc) [process, right=of amount] {KYC process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow invoicing};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (wallet) {};
|
||||
|
||||
\draw[arr] (wallet) -> (amount);
|
||||
\draw (wallet) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (wallet.east) -> (domestic);
|
||||
\draw (wallet.east) edge["No"] (domestic);
|
||||
|
||||
\draw[arr] (domestic) -> (amount);
|
||||
\draw (domestic) edge["Confirmed"] (amount);
|
||||
|
||||
\draw[arr] (domestic) -> (deny);
|
||||
\draw (domestic) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when receiving payments from another wallet.
|
||||
The threshold depends on the risk profile from the KYC process.
|
||||
When KYC thresholds would be passed, the receiving wallet cannot
|
||||
generate a valid invoice until it has provided the KYC data.
|
||||
When a transfer is denied by AML staff, the money is held in escrow
|
||||
until authorities authorize the transfer.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the pull payment trigger. Note that the operation
|
||||
must satisfy all of the given rules.}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Permitted phone numbers & Dialing prefix & {\em +41} \\
|
||||
P2P KYC threshold & Amount/month & {\em 1000 CHF} \\
|
||||
P2P KYC threshold & Amount/year & {\em 5000 CHF} \\
|
||||
Default P2P AML threshold & Amount/month & {\em 2500 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The P2P KYC thresholds of 1'000 \CURRENCY{} per month and than 5'000
|
||||
\CURRENCY{} per year ensure compliance with article 49-2c.
|
84
doc/flows/kyc-push.tex
Normal file
84
doc/flows/kyc-push.tex
Normal file
@ -0,0 +1,84 @@
|
||||
\section{KYC/AML: Push Payment} \label{sec:kyc:push}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
|
||||
\node (domestic) [process, right=of wallet] {Validate phone number};
|
||||
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
|
||||
\node (kyc) [process, right=of amount] {KYC process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (wallet) {};
|
||||
|
||||
\draw[arr] (wallet) -> (amount);
|
||||
\draw (wallet) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (wallet.east) -> (domestic);
|
||||
\draw (wallet.east) edge["No"] (domestic);
|
||||
|
||||
\draw[arr] (domestic) -> (amount);
|
||||
\draw (domestic) edge["Confirmed"] (amount);
|
||||
|
||||
\draw[arr] (domestic) -> (deny);
|
||||
\draw (domestic) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when receiving payments from another wallet.
|
||||
The threshold depends on the risk profile from the KYC process.
|
||||
When the transfer is denied, the money is held in escrow
|
||||
until authorities authorize the transfer.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the push payment trigger. Note that the operation
|
||||
must satisfy all of the given rules.}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Permitted phone numbers & Dialing prefix & {\em +41} \\
|
||||
P2P KYC threshold & Amount/month & {\em 1000 CHF} \\
|
||||
P2P KYC threshold & Amount/year & {\em 5000 CHF} \\
|
||||
Default P2P AML threshold & Amount/month & {\em 2500 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The P2P KYC thresholds of 1'000 \CURRENCY{} per month and than 5'000
|
||||
\CURRENCY{} per year ensure compliance with article 49-2c.
|
55
doc/flows/kyc-withdraw.tex
Normal file
55
doc/flows/kyc-withdraw.tex
Normal file
@ -0,0 +1,55 @@
|
||||
\section{KYC: Withdraw} \label{sec:kyc:withdraw}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (country) [decision,below=of start,text width=3cm] {Wire transfer originates from allowed country?};
|
||||
\node (amount) [decision, below=of country,text width=3cm] {Transferred less than maximum amount from origin account over last month?};
|
||||
\node (allow) [end, below=of amount] {Allow};
|
||||
\node (deny) [failed, right=of allow] {Deny};
|
||||
\draw[arr] (start) -> (country) {};
|
||||
\draw[arr] (country) -> (amount);
|
||||
\draw (country) edge["Yes"] (amount);
|
||||
\draw[arr] (country.east) -> (deny);
|
||||
\draw (country.east) edge["No"] (deny);
|
||||
\draw[arr] (amount) -> (allow);
|
||||
\draw (amount) edge["Yes"] (allow);
|
||||
\draw[arr] (amount.east) -> (deny);
|
||||
\draw (amount.east) edge["No"] (deny);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when withdrawing digital cash from a
|
||||
bank account.
|
||||
If the transfer is denied or the user fails to withdraw the
|
||||
funds for any other reason, the money is automatically returned
|
||||
after the bounce period (see Table~\ref{table:kyc:withdraw:settings}) to
|
||||
the originating bank account.}
|
||||
\label{fig:kyc:withdraw}
|
||||
\end{figure}
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the withdraw trigger. Note that the operation
|
||||
must satisfy all of the given rules.} \label{table:kyc:withdraw:settings}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
|
||||
SMS-Identification & Amount/month & {\em 200 CHF} \\
|
||||
Withdraw limit & Amount/month & {\em 5000 CHF} \\
|
||||
Withdraw limit & Amount/year & {\em 25000 CHF} \\
|
||||
Bounce period & Delay & 1 month \\
|
||||
\end{tabular}
|
||||
\end{table}
|
||||
|
||||
The limit of 200 \CURRENCY{} results from article 48-2. Strictly limiting
|
||||
withdrawals to less than 5'000 \CURRENCY{} per month and less than 25'000
|
||||
\CURRENCY{} per year assures compliance with article 48-1c.
|
239
doc/flows/main.de.tex
Normal file
239
doc/flows/main.de.tex
Normal file
@ -0,0 +1,239 @@
|
||||
% This is a (partial) translation of main.tex into
|
||||
% German. Please keep the structure as parallel as
|
||||
% possible when improving / expanding the translation!
|
||||
\documentclass[10pt,a4paper,oneside]{book}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{url}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{qrcode}
|
||||
\usepackage{pgf-umlsd}
|
||||
\usepackage{tikz}
|
||||
\usetikzlibrary{shapes,arrows}
|
||||
\usetikzlibrary{positioning}
|
||||
\usetikzlibrary{calc}
|
||||
\usetikzlibrary{quotes}
|
||||
\author{Christian Grothoff}
|
||||
\title{Flows in the GNU Taler System}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\newcommand\TALER{TALER OPERATIONS AG}
|
||||
\newcommand\CURRENCY{CHF}
|
||||
\newcommand\LAND{der Schweiz}
|
||||
|
||||
\section{Transaktionen im Taler-Bezahlsystem}\label{sec:Transaktionen}
|
||||
|
||||
Dieser Abschnitt stellt die Transaktionen im Taler-Bezahlsystem
|
||||
vor. Die Grafiken geben wieder, in welcher Reihenfolge die beteiligten
|
||||
Parteien interagieren. \\
|
||||
F\"ur jede einzelne Transaktion ist die automatische Ausl\"osung von
|
||||
Compliance-Prozessen durch den Taler-Exchange einstellbar.
|
||||
Die im Rahmen des jeweiligen Compliance-Prozesses erzwungenen
|
||||
Pr\"ufschritte beschreibt Abschnitt~\ref{sec:triggers}.
|
||||
|
||||
Folgende Transaktionen kommen als Ausl\"oser f\"ur AML- und KYC-Prozesse
|
||||
in Betracht:
|
||||
\begin{description}[noitemsep]
|
||||
\item[withdraw] Ein Nutzer hebt digitales Bargeld (e-money) in Form von
|
||||
Taler-Coins in ein Taler-Wallet ab
|
||||
\item[reimburse] Ein Nutzer l\"asst den Gegenwert von Taler-Coins vom
|
||||
Taler-Exchange an das urspr\"ungliche IBAN-Bankkonto zur\"uck\"uberweisen
|
||||
\item[pay] Ein Nutzer zahlt zugunsten eines IBAN-Bankkontos des Empf\"angers
|
||||
\item[refund] Ein Verk\"aufer erteilt einem Zahlenden die R\"uckerstattung
|
||||
eines Zahlbetrags
|
||||
\item[push] Ein Nutzer sendet einen Zahlbetrag an ein anderes Taler-Wallet
|
||||
\item[pull] Ein Nutzer stellt einem anderen Taler-Wallet eine Rechnung aus
|
||||
und fordert eine Zahlung von diesem Wallet
|
||||
\item[shutdown] Der Betreiber des Taler-Exchange informiert die Inhaber von
|
||||
Coins, die diese von jenem Exchange abgehoben hatten, dass der Exchange
|
||||
geplant eingestellt und die Gegenwerte der Coins restituiert werden
|
||||
\end{description}
|
||||
|
||||
Die Nutzer beginnen ein gesch\"aftliches Nutzungsverh\"altnis mit
|
||||
\TALER{}, wenn sie ihre Taler-Wallets anweisen, eine Abhebung durchzuf\"uhren.
|
||||
Das Taler-Bezahlsystem verwendet jedoch keine Konten, sondern wert-basierte
|
||||
Token und explizit keine konten-basierten Geld-\"Aquivalente.
|
||||
Taler soll digitales Bargeld sein und erlaubt technisch bedingt
|
||||
kein Nachvollziehen der Transaktionen seiner Nutzer, wie es Konten mit
|
||||
Eing\"angen und Ausg\"angen von Zahlungen erm\"oglichen w\"urden.
|
||||
Es gibt daher kein ``Er\"offnen'' oder ``Schliessen'' von Konten der Nutzer.
|
||||
Die Begriffe ``opening'' und ``closing'' lassen sich deshalb auch nicht auf
|
||||
das System anwenden oder \"ubertragen. \\
|
||||
|
||||
Die Nutzer k\"onnen
|
||||
\begin{enumerate}[noitemsep]
|
||||
\item die treuh\"andisch verwalteten Einlagen gezielt auf ein bestimmtes
|
||||
Bankkonto auszahlen lassen,
|
||||
%(siehe Abschnitt~\ref{sec:deposit})
|
||||
\item an einen Verk\"aufer zahlen,
|
||||
%(siehe Abschnitt~\ref{sec:deposit})
|
||||
\item einem anderen Empf\"anger mittels peer-to-peer-Verfahren Coins zukommen
|
||||
lassen
|
||||
%(siehe Abschnitte~\ref{sec:push} und~\ref{sec:pull})
|
||||
\item die Coins in ihrem Wallet, das verloren ging oder zerst\"ort wurde,
|
||||
durch Ablauf der G\"ultigkeit entwerten lassen (dies w\"are ebenso der Fall
|
||||
bei einer langen Zeit ohne Internet-Anbindung oder ohne Installation),
|
||||
\item den Wert der Coins im Wallet durch Zahlung von Geb\"uhren f\"ur
|
||||
die Verl\"angerung ihrer G\"ultigkeit langsam verringern lassen.
|
||||
%(siehe Abschnitt~\ref{sec:fees:coin})
|
||||
\end{enumerate}
|
||||
|
||||
Das Taler-Bezahlsystem verwehrt den Nutzern kategorisch die Abhebung
|
||||
von h\"oheren Betr\"agen als 5.000 \CURRENCY{} pro Monat bzw. von
|
||||
mehr als 15.000 \CURRENCY{} pro Jahr. Damit wird gew\"ahrleistet,
|
||||
dass die Nutzer stets unterhalb der Grenzwerte bleiben, ab denen die
|
||||
meisten Pr\"ufschritte aufgrund regulatorischer Bestimmungen erforderlich
|
||||
werden. \TALER{} stellt dar\"uber hinaus sicher, dass die Nutzer
|
||||
ausschliesslich in \LAND{} ans\"assig sind
|
||||
(siehe Abschnitt~\ref{sec:proc:domestic}), da auf ihrer Seite ein Bankkonto
|
||||
in \LAND{} f\"ur die \"Uberweisungen an den Taler-Exchange und/oder
|
||||
eine Telefonnummer mit entsprechender Vorwahl (++41) ben\"otigt werden.
|
||||
Zus\"atzlich setzt das Taler-Wallet zu jeder Zeit eine Obergrenze
|
||||
von 5.000 \CURRENCY{} auf die Coin-Betr\"age in Summe fest, so dass es
|
||||
keine weitere Abhebung \"uber diesen Grenzwert hinaus bewirken kann.
|
||||
|
||||
F\"ur {\bf Verk\"aufer} beginnt ein gesch\"aftliches Nutzungsverh\"altnis
|
||||
mit \TALER{}, sobald sie Geldeing\"ange auf ihren IBAN-Bankkonten erhalten,
|
||||
die als Zahlungen von Nutzern des Taler-Bezahlsystems ausgel\"ost wurden
|
||||
(siehe Abschnitt~\ref{sec:deposit}). Sollten die Summen der Eing\"ange
|
||||
5.000 \CURRENCY{} pro Monat bzw. 15.000 \CURRENCY{} pro Jahr \"ubersteigen,
|
||||
kommt es zu einer KYB-Pr\"ufung, die dem Begriff ``Er\"offnen'' eines
|
||||
Kontos entspricht und die eine aktualisierte KYB-Information sowie
|
||||
die Pr\"ufung von Sanktionslisten erfordert, sofern der Verk\"aufer
|
||||
innerhalb von 24 Monaten wenigstens einen Geldeingang erhielt.
|
||||
|
||||
Im Gegensatz zu normalen Nutzern k\"onnen Verk\"aufer im Prinzip
|
||||
Zahlungen ohne Limit empfangen. Allerdings m\"ussen diese Transaktionen
|
||||
auch wirklich als Eing\"ange auf dem Bankkonto des Unternehmens verzeichnet
|
||||
werden (im Kontoauszug). In Abh\"angigkeit von den an das Gesch\"aftskonto
|
||||
\"uberwiesenen Betr\"agen wird der Verk\"aufer einer KYB-Pr\"ufung unterzogen
|
||||
(siehe Abschnitt~\ref{sec:KYB}). Dies gilt ebenso f\"ur
|
||||
Geldw\"asche-\"Uberpr\"ufungen (AML checks).
|
||||
|
||||
Das Taler-Bezahlsystem transferiert lediglich Gelder auf die bestehenden
|
||||
Bankkonten der Verk\"aufer, die f\"ur ihre G\"uterleistungen Zahlungen
|
||||
der Nutzer erhalten, f\"ur die bereits bei der \"Uberweisung von deren
|
||||
Kundenkonten eine KYC-Pr\"ufung erfolgte. Daher wird unseres Erachtens
|
||||
der Betreiber eines Taler-Exchange keine Mittelherkunft verlangen bzw.
|
||||
nachweisen m\"ussen
|
||||
\footnote{Wenn Unternehmen das Taler-Bezahlsystem ihrerseits f\"ur
|
||||
Zahlungen nutzen wollen, m\"ussen sie genauso wie alle anderen Nutzer
|
||||
zuerst Geld von ihrem Bankkonto an einen Taler-Exchange \"uberweisen,
|
||||
eine KYC-Pr\"ufung absolvieren und dann ihr Wallet Coins abheben lassen.
|
||||
F\"ur die gesch\"aftlichen K\"aufer gelten ebenfalls die Limits wie
|
||||
f\"ur alle anderen Nutzer.}.
|
||||
|
||||
|
||||
\include{int-withdraw}
|
||||
\include{int-deposit}
|
||||
\include{int-pay}
|
||||
\include{int-refund}
|
||||
\include{int-push}
|
||||
\include{int-pull}
|
||||
\include{int-shutdown}
|
||||
|
||||
|
||||
|
||||
\chapter{Regulatory Triggers} \label{chap:triggers}
|
||||
|
||||
In this chapter we show decision diagrams for regulatory processes of the
|
||||
various core operations of the GNU Taler payment system. In each case, the
|
||||
{\bf start} state refers to one of the interactions described in the previous
|
||||
chapter. The payment system will then use the process to arrive at an {\bf
|
||||
allow} decision which permits the transaction to go through, or at a {\bf
|
||||
deny} decision which ensures that the funds are not moved.
|
||||
|
||||
The specific {\em decisions} (in green) depend on the risk profile and the
|
||||
regulatory environment. The tables in each section list the specific values
|
||||
that are to be configured.
|
||||
|
||||
There are five types if interactions that can trigger regulatory processes:
|
||||
|
||||
\begin{description}
|
||||
\item[withdraw] a customer withdraws digital cash from their {\bf bank account}
|
||||
\item[deposit] a merchant's {\bf bank account} is designated to receive a payment in digital cash
|
||||
\item[push] a {\bf wallet} accepts a payment from another wallet
|
||||
\item[pull] a {\bf wallet} requests a payment from another wallet
|
||||
\item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
|
||||
\end{description}
|
||||
|
||||
We note in bold the {\bf anchor} for the regulator process. The anchor is used
|
||||
to link the interaction to an identity. Once an identity has been established
|
||||
for a particular anchor, that link is considered established for all types of
|
||||
activities involving that anchor. A wallet is uniquely identified in the
|
||||
system by its unique cryptographic key. A bank account is uniquely identified
|
||||
in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
|
||||
and account owner name).
|
||||
|
||||
The KYC and AML processes themselves are described in
|
||||
Chapter~\ref{chap:regproc}.
|
||||
|
||||
\include{kyc-withdraw}
|
||||
\include{kyc-deposit}
|
||||
\include{kyc-push}
|
||||
\include{kyc-pull}
|
||||
\include{kyc-balance}
|
||||
|
||||
\chapter{Regulatory Processes} \label{chap:regproc}
|
||||
|
||||
This chapter describes the interactions between the customer, exchange and
|
||||
organizations or staff assisting with regulatory processes designed to ensure
|
||||
that customers are residents in the area of operation of the payment service
|
||||
provider, are properly identified, and do not engage in money laundering.
|
||||
|
||||
The three main regulatory processes are:
|
||||
|
||||
\begin{description}
|
||||
\item[domestic check] This process establishes that a user is generally
|
||||
eligible to use the payment system. The process checks that the user has an
|
||||
eligible address, but stops short of establishing the user's identity.
|
||||
\item[kyc] This process establishes a user's legal identity, possibly
|
||||
using external providers to review documents and check against blacklists.
|
||||
\item[aml] The AML process reviews suspicious payment activities for
|
||||
money laundering. Here AML staff reviews all collected information.
|
||||
\end{description}
|
||||
|
||||
\include{proc-domestic}
|
||||
%\include{proc-kyc}
|
||||
\include{proc-kyb}
|
||||
\include{proc-aml}
|
||||
|
||||
\chapter{Fees} \label{chap:fees}
|
||||
|
||||
The business model for operating a Taler exchange is to charge transaction
|
||||
fees. Fees are charged on certain operations by the exchange. There are two
|
||||
types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
|
||||
the fee structure.
|
||||
|
||||
Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
|
||||
the core banking system. Details on wire fees are described in
|
||||
Section~\ref{sec:fees:wire}.
|
||||
|
||||
Coin fees are more complex, as they do not exactly follow neither the usual
|
||||
percentage of volume model of other payment systems. Instead, coin fees are
|
||||
applied per coin, resulting in a {\em logarithmic} fee structure. As a
|
||||
result, the effective fee {\em percentage} for tiny transactions is high (for
|
||||
example 50\% for transactions of 0.0025 CHF) while the effective fee
|
||||
percentage for large transactions is nominal (for example $\approx$ 0.05\% for
|
||||
transactions of $\approx$ 40 CHF). Details on coin fees are described in
|
||||
Section~\ref{sec:fees:coin}.
|
||||
|
||||
Fees are configurable (and that fee types beyond those described here are
|
||||
supported by the software). Thus, the specific fees may be adjusted in the
|
||||
future based on business decisions. However, changes to the fees are never
|
||||
retroactively applied to coins already in circulation. Wire fees that have
|
||||
been publicly announced for a particular time period also cannot be changed.
|
||||
Finally, any change to the terms of service must also be explicitly accepted
|
||||
by the users before they withdraw additional funds.
|
||||
|
||||
|
||||
\include{fees-wire}
|
||||
\include{fees-coins}
|
||||
%\include{fees-other}
|
||||
|
||||
|
||||
\end{document}
|
206
doc/flows/main.tex
Normal file
206
doc/flows/main.tex
Normal file
@ -0,0 +1,206 @@
|
||||
\documentclass[10pt,a4paper,oneside]{book}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{url}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{qrcode}
|
||||
\usepackage{pgf-umlsd}
|
||||
\usepackage{tikz}
|
||||
\usetikzlibrary{shapes,arrows}
|
||||
\usetikzlibrary{positioning}
|
||||
\usetikzlibrary{calc}
|
||||
\usetikzlibrary{quotes}
|
||||
\author{Christian Grothoff}
|
||||
\title{Flows in the GNU Taler System}
|
||||
|
||||
\newcommand\CURRENCY{CHF}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
\tableofcontents
|
||||
|
||||
\chapter{Interactions} \label{chap:interactions}
|
||||
|
||||
This chapter introduces the main payment interactions in the GNU Taler payment
|
||||
system. For each interaction, we introduce the parties involved and in which
|
||||
order they interact and how. In each interaction it is possible that the
|
||||
Taler exchange needs to trigger a compliance process. These regulatory
|
||||
riggers are described in more detail in Chapter~\ref{chap:triggers}.
|
||||
|
||||
The main interactions of the system are:
|
||||
|
||||
\begin{description}
|
||||
\item[withdraw] a customer withdraws digital cash to their wallet
|
||||
\item[deposit] a customer returns digital cash into their bank account
|
||||
\item[pay] a customer pays into bank account of a merchant
|
||||
\item[refund] a merchant decides to return funds to a customer
|
||||
\item[push] a customer sends a payment to another wallet
|
||||
\item[pull] a customer requests a payment from another wallet (effectively sending an invoice)
|
||||
\item[shutdown] the Taler payment system operator informs the customers that the system is being shut down for good
|
||||
\end{description}
|
||||
|
||||
In the analysis of the legal requirements, it is important to differentiate
|
||||
between transactions between wallets (customer-to-customer) and transactions
|
||||
where money flows from a wallet into a bank account (customer-to-merchant) as
|
||||
these have different limits: When digital coins are used to pay at a business in
|
||||
Taler, the business never actually receives usable digital coins but instead
|
||||
the amount is always directly credited to their bank account. Depending on
|
||||
the transacted amounts, the business will nevertheless be subject to KYB
|
||||
(Section~\ref{sec:proc:kyb}) and AML checks.
|
||||
|
||||
{\bf Customers} begin their business relationship with us when they withdraw
|
||||
digital cash. Taler has no accounts (this is digital cash) and thus there is
|
||||
no ``opening'' or ``closing'' of accounts for consumers. Given digital cash,
|
||||
the customers can either (1) deposit the funds explicitly into a bank account
|
||||
(see Section~\ref{sec:deposit}), (2) pay a merchant (see
|
||||
Section~\ref{sec:pay}), (3) pay another customer using a peer-to-peer
|
||||
transfer (see Sections~\ref{sec:push} and~\ref{sec:pull}), or (4) the coins
|
||||
will expire if the wallet was lost (including offline for a long time or
|
||||
uninstalled). Finally, if a wallet remains (occasionally) online but a user
|
||||
does simply not spend the coins will (5) diminish in value from the change
|
||||
fees (see Section~\ref{sec:fees:coin}) that apply to prevent the coins from
|
||||
expiring outright.
|
||||
|
||||
For customers, we will categorically limit of digital cash withdrawn per month
|
||||
to less than CHF 5'000 per month and less than CHF 25'000 per year, thus
|
||||
ensuring that consumers remain below the thresholds where most regulatory
|
||||
processes become applicable. Payments between users will be limited
|
||||
to receiving less than CHF 1'000 per month and less than CHF 5'000 per year.
|
||||
We will ensure that customers are Swiss
|
||||
(see Section~\ref{sec:proc:domestic}) by requiring them to have a Swiss bank
|
||||
account and/or Swiss phone number (+41-prefix).
|
||||
%Furthermore, the wallet will
|
||||
%impose an upper limit of CHF 5000 on its balance at any point in time.
|
||||
|
||||
For {\bf merchants}, the Taler equivalent of ``opening'' an account and thus
|
||||
establishing an ongoing business relationship is for a business to receive
|
||||
payments (see Section~\ref{sec:pay}) exceeding CHF 5'000/month or CHF
|
||||
25'000/year. We will consider the account ``open'' (and require up-to-date KYB
|
||||
information and check sanction lists) as long as the business has made any
|
||||
transactions within the last 24 months.
|
||||
|
||||
As we will only transfer money into the existing bank accounts of the
|
||||
merchants to compensate them for sales made using the Taler payment system, we
|
||||
do not need to check the origin of funds for those merchants as they will only
|
||||
receive funds from us.\footnote{Should businesses want to use Taler for
|
||||
expenditures, they will need to withdraw digital coins from their bank account
|
||||
just like customers, and the limits for customers will continue to apply.}
|
||||
|
||||
For individual {\bf transactions}, we will impose a limit of CHF
|
||||
1'000/transaction (even though our reading of the regulations would permit
|
||||
individual transactions up to CHF 15'000).
|
||||
|
||||
The following sections describe the respective processes for each of these
|
||||
interactions.
|
||||
|
||||
\include{int-withdraw}
|
||||
\include{int-deposit}
|
||||
\include{int-pay}
|
||||
\include{int-refund}
|
||||
\include{int-push}
|
||||
\include{int-pull}
|
||||
\include{int-shutdown}
|
||||
|
||||
|
||||
\chapter{Regulatory Triggers} \label{chap:triggers}
|
||||
|
||||
In this chapter we show decision diagrams for regulatory processes of the
|
||||
various core operations of the GNU Taler payment system. In each case, the
|
||||
{\bf start} state refers to one of the interactions described in the previous
|
||||
chapter. The payment system will then use the process to arrive at an {\bf
|
||||
allow} decision which permits the transaction to go through, or at a {\bf
|
||||
deny} decision which ensures that the funds are not moved.
|
||||
|
||||
The specific {\em decisions} (in green) depend on the risk profile and the
|
||||
regulatory environment. The tables in each section list the specific values
|
||||
that are to be configured.
|
||||
|
||||
There are five types if interactions that can trigger regulatory processes:
|
||||
|
||||
\begin{description}
|
||||
\item[withdraw] a customer withdraws digital cash from their {\bf bank account}
|
||||
\item[deposit] a customer or merchant's {\bf bank account} is
|
||||
designated to receive a payment due someone paying with or
|
||||
depositing digital cash
|
||||
\item[push] a {\bf wallet} accepts a payment from another wallet
|
||||
\item[pull] a {\bf wallet} requests a payment from another wallet
|
||||
% \item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
|
||||
\end{description}
|
||||
|
||||
We note in bold the {\bf anchor} for the regulator process. The anchor is used
|
||||
to link the interaction to an identity. Once an identity has been established
|
||||
for a particular anchor, that link is considered established for all types of
|
||||
activities involving that anchor. A wallet is uniquely identified in the
|
||||
system by its unique cryptographic key. A bank account is uniquely identified
|
||||
in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
|
||||
and account owner name).
|
||||
|
||||
The KYC and AML processes themselves are described in
|
||||
Chapter~\ref{chap:regproc}.
|
||||
|
||||
\include{kyc-withdraw}
|
||||
\include{kyc-deposit}
|
||||
\include{kyc-push}
|
||||
\include{kyc-pull}
|
||||
%\include{kyc-balance}
|
||||
|
||||
\chapter{Regulatory Processes} \label{chap:regproc}
|
||||
|
||||
This chapter describes the interactions between the customer, exchange and
|
||||
organizations or staff assisting with regulatory processes designed to ensure
|
||||
that customers are residents in the area of operation of the payment service
|
||||
provider, are properly identified, and do not engage in money laundering.
|
||||
|
||||
The three main regulatory processes are:
|
||||
|
||||
\begin{description}
|
||||
\item[domestic check] This process establishes that a user is generally
|
||||
eligible to use the payment system. The process checks that the user has an
|
||||
eligible address, but stops short of establishing the user's identity.
|
||||
\item[kyc] This process establishes a user's legal identity, possibly
|
||||
using external providers to review documents and check against blacklists.
|
||||
\item[aml] The AML process reviews suspicious payment activities for
|
||||
money laundering. Here AML staff reviews all collected information.
|
||||
\end{description}
|
||||
|
||||
\include{proc-domestic}
|
||||
\include{proc-kyc}
|
||||
\include{proc-kyb}
|
||||
\include{proc-aml}
|
||||
|
||||
\chapter{Fees} \label{chap:fees}
|
||||
|
||||
The business model for operating a Taler exchange is to charge transaction
|
||||
fees. Fees are charged on certain operations by the exchange. There are two
|
||||
types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
|
||||
the fee structure.
|
||||
|
||||
Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
|
||||
the core banking system. Details on wire fees are described in
|
||||
Section~\ref{sec:fees:wire}.
|
||||
|
||||
Coin fees are more complex, as they do not exactly follow neither the usual
|
||||
percentage of volume model of other payment systems. Instead, coin fees are
|
||||
applied per coin, resulting in a {\em logarithmic} fee structure. As a
|
||||
result, the effective fee {\em percentage} for tiny transactions is high (for
|
||||
example 50\% for transactions of 0.0025 CHF) while the effective fee
|
||||
percentage for large transactions is nominal (for example $\approx$ 0.05\% for
|
||||
transactions of $\approx$ 40 CHF). Details on coin fees are described in
|
||||
Section~\ref{sec:fees:coin}.
|
||||
|
||||
Fees are configurable (and that fee types beyond those described here are
|
||||
supported by the software). Thus, the specific fees may be adjusted in the
|
||||
future based on business decisions. However, changes to the fees are never
|
||||
retroactively applied to coins already in circulation. Wire fees that have
|
||||
been publicly announced for a particular time period also cannot be changed.
|
||||
Finally, any change to the terms of service must also be explicitly accepted
|
||||
by the users before they withdraw additional funds.
|
||||
|
||||
|
||||
\include{fees-wire}
|
||||
\include{fees-coins}
|
||||
%\include{fees-other}
|
||||
|
||||
|
||||
\end{document}
|
47
doc/flows/proc-aml.tex
Normal file
47
doc/flows/proc-aml.tex
Normal file
@ -0,0 +1,47 @@
|
||||
\section{AML process}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{staff}{\shortstack{AML staff \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Access \\ Token};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{Initial action}}{exchange}
|
||||
\begin{callself}{exchange}{Establish AML requirement}{}
|
||||
\end{callself}
|
||||
\begin{callself}{exchange}{Queue AML task}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Wait for AML}{wallet}
|
||||
\mess[0]{staff}{Request AML work}{exchange}
|
||||
\mess[0]{exchange}{{Open AML task(s)}}{staff}
|
||||
\mess[0]{staff}{Request details}{exchange}
|
||||
\mess[0]{exchange}{KYC/AML data}{staff}
|
||||
\begin{callself}{staff}{Review and decide}{}
|
||||
\end{callself}
|
||||
\mess[0]{staff}{{Decision documentation}}{exchange}
|
||||
\mess[0]{exchange}{AML decision}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and the AML staff. The process can be
|
||||
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.
|
||||
AML staff interactions are cryptographically secured and
|
||||
decisions and the provided reasoning are archived by the exchange.
|
||||
AML staff may interact with the customer (out-of-band)
|
||||
in its decision process.
|
||||
}
|
||||
\label{fig:proc:aml}
|
||||
\end{figure}
|
66
doc/flows/proc-domestic.tex
Normal file
66
doc/flows/proc-domestic.tex
Normal file
@ -0,0 +1,66 @@
|
||||
\section{Domestic wallet check} \label{sec:proc:domestic}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{sms}{\shortstack{Address validator}}
|
||||
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{P2P payment (Wallet ID)}}{exchange}
|
||||
\begin{callself}{exchange}{New wallet?}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Request address validation}{sms}
|
||||
\mess[0]{sms}{Validation process ID}{exchange}
|
||||
\mess[0]{exchange}{Request address validation}{wallet}
|
||||
\mess[0]{wallet}{Send address}{sms}
|
||||
\mess[0]{sms}{{Send confirmation code (to address)}}{wallet}
|
||||
\mess[0]{wallet}{Supply confirmation code}{sms}
|
||||
\mess[0]{sms}{{Confirmed customer address}}{exchange}
|
||||
\mess[0]{exchange}{{Confirm completion}}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and external address validation service. The process can be
|
||||
triggered by wallet-to-wallet (P2P) payments described in Chapter~\ref{chap:triggers}.}
|
||||
\label{fig:proc:domestic}
|
||||
\end{figure}
|
||||
|
||||
Our users have to accept the terms of service which restrict the use of the
|
||||
service to domestic customers. For interactions with the core banking system,
|
||||
this simply means that we only accept payments from or to domestic bank
|
||||
accounts. For P2P payments between wallets, we require that the wallets are
|
||||
controlled by a domestic entity. We define domestic entities as those that
|
||||
are able to receive messages at a domestic address. Two types of addresses are
|
||||
supported:
|
||||
|
||||
\begin{itemize}
|
||||
\item Control over a domestic {\bf mobile phone number} is established
|
||||
by sending an SMS message with a confirmation code to the MSIN.
|
||||
\item Control over a domestic {\bf postal address} is established by
|
||||
sending a letter with a confirmation code to the address.
|
||||
\end{itemize}
|
||||
|
||||
Depending on the type of address, a validation has a limited validity period,
|
||||
as shown in Table~\ref{table:proc:domestic}. When the validity period is
|
||||
over, a wallet has to re-do the address validation before they can receive any
|
||||
further funds through the service.
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Restrictions on address validations}
|
||||
\label{table:proc:domestic}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Type} & {\bf Validity period} & {\bf Restricted to} \\ \hline \hline
|
||||
Mobile phone number & 12 months & {\em +41} \\
|
||||
Postal address & 36 months & {\em Switzerland} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
97
doc/flows/proc-kyb.tex
Normal file
97
doc/flows/proc-kyb.tex
Normal file
@ -0,0 +1,97 @@
|
||||
\section{KYB process} \label{sec:proc:kyb}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{merchant}{\shortstack{Merchant \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{kyb}{\shortstack{KYB provider \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
|
||||
\postlevel
|
||||
\mess[0]{merchant}{{Initial action}}{exchange}
|
||||
\begin{callself}{exchange}{Establish KYB requirement}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Request new KYB process}{kyb}
|
||||
\mess[0]{kyb}{{Process identifier (PI)}}{exchange}
|
||||
\mess[0]{exchange}{{KYB required (PI)}}{merchant}
|
||||
\mess[0]{merchant}{{KYB start (PI)}}{kyb}
|
||||
\mess[0]{kyb}{{Request identity documentation}}{merchant}
|
||||
\mess[0]{merchant}{{Upload identity documentation}}{kyb}
|
||||
\begin{callself}{kyb}{Validate documentation}{}
|
||||
\end{callself}
|
||||
\mess[0]{kyb}{{Share documentation (PI)}}{exchange}
|
||||
\mess[0]{kyb}{{Confirm completion}}{merchant}
|
||||
\mess[0]{merchant}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and external KYB provider. The process can be
|
||||
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
|
||||
\label{fig:proc:kyb}
|
||||
\end{figure}
|
||||
|
||||
At the beginning of the KYB process, the user needs to specify whether they
|
||||
are an {\bf individual} (not incorporated) or a {\bf business}.\footnote{In
|
||||
practice, we expect most owners of bank accounts crossing the KYB threshold to
|
||||
be businesses, but in principle such a bank account could be owned by an
|
||||
individual operating a business without a separate legal entity.} This then
|
||||
determines which types of attributes are collected in the KYB process
|
||||
(Table~\ref{table:proc:kyb:individual}
|
||||
vs. Table~\ref{table:proc:kyb:business}).
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for unincorporated individuals}
|
||||
\label{table:proc:kyb:individual}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Surname & yes & Mustermann \\
|
||||
First name(s) & yes & Max \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\
|
||||
Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
E-mail & no & me@example.com \\
|
||||
Identification document & yes & JPG image \\
|
||||
Taxpayer identification & yes & ZPV Nr. 253'123'456 \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for businesses. Information on individals is
|
||||
collected for owners with more than 25\% ownership and for those with
|
||||
signature authority for the business.}
|
||||
\label{table:proc:kyb:business}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Company name & yes & Mega AG \\
|
||||
Registered office & yes & Seestrasse 4, 8008 Zuerich \\
|
||||
Company identification document & yes & PDF file \\
|
||||
Power of attorney arrangement & yes & PDF file \\
|
||||
Business registration number & yes & \\
|
||||
Business registration document & yes & PDF file \\
|
||||
Registration authority & yes & \\ \hline
|
||||
Contact person name & yes & Max Mustermann \\
|
||||
Identification document & yes & JPG image \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\
|
||||
E-mail & yes & me@example.com \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
88
doc/flows/proc-kyc.tex
Normal file
88
doc/flows/proc-kyc.tex
Normal file
@ -0,0 +1,88 @@
|
||||
\section{KYC process}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{kyc}{\shortstack{KYC provider \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{Initial action}}{exchange}
|
||||
\begin{callself}{exchange}{Establish KYC requirement}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Request new KYC process}{kyc}
|
||||
\mess[0]{kyc}{{Process identifier (PI)}}{exchange}
|
||||
\mess[0]{exchange}{{KYC required (PI)}}{wallet}
|
||||
\mess[0]{wallet}{{KYC start (PI)}}{kyc}
|
||||
\mess[0]{kyc}{{Request identity documentation}}{wallet}
|
||||
\mess[0]{wallet}{{Upload identity documentation}}{kyc}
|
||||
\begin{callself}{kyc}{Validate documentation}{}
|
||||
\end{callself}
|
||||
\mess[0]{kyc}{{Share documentation (PI)}}{exchange}
|
||||
\mess[0]{kyc}{{Confirm completion}}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and external KYC provider. The process can be
|
||||
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
|
||||
\label{fig:proc:kyc}
|
||||
\end{figure}
|
||||
|
||||
At the beginning of the KYC process, the user needs to specify whether they
|
||||
are an {\bf individual} or a {\bf business}.\footnote{ In practice, we expect
|
||||
most wallet-users to be individuals, but in principle a wallet could be owned
|
||||
by a business.} This then determines which types of attributes are collected
|
||||
in the KYC process (Table~\ref{table:proc:kyc:individual} vs.
|
||||
Table~\ref{table:proc:kyc:business}).
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for individuals}
|
||||
\label{table:proc:kyc:individual}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Surname & yes & Mustermann \\
|
||||
First name(s) & yes & Max \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\
|
||||
Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
E-mail & no & me@example.com \\
|
||||
Identification document & yes & JPG image \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for businesses}
|
||||
\label{table:proc:kyc:business}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Company name & yes & Mega AG \\
|
||||
Registered office & yes & Seestrasse 4, 8008 Zuerich \\
|
||||
Company identification document & yes & PDF file \\ \hline
|
||||
Contact person name & yes & Max Mustermann \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
E-mail & yes & me@example.com \\
|
||||
Identification document & yes & JPG image \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\ \hline
|
||||
Power of attorney arrangement & yes & PDF file \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
@ -1 +1 @@
|
||||
Subproject commit 8452f991dd967328207fab52a99beb19e2cb4dff
|
||||
Subproject commit 6026efb59ef8c41e5b86e68332780d387fdaab0a
|
@ -218,6 +218,7 @@ EXTRA_DIST = \
|
||||
taler-auditor.in \
|
||||
taler-helper-auditor-render.py \
|
||||
auditor.conf \
|
||||
setup.sh \
|
||||
test-sync-in.conf \
|
||||
test-sync-out.conf \
|
||||
generate-auditor-basedb.sh \
|
||||
|
@ -17,7 +17,7 @@ AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
|
||||
|
||||
# What is the Web site of the auditor (i.e. to file complaints about
|
||||
# a misbehaving exchange)?
|
||||
# BASE_URL = https://auditor.taler.net/
|
||||
BASE_URL = http://localhost:8083/
|
||||
|
||||
|
||||
# Network configuration for the normal API/service HTTP server
|
||||
|
@ -1,14 +1,27 @@
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE = auditor-basedb.mpriv
|
||||
[PATHS]
|
||||
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
|
||||
TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
|
||||
TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
|
||||
TALER_HOME = ${PWD}/generate_auditordb_home/
|
||||
|
||||
[instance-default]
|
||||
KEYFILE = ${TALER_DATA_HOME}/merchant/default.priv
|
||||
NAME = Merchant Inc.
|
||||
[taler]
|
||||
CURRENCY = TESTKUDOS
|
||||
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
|
||||
|
||||
[exchange]
|
||||
MASTER_PUBLIC_KEY = M4FGP18EQFXFGGFQ1AWXHACN2JX0SMVK9CNF6459Z1WG18JSN0BG
|
||||
SIGNKEY_DURATION = 4 weeks
|
||||
LOOKAHEAD_SIGN = 32 weeks 1 day
|
||||
SIGNKEY_LEGAL_DURATION = 4 weeks
|
||||
AML_THRESHOLD = TESTKUDOS:1000000
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[exchange-account-1]
|
||||
PAYTO_URI = payto://iban/SANDBOXX/DE989651?receiver-name=Exchange+Company
|
||||
enable_debit = yes
|
||||
enable_credit = yes
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-1]
|
||||
WIRE_GATEWAY_URL = http://localhost:8082/facades/test-facade/taler-wire-gateway/
|
||||
@ -16,24 +29,38 @@ WIRE_GATEWAY_AUTH_METHOD = basic
|
||||
USERNAME = exchange
|
||||
PASSWORD = x
|
||||
|
||||
[merchant-account-merchant]
|
||||
PAYTO_URI = payto://x-taler-bank/localhost/42
|
||||
HONOR_default = YES
|
||||
ACTIVE_default = YES
|
||||
[merchant]
|
||||
WIREFORMAT = default
|
||||
DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
|
||||
KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
|
||||
DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
|
||||
WIRE_TRANSFER_DELAY = 1 minute
|
||||
FORCE_AUDIT = YES
|
||||
|
||||
[merchantdb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[merchant-exchange-default]
|
||||
MASTER_KEY = RKNMPRGXCX35H11WEYXDXYHPR7NX2QK9BG15MT0QEF75PC5KR470
|
||||
MASTER_KEY = M4FGP18EQFXFGGFQ1AWXHACN2JX0SMVK9CNF6459Z1WG18JSN0BG
|
||||
EXCHANGE_BASE_URL = http://localhost:8081/
|
||||
CURRENCY = TESTKUDOS
|
||||
|
||||
[payments-generator]
|
||||
currency = TESTKUDOS
|
||||
instance = default
|
||||
bank = http://localhost:8082/
|
||||
merchant = http://localhost:9966/
|
||||
exchange_admin = http://localhost:18080/
|
||||
exchange-admin = http://localhost:18080/
|
||||
exchange = http://localhost:8081/
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
|
||||
[libeufin-nexus]
|
||||
DB_CONNECTION="jdbc:postgresql://localhost/auditor-basedb?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
|
||||
|
||||
[libeufin-sandbox]
|
||||
DB_CONNECTION="jdbc:postgresql://localhost/auditor-basedb?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
|
||||
|
||||
[auditor]
|
||||
BASE_URL = http://localhost:8083/
|
||||
TINY_AMOUNT = TESTKUDOS:0.01
|
||||
PUBLIC_KEY = 0EHPW5WEKHXPPN4MPJNGA7Z6D29JP21GKVNV8ARFB1YW7WWJX20G
|
||||
|
||||
[auditordb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[coin_kudos_ct_1]
|
||||
value = TESTKUDOS:0.01
|
||||
@ -130,61 +157,3 @@ fee_refresh = TESTKUDOS:0.03
|
||||
fee_refund = TESTKUDOS:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 1024
|
||||
|
||||
[benchmark]
|
||||
BANK_DETAILS = bank_details.json
|
||||
MERCHANT_DETAILS = merchant_details.json
|
||||
|
||||
[arm]
|
||||
CONFIG = /research/taler/exchange/src/auditor/auditor-basedb.conf
|
||||
|
||||
[taler]
|
||||
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
|
||||
CURRENCY = TESTKUDOS
|
||||
AML_THRESHOLD = TESTKUDOS:1000000
|
||||
|
||||
[merchantdb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[merchant]
|
||||
WIREFORMAT = default
|
||||
DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
|
||||
KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
|
||||
DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
|
||||
WIRE_TRANSFER_DELAY = 1 minute
|
||||
FORCE_AUDIT = YES
|
||||
UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[exchange]
|
||||
MASTER_PUBLIC_KEY = RKNMPRGXCX35H11WEYXDXYHPR7NX2QK9BG15MT0QEF75PC5KR470
|
||||
SIGNKEY_DURATION = 4 weeks
|
||||
LOOKAHEAD_SIGN = 32 weeks 1 day
|
||||
SIGNKEY_LEGAL_DURATION = 4 weeks
|
||||
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
SUGGESTED_EXCHANGE = http://localhost:8081/
|
||||
SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
|
||||
ALLOW_REGISTRATIONS = YES
|
||||
SERVE = http
|
||||
MAX_DEBT_BANK = TESTKUDOS:100000.0
|
||||
MAX_DEBT = TESTKUDOS:50.0
|
||||
DATABASE = postgres:///auditor-basedb
|
||||
|
||||
[auditordb-postgres]
|
||||
CONFIG = postgres:///auditor-basedb
|
||||
|
||||
[auditor]
|
||||
BASE_URL = http://localhost:8083/
|
||||
TINY_AMOUNT = TESTKUDOS:0.01
|
||||
PUBLIC_KEY = 0EHPW5WEKHXPPN4MPJNGA7Z6D29JP21GKVNV8ARFB1YW7WWJX20G
|
||||
|
||||
[PATHS]
|
||||
TALER_CACHE_HOME = $TALER_HOME/.cache/taler/
|
||||
TALER_CONFIG_HOME = $TALER_HOME/.config/taler/
|
||||
TALER_DATA_HOME = $TALER_HOME/.local/share/taler/
|
||||
TALER_HOME = ${PWD}/generate_auditordb_home/
|
||||
|
@ -1,412 +1,61 @@
|
||||
#!/bin/bash
|
||||
# Script to generate the basic database for auditor
|
||||
# testing from a 'correct' interaction between exchange,
|
||||
# wallet and merchant.
|
||||
# This file is in the public domain.
|
||||
#
|
||||
# Creates $BASEDB.sql, $BASEDB.fees,
|
||||
# $BASEDB.{mpub,mpriv}.
|
||||
# Default $BASEDB is "auditor-basedb", override via $1.
|
||||
# Script to generate the basic database for auditor testing from a 'correct'
|
||||
# interaction between exchange, wallet and merchant.
|
||||
#
|
||||
# Currently must be run online as it interacts with
|
||||
# bank.test.taler.net; also requires the wallet CLI
|
||||
# to be installed and in the path. Furthermore, the
|
||||
# user running this script must be Postgres superuser
|
||||
# and be allowed to create/drop databases.
|
||||
# Creates "$1.sql".
|
||||
#
|
||||
# Requires the wallet CLI to be installed and in the path. Furthermore, the
|
||||
# user running this script must be Postgres superuser and be allowed to
|
||||
# create/drop databases.
|
||||
#
|
||||
set -eu
|
||||
#set -x
|
||||
|
||||
# Cleanup to run whenever we exit
|
||||
function exit_cleanup()
|
||||
{
|
||||
echo "Running generate-auditor-basedb exit cleanup logic..."
|
||||
if test -f ${MY_TMP_DIR:-/}/libeufin-sandbox.pid
|
||||
then
|
||||
PID=`cat ${MY_TMP_DIR}/libeufin-sandbox.pid 2> /dev/null`
|
||||
kill $PID 2> /dev/null || true
|
||||
rm ${MY_TMP_DIR}/libeufin-sandbox.pid
|
||||
echo "Killed libeufin sandbox $PID"
|
||||
wait $PID || true
|
||||
fi
|
||||
if test -f ${MY_TMP_DIR:-/}/libeufin-nexus.pid
|
||||
then
|
||||
PID=`cat ${MY_TMP_DIR}/libeufin-nexus.pid 2> /dev/null`
|
||||
kill $PID 2> /dev/null || true
|
||||
rm ${MY_TMP_DIR}/libeufin-nexus.pid
|
||||
echo "Killed libeufin nexus $PID"
|
||||
wait $PID || true
|
||||
fi
|
||||
echo "killing libeufin DONE"
|
||||
for n in `jobs -p`
|
||||
do
|
||||
kill $n 2> /dev/null || true
|
||||
done
|
||||
wait || true
|
||||
}
|
||||
|
||||
# Install cleanup handler (except for kill -9)
|
||||
trap exit_cleanup EXIT
|
||||
|
||||
|
||||
# Exit, with status code "skip" (no 'real' failure)
|
||||
function exit_skip() {
|
||||
echo "SKIPPING: $1"
|
||||
exit 77
|
||||
}
|
||||
# Where do we write the result?
|
||||
BASEDB=${1:-"auditor-basedb"}
|
||||
# Name of the Postgres database we will use for the script.
|
||||
# Will be dropped, do NOT use anything that might be used
|
||||
# elsewhere
|
||||
export TARGET_DB=`basename ${BASEDB}`
|
||||
BASEDB="$1"
|
||||
|
||||
export WALLET_DB=${BASEDB:-"wallet"}.wdb
|
||||
. setup.sh
|
||||
|
||||
# delete existing wallet database
|
||||
rm -f $WALLET_DB
|
||||
|
||||
# Configuration file will be edited, so we create one
|
||||
# from the template.
|
||||
export CONF=$1.conf
|
||||
cp generate-auditor-basedb.conf $CONF
|
||||
echo "Created configuration at ${CONF}"
|
||||
DATA_DIR=$1/exchange-data-dir/
|
||||
mkdir -p $DATA_DIR
|
||||
taler-config -c $CONF -s PATHS -o TALER_HOME -V $DATA_DIR
|
||||
|
||||
echo -n "Testing for libeufin"
|
||||
libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
echo -n "Testing for taler-wallet-cli"
|
||||
taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
echo -n "Testing for curl"
|
||||
echo -n "Testing for curl ..."
|
||||
curl --help >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
|
||||
CONF="generate-auditor-basedb.conf"
|
||||
|
||||
# reset database
|
||||
dropdb $TARGET_DB >/dev/null 2>/dev/null || true
|
||||
createdb $TARGET_DB || exit_skip "Could not create database $TARGET_DB"
|
||||
ORIGIN=`pwd`
|
||||
MY_TMP_DIR=`dirname $1`
|
||||
echo -n "Reset 'auditor-basedb' database ..."
|
||||
dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
|
||||
createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB'"
|
||||
echo " DONE"
|
||||
|
||||
# Launch exchange, merchant and bank.
|
||||
setup -c "$CONF" \
|
||||
-aenmsw \
|
||||
-d "iban"
|
||||
|
||||
# obtain key configuration data
|
||||
MASTER_PRIV_FILE=$1.mpriv
|
||||
MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
|
||||
taler-config -f -c ${CONF} -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
|
||||
rm -f "${MASTER_PRIV_FILE}"
|
||||
mkdir -p $MASTER_PRIV_DIR
|
||||
gnunet-ecc -l/dev/null -g1 $MASTER_PRIV_FILE > /dev/null
|
||||
export MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
|
||||
export EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
|
||||
MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
|
||||
export MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
|
||||
BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
|
||||
EXCHANGE_URL=$(taler-config -c "$CONF" -s EXCHANGE -o BASE_URL)
|
||||
MERCHANT_PORT=$(taler-config -c "$CONF" -s MERCHANT -o PORT)
|
||||
MERCHANT_URL="http://localhost:${MERCHANT_PORT}/"
|
||||
BANK_PORT=$(taler-config -c "$CONF" -s BANK -o HTTP_PORT)
|
||||
BANK_URL="http://localhost:1${BANK_PORT}"
|
||||
export AUDITOR_URL=http://localhost:8083/
|
||||
AUDITOR_PRIV_FILE=$1.apriv
|
||||
AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
|
||||
taler-config -f -c ${CONF} -s auditor -o AUDITOR_PRIV_FILE -V ${AUDITOR_PRIV_FILE}
|
||||
mkdir -p $AUDITOR_PRIV_DIR
|
||||
gnunet-ecc -l/dev/null -g1 $AUDITOR_PRIV_FILE > /dev/null
|
||||
AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
|
||||
|
||||
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
|
||||
echo "AUDITOR PUB is ${AUDITOR_PUB} using file ${AUDITOR_PRIV_FILE}"
|
||||
|
||||
# patch configuration
|
||||
taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
|
||||
taler-config -c $CONF -s auditor -o PUBLIC_KEY -V $AUDITOR_PUB
|
||||
taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
|
||||
|
||||
taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s bank -o database -V postgres:///$TARGET_DB
|
||||
|
||||
# setup exchange
|
||||
echo "Setting up exchange"
|
||||
taler-exchange-dbinit -c $CONF
|
||||
|
||||
echo "Setting up merchant"
|
||||
taler-merchant-dbinit -c $CONF
|
||||
|
||||
# setup auditor
|
||||
echo "Setting up auditor"
|
||||
taler-auditor-dbinit -c $CONF || exit_skip "Failed to initialize auditor DB"
|
||||
taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL || exit_skip "Failed to add exchange to auditor"
|
||||
|
||||
# Launch services
|
||||
echo "Launching services (pre audit DB: $TARGET_DB)"
|
||||
|
||||
rm -rf ${TARGET_DB}-sandbox.sqlite3
|
||||
export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-sandbox.sqlite3"
|
||||
# Create the default demobank.
|
||||
cd $MY_TMP_DIR
|
||||
export LIBEUFIN_SANDBOX_ADMIN_PASSWORD=secret
|
||||
libeufin-sandbox config --currency "TESTKUDOS" default
|
||||
libeufin-sandbox serve --port "1${BANK_PORT}" \
|
||||
> ${MY_TMP_DIR}/libeufin-sandbox-stdout.log \
|
||||
2> ${MY_TMP_DIR}/libeufin-sandbox-stderr.log &
|
||||
echo $! > ${MY_TMP_DIR}/libeufin-sandbox.pid
|
||||
cd $ORIGIN
|
||||
export LIBEUFIN_SANDBOX_URL="http://localhost:1${BANK_PORT}"
|
||||
set +e
|
||||
echo -n "Waiting for Sandbox..."
|
||||
OK=0
|
||||
for n in `seq 1 100`; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
if wget --timeout=1 \
|
||||
--user admin --password secret --auth-no-challenge \
|
||||
--tries=3 --waitretry=0 \
|
||||
-o /dev/null -O /dev/null \
|
||||
${LIBEUFIN_SANDBOX_URL};
|
||||
then
|
||||
OK=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test $OK != 1
|
||||
then
|
||||
exit_skip " Failed to launch sandbox"
|
||||
fi
|
||||
echo "OK"
|
||||
|
||||
register_sandbox_account() {
|
||||
export LIBEUFIN_SANDBOX_USERNAME=$1
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=$2
|
||||
cd $MY_TMP_DIR
|
||||
libeufin-cli sandbox \
|
||||
demobank \
|
||||
register --name "$3"
|
||||
cd $ORIGIN
|
||||
unset LIBEUFIN_SANDBOX_USERNAME
|
||||
unset LIBEUFIN_SANDBOX_PASSWORD
|
||||
}
|
||||
set -e
|
||||
echo -n "Register the 'fortytwo' Sandbox user.."
|
||||
register_sandbox_account fortytwo x "Forty Two"
|
||||
echo OK
|
||||
echo -n "Register the 'fortythree' Sandbox user.."
|
||||
register_sandbox_account fortythree x "Forty Three"
|
||||
echo OK
|
||||
echo -n "Register 'exchange' Sandbox user.."
|
||||
register_sandbox_account exchange x "Exchange Company"
|
||||
echo OK
|
||||
echo -n "Specify exchange's PAYTO_URI in the config ..."
|
||||
export LIBEUFIN_SANDBOX_USERNAME=exchange
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=x
|
||||
cd $MY_TMP_DIR
|
||||
PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
|
||||
taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI -V $PAYTO
|
||||
echo " OK"
|
||||
echo -n "Setting this exchange as the bank's default ..."
|
||||
EXCHANGE_PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
|
||||
libeufin-sandbox default-exchange "$EXCHANGE_URL" "$EXCHANGE_PAYTO"
|
||||
echo " OK"
|
||||
# Prepare EBICS: create Ebics host and Exchange subscriber.
|
||||
# Shortly becoming admin to setup Ebics.
|
||||
export LIBEUFIN_SANDBOX_USERNAME=admin
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=secret
|
||||
echo -n "Create EBICS host at Sandbox.."
|
||||
libeufin-cli sandbox \
|
||||
--sandbox-url "http://localhost:1${BANK_PORT}" \
|
||||
ebicshost create --host-id "talerebics"
|
||||
echo "OK"
|
||||
echo -n "Create exchange EBICS subscriber at Sandbox.."
|
||||
libeufin-cli sandbox \
|
||||
demobank new-ebicssubscriber --host-id talerebics \
|
||||
--user-id exchangeebics --partner-id talerpartner \
|
||||
--bank-account exchange # that's a username _and_ a bank account name
|
||||
echo "OK"
|
||||
unset LIBEUFIN_SANDBOX_USERNAME
|
||||
unset LIBEUFIN_SANDBOX_PASSWORD
|
||||
# Prepare Nexus, which is the side actually talking
|
||||
# to the exchange.
|
||||
rm -rf ${TARGET_DB}-nexus.sqlite3
|
||||
export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-nexus.sqlite3"
|
||||
# For convenience, username and password are
|
||||
# identical to those used at the Sandbox.
|
||||
echo -n "Create exchange Nexus user..."
|
||||
libeufin-nexus superuser exchange --password x
|
||||
echo " OK"
|
||||
libeufin-nexus serve --port ${BANK_PORT} \
|
||||
2> ${MY_TMP_DIR}/libeufin-nexus-stderr.log \
|
||||
> ${MY_TMP_DIR}/libeufin-nexus-stdout.log &
|
||||
echo $! > ${MY_TMP_DIR}/libeufin-nexus.pid
|
||||
export LIBEUFIN_NEXUS_URL="http://localhost:${BANK_PORT}"
|
||||
echo -n "Waiting for Nexus..."
|
||||
set +e
|
||||
OK=0
|
||||
for n in `seq 1 50`; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
if wget --timeout=1 \
|
||||
--tries=3 --waitretry=0 \
|
||||
-o /dev/null -O /dev/null \
|
||||
$LIBEUFIN_NEXUS_URL;
|
||||
then
|
||||
OK=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test $OK != 1
|
||||
then
|
||||
exit_skip " Failed to launch Nexus at $LIBEUFIN_NEXUS_URL"
|
||||
fi
|
||||
set -e
|
||||
echo "OK"
|
||||
export LIBEUFIN_NEXUS_USERNAME=exchange
|
||||
export LIBEUFIN_NEXUS_PASSWORD=x
|
||||
echo -n "Creating an EBICS connection at Nexus..."
|
||||
libeufin-cli connections new-ebics-connection \
|
||||
--ebics-url "http://localhost:1${BANK_PORT}/ebicsweb" \
|
||||
--host-id "talerebics" \
|
||||
--partner-id "talerpartner" \
|
||||
--ebics-user-id "exchangeebics" \
|
||||
talerconn
|
||||
echo "OK"
|
||||
echo -n "Setup EBICS keying..."
|
||||
libeufin-cli connections connect "talerconn" > /dev/null
|
||||
echo "OK"
|
||||
echo -n "Download bank account name from Sandbox..."
|
||||
libeufin-cli connections download-bank-accounts "talerconn"
|
||||
echo "OK"
|
||||
echo -n "Importing bank account info into Nexus..."
|
||||
libeufin-cli connections import-bank-account \
|
||||
--offered-account-id "exchange" \
|
||||
--nexus-bank-account-id "exchange-nexus" \
|
||||
"talerconn"
|
||||
echo "OK"
|
||||
echo -n "Setup payments submission task..."
|
||||
# Tries every second.
|
||||
libeufin-cli accounts task-schedule \
|
||||
--task-type submit \
|
||||
--task-name "exchange-payments" \
|
||||
--task-cronspec "* * *" \
|
||||
"exchange-nexus"
|
||||
echo "OK"
|
||||
# Tries every second. Ask C52
|
||||
echo -n "Setup history fetch task..."
|
||||
libeufin-cli accounts task-schedule \
|
||||
--task-type fetch \
|
||||
--task-name "exchange-history" \
|
||||
--task-cronspec "* * *" \
|
||||
--task-param-level report \
|
||||
--task-param-range-type latest \
|
||||
"exchange-nexus"
|
||||
echo "OK"
|
||||
# create Taler facade.
|
||||
echo -n "Create the Taler facade at Nexus..."
|
||||
libeufin-cli facades \
|
||||
new-taler-wire-gateway-facade \
|
||||
--currency "TESTKUDOS" --facade-name "test-facade" \
|
||||
"talerconn" "exchange-nexus"
|
||||
echo "OK"
|
||||
cd $ORIGIN
|
||||
# Facade schema: http://localhost:$BANK_PORT/facades/test-facade/taler-wire-gateway/
|
||||
|
||||
|
||||
TFN=`which taler-exchange-httpd`
|
||||
TBINPFX=`dirname $TFN`
|
||||
TLIBEXEC=${TBINPFX}/../lib/taler/libexec/
|
||||
taler-exchange-secmod-eddsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-eddsa.log &
|
||||
taler-exchange-secmod-rsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-rsa.log &
|
||||
taler-exchange-secmod-cs -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-cs.log &
|
||||
taler-exchange-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-httpd.log &
|
||||
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
|
||||
taler-exchange-wirewatch -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-wirewatch.log &
|
||||
taler-auditor-httpd -L INFO -c $CONF 2> ${MY_TMP_DIR}/taler-auditor-httpd.log &
|
||||
export BANK_PORT
|
||||
export EXCHANGE_URL
|
||||
export MERCHANT_URL
|
||||
export AUDITOR_URL
|
||||
|
||||
echo -n "Waiting for services to be available "
|
||||
# Wait for all bank to be available (usually the slowest)
|
||||
for n in `seq 1 50`
|
||||
do
|
||||
echo -n "."
|
||||
sleep 0.2
|
||||
OK=0
|
||||
# bank
|
||||
wget http://localhost:${BANK_PORT}/ -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
exit_skip "Failed to launch services (bank)"
|
||||
fi
|
||||
|
||||
# Wait for all services to be available
|
||||
for n in `seq 1 50`
|
||||
do
|
||||
echo -n "."
|
||||
sleep 0.1
|
||||
OK=0
|
||||
# exchange
|
||||
wget ${EXCHANGE_URL}seed -o /dev/null -O /dev/null >/dev/null || continue
|
||||
# merchant
|
||||
wget ${MERCHANT_URL} -o /dev/null -O /dev/null >/dev/null || continue
|
||||
# Auditor
|
||||
wget ${AUDITOR_URL} -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
bash
|
||||
exit_skip "Failed to launch services (Taler)"
|
||||
fi
|
||||
echo -n "Setting up keys"
|
||||
taler-exchange-offline -c $CONF \
|
||||
download sign \
|
||||
enable-account `taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI` \
|
||||
enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
|
||||
wire-fee now iban TESTKUDOS:0.07 TESTKUDOS:0.01 \
|
||||
global-fee now TESTKUDOS:0.01 TESTKUDOS:0.01 TESTKUDOS:0.01 1h 1year 5 \
|
||||
upload &> ${MY_TMP_DIR}/taler-exchange-offline.log
|
||||
|
||||
echo -n "."
|
||||
|
||||
for n in `seq 1 2`
|
||||
do
|
||||
echo -n "."
|
||||
OK=0
|
||||
wget --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
exit_skip "Failed to setup keys"
|
||||
fi
|
||||
|
||||
echo " DONE"
|
||||
echo -n "Adding auditor signatures ..."
|
||||
|
||||
taler-auditor-offline -c $CONF \
|
||||
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
|
||||
|
||||
echo " DONE"
|
||||
# Setup merchant
|
||||
|
||||
echo -n "Setting up merchant"
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
||||
|
||||
|
||||
echo -n "Setting up merchant ..."
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' "${MERCHANT_URL}management/instances"
|
||||
echo " DONE"
|
||||
|
||||
# run wallet CLI
|
||||
echo "Running wallet"
|
||||
# delete existing wallet database
|
||||
export WALLET_DB="wallet.wdb"
|
||||
rm -f "$WALLET_DB"
|
||||
|
||||
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'runIntegrationTest' \
|
||||
echo -n "Running wallet ..."
|
||||
taler-wallet-cli \
|
||||
--no-throttle \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
api \
|
||||
--expect-success \
|
||||
'runIntegrationTest' \
|
||||
"$(jq -n '
|
||||
{
|
||||
amountToSpend: "TESTKUDOS:4",
|
||||
@ -418,28 +67,25 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'runI
|
||||
--arg MERCHANT_URL "$MERCHANT_URL" \
|
||||
--arg EXCHANGE_URL "$EXCHANGE_URL" \
|
||||
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/"
|
||||
)" &> ${MY_TMP_DIR}/taler-wallet-cli.log
|
||||
|
||||
echo "Shutting down services"
|
||||
exit_cleanup
|
||||
)" &> taler-wallet-cli.log
|
||||
echo " DONE"
|
||||
|
||||
# Dump database
|
||||
echo "Dumping database ${BASEDB}(-libeufin).sql"
|
||||
pg_dump -O $TARGET_DB | sed -e '/AS integer/d' > ${BASEDB}.sql
|
||||
cd $MY_TMP_DIR
|
||||
sqlite3 ${TARGET_DB}-nexus.sqlite3 ".dump" > ${BASEDB}-libeufin-nexus.sql
|
||||
sqlite3 ${TARGET_DB}-sandbox.sqlite3 ".dump" > ${BASEDB}-libeufin-sandbox.sql
|
||||
rm ${TARGET_DB}-sandbox.sqlite3 ${TARGET_DB}-nexus.sqlite3 # libeufin DB
|
||||
cd $ORIGIN
|
||||
mkdir -p "$(dirname "$BASEDB")"
|
||||
|
||||
echo $MASTER_PUB > ${BASEDB}.mpub
|
||||
echo "Dumping database ${BASEDB}.sql"
|
||||
pg_dump -O "auditor-basedb" | sed -e '/AS integer/d' > "${BASEDB}.sql"
|
||||
|
||||
# clean up
|
||||
echo "Final clean up"
|
||||
dropdb $TARGET_DB
|
||||
echo -n "Final clean up ..."
|
||||
kill -TERM "$SETUP_PID"
|
||||
wait
|
||||
unset SETUP_PID
|
||||
dropdb "auditor-basedb"
|
||||
echo " DONE"
|
||||
|
||||
echo "====================================="
|
||||
echo " Finished generation of $BASEDB"
|
||||
echo "Finished generation of ${BASEDB}.sql"
|
||||
echo "====================================="
|
||||
|
||||
exit 0
|
||||
|
@ -8,405 +8,50 @@
|
||||
set -eu
|
||||
# set -x
|
||||
|
||||
# Cleanup to run whenever we exit
|
||||
function exit_cleanup()
|
||||
{
|
||||
echo "Running generate-revoke-basedb exit cleanup logic..."
|
||||
if test -f ${MY_TMP_DIR:-/}/libeufin-sandbox.pid
|
||||
then
|
||||
PID=`cat ${MY_TMP_DIR}/libeufin-sandbox.pid 2> /dev/null`
|
||||
kill $PID 2> /dev/null || true
|
||||
rm ${MY_TMP_DIR}/libeufin-sandbox.pid
|
||||
echo "Killed libeufin sandbox $PID"
|
||||
wait $PID || true
|
||||
fi
|
||||
if test -f ${MY_TMP_DIR}/libeufin-nexus.pid
|
||||
then
|
||||
PID=`cat ${MY_TMP_DIR}/libeufin-nexus.pid 2> /dev/null`
|
||||
kill $PID 2> /dev/null || true
|
||||
rm ${MY_TMP_DIR}/libeufin-nexus.pid
|
||||
echo "Killed libeufin nexus $PID"
|
||||
wait $PID || true
|
||||
fi
|
||||
echo "killing libeufin DONE"
|
||||
for n in `jobs -p`
|
||||
do
|
||||
kill $n 2> /dev/null || true
|
||||
done
|
||||
wait
|
||||
}
|
||||
. setup.sh
|
||||
|
||||
function get_payto_uri() {
|
||||
export LIBEUFIN_SANDBOX_USERNAME=$1
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=$2
|
||||
export LIBEUFIN_SANDBOX_URL=$BANK_URL
|
||||
cd $MY_TMP_DIR
|
||||
libeufin-cli sandbox demobank info --bank-account $1 | jq --raw-output '.paytoUri'
|
||||
cd $ORIGIN
|
||||
}
|
||||
|
||||
# Install cleanup handler (except for kill -9)
|
||||
trap exit_cleanup EXIT
|
||||
|
||||
# Exit, with status code "skip" (no 'real' failure)
|
||||
function exit_skip() {
|
||||
echo $1
|
||||
exit 77
|
||||
}
|
||||
|
||||
# Where do we write the result?
|
||||
export BASEDB=${1:-"revoke-basedb"}
|
||||
|
||||
# Name of the Postgres database we will use for the script.
|
||||
# Will be dropped, do NOT use anything that might be used
|
||||
# elsewhere
|
||||
export TARGET_DB=`basename ${BASEDB}`
|
||||
TMP_DIR=`mktemp -d revocation-tmp-XXXXXX`
|
||||
export WALLET_DB=wallet-revocation.json
|
||||
rm -f $WALLET_DB
|
||||
|
||||
# Configuration file will be edited, so we create one
|
||||
# from the template.
|
||||
export CONF=${BASEDB}.conf
|
||||
cp generate-auditor-basedb.conf $CONF
|
||||
echo "Created configuration at ${CONF}"
|
||||
DATA_DIR=$1/exchange-data-dir/
|
||||
mkdir -p $DATA_DIR
|
||||
taler-config -c $CONF -s PATHS -o TALER_HOME -V $DATA_DIR
|
||||
|
||||
echo -n "Testing for libeufin(-cli)"
|
||||
libeufin-cli --help >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
echo -n "Testing for taler-wallet-cli"
|
||||
taler-wallet-cli -v >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
echo -n "Testing for curl"
|
||||
echo -n "Testing for curl ..."
|
||||
curl --help >/dev/null </dev/null || exit_skip " MISSING"
|
||||
echo " FOUND"
|
||||
|
||||
# reset database
|
||||
dropdb $TARGET_DB >/dev/null 2>/dev/null || true
|
||||
createdb $TARGET_DB || exit_skip "Could not create database $TARGET_DB"
|
||||
ORIGIN=`pwd`
|
||||
MY_TMP_DIR=`dirname $1`
|
||||
CONF="generate-auditor-basedb.conf"
|
||||
|
||||
# reset database
|
||||
echo -n "Reset 'auditor-basedb' database ..."
|
||||
dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
|
||||
createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB'"
|
||||
echo " DONE"
|
||||
|
||||
# Launch exchange, merchant and bank.
|
||||
setup -c "$CONF" \
|
||||
-aenmsw \
|
||||
-d "iban"
|
||||
|
||||
# obtain key configuration data
|
||||
MASTER_PRIV_FILE=$1.mpriv
|
||||
MASTER_PRIV_DIR=`dirname $MASTER_PRIV_FILE`
|
||||
taler-config -f -c $CONF -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
|
||||
mkdir -p $MASTER_PRIV_DIR
|
||||
rm -f "${MASTER_PRIV_FILE}"
|
||||
gnunet-ecc -g1 $MASTER_PRIV_FILE > /dev/null
|
||||
export MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
|
||||
export EXCHANGE_URL=`taler-config -c $CONF -s EXCHANGE -o BASE_URL`
|
||||
MERCHANT_PORT=`taler-config -c $CONF -s MERCHANT -o PORT`
|
||||
export MERCHANT_URL=http://localhost:${MERCHANT_PORT}/
|
||||
BANK_PORT=`taler-config -c $CONF -s BANK -o HTTP_PORT`
|
||||
export BANK_URL=http://localhost:1${BANK_PORT}
|
||||
export AUDITOR_URL=http://localhost:8083/
|
||||
AUDITOR_PRIV_FILE=$1.apriv
|
||||
AUDITOR_PRIV_DIR=`dirname $AUDITOR_PRIV_FILE`
|
||||
taler-config -f -c ${CONF} -s auditor -o AUDITOR_PRIV_FILE -V ${AUDITOR_PRIV_FILE}
|
||||
mkdir -p $AUDITOR_PRIV_DIR
|
||||
gnunet-ecc -l /dev/null -g1 $AUDITOR_PRIV_FILE > /dev/null
|
||||
AUDITOR_PUB=`gnunet-ecc -p $AUDITOR_PRIV_FILE`
|
||||
EXCHANGE_URL=$(taler-config -c "$CONF" -s EXCHANGE -o BASE_URL)
|
||||
MERCHANT_PORT=$(taler-config -c "$CONF" -s MERCHANT -o PORT)
|
||||
MERCHANT_URL="http://localhost:${MERCHANT_PORT}/"
|
||||
BANK_PORT=$(taler-config -c "$CONF" -s BANK -o HTTP_PORT)
|
||||
BANK_URL="http://localhost:1${BANK_PORT}"
|
||||
|
||||
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
|
||||
echo "AUDITOR PUB is ${AUDITOR_PUB} using file ${AUDITOR_PRIV_FILE}"
|
||||
|
||||
|
||||
# patch configuration
|
||||
taler-config -c $CONF -s exchange -o MASTER_PUBLIC_KEY -V $MASTER_PUB
|
||||
taler-config -c $CONF -s auditor -o PUBLIC_KEY -V $AUDITOR_PUB
|
||||
taler-config -c $CONF -s merchant-exchange-default -o MASTER_KEY -V $MASTER_PUB
|
||||
taler-config -c $CONF -s exchangedb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s auditordb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s merchantdb-postgres -o CONFIG -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s bank -o database -V postgres:///$TARGET_DB
|
||||
taler-config -c $CONF -s exchange -o KEYDIR -V "${TMP_DIR}/keydir/"
|
||||
taler-config -c $CONF -s exchange -o REVOCATION_DIR -V "${TMP_DIR}/revdir/"
|
||||
|
||||
# setup exchange
|
||||
echo "Setting up exchange"
|
||||
taler-exchange-dbinit -c $CONF
|
||||
|
||||
echo "Setting up merchant"
|
||||
taler-merchant-dbinit -c $CONF
|
||||
|
||||
# setup auditor
|
||||
echo "Setting up auditor"
|
||||
taler-auditor-dbinit -c $CONF
|
||||
taler-auditor-exchange -c $CONF -m $MASTER_PUB -u $EXCHANGE_URL
|
||||
|
||||
# Launch services
|
||||
echo "Launching services"
|
||||
|
||||
export LIBEUFIN_SANDBOX_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-sandbox.sqlite3"
|
||||
# Create the default demobank.
|
||||
cd $MY_TMP_DIR
|
||||
export LIBEUFIN_SANDBOX_ADMIN_PASSWORD=secret
|
||||
libeufin-sandbox config --currency "TESTKUDOS" default
|
||||
libeufin-sandbox serve --port "1${BANK_PORT}" \
|
||||
> ${MY_TMP_DIR}/libeufin-sandbox-stdout.log \
|
||||
2> ${MY_TMP_DIR}/libeufin-sandbox-stderr.log &
|
||||
echo $! > ${MY_TMP_DIR}/libeufin-sandbox.pid
|
||||
cd $ORIGIN
|
||||
export LIBEUFIN_SANDBOX_URL="http://localhost:1${BANK_PORT}"
|
||||
set +e
|
||||
echo -n "Waiting for Sandbox..."
|
||||
OK=0
|
||||
for n in `seq 1 50`; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
if wget --timeout=1 \
|
||||
--user admin --password secret --auth-no-challenge \
|
||||
--tries=3 --waitretry=0 \
|
||||
-o /dev/null -O /dev/null \
|
||||
${LIBEUFIN_SANDBOX_URL};
|
||||
then
|
||||
OK=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test $OK != 1
|
||||
then
|
||||
exit_skip " Failed to launch sandbox"
|
||||
fi
|
||||
echo "OK"
|
||||
|
||||
register_sandbox_account() {
|
||||
export LIBEUFIN_SANDBOX_USERNAME=$1
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=$2
|
||||
cd $MY_TMP_DIR
|
||||
libeufin-cli sandbox \
|
||||
demobank \
|
||||
register --name "$3"
|
||||
cd $ORIGIN
|
||||
unset LIBEUFIN_SANDBOX_USERNAME
|
||||
unset LIBEUFIN_SANDBOX_PASSWORD
|
||||
}
|
||||
set -e
|
||||
echo -n "Register the 'fortytwo' Sandbox user.."
|
||||
register_sandbox_account fortytwo x "Forty Two"
|
||||
echo OK
|
||||
echo -n "Register the 'fortythree' Sandbox user.."
|
||||
register_sandbox_account fortythree x "Forty Three"
|
||||
echo OK
|
||||
echo -n "Register 'exchange' Sandbox user.."
|
||||
register_sandbox_account exchange x "Exchange Company"
|
||||
echo OK
|
||||
echo -n "Specify exchange's PAYTO_URI in the config ..."
|
||||
export LIBEUFIN_SANDBOX_USERNAME=exchange
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=x
|
||||
cd $MY_TMP_DIR
|
||||
PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
|
||||
taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI -V $PAYTO
|
||||
echo " OK"
|
||||
echo -n "Setting this exchange as the bank's default ..."
|
||||
EXCHANGE_PAYTO=`libeufin-cli sandbox demobank info --bank-account exchange | jq --raw-output '.paytoUri'`
|
||||
libeufin-sandbox default-exchange "$EXCHANGE_URL" "$EXCHANGE_PAYTO"
|
||||
echo " OK"
|
||||
# Prepare EBICS: create Ebics host and Exchange subscriber.
|
||||
# Shortly becoming admin to setup Ebics.
|
||||
export LIBEUFIN_SANDBOX_USERNAME=admin
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=secret
|
||||
echo -n "Create EBICS host at Sandbox.."
|
||||
libeufin-cli sandbox \
|
||||
--sandbox-url "http://localhost:1${BANK_PORT}" \
|
||||
ebicshost create --host-id "talerebics"
|
||||
echo "OK"
|
||||
echo -n "Create exchange EBICS subscriber at Sandbox.."
|
||||
libeufin-cli sandbox \
|
||||
demobank new-ebicssubscriber --host-id talerebics \
|
||||
--user-id exchangeebics --partner-id talerpartner \
|
||||
--bank-account exchange # that's a username _and_ a bank account name
|
||||
echo "OK"
|
||||
unset LIBEUFIN_SANDBOX_USERNAME
|
||||
unset LIBEUFIN_SANDBOX_PASSWORD
|
||||
# Prepare Nexus, which is the side actually talking
|
||||
# to the exchange.
|
||||
export LIBEUFIN_NEXUS_DB_CONNECTION="jdbc:sqlite:${TARGET_DB}-nexus.sqlite3"
|
||||
# For convenience, username and password are
|
||||
# identical to those used at the Sandbox.
|
||||
echo -n "Create exchange Nexus user..."
|
||||
libeufin-nexus superuser exchange --password x
|
||||
echo " OK"
|
||||
libeufin-nexus serve --port ${BANK_PORT} \
|
||||
2> ${MY_TMP_DIR}/libeufin-nexus-stderr.log \
|
||||
> ${MY_TMP_DIR}/libeufin-nexus-stdout.log &
|
||||
echo $! > ${MY_TMP_DIR}/libeufin-nexus.pid
|
||||
export LIBEUFIN_NEXUS_URL="http://localhost:${BANK_PORT}"
|
||||
echo -n "Waiting for Nexus..."
|
||||
set +e
|
||||
OK=0
|
||||
for n in `seq 1 50`; do
|
||||
echo -n "."
|
||||
sleep 1
|
||||
if wget --timeout=1 \
|
||||
--tries=3 --waitretry=0 \
|
||||
-o /dev/null -O /dev/null \
|
||||
$LIBEUFIN_NEXUS_URL;
|
||||
then
|
||||
OK=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test $OK != 1
|
||||
then
|
||||
exit_skip " Failed to launch Nexus at $LIBEUFIN_NEXUS_URL"
|
||||
fi
|
||||
set -e
|
||||
echo "OK"
|
||||
export LIBEUFIN_NEXUS_USERNAME=exchange
|
||||
export LIBEUFIN_NEXUS_PASSWORD=x
|
||||
echo -n "Creating an EBICS connection at Nexus..."
|
||||
libeufin-cli connections new-ebics-connection \
|
||||
--ebics-url "http://localhost:1${BANK_PORT}/ebicsweb" \
|
||||
--host-id "talerebics" \
|
||||
--partner-id "talerpartner" \
|
||||
--ebics-user-id "exchangeebics" \
|
||||
talerconn
|
||||
echo "OK"
|
||||
echo -n "Setup EBICS keying..."
|
||||
libeufin-cli connections connect "talerconn" > /dev/null
|
||||
echo "OK"
|
||||
echo -n "Download bank account name from Sandbox..."
|
||||
libeufin-cli connections download-bank-accounts "talerconn"
|
||||
echo "OK"
|
||||
echo -n "Importing bank account info into Nexus..."
|
||||
libeufin-cli connections import-bank-account \
|
||||
--offered-account-id "exchange" \
|
||||
--nexus-bank-account-id "exchange-nexus" \
|
||||
"talerconn"
|
||||
echo "OK"
|
||||
echo -n "Setup payments submission task..."
|
||||
# Tries every second.
|
||||
libeufin-cli accounts task-schedule \
|
||||
--task-type submit \
|
||||
--task-name "exchange-payments" \
|
||||
--task-cronspec "* * *" \
|
||||
"exchange-nexus"
|
||||
echo "OK"
|
||||
# Tries every second. Ask C52
|
||||
echo -n "Setup history fetch task..."
|
||||
libeufin-cli accounts task-schedule \
|
||||
--task-type fetch \
|
||||
--task-name "exchange-history" \
|
||||
--task-cronspec "* * *" \
|
||||
--task-param-level report \
|
||||
--task-param-range-type latest \
|
||||
"exchange-nexus"
|
||||
echo "OK"
|
||||
# create Taler facade.
|
||||
echo -n "Create the Taler facade at Nexus..."
|
||||
libeufin-cli facades \
|
||||
new-taler-wire-gateway-facade \
|
||||
--currency "TESTKUDOS" --facade-name "test-facade" \
|
||||
"talerconn" "exchange-nexus"
|
||||
echo "OK"
|
||||
cd $ORIGIN
|
||||
# Facade schema: http://localhost:$BANK_PORT/facades/test-facade/taler-wire-gateway/
|
||||
|
||||
TFN=`which taler-exchange-httpd`
|
||||
TBINPFX=`dirname $TFN`
|
||||
TLIBEXEC=${TBINPFX}/../lib/taler/libexec/
|
||||
taler-exchange-secmod-eddsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-eddsa.log &
|
||||
SIGNKEY_HELPER_PID=$!
|
||||
taler-exchange-secmod-rsa -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-rsa.log &
|
||||
RSA_DENOM_HELPER_PID=$!
|
||||
taler-exchange-secmod-cs -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-secmod-cs.log &
|
||||
CS_DENOM_HELPER_PID=$!
|
||||
taler-exchange-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-httpd.log &
|
||||
EXCHANGE_PID=$!
|
||||
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
|
||||
MERCHANT_PID=$!
|
||||
taler-exchange-wirewatch -c $CONF 2> ${MY_TMP_DIR}/taler-exchange-wirewatch.log &
|
||||
taler-auditor-httpd -c $CONF 2> ${MY_TMP_DIR}/taler-auditor-httpd.log &
|
||||
|
||||
# Wait for all bank to be available (usually the slowest)
|
||||
for n in `seq 1 50`
|
||||
do
|
||||
echo -n "."
|
||||
sleep 0.2
|
||||
OK=0
|
||||
# bank
|
||||
wget http://localhost:8082/ -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
exit_skip "Failed to launch Bank services"
|
||||
fi
|
||||
|
||||
# Wait for all other services to be available
|
||||
for n in `seq 1 50`
|
||||
do
|
||||
echo -n "."
|
||||
sleep 0.1
|
||||
OK=0
|
||||
# exchange
|
||||
wget http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null || continue
|
||||
# merchant
|
||||
wget http://localhost:9966/ -o /dev/null -O /dev/null >/dev/null || continue
|
||||
# Auditor
|
||||
wget http://localhost:8083/ -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
exit_cleanup
|
||||
exit_skip "Failed to launch Taler services"
|
||||
fi
|
||||
echo " DONE"
|
||||
|
||||
echo -n "Setting up keys"
|
||||
|
||||
taler-exchange-offline -c $CONF \
|
||||
download sign \
|
||||
enable-account `taler-config -c $CONF -s exchange-account-1 -o PAYTO_URI` \
|
||||
enable-auditor $AUDITOR_PUB $AUDITOR_URL "TESTKUDOS Auditor" \
|
||||
wire-fee now iban TESTKUDOS:0.01 TESTKUDOS:0.01 \
|
||||
global-fee now TESTKUDOS:0.01 TESTKUDOS:0.01 TESTKUDOS:0.01 1h 1year 5 \
|
||||
upload &> ${MY_TMP_DIR}/taler-exchange-offline.log
|
||||
|
||||
echo -n "."
|
||||
|
||||
for n in `seq 1 2`
|
||||
do
|
||||
echo -n "."
|
||||
OK=0
|
||||
# bank
|
||||
wget --timeout=1 http://localhost:8081/keys -o /dev/null -O /dev/null >/dev/null || continue
|
||||
OK=1
|
||||
break
|
||||
done
|
||||
|
||||
if [ 1 != $OK ]
|
||||
then
|
||||
exit_skip "Failed to setup keys"
|
||||
fi
|
||||
|
||||
|
||||
taler-auditor-offline -c $CONF \
|
||||
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
|
||||
|
||||
echo " DONE"
|
||||
|
||||
# Setup merchant
|
||||
echo -n "Setting up merchant"
|
||||
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
||||
echo -n "Setting up merchant ..."
|
||||
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' "${MERCHANT_URL}management/instances"
|
||||
echo " DONE"
|
||||
|
||||
|
||||
# run wallet CLI
|
||||
echo "Running wallet"
|
||||
|
||||
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'withdrawTestBalance' \
|
||||
export WALLET_DB="wallet.wdb"
|
||||
rm -f "$WALLET_DB"
|
||||
|
||||
taler-wallet-cli \
|
||||
--no-throttle \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
api \
|
||||
--expect-success 'withdrawTestBalance' \
|
||||
"$(jq -n '
|
||||
{
|
||||
amount: "TESTKUDOS:8",
|
||||
@ -414,57 +59,85 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'with
|
||||
exchangeBaseUrl: $EXCHANGE_URL,
|
||||
}' \
|
||||
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/" \
|
||||
--arg EXCHANGE_URL $EXCHANGE_URL
|
||||
)"
|
||||
--arg EXCHANGE_URL "$EXCHANGE_URL"
|
||||
)" &> taler-wallet-cli-withdraw.log
|
||||
|
||||
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
--no-throttle \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
run-until-done \
|
||||
&> taler-wallet-cli-withdraw-finish.log
|
||||
|
||||
export coins=$(taler-wallet-cli --wallet-db=$WALLET_DB advanced dump-coins)
|
||||
export COINS=$(taler-wallet-cli --wallet-db="$WALLET_DB" advanced dump-coins)
|
||||
|
||||
echo -n "COINS are:"
|
||||
echo $coins
|
||||
echo "$COINS"
|
||||
|
||||
# Find coin we want to revoke
|
||||
export rc=$(echo "$coins" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .coin_pub')
|
||||
export rc=$(echo "$COINS" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .coin_pub')
|
||||
# Find the denom
|
||||
export rd=$(echo "$coins" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .denom_pub_hash')
|
||||
echo "Revoking denomination ${rd} (to affect coin ${rc})"
|
||||
export rd=$(echo "$COINS" | jq -r '[.coins[] | select((.denom_value == "TESTKUDOS:2"))][0] | .denom_pub_hash')
|
||||
echo -n "Revoking denomination ${rd} (to affect coin ${rc}) ..."
|
||||
# Find all other coins, which will be suspended
|
||||
export susp=$(echo "$coins" | jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')
|
||||
export susp=$(echo "$COINS" | jq --arg rc "$rc" '[.coins[] | select(.coin_pub != $rc) | .coin_pub]')
|
||||
|
||||
# Do the revocation
|
||||
taler-exchange-offline -c $CONF \
|
||||
revoke-denomination "${rd}" upload &> ${MY_TMP_DIR}/taler-exchange-offline-revoke.log
|
||||
taler-exchange-offline \
|
||||
-c $CONF \
|
||||
revoke-denomination "${rd}" \
|
||||
upload \
|
||||
&> taler-exchange-offline-revoke.log
|
||||
echo "DONE"
|
||||
|
||||
echo -n "Signing replacement keys ..."
|
||||
sleep 1 # Give exchange time to create replacmenent key
|
||||
|
||||
# Re-sign replacement keys
|
||||
taler-auditor-offline -c $CONF \
|
||||
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
|
||||
taler-auditor-offline \
|
||||
-c $CONF \
|
||||
download \
|
||||
sign \
|
||||
upload \
|
||||
&> taler-auditor-offline-reinit.log
|
||||
echo " DONE"
|
||||
|
||||
# Now we suspend the other coins, so later we will pay with the recouped coin
|
||||
taler-wallet-cli --wallet-db=$WALLET_DB advanced suspend-coins "$susp"
|
||||
taler-wallet-cli \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
advanced \
|
||||
suspend-coins "$susp"
|
||||
|
||||
# Update exchange /keys so recoup gets scheduled
|
||||
taler-wallet-cli --wallet-db=$WALLET_DB exchanges update \
|
||||
-f $EXCHANGE_URL
|
||||
taler-wallet-cli \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
exchanges \
|
||||
update \
|
||||
-f "$EXCHANGE_URL"
|
||||
|
||||
# Block until scheduled operations are done
|
||||
taler-wallet-cli --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
--wallet-db="$WALLET_DB"\
|
||||
run-until-done
|
||||
|
||||
# Now we buy something, only the coins resulting from recouped will be
|
||||
# Now we buy something, only the coins resulting from recoup will be
|
||||
# used, as other ones are suspended
|
||||
taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api 'testPay' \
|
||||
taler-wallet-cli \
|
||||
--no-throttle \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
api \
|
||||
'testPay' \
|
||||
"$(jq -n '
|
||||
{
|
||||
amount: "TESTKUDOS:1",
|
||||
merchantBaseUrl: $MERCHANT_URL,
|
||||
summary: "foo",
|
||||
}' \
|
||||
--arg MERCHANT_URL $MERCHANT_URL
|
||||
--arg MERCHANT_URL "$MERCHANT_URL"
|
||||
)"
|
||||
|
||||
taler-wallet-cli --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
run-until-done
|
||||
|
||||
echo "Purchase with recoup'ed coin (via reserve) done"
|
||||
|
||||
@ -477,9 +150,6 @@ echo "Will refresh coin ${rrc} of denomination ${zombie_denom}"
|
||||
# Find all other coins, which will be suspended
|
||||
export susp=$(echo "$coins" | jq --arg rrc "$rrc" '[.coins[] | select(.coin_pub != $rrc) | .coin_pub]')
|
||||
|
||||
export rrc
|
||||
export zombie_denom
|
||||
|
||||
# Travel into the future! (must match DURATION_WITHDRAW option)
|
||||
export TIMETRAVEL="--timetravel=604800000000"
|
||||
|
||||
@ -510,8 +180,15 @@ do
|
||||
done
|
||||
|
||||
echo "Refreshing coin $rrc"
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced force-refresh "$rrc"
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL" \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
advanced force-refresh \
|
||||
"$rrc"
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL" \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
run-until-done
|
||||
|
||||
# Update our list of the coins
|
||||
export coins=$(taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced dump-coins)
|
||||
@ -534,29 +211,49 @@ export susp=$(echo "$coins" | jq --arg freshc "$freshc" '[.coins[] | select(.coi
|
||||
|
||||
# Do the revocation of freshc
|
||||
echo "Revoking ${fresh_denom} (to affect coin ${freshc})"
|
||||
taler-exchange-offline -c $CONF \
|
||||
revoke-denomination "${fresh_denom}" upload &> ${MY_TMP_DIR}/taler-exchange-offline-revoke-2.log
|
||||
taler-exchange-offline \
|
||||
-c "$CONF" \
|
||||
revoke-denomination \
|
||||
"${fresh_denom}" \
|
||||
upload &> taler-exchange-offline-revoke-2.log
|
||||
|
||||
sleep 1 # Give exchange time to create replacmenent key
|
||||
|
||||
# Re-sign replacement keys
|
||||
taler-auditor-offline -c $CONF \
|
||||
download sign upload &> ${MY_TMP_DIR}/taler-auditor-offline.log
|
||||
taler-auditor-offline \
|
||||
-c "$CONF" \
|
||||
download \
|
||||
sign \
|
||||
upload &> taler-auditor-offline.log
|
||||
|
||||
# Now we suspend the other coins, so later we will pay with the recouped coin
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB advanced suspend-coins "$susp"
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL" \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
advanced \
|
||||
suspend-coins "$susp"
|
||||
|
||||
# Update exchange /keys so recoup gets scheduled
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB exchanges update \
|
||||
-f $EXCHANGE_URL
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL"\
|
||||
--wallet-db="$WALLET_DB" \
|
||||
exchanges update \
|
||||
-f "$EXCHANGE_URL"
|
||||
|
||||
# Block until scheduled operations are done
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL" \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
run-until-done
|
||||
|
||||
echo "Restarting merchant (so new keys are known)"
|
||||
kill -TERM $MERCHANT_PID
|
||||
taler-merchant-httpd -c $CONF -L INFO 2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
|
||||
taler-merchant-httpd \
|
||||
-c "$CONF" \
|
||||
-L INFO \
|
||||
2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
|
||||
MERCHANT_PID=$!
|
||||
|
||||
# Wait for merchant to be again available
|
||||
for n in `seq 1 50`
|
||||
do
|
||||
@ -580,7 +277,10 @@ taler-wallet-cli $TIMETRAVEL --no-throttle --wallet-db=$WALLET_DB api 'testPay'
|
||||
}' \
|
||||
--arg MERCHANT_URL $MERCHANT_URL
|
||||
)"
|
||||
taler-wallet-cli $TIMETRAVEL --wallet-db=$WALLET_DB run-until-done
|
||||
taler-wallet-cli \
|
||||
"$TIMETRAVEL" \
|
||||
--wallet-db="$WALLET_DB" \
|
||||
run-until-done
|
||||
|
||||
echo "Bought something with refresh-recouped coin"
|
||||
|
||||
@ -588,26 +288,24 @@ echo "Shutting down services"
|
||||
exit_cleanup
|
||||
|
||||
|
||||
# Where do we write the result?
|
||||
export BASEDB=${1:-"revoke-basedb"}
|
||||
|
||||
|
||||
# Dump database
|
||||
echo "Dumping database"
|
||||
echo "Dumping PostgreSQL database: ${BASEDB}.sql"
|
||||
pg_dump -O $TARGET_DB | sed -e '/AS integer/d' > ${BASEDB}.sql
|
||||
echo "Dumping libeufin database: ${TARGET_DB}-libeufin-*.sql"
|
||||
cd $MY_TMP_DIR
|
||||
sqlite3 ${TARGET_DB}-nexus.sqlite3 ".dump" > ${BASEDB}-libeufin-nexus.sql
|
||||
sqlite3 ${TARGET_DB}-sandbox.sqlite3 ".dump" > ${BASEDB}-libeufin-sandbox.sql
|
||||
echo "Dumping database ${BASEDB}.sql"
|
||||
pg_dump -O "auditor-basedb" | sed -e '/AS integer/d' > "${BASEDB}.sql"
|
||||
|
||||
rm ${TARGET_DB}-sandbox.sqlite3 ${TARGET_DB}-nexus.sqlite3 # libeufin DB
|
||||
|
||||
cd $ORIGIN
|
||||
|
||||
echo $MASTER_PUB > ${BASEDB}.mpub
|
||||
|
||||
echo "Final clean up"
|
||||
dropdb $TARGET_DB
|
||||
# clean up
|
||||
echo -n "Final clean up ..."
|
||||
kill -TERM "$SETUP_PID"
|
||||
wait
|
||||
unset SETUP_PID
|
||||
dropdb "auditor-basedb"
|
||||
echo " DONE"
|
||||
|
||||
echo "====================================="
|
||||
echo " Finished generation of $BASEDB "
|
||||
echo "Finished generation of ${BASEDB}.sql"
|
||||
echo "====================================="
|
||||
|
||||
exit 0
|
||||
|
@ -0,0 +1 @@
|
||||
%I7qYÿ®ÜX˜2@–šò%'1†”ÂOàÔæJ³Ô¦‘
|
72
src/auditor/setup.sh
Executable file
72
src/auditor/setup.sh
Executable file
@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
# This file is in the public domain
|
||||
|
||||
# Script to be inlined into the main test scripts. Defines function 'setup()'
|
||||
# which wraps around 'taler-unified-setup.sh' to launch GNU Taler services.
|
||||
# Call setup() with the arguments to pass to 'taler-unified-setup'. setup()
|
||||
# will then launch GNU Taler, wait for the process to be complete before
|
||||
# returning. The script will also install an exit handler to ensure the GNU
|
||||
# Taler processes are stopped when the shell exits.
|
||||
|
||||
set -eu
|
||||
|
||||
# Cleanup to run whenever we exit
|
||||
function exit_cleanup()
|
||||
{
|
||||
if [ ! -z ${SETUP_PID+x} ]
|
||||
then
|
||||
echo "Killing taler-unified-setup ($SETUP_PID)" >&2
|
||||
kill -TERM "$SETUP_PID"
|
||||
wait
|
||||
fi
|
||||
}
|
||||
|
||||
# Install cleanup handler (except for kill -9)
|
||||
trap exit_cleanup EXIT
|
||||
|
||||
function setup()
|
||||
{
|
||||
echo "Starting test system ..." >&2
|
||||
# Create a named pipe in a temp directory we own.
|
||||
FIFO_DIR=$(mktemp -d fifo-XXXXXX)
|
||||
FIFO_OUT=$(echo "$FIFO_DIR/out")
|
||||
mkfifo "$FIFO_OUT"
|
||||
# Open pipe as FD 3 (RW) and FD 4 (RO)
|
||||
exec 3<> "$FIFO_OUT" 4< "$FIFO_OUT"
|
||||
rm -rf "$FIFO_DIR"
|
||||
# We require '-W' for our termination logic to work.
|
||||
taler-unified-setup.sh -W "$@" >&3 &
|
||||
SETUP_PID=$!
|
||||
# Close FD3
|
||||
exec 3>&-
|
||||
sed -u '/<<READY>>/ q' <&4
|
||||
# Close FD4
|
||||
exec 4>&-
|
||||
echo "Test system ready" >&2
|
||||
}
|
||||
|
||||
# Exit, with status code "skip" (no 'real' failure)
|
||||
function exit_fail() {
|
||||
echo "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Exit, with status code "skip" (no 'real' failure)
|
||||
function exit_skip() {
|
||||
echo "SKIPPING: $1"
|
||||
exit 77
|
||||
}
|
||||
|
||||
function get_payto_uri() {
|
||||
export LIBEUFIN_SANDBOX_USERNAME="$1"
|
||||
export LIBEUFIN_SANDBOX_PASSWORD="$2"
|
||||
export LIBEUFIN_SANDBOX_URL="http://localhost:18082"
|
||||
libeufin-cli sandbox demobank info --bank-account "$1" | jq --raw-output '.paytoUri'
|
||||
}
|
||||
|
||||
function get_bankaccount_transactions() {
|
||||
export LIBEUFIN_SANDBOX_USERNAME=$1
|
||||
export LIBEUFIN_SANDBOX_PASSWORD=$2
|
||||
export LIBEUFIN_SANDBOX_URL="http://localhost:18082"
|
||||
libeufin-cli sandbox demobank list-transactions --bank-account $1
|
||||
}
|
@ -213,7 +213,7 @@ main (int argc,
|
||||
? "Could not remove exchange from database: entry already absent\n"
|
||||
: "Could not add exchange to database: entry already exists\n");
|
||||
TALER_AUDITORDB_plugin_unload (adb);
|
||||
return EXIT_FAILURE;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
TALER_AUDITORDB_plugin_unload (adb);
|
||||
|
@ -132,7 +132,7 @@ handle_mhd_completion_callback (void *cls,
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/version" request.
|
||||
* Handle a "/config" request.
|
||||
*
|
||||
* @param rh context of the handler
|
||||
* @param connection the MHD connection to handle
|
||||
@ -142,11 +142,11 @@ handle_mhd_completion_callback (void *cls,
|
||||
* @return MHD result code
|
||||
*/
|
||||
static MHD_RESULT
|
||||
handle_version (struct TAH_RequestHandler *rh,
|
||||
struct MHD_Connection *connection,
|
||||
void **connection_cls,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size)
|
||||
handle_config (struct TAH_RequestHandler *rh,
|
||||
struct MHD_Connection *connection,
|
||||
void **connection_cls,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size)
|
||||
{
|
||||
static json_t *ver; /* we build the response only once, keep around for next query! */
|
||||
|
||||
@ -157,6 +157,8 @@ handle_version (struct TAH_RequestHandler *rh,
|
||||
if (NULL == ver)
|
||||
{
|
||||
ver = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("name",
|
||||
"taler-auditor"),
|
||||
GNUNET_JSON_pack_string ("version",
|
||||
AUDITOR_PROTOCOL_VERSION),
|
||||
GNUNET_JSON_pack_string ("currency",
|
||||
@ -207,9 +209,9 @@ handle_mhd_request (void *cls,
|
||||
{ "/exchanges", MHD_HTTP_METHOD_GET, "application/json",
|
||||
NULL, 0,
|
||||
&TAH_EXCHANGES_handler, MHD_HTTP_OK },
|
||||
{ "/version", MHD_HTTP_METHOD_GET, "application/json",
|
||||
{ "/config", MHD_HTTP_METHOD_GET, "application/json",
|
||||
NULL, 0,
|
||||
&handle_version, MHD_HTTP_OK },
|
||||
&handle_config, MHD_HTTP_OK },
|
||||
/* Landing page, for now tells humans to go away
|
||||
* (NOTE: ideally, the reverse proxy will respond with a nicer page) */
|
||||
{ "/", MHD_HTTP_METHOD_GET, "text/plain",
|
||||
|
@ -605,6 +605,9 @@ main (int argc,
|
||||
level,
|
||||
NULL));
|
||||
GNUNET_free (level);
|
||||
/* suppress compiler warnings... */
|
||||
GNUNET_assert (NULL != src_cfgfile);
|
||||
GNUNET_assert (NULL != dst_cfgfile);
|
||||
if (0 == strcmp (src_cfgfile,
|
||||
dst_cfgfile))
|
||||
{
|
||||
|
@ -83,7 +83,9 @@ optcheck "$@"
|
||||
ARGS=("$@")
|
||||
ARGS=(${ARGS[@]/$INF})
|
||||
|
||||
DIR=`mktemp -d reportXXXXXX`
|
||||
DATE=`date +%F_%H:%M:%S`
|
||||
DIR="report_$DATE"
|
||||
mkdir $DIR
|
||||
for n in aggregation coins deposits purses reserves
|
||||
do
|
||||
taler-helper-auditor-$n ${ARGS[*]} > ${DIR}/$n.json
|
||||
|
@ -172,9 +172,9 @@ coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
memcpy (&i,
|
||||
coin_pub,
|
||||
sizeof (i));
|
||||
GNUNET_memcpy (&i,
|
||||
coin_pub,
|
||||
sizeof (i));
|
||||
return i % MAX_COIN_HISTORIES;
|
||||
}
|
||||
|
||||
|
@ -674,12 +674,12 @@ hash_rc (const char *receiver_account,
|
||||
size_t slen = strlen (receiver_account);
|
||||
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
|
||||
|
||||
memcpy (buf,
|
||||
wtid,
|
||||
sizeof (*wtid));
|
||||
memcpy (&buf[sizeof (*wtid)],
|
||||
receiver_account,
|
||||
slen);
|
||||
GNUNET_memcpy (buf,
|
||||
wtid,
|
||||
sizeof (*wtid));
|
||||
GNUNET_memcpy (&buf[sizeof (*wtid)],
|
||||
receiver_account,
|
||||
slen);
|
||||
GNUNET_CRYPTO_hash (buf,
|
||||
sizeof (buf),
|
||||
key);
|
||||
@ -1483,10 +1483,10 @@ history_debit_cb (void *cls,
|
||||
switch (dhr->http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<dhr->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<dhr->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_DebitDetails *dd
|
||||
= &dhr->details.success.details[i];
|
||||
= &dhr->details.ok.details[i];
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
|
||||
GNUNET_TIME_timestamp2s (dd->execution_date),
|
||||
@ -1504,9 +1504,9 @@ history_debit_cb (void *cls,
|
||||
roi->details.execution_date = dd->execution_date;
|
||||
roi->details.wtid = dd->wtid;
|
||||
roi->details.credit_account_uri = (const char *) &roi[1];
|
||||
memcpy (&roi[1],
|
||||
dd->credit_account_uri,
|
||||
slen);
|
||||
GNUNET_memcpy (&roi[1],
|
||||
dd->credit_account_uri,
|
||||
slen);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONTAINER_multihashmap_put (out_map,
|
||||
&roi->subject_hash,
|
||||
@ -1678,9 +1678,9 @@ reserve_in_cb (void *cls,
|
||||
rii->details.execution_date = execution_date;
|
||||
rii->details.reserve_pub = *reserve_pub;
|
||||
rii->details.debit_account_uri = (const char *) &rii[1];
|
||||
memcpy (&rii[1],
|
||||
sender_account_details,
|
||||
slen);
|
||||
GNUNET_memcpy (&rii[1],
|
||||
sender_account_details,
|
||||
slen);
|
||||
GNUNET_CRYPTO_hash (&wire_reference,
|
||||
sizeof (uint64_t),
|
||||
&rii->row_off_hash);
|
||||
@ -1978,10 +1978,10 @@ history_credit_cb (void *cls,
|
||||
switch (chr->http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<chr->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<chr->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_CreditDetails *cd
|
||||
= &chr->details.success.details[i];
|
||||
= &chr->details.ok.details[i];
|
||||
|
||||
if (! analyze_credit (wa,
|
||||
cd))
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This file is part of TALER
|
||||
# Copyright (C) 2014-2022 Taler Systems SA
|
||||
# Copyright (C) 2014-2023 Taler Systems SA
|
||||
#
|
||||
# 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
|
||||
@ -49,17 +49,7 @@ VALGRIND=""
|
||||
# history request.
|
||||
LIBEUFIN_SETTLE_TIME=1
|
||||
|
||||
# Exit, with status code "skip" (no 'real' failure)
|
||||
function exit_skip() {
|
||||
echo "SKIPPING test: $1"
|
||||
exit 77
|
||||
}
|
||||
|
||||
# Exit, with error message (hard failure)
|
||||
function exit_fail() {
|
||||
echo "FAILING test: $1"
|
||||
exit 1
|
||||
}
|
||||
. setup.sh
|
||||
|
||||
# Stop libeufin sandbox and nexus (if running)
|
||||
function stop_libeufin()
|
||||
@ -2002,12 +1992,12 @@ function check_with_database()
|
||||
{
|
||||
BASEDB=$1
|
||||
CONF=$1.conf
|
||||
ORIGIN=`pwd`
|
||||
MY_TMP_DIR=`dirname $1`
|
||||
ORIGIN=$(pwd)
|
||||
MY_TMP_DIR=$(dirname $1)
|
||||
echo "Running test suite with database $BASEDB using configuration $CONF"
|
||||
MASTER_PRIV_FILE=${BASEDB}.mpriv
|
||||
taler-config -f -c ${CONF} -s exchange-offline -o MASTER_PRIV_FILE -V ${MASTER_PRIV_FILE}
|
||||
MASTER_PUB=`gnunet-ecc -p $MASTER_PRIV_FILE`
|
||||
MASTER_PUB=$(gnunet-ecc -p $MASTER_PRIV_FILE)
|
||||
|
||||
echo "MASTER PUB is ${MASTER_PUB} using file ${MASTER_PRIV_FILE}"
|
||||
|
||||
@ -2037,7 +2027,7 @@ function check_with_database()
|
||||
|
||||
# ####### Setup globals ######
|
||||
# Postgres database to use
|
||||
export DB=auditor-basedb
|
||||
export DB="auditor-basedb"
|
||||
|
||||
# test required commands exist
|
||||
echo "Testing for jq"
|
||||
@ -2059,12 +2049,12 @@ INITDB_BIN=$(command -v initdb) || true
|
||||
if [[ ! -z "$INITDB_BIN" ]]; then
|
||||
echo " FOUND (in path) at" $INITDB_BIN
|
||||
else
|
||||
HAVE_INITDB=`find /usr -name "initdb" | head -1 2> /dev/null | grep postgres` || exit_skip " MISSING"
|
||||
echo " FOUND at" `dirname $HAVE_INITDB`
|
||||
INITDB_BIN=`echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1`
|
||||
HAVE_INITDB=$(find /usr -name "initdb" | head -1 2> /dev/null | grep postgres) || exit_skip " MISSING"
|
||||
echo " FOUND at" $(dirname $HAVE_INITDB)
|
||||
INITDB_BIN=$(echo $HAVE_INITDB | grep bin/initdb | grep postgres | sort -n | tail -n1)
|
||||
fi
|
||||
POSTGRES_PATH=`dirname $INITDB_BIN`
|
||||
MYDIR=`mktemp -d /tmp/taler-auditor-basedbXXXXXX`
|
||||
POSTGRES_PATH=$(dirname $INITDB_BIN)
|
||||
MYDIR=$(mktemp -d /tmp/taler-auditor-basedbXXXXXX)
|
||||
echo "Using $MYDIR for logging and temporary data"
|
||||
TMPDIR="$MYDIR/postgres/"
|
||||
mkdir -p $TMPDIR
|
||||
@ -2089,9 +2079,9 @@ PGHOST="$TMPDIR/sockets"
|
||||
export PGHOST
|
||||
|
||||
echo "Generating fresh database at $MYDIR"
|
||||
if faketime -f '-1 d' ./generate-auditor-basedb.sh $MYDIR/$DB
|
||||
if faketime -f '-1 d' ./generate-auditor-basedb.sh "$MYDIR/$DB"
|
||||
then
|
||||
check_with_database $MYDIR/$DB
|
||||
check_with_database "$MYDIR/$DB"
|
||||
if test x$fail != x0
|
||||
then
|
||||
exit $fail
|
||||
|
@ -74,25 +74,25 @@ handle_admin_add_incoming_finished (void *cls,
|
||||
const void *response)
|
||||
{
|
||||
struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
|
||||
uint64_t row_id = UINT64_MAX;
|
||||
struct GNUNET_TIME_Timestamp timestamp;
|
||||
enum TALER_ErrorCode ec;
|
||||
const json_t *j = response;
|
||||
struct TALER_BANK_AdminAddIncomingResponse ir = {
|
||||
.http_status = response_code,
|
||||
.response = response
|
||||
};
|
||||
|
||||
aai->job = NULL;
|
||||
timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint64 ("row_id",
|
||||
&row_id),
|
||||
&ir.details.ok.serial_id),
|
||||
GNUNET_JSON_spec_timestamp ("timestamp",
|
||||
×tamp),
|
||||
&ir.details.ok.timestamp),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
@ -102,42 +102,41 @@ handle_admin_add_incoming_finished (void *cls,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
response_code = 0;
|
||||
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
ir.http_status = 0;
|
||||
ir.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
}
|
||||
ec = TALER_EC_NONE;
|
||||
}
|
||||
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 */
|
||||
GNUNET_break_op (0);
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
/* Access denied */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_UNAUTHORIZED:
|
||||
/* Nothing really to verify, bank says the password is invalid; we should
|
||||
pass the JSON reply to the application */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, maybe account really does not exist.
|
||||
We should pass the JSON reply to the application */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
/* Nothing to verify, we used the same wire subject
|
||||
twice? */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
@ -145,15 +144,11 @@ handle_admin_add_incoming_finished (void *cls,
|
||||
"Unexpected response code %u\n",
|
||||
(unsigned int) response_code);
|
||||
GNUNET_break (0);
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
ir.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
}
|
||||
aai->cb (aai->cb_cls,
|
||||
response_code,
|
||||
ec,
|
||||
row_id,
|
||||
timestamp,
|
||||
j);
|
||||
&ir);
|
||||
TALER_BANK_admin_add_incoming_cancel (aai);
|
||||
}
|
||||
|
||||
|
@ -131,8 +131,8 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
chr.details.success.details_length = len;
|
||||
chr.details.success.details = cd;
|
||||
chr.details.ok.details_length = len;
|
||||
chr.details.ok.details = cd;
|
||||
hh->hcb (hh->hcb_cls,
|
||||
&chr);
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
dhr.details.success.details_length = len;
|
||||
dhr.details.success.details = dd;
|
||||
dhr.details.ok.details_length = len;
|
||||
dhr.details.ok.details = dd;
|
||||
hh->hcb (hh->hcb_cls,
|
||||
&dhr);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2015--2020 Taler Systems SA
|
||||
Copyright (C) 2015--2023 Taler Systems SA
|
||||
|
||||
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
|
||||
@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
|
||||
wp->account_len = htonl ((uint32_t) d_len);
|
||||
wp->exchange_url_len = htonl ((uint32_t) u_len);
|
||||
end = (char *) &wp[1];
|
||||
memcpy (end,
|
||||
destination_account_payto_uri,
|
||||
d_len);
|
||||
memcpy (end + d_len,
|
||||
exchange_base_url,
|
||||
u_len);
|
||||
GNUNET_memcpy (end,
|
||||
destination_account_payto_uri,
|
||||
d_len);
|
||||
GNUNET_memcpy (end + d_len,
|
||||
exchange_base_url,
|
||||
u_len);
|
||||
*buf = (char *) wp;
|
||||
}
|
||||
|
||||
@ -158,23 +158,24 @@ handle_transfer_finished (void *cls,
|
||||
{
|
||||
struct TALER_BANK_TransferHandle *th = cls;
|
||||
const json_t *j = response;
|
||||
uint64_t row_id = UINT64_MAX;
|
||||
struct GNUNET_TIME_Timestamp timestamp = GNUNET_TIME_UNIT_FOREVER_TS;
|
||||
enum TALER_ErrorCode ec;
|
||||
struct TALER_BANK_TransferResponse tr = {
|
||||
.http_status = response_code,
|
||||
.response = j
|
||||
};
|
||||
|
||||
th->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_uint64 ("row_id",
|
||||
&row_id),
|
||||
&tr.details.ok.row_id),
|
||||
GNUNET_JSON_spec_timestamp ("timestamp",
|
||||
×tamp),
|
||||
&tr.details.ok.timestamp),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
@ -184,39 +185,38 @@ handle_transfer_finished (void *cls,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
response_code = 0;
|
||||
ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
tr.http_status = 0;
|
||||
tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
}
|
||||
ec = TALER_EC_NONE;
|
||||
}
|
||||
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 */
|
||||
GNUNET_break_op (0);
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_UNAUTHORIZED:
|
||||
/* Nothing really to verify, bank says our credentials are
|
||||
invalid. We should pass the JSON reply to the application. */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, endpoint wrong -- could be user unknown */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
/* Nothing really to verify. Server says we used the same transfer request
|
||||
UID before, but with different details. Should not happen if the user
|
||||
properly used #TALER_BANK_prepare_transfer() and our PRNG is not
|
||||
broken... */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
@ -224,14 +224,11 @@ handle_transfer_finished (void *cls,
|
||||
"Unexpected response code %u\n",
|
||||
(unsigned int) response_code);
|
||||
GNUNET_break (0);
|
||||
ec = TALER_JSON_get_error_code (j);
|
||||
tr.ec = TALER_JSON_get_error_code (j);
|
||||
break;
|
||||
}
|
||||
th->cb (th->cb_cls,
|
||||
response_code,
|
||||
ec,
|
||||
row_id,
|
||||
timestamp);
|
||||
&tr);
|
||||
TALER_BANK_transfer_cancel (th);
|
||||
}
|
||||
|
||||
|
@ -1389,9 +1389,9 @@ make_transfer (
|
||||
if (NULL != timestamp)
|
||||
*timestamp = t->date;
|
||||
t->type = T_DEBIT;
|
||||
memcpy (t->subject.debit.exchange_base_url,
|
||||
exchange_base_url,
|
||||
url_len);
|
||||
GNUNET_memcpy (t->subject.debit.exchange_base_url,
|
||||
exchange_base_url,
|
||||
url_len);
|
||||
t->subject.debit.wtid = *subject;
|
||||
if (NULL == request_uid)
|
||||
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
|
@ -179,10 +179,10 @@ credit_history_cb (void *cls,
|
||||
global_ret = 0;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_CreditDetails *cd =
|
||||
&reply->details.success.details[i];
|
||||
&reply->details.ok.details[i];
|
||||
|
||||
/* If credit/debit accounts were specified, use as a filter */
|
||||
if ( (NULL != credit_account) &&
|
||||
@ -279,10 +279,10 @@ debit_history_cb (void *cls,
|
||||
global_ret = 0;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_DebitDetails *dd =
|
||||
&reply->details.success.details[i];
|
||||
&reply->details.ok.details[i];
|
||||
|
||||
/* If credit/debit accounts were specified, use as a filter */
|
||||
if ( (NULL != credit_account) &&
|
||||
@ -357,34 +357,28 @@ execute_debit_history (void)
|
||||
* execution.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param response_code HTTP status code
|
||||
* @param ec taler error code
|
||||
* @param row_id unique ID of the wire transfer in the bank's records
|
||||
* @param timestamp when did the transaction go into effect
|
||||
* @param tr response details
|
||||
*/
|
||||
static void
|
||||
confirmation_cb (void *cls,
|
||||
unsigned int response_code,
|
||||
enum TALER_ErrorCode ec,
|
||||
uint64_t row_id,
|
||||
struct GNUNET_TIME_Timestamp timestamp)
|
||||
const struct TALER_BANK_TransferResponse *tr)
|
||||
{
|
||||
(void) cls;
|
||||
eh = NULL;
|
||||
if (MHD_HTTP_OK != response_code)
|
||||
if (MHD_HTTP_OK != tr->http_status)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"The wire transfer didn't execute correctly (%u/%d).\n",
|
||||
response_code,
|
||||
ec);
|
||||
tr->http_status,
|
||||
tr->ec);
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf (stdout,
|
||||
"Wire transfer #%llu executed successfully at %s.\n",
|
||||
(unsigned long long) row_id,
|
||||
GNUNET_TIME_timestamp2s (timestamp));
|
||||
(unsigned long long) tr->details.ok.row_id,
|
||||
GNUNET_TIME_timestamp2s (tr->details.ok.timestamp));
|
||||
global_ret = 0;
|
||||
GNUNET_SCHEDULER_shutdown ();
|
||||
}
|
||||
@ -464,39 +458,29 @@ execute_wire_transfer (void)
|
||||
* Function called with the result of the operation.
|
||||
*
|
||||
* @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)
|
||||
* @param ec detailed error code
|
||||
* @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
|
||||
* @param timestamp timestamp when the transaction got settled at the bank.
|
||||
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
|
||||
* @param air response details
|
||||
*/
|
||||
static void
|
||||
res_cb (void *cls,
|
||||
unsigned int http_status,
|
||||
enum TALER_ErrorCode ec,
|
||||
uint64_t serial_id,
|
||||
struct GNUNET_TIME_Timestamp timestamp,
|
||||
const json_t *json)
|
||||
const struct TALER_BANK_AdminAddIncomingResponse *air)
|
||||
{
|
||||
(void) cls;
|
||||
(void) timestamp;
|
||||
op = NULL;
|
||||
switch (ec)
|
||||
switch (air->http_status)
|
||||
{
|
||||
case TALER_EC_NONE:
|
||||
case MHD_HTTP_OK:
|
||||
global_ret = 0;
|
||||
fprintf (stdout,
|
||||
"%llu\n",
|
||||
(unsigned long long) serial_id);
|
||||
(unsigned long long) air->details.ok.serial_id);
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr,
|
||||
"Operation failed with status code %u/%u\n",
|
||||
(unsigned int) ec,
|
||||
http_status);
|
||||
if (NULL != json)
|
||||
json_dumpf (json,
|
||||
(unsigned int) air->ec,
|
||||
air->http_status);
|
||||
if (NULL != air->response)
|
||||
json_dumpf (air->response,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
break;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#!/bin/bash
|
||||
# This file is in the public domain.
|
||||
|
||||
set -eu
|
||||
|
||||
|
1
src/benchmark/.gitignore
vendored
1
src/benchmark/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
taler-bank-benchmark
|
||||
taler-aggregator-benchmark
|
||||
*.edited
|
||||
|
@ -15,6 +15,7 @@ bin_PROGRAMS = \
|
||||
taler-bank-benchmark \
|
||||
taler-exchange-benchmark
|
||||
|
||||
|
||||
taler_aggregator_benchmark_SOURCES = \
|
||||
taler-aggregator-benchmark.c
|
||||
taler_aggregator_benchmark_LDADD = \
|
||||
@ -64,6 +65,11 @@ taler_exchange_benchmark_LDADD = \
|
||||
$(XLIB)
|
||||
|
||||
EXTRA_DIST = \
|
||||
benchmark-common.conf \
|
||||
benchmark-cs.conf \
|
||||
benchmark-rsa.conf \
|
||||
bank-benchmark-cs.conf \
|
||||
bank-benchmark-rsa.conf \
|
||||
coins-cs.conf \
|
||||
coins-rsa.conf \
|
||||
exchange_benchmark_home/.local/share/taler/exchange/offline-keys/master.priv
|
||||
|
@ -1,128 +1,17 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
[paths]
|
||||
# Persistent data storage for the testcase
|
||||
# This value is a default for `taler_config_home'
|
||||
taler_test_home = exchange_benchmark_home/
|
||||
|
||||
[taler]
|
||||
# Currency supported by the exchange (can only be one)
|
||||
currency = EUR
|
||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||
|
||||
[exchange]
|
||||
# how long is one signkey valid?
|
||||
signkey_duration = 4 weeks
|
||||
signkey_legal_duration = 2 years
|
||||
# how long do we provide to clients denomination and signing keys
|
||||
# ahead of time?
|
||||
# Keep it short so the test runs fast.
|
||||
lookahead_sign = 12h
|
||||
# HTTP port the exchange listens to
|
||||
port = 8081
|
||||
# Master public key used to sign the exchange's various keys
|
||||
master_public_key = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
# How to access our database
|
||||
DB = postgres
|
||||
# Base URL of the exchange. Must be set to a URL where the
|
||||
# exchange (or the twister) is actually listening.
|
||||
base_url = "http://localhost:8081/"
|
||||
|
||||
WIREWATCH_IDLE_SLEEP_INTERVAL = 500 ms
|
||||
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||
|
||||
[auditor]
|
||||
BASE_URL = "http://localhost:8083/"
|
||||
|
||||
[exchangedb-postgres]
|
||||
config = "postgres:///talercheck"
|
||||
|
||||
[benchmark-remote-exchange]
|
||||
host = localhost
|
||||
# Adjust $HOME to match remote target!
|
||||
dir = $HOME/repos/taler/exchange/src/benchmark
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
SERVE = http
|
||||
MAX_DEBT = EUR:100000000000.0
|
||||
MAX_DEBT_BANK = EUR:1000000000000000.0
|
||||
|
||||
[benchmark]
|
||||
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42?receiver-name=user42
|
||||
@INLINE@ benchmark-common.conf
|
||||
@INLINE@ coins-cs.conf
|
||||
|
||||
[exchange-account-2]
|
||||
# What is the payto://-URL of the exchange (to generate wire response)
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
|
||||
enable_debit = YES
|
||||
enable_credit = YES
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-2]
|
||||
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
|
||||
WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
|
||||
# Authentication information for basic authentication
|
||||
WIRE_GATEWAY_AUTH_METHOD = "basic"
|
||||
username = Exchange
|
||||
password = x
|
||||
|
||||
|
||||
|
||||
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
USERNAME = Exchange
|
||||
PASSWORD = x
|
||||
|
@ -1,133 +1,17 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
[paths]
|
||||
# Persistent data storage for the testcase
|
||||
# This value is a default for `taler_config_home'
|
||||
taler_test_home = exchange_benchmark_home/
|
||||
|
||||
[taler]
|
||||
# Currency supported by the exchange (can only be one)
|
||||
currency = EUR
|
||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||
|
||||
[exchange]
|
||||
# how long is one signkey valid?
|
||||
signkey_duration = 4 weeks
|
||||
signkey_legal_duration = 2 years
|
||||
# how long do we provide to clients denomination and signing keys
|
||||
# ahead of time?
|
||||
# Keep it short so the test runs fast.
|
||||
lookahead_sign = 12h
|
||||
# HTTP port the exchange listens to
|
||||
port = 8081
|
||||
# Master public key used to sign the exchange's various keys
|
||||
master_public_key = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
# How to access our database
|
||||
DB = postgres
|
||||
# Base URL of the exchange. Must be set to a URL where the
|
||||
# exchange (or the twister) is actually listening.
|
||||
base_url = "http://localhost:8081/"
|
||||
|
||||
WIREWATCH_IDLE_SLEEP_INTERVAL = 1500 ms
|
||||
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||
|
||||
[auditor]
|
||||
BASE_URL = "http://localhost:8083/"
|
||||
|
||||
[exchangedb-postgres]
|
||||
config = "postgres://exchange:taler@192.168.42.42/exchange"
|
||||
|
||||
[benchmark-remote-exchange]
|
||||
host = localhost
|
||||
# Adjust $HOME to match remote target!
|
||||
dir = $HOME/repos/taler/exchange/src/benchmark
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
SERVE = http
|
||||
MAX_DEBT = EUR:100000000000.0
|
||||
MAX_DEBT_BANK = EUR:1000000000000000.0
|
||||
|
||||
[benchmark]
|
||||
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
|
||||
@INLINE@ benchmark-common.conf
|
||||
@INLINE@ coins-cs.conf
|
||||
|
||||
[exchange-account-2]
|
||||
# What is the payto://-URL of the exchange (to generate wire response)
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange"
|
||||
enable_debit = YES
|
||||
enable_credit = YES
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-2]
|
||||
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
|
||||
WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
|
||||
# Authentication information for basic authentication
|
||||
WIRE_GATEWAY_AUTH_METHOD = "basic"
|
||||
username = Exchange
|
||||
password = x
|
||||
|
||||
|
||||
|
||||
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
USERNAME = Exchange
|
||||
PASSWORD = x
|
||||
|
89
src/benchmark/benchmark-common.conf
Normal file
89
src/benchmark/benchmark-common.conf
Normal file
@ -0,0 +1,89 @@
|
||||
# This file is in the public domain.
|
||||
[paths]
|
||||
TALER_TEST_HOME=exchange_benchmark_home/
|
||||
|
||||
[taler]
|
||||
CURRENCY=EUR
|
||||
CURRENCY_ROUND_UNIT=EUR:0.01
|
||||
|
||||
[exchange]
|
||||
AML_THRESHOLD=EUR:99999999
|
||||
SIGNKEY_LEGAL_DURATION=2 years
|
||||
PORT=8081
|
||||
MASTER_PUBLIC_KEY=98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
DB=postgres
|
||||
BASE_URL="http://localhost:8081/"
|
||||
# Only set this option if you are actually running
|
||||
# multiple aggregators!
|
||||
# AGGREGATOR_SHARD_SIZE=67108864
|
||||
WIREWATCH_IDLE_SLEEP_INTERVAL=5 ms
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG="postgres:///talercheck"
|
||||
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE=${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||
|
||||
[taler-exchange-secmod-rsa]
|
||||
LOOKAHEAD_SIGN="1 d"
|
||||
|
||||
[taler-exchange-secmod-cs]
|
||||
LOOKAHEAD_SIGN="1 d"
|
||||
|
||||
[taler-exchange-secmod-eddsa]
|
||||
DURATION="2 d"
|
||||
LOOKAHEAD_SIGN="1 d"
|
||||
|
||||
# account-2 is suitable for fakebank
|
||||
[exchange-account-1]
|
||||
PAYTO_URI = "payto://x-taler-bank/localhost/42?receiver-name=42"
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
|
||||
[exchange-accountcredentials-1]
|
||||
WIRE_GATEWAY_AUTH_METHOD = none
|
||||
WIRE_GATEWAY_URL = "http://localhost:8082/42/"
|
||||
|
||||
# account-2 is suitable for libeufin
|
||||
[exchange-account-2]
|
||||
ENABLE_DEBIT = YES
|
||||
ENABLE_CREDIT = YES
|
||||
PAYTO_URI = payto://iban/SANDBOXX/DE033310?receiver-name=Exchange+Company
|
||||
|
||||
[exchange-accountcredentials-2]
|
||||
WIRE_GATEWAY_AUTH_METHOD = basic
|
||||
USERNAME = exchange
|
||||
PASSWORD = x
|
||||
WIRE_GATEWAY_URL = "http://localhost:8082/facades/test-facade/taler-wire-gateway/"
|
||||
|
||||
|
||||
# Trust local exchange for "EUR" currency
|
||||
[merchant-exchange-benchmark]
|
||||
EXCHANGE_BASE_URL = http://localhost:8081/
|
||||
MASTER_KEY=98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
# If currency does not match [TALER] section, the exchange
|
||||
# will be ignored!
|
||||
CURRENCY = EUR
|
||||
|
||||
|
||||
[merchantdb-postgres]
|
||||
CONFIG="postgres:///talercheck"
|
||||
|
||||
[auditordb-postgres]
|
||||
CONFIG="postgres:///talercheck"
|
||||
|
||||
[syncdb-postgres]
|
||||
CONFIG="postgres:///talercheck"
|
||||
|
||||
[bank]
|
||||
HTTP_PORT=8082
|
||||
SERVE=http
|
||||
|
||||
[libeufin-nexus]
|
||||
DB_CONNECTION="jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
|
||||
|
||||
[libeufin-sandbox]
|
||||
DB_CONNECTION="jdbc:postgresql://localhost/talercheck?socketFactory=org.newsclub.net.unix.AFUNIXSocketFactory$FactoryArg&socketFactoryArg=/var/run/postgresql/.s.PGSQL.5432"
|
||||
|
||||
[auditor]
|
||||
BASE_URL="http://localhost:8083/"
|
@ -1,58 +1,6 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
[paths]
|
||||
# Persistent data storage for the testcase
|
||||
# This value is a default for `taler_config_home'
|
||||
TALER_TEST_HOME = exchange_benchmark_home/
|
||||
|
||||
[taler]
|
||||
# Currency supported by the exchange (can only be one)
|
||||
CURRENCY = EUR
|
||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||
|
||||
[exchange]
|
||||
|
||||
SIGNKEY_LEGAL_DURATION = 2 years
|
||||
|
||||
# HTTP port the exchange listens to
|
||||
PORT = 8081
|
||||
# Master public key used to sign the exchange's various keys
|
||||
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
# How to access our database
|
||||
DB = postgres
|
||||
# Base URL of the exchange. Must be set to a URL where the
|
||||
# exchange (or the twister) is actually listening.
|
||||
BASE_URL = "http://localhost:8081/"
|
||||
|
||||
AGGREGATOR_SHARD_SIZE = 67108864
|
||||
#AGGREGATOR_SHARD_SIZE = 2147483648
|
||||
|
||||
|
||||
|
||||
WIREWATCH_IDLE_SLEEP_INTERVAL = 5 ms
|
||||
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||
|
||||
[auditor]
|
||||
BASE_URL = "http://localhost:8083/"
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = "postgres:///talercheck"
|
||||
|
||||
[benchmark-remote-exchange]
|
||||
HOST = localhost
|
||||
# Adjust $HOME to match remote target!
|
||||
DIR = $HOME/repos/taler/exchange/src/benchmark
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
SERVE = http
|
||||
MAX_DEBT = EUR:100000000000.0
|
||||
MAX_DEBT_BANK = EUR:1000000000000000.0
|
||||
|
||||
[benchmark]
|
||||
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
|
||||
@INLINE@ benchmark-common.conf
|
||||
@INLINE@ coins-cs.conf
|
||||
|
||||
[exchange-account-test]
|
||||
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
|
||||
@ -66,61 +14,3 @@ WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
|
||||
WIRE_GATEWAY_AUTH_METHOD = "basic"
|
||||
USERNAME = Exchange
|
||||
PASSWORD = x
|
||||
|
||||
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
@ -1,58 +1,6 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
[paths]
|
||||
# Persistent data storage for the testcase
|
||||
# This value is a default for `taler_config_home'
|
||||
TALER_TEST_HOME = exchange_benchmark_home/
|
||||
|
||||
[taler]
|
||||
# Currency supported by the exchange (can only be one)
|
||||
CURRENCY = EUR
|
||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||
|
||||
[exchange]
|
||||
|
||||
SIGNKEY_LEGAL_DURATION = 2 years
|
||||
|
||||
# HTTP port the exchange listens to
|
||||
PORT = 8081
|
||||
# Master public key used to sign the exchange's various keys
|
||||
MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
# How to access our database
|
||||
DB = postgres
|
||||
# Base URL of the exchange. Must be set to a URL where the
|
||||
# exchange (or the twister) is actually listening.
|
||||
BASE_URL = "http://localhost:8081/"
|
||||
|
||||
AGGREGATOR_SHARD_SIZE = 67108864
|
||||
#AGGREGATOR_SHARD_SIZE = 2147483648
|
||||
|
||||
|
||||
|
||||
WIREWATCH_IDLE_SLEEP_INTERVAL = 5 ms
|
||||
|
||||
[exchange-offline]
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||
|
||||
[auditor]
|
||||
BASE_URL = "http://localhost:8083/"
|
||||
|
||||
[exchangedb-postgres]
|
||||
CONFIG = "postgres:///talercheck"
|
||||
|
||||
[benchmark-remote-exchange]
|
||||
HOST = localhost
|
||||
# Adjust $HOME to match remote target!
|
||||
DIR = $HOME/repos/taler/exchange/src/benchmark
|
||||
|
||||
[bank]
|
||||
HTTP_PORT = 8082
|
||||
SERVE = http
|
||||
MAX_DEBT = EUR:100000000000.0
|
||||
MAX_DEBT_BANK = EUR:1000000000000000.0
|
||||
|
||||
[benchmark]
|
||||
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
|
||||
@INLINE@ benchmark-common.conf
|
||||
@INLINE@ coins-rsa.conf
|
||||
|
||||
[exchange-account-test]
|
||||
# What is the bank account (with the "Taler Bank" demo system)? Must end with "/".
|
||||
@ -66,66 +14,3 @@ WIRE_GATEWAY_URL = http://localhost:8082/Exchange/
|
||||
WIRE_GATEWAY_AUTH_METHOD = "basic"
|
||||
USERNAME = Exchange
|
||||
PASSWORD = x
|
||||
|
||||
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
58
src/benchmark/coins-cs.conf
Normal file
58
src/benchmark/coins-cs.conf
Normal file
@ -0,0 +1,58 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = CS
|
63
src/benchmark/coins-rsa.conf
Normal file
63
src/benchmark/coins-rsa.conf
Normal file
@ -0,0 +1,63 @@
|
||||
# This file is in the public domain.
|
||||
#
|
||||
# Sections starting with "coin_" specify which denominations
|
||||
# the exchange should support (and their respective fee structure)
|
||||
[coin_eur_ct_1]
|
||||
value = EUR:0.01
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.00
|
||||
fee_deposit = EUR:0.00
|
||||
fee_refresh = EUR:0.01
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_ct_10]
|
||||
value = EUR:0.10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_1]
|
||||
value = EUR:1
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_5]
|
||||
value = EUR:5
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
||||
|
||||
[coin_eur_10]
|
||||
value = EUR:10
|
||||
duration_withdraw = 7 days
|
||||
duration_spend = 2 years
|
||||
duration_legal = 3 years
|
||||
fee_withdraw = EUR:0.01
|
||||
fee_deposit = EUR:0.01
|
||||
fee_refresh = EUR:0.03
|
||||
fee_refund = EUR:0.01
|
||||
CIPHER = RSA
|
||||
rsa_keysize = 2048
|
@ -251,7 +251,7 @@ add_refund (const struct Merchant *m,
|
||||
r.details.rtransaction_id = 42;
|
||||
make_amount (0, 5000000, &r.details.refund_amount);
|
||||
make_amount (0, 5, &r.details.refund_fee);
|
||||
if (0 <=
|
||||
if (0 >=
|
||||
plugin->insert_refund (plugin->cls,
|
||||
&r))
|
||||
{
|
||||
@ -307,8 +307,13 @@ add_deposit (const struct Merchant *m)
|
||||
deposit.wire_salt = m->wire_salt;
|
||||
deposit.receiver_wire_account = m->payto_uri;
|
||||
deposit.timestamp = random_time ();
|
||||
deposit.refund_deadline = random_time ();
|
||||
deposit.wire_deadline = random_time ();
|
||||
do {
|
||||
deposit.refund_deadline = random_time ();
|
||||
deposit.wire_deadline = random_time ();
|
||||
} while (GNUNET_TIME_timestamp_cmp (deposit.wire_deadline,
|
||||
<,
|
||||
deposit.refund_deadline));
|
||||
|
||||
make_amount (1, 0, &deposit.amount_with_fee);
|
||||
make_amount (0, 5, &deposit.deposit_fee);
|
||||
if (0 >=
|
||||
@ -446,6 +451,9 @@ run (void *cls,
|
||||
}
|
||||
GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
|
||||
NULL);
|
||||
memset (&issue,
|
||||
0,
|
||||
sizeof (issue));
|
||||
RANDOMIZE (&issue.signature);
|
||||
issue.start
|
||||
= start;
|
||||
@ -478,6 +486,7 @@ run (void *cls,
|
||||
TALER_DENOMINATION_RSA,
|
||||
1024));
|
||||
alg_values.cipher = TALER_DENOMINATION_RSA;
|
||||
denom_pub.age_mask = issue.age_mask;
|
||||
TALER_denom_pub_hash (&denom_pub,
|
||||
&h_denom_pub);
|
||||
make_amount (2, 0, &issue.value);
|
||||
@ -497,7 +506,6 @@ run (void *cls,
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
TALER_planchet_blinding_secret_create (&ps,
|
||||
&alg_values,
|
||||
&bks);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2014-2021 Taler Systems SA
|
||||
(C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Affero General Public License as
|
||||
@ -39,65 +39,16 @@
|
||||
#include "taler_error_codes.h"
|
||||
|
||||
|
||||
/* Error codes. */
|
||||
enum BenchmarkError
|
||||
{
|
||||
MISSING_BANK_URL,
|
||||
FAILED_TO_LAUNCH_BANK,
|
||||
BAD_CLI_ARG,
|
||||
BAD_CONFIG_FILE,
|
||||
NO_CONFIG_FILE_GIVEN
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* What mode should the benchmark run in?
|
||||
* Credentials to use for the benchmark.
|
||||
*/
|
||||
enum BenchmarkMode
|
||||
{
|
||||
/**
|
||||
* Run as client against the bank.
|
||||
*/
|
||||
MODE_CLIENT = 1,
|
||||
|
||||
/**
|
||||
* Run the bank.
|
||||
*/
|
||||
MODE_BANK = 2,
|
||||
|
||||
/**
|
||||
* Run both, for a local benchmark.
|
||||
*/
|
||||
MODE_BOTH = 3,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hold information regarding which bank has the exchange account.
|
||||
*/
|
||||
static const struct TALER_EXCHANGEDB_AccountInfo *exchange_bank_account;
|
||||
|
||||
/**
|
||||
* Time snapshot taken right before executing the CMDs.
|
||||
*/
|
||||
static struct GNUNET_TIME_Absolute start_time;
|
||||
|
||||
/**
|
||||
* Benchmark duration time taken right after the CMD interpreter
|
||||
* returns.
|
||||
*/
|
||||
static struct GNUNET_TIME_Relative duration;
|
||||
static struct TALER_TESTING_Credentials cred;
|
||||
|
||||
/**
|
||||
* Array of all the commands the benchmark is running.
|
||||
*/
|
||||
static struct TALER_TESTING_Command *all_commands;
|
||||
|
||||
/**
|
||||
* Dummy keepalive task.
|
||||
*/
|
||||
static struct GNUNET_SCHEDULER_Task *keepalive;
|
||||
|
||||
/**
|
||||
* Name of our configuration file.
|
||||
*/
|
||||
@ -105,27 +56,14 @@ static char *cfg_filename;
|
||||
|
||||
/**
|
||||
* Use the fakebank instead of LibEuFin.
|
||||
* NOTE: LibEuFin not yet supported! Set
|
||||
* to 0 once we do support it!
|
||||
*/
|
||||
static int use_fakebank = 1;
|
||||
|
||||
/**
|
||||
* Number of taler-exchange-wirewatchers to launch.
|
||||
*/
|
||||
static unsigned int start_wirewatch;
|
||||
static int use_fakebank;
|
||||
|
||||
/**
|
||||
* Verbosity level.
|
||||
*/
|
||||
static unsigned int verbose;
|
||||
|
||||
/**
|
||||
* Size of the transaction history the fakebank
|
||||
* should keep in RAM.
|
||||
*/
|
||||
static unsigned long long history_size = 65536;
|
||||
|
||||
/**
|
||||
* How many reserves we want to create per client.
|
||||
*/
|
||||
@ -141,6 +79,11 @@ static unsigned int howmany_clients = 1;
|
||||
*/
|
||||
static unsigned int howmany_threads;
|
||||
|
||||
/**
|
||||
* How many wirewatch processes do we want to create.
|
||||
*/
|
||||
static unsigned int start_wirewatch;
|
||||
|
||||
/**
|
||||
* Log level used during the run.
|
||||
*/
|
||||
@ -151,32 +94,17 @@ static char *loglev;
|
||||
*/
|
||||
static char *logfile;
|
||||
|
||||
/**
|
||||
* Benchmarking mode (run as client, exchange, both) as string.
|
||||
*/
|
||||
static char *mode_str;
|
||||
|
||||
/**
|
||||
* Benchmarking mode (run as client, bank, both).
|
||||
*/
|
||||
static enum BenchmarkMode mode;
|
||||
|
||||
/**
|
||||
* Don't kill exchange/fakebank/wirewatch until
|
||||
* requested by the user explicitly.
|
||||
*/
|
||||
static int linger;
|
||||
|
||||
/**
|
||||
* Do not initialize or reset the database.
|
||||
*/
|
||||
static int incremental;
|
||||
|
||||
/**
|
||||
* Configuration.
|
||||
*/
|
||||
static struct GNUNET_CONFIGURATION_Handle *cfg;
|
||||
|
||||
/**
|
||||
* Section with the configuration data for the exchange
|
||||
* bank account.
|
||||
*/
|
||||
static char *exchange_bank_section;
|
||||
|
||||
/**
|
||||
* Currency used.
|
||||
*/
|
||||
@ -237,10 +165,10 @@ print_stats (void)
|
||||
|
||||
total = GNUNET_strdup (
|
||||
GNUNET_STRINGS_relative_time_to_string (timings[i].total_duration,
|
||||
GNUNET_YES));
|
||||
true));
|
||||
latency = GNUNET_strdup (
|
||||
GNUNET_STRINGS_relative_time_to_string (timings[i].success_latency,
|
||||
GNUNET_YES));
|
||||
true));
|
||||
fprintf (stderr,
|
||||
"%s-%d took %s in total with %s for latency for %u executions (%u repeats)\n",
|
||||
timings[i].prefix,
|
||||
@ -270,38 +198,39 @@ run (void *cls,
|
||||
|
||||
(void) cls;
|
||||
len = howmany_reserves + 2;
|
||||
all_commands = GNUNET_malloc_large (len
|
||||
all_commands = GNUNET_malloc_large ((1 + len)
|
||||
* sizeof (struct TALER_TESTING_Command));
|
||||
GNUNET_assert (NULL != all_commands);
|
||||
all_commands[0]
|
||||
= TALER_TESTING_cmd_get_exchange ("get-exchange",
|
||||
cred.cfg,
|
||||
NULL,
|
||||
true,
|
||||
true);
|
||||
|
||||
GNUNET_asprintf (&total_reserve_amount,
|
||||
"%s:5",
|
||||
currency);
|
||||
for (unsigned int j = 0; j < howmany_reserves; j++)
|
||||
{
|
||||
char *create_reserve_label;
|
||||
char *user_payto_uri;
|
||||
|
||||
// FIXME: vary user accounts more...
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
GNUNET_CONFIGURATION_get_value_string (cfg,
|
||||
"benchmark",
|
||||
"USER_PAYTO_URI",
|
||||
&user_payto_uri));
|
||||
GNUNET_asprintf (&create_reserve_label,
|
||||
"createreserve-%u",
|
||||
j);
|
||||
all_commands[j]
|
||||
// TODO: vary user accounts more...
|
||||
all_commands[1 + j]
|
||||
= TALER_TESTING_cmd_admin_add_incoming_retry (
|
||||
TALER_TESTING_cmd_admin_add_incoming (add_label (
|
||||
create_reserve_label),
|
||||
total_reserve_amount,
|
||||
exchange_bank_account->auth,
|
||||
add_label (user_payto_uri)));
|
||||
&cred.ba,
|
||||
cred.user42_payto));
|
||||
}
|
||||
GNUNET_free (total_reserve_amount);
|
||||
all_commands[howmany_reserves]
|
||||
all_commands[1 + howmany_reserves]
|
||||
= TALER_TESTING_cmd_stat (timings);
|
||||
all_commands[howmany_reserves + 1]
|
||||
all_commands[1 + howmany_reserves + 1]
|
||||
= TALER_TESTING_cmd_end ();
|
||||
TALER_TESTING_run2 (is,
|
||||
all_commands,
|
||||
@ -318,15 +247,11 @@ launch_clients (void)
|
||||
enum GNUNET_GenericReturnValue result = GNUNET_OK;
|
||||
pid_t cpids[howmany_clients];
|
||||
|
||||
start_time = GNUNET_TIME_absolute_get ();
|
||||
if (1 == howmany_clients)
|
||||
{
|
||||
/* do everything in this process */
|
||||
result = TALER_TESTING_setup (&run,
|
||||
NULL,
|
||||
cfg,
|
||||
NULL,
|
||||
GNUNET_NO);
|
||||
result = TALER_TESTING_loop (&run,
|
||||
NULL);
|
||||
if (verbose)
|
||||
print_stats ();
|
||||
return result;
|
||||
@ -340,11 +265,8 @@ launch_clients (void)
|
||||
GNUNET_log_setup ("benchmark-worker",
|
||||
NULL == loglev ? "INFO" : loglev,
|
||||
logfile);
|
||||
result = TALER_TESTING_setup (&run,
|
||||
NULL,
|
||||
cfg,
|
||||
NULL,
|
||||
GNUNET_NO);
|
||||
result = TALER_TESTING_loop (&run,
|
||||
NULL);
|
||||
if (verbose)
|
||||
print_stats ();
|
||||
if (GNUNET_OK != result)
|
||||
@ -393,78 +315,6 @@ again:
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stop the fakebank.
|
||||
*
|
||||
* @param cls fakebank handle
|
||||
*/
|
||||
static void
|
||||
stop_fakebank (void *cls)
|
||||
{
|
||||
struct TALER_FAKEBANK_Handle *fakebank = cls;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Stopping fakebank\n");
|
||||
TALER_FAKEBANK_stop (fakebank);
|
||||
GNUNET_SCHEDULER_cancel (keepalive);
|
||||
keepalive = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dummy task that is never run.
|
||||
*/
|
||||
static void
|
||||
never_task (void *cls)
|
||||
{
|
||||
(void) cls;
|
||||
GNUNET_assert (0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start the fakebank.
|
||||
*
|
||||
* @param cls NULL
|
||||
*/
|
||||
static void
|
||||
launch_fakebank (void *cls)
|
||||
{
|
||||
struct TALER_FAKEBANK_Handle *fakebank;
|
||||
unsigned long long pnum;
|
||||
|
||||
(void) cls;
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_number (cfg,
|
||||
"bank",
|
||||
"HTTP_PORT",
|
||||
&pnum))
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
"bank",
|
||||
"HTTP_PORT",
|
||||
"must be valid port number");
|
||||
return;
|
||||
}
|
||||
fakebank
|
||||
= TALER_FAKEBANK_start2 ((uint16_t) pnum,
|
||||
currency,
|
||||
history_size,
|
||||
howmany_threads);
|
||||
if (NULL == fakebank)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return;
|
||||
}
|
||||
keepalive
|
||||
= GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
|
||||
&never_task,
|
||||
NULL);
|
||||
GNUNET_SCHEDULER_add_shutdown (&stop_fakebank,
|
||||
fakebank);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run the benchmark in parallel in many (client) processes
|
||||
* and summarize result.
|
||||
@ -475,278 +325,94 @@ static enum GNUNET_GenericReturnValue
|
||||
parallel_benchmark (void)
|
||||
{
|
||||
enum GNUNET_GenericReturnValue result = GNUNET_OK;
|
||||
pid_t fakebank = -1;
|
||||
struct GNUNET_OS_Process *bankd = NULL;
|
||||
struct GNUNET_OS_Process *wirewatch[GNUNET_NZL (start_wirewatch)];
|
||||
|
||||
memset (wirewatch,
|
||||
0,
|
||||
sizeof (wirewatch));
|
||||
if ( (MODE_BANK == mode) ||
|
||||
(MODE_BOTH == mode) )
|
||||
/* start exchange wirewatch */
|
||||
for (unsigned int w = 0; w<start_wirewatch; w++)
|
||||
{
|
||||
if (use_fakebank)
|
||||
{
|
||||
unsigned long long pnum;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_number (cfg,
|
||||
"bank",
|
||||
"HTTP_PORT",
|
||||
&pnum))
|
||||
{
|
||||
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
|
||||
"bank",
|
||||
"HTTP_PORT",
|
||||
"must be valid port number");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* start fakebank */
|
||||
fakebank = fork ();
|
||||
if (0 == fakebank)
|
||||
{
|
||||
GNUNET_log_setup ("benchmark-fakebank",
|
||||
NULL == loglev ? "INFO" : loglev,
|
||||
logfile);
|
||||
GNUNET_SCHEDULER_run (&launch_fakebank,
|
||||
NULL);
|
||||
exit (0);
|
||||
}
|
||||
if (-1 == fakebank)
|
||||
{
|
||||
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
|
||||
"fork");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* wait for fakebank to be ready */
|
||||
{
|
||||
char *bank_url;
|
||||
int ret;
|
||||
|
||||
GNUNET_asprintf (&bank_url,
|
||||
"http://localhost:%u/",
|
||||
(unsigned int) (uint16_t) pnum);
|
||||
ret = TALER_TESTING_wait_httpd_ready (bank_url);
|
||||
GNUNET_free (bank_url);
|
||||
if (0 != ret)
|
||||
{
|
||||
int wstatus;
|
||||
|
||||
kill (fakebank,
|
||||
SIGTERM);
|
||||
if (fakebank !=
|
||||
waitpid (fakebank,
|
||||
&wstatus,
|
||||
0))
|
||||
{
|
||||
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
|
||||
"waitpid");
|
||||
}
|
||||
fakebank = -1;
|
||||
exit (ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
wirewatch[w] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
if (NULL == wirewatch[w])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"FIXME #7273: launching LibEuFin not yet supported\n");
|
||||
bankd = NULL; // FIXME #7273
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
if (0 == incremental)
|
||||
{
|
||||
struct GNUNET_OS_Process *dbinit;
|
||||
|
||||
dbinit = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-dbinit",
|
||||
"taler-exchange-dbinit",
|
||||
"-c", cfg_filename,
|
||||
"-r",
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
if (NULL == dbinit)
|
||||
{
|
||||
if (NULL != bankd)
|
||||
{
|
||||
GNUNET_OS_process_kill (bankd,
|
||||
SIGTERM);
|
||||
GNUNET_OS_process_destroy (bankd);
|
||||
bankd = NULL;
|
||||
}
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (dbinit));
|
||||
GNUNET_OS_process_destroy (dbinit);
|
||||
}
|
||||
/* start exchange wirewatch */
|
||||
for (unsigned int w = 0; w<start_wirewatch; w++)
|
||||
{
|
||||
wirewatch[w] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
if (NULL == wirewatch[w])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Failed to launch wirewatch, aborting benchmark\n");
|
||||
for (unsigned int x = 0; x<w; x++)
|
||||
{
|
||||
GNUNET_break (0 ==
|
||||
GNUNET_OS_process_kill (wirewatch[x],
|
||||
SIGTERM));
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[x]));
|
||||
GNUNET_OS_process_destroy (wirewatch[x]);
|
||||
wirewatch[x] = NULL;
|
||||
}
|
||||
if (-1 != fakebank)
|
||||
{
|
||||
int wstatus;
|
||||
|
||||
kill (fakebank,
|
||||
SIGTERM);
|
||||
if (fakebank !=
|
||||
waitpid (fakebank,
|
||||
&wstatus,
|
||||
0))
|
||||
{
|
||||
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
|
||||
"waitpid");
|
||||
}
|
||||
fakebank = -1;
|
||||
}
|
||||
if (NULL != bankd)
|
||||
{
|
||||
GNUNET_OS_process_kill (bankd,
|
||||
SIGTERM);
|
||||
GNUNET_OS_process_destroy (bankd);
|
||||
bankd = NULL;
|
||||
}
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (MODE_CLIENT == mode) ||
|
||||
(MODE_BOTH == mode) )
|
||||
result = launch_clients ();
|
||||
if ( (GNUNET_YES == linger) ||
|
||||
(MODE_BANK == mode) )
|
||||
{
|
||||
printf ("Press ENTER to stop!\n");
|
||||
if (MODE_BANK != mode)
|
||||
duration = GNUNET_TIME_absolute_get_duration (start_time);
|
||||
(void) getchar ();
|
||||
if (MODE_BANK == mode)
|
||||
duration = GNUNET_TIME_absolute_get_duration (start_time);
|
||||
}
|
||||
|
||||
if ( (MODE_BANK == mode) ||
|
||||
(MODE_BOTH == mode) )
|
||||
{
|
||||
/* Ensure wirewatch runs to completion! */
|
||||
if (0 != start_wirewatch)
|
||||
{
|
||||
/* replace ONE of the wirewatchers with one that is in test-mode */
|
||||
GNUNET_break (0 ==
|
||||
GNUNET_OS_process_kill (wirewatch[0],
|
||||
SIGTERM));
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
"-t",
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
/* wait for it to finish! */
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = NULL;
|
||||
/* Then stop the rest, which should basically also be finished */
|
||||
for (unsigned int w = 1; w<start_wirewatch; w++)
|
||||
"Failed to launch wirewatch, aborting benchmark\n");
|
||||
for (unsigned int x = 0; x<w; x++)
|
||||
{
|
||||
GNUNET_break (0 ==
|
||||
GNUNET_OS_process_kill (wirewatch[w],
|
||||
GNUNET_OS_process_kill (wirewatch[x],
|
||||
SIGTERM));
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[w]));
|
||||
GNUNET_OS_process_destroy (wirewatch[w]);
|
||||
GNUNET_OS_process_wait (wirewatch[x]));
|
||||
GNUNET_OS_process_destroy (wirewatch[x]);
|
||||
wirewatch[x] = NULL;
|
||||
}
|
||||
|
||||
/* But be extra sure we did finish all shards by doing one more */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
|
||||
"Shard check phase\n");
|
||||
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
"-t",
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
/* wait for it to finish! */
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = NULL;
|
||||
}
|
||||
|
||||
/* Now stop the time, if this was the right mode */
|
||||
if ( (GNUNET_YES != linger) &&
|
||||
(MODE_BANK != mode) )
|
||||
duration = GNUNET_TIME_absolute_get_duration (start_time);
|
||||
|
||||
/* stop fakebank */
|
||||
if (-1 != fakebank)
|
||||
{
|
||||
int wstatus;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Telling fakebank to shut down\n");
|
||||
kill (fakebank,
|
||||
SIGTERM);
|
||||
if (fakebank !=
|
||||
waitpid (fakebank,
|
||||
&wstatus,
|
||||
0))
|
||||
{
|
||||
GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
|
||||
"waitpid");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (! WIFEXITED (wstatus)) ||
|
||||
(0 != WEXITSTATUS (wstatus)) )
|
||||
{
|
||||
GNUNET_break (0);
|
||||
result = GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
fakebank = -1;
|
||||
}
|
||||
if (NULL != bankd)
|
||||
{
|
||||
GNUNET_OS_process_kill (bankd,
|
||||
SIGTERM);
|
||||
GNUNET_OS_process_destroy (bankd);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
result = launch_clients ();
|
||||
/* Ensure wirewatch runs to completion! */
|
||||
if (0 != start_wirewatch)
|
||||
{
|
||||
/* replace ONE of the wirewatchers with one that is in test-mode */
|
||||
GNUNET_break (0 ==
|
||||
GNUNET_OS_process_kill (wirewatch[0],
|
||||
SIGTERM));
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
"-t",
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
/* wait for it to finish! */
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = NULL;
|
||||
/* Then stop the rest, which should basically also be finished */
|
||||
for (unsigned int w = 1; w<start_wirewatch; w++)
|
||||
{
|
||||
GNUNET_break (0 ==
|
||||
GNUNET_OS_process_kill (wirewatch[w],
|
||||
SIGTERM));
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[w]));
|
||||
GNUNET_OS_process_destroy (wirewatch[w]);
|
||||
}
|
||||
|
||||
/* But be extra sure we did finish all shards by doing one more */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
|
||||
"Shard check phase\n");
|
||||
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||
NULL, NULL, NULL,
|
||||
"taler-exchange-wirewatch",
|
||||
"taler-exchange-wirewatch",
|
||||
"-c", cfg_filename,
|
||||
"-t",
|
||||
(NULL != loglev) ? "-L" : NULL,
|
||||
loglev,
|
||||
NULL);
|
||||
/* wait for it to finish! */
|
||||
GNUNET_break (GNUNET_OK ==
|
||||
GNUNET_OS_process_wait (wirewatch[0]));
|
||||
GNUNET_OS_process_destroy (wirewatch[0]);
|
||||
wirewatch[0] = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -766,32 +432,17 @@ main (int argc,
|
||||
struct GNUNET_GETOPT_CommandLineOption options[] = {
|
||||
GNUNET_GETOPT_option_mandatory (
|
||||
GNUNET_GETOPT_option_cfgfile (&cfg_filename)),
|
||||
#if FIXME_SUPPORT_LIBEUFIN
|
||||
GNUNET_GETOPT_option_flag ('f',
|
||||
"fakebank",
|
||||
"start a fakebank instead of the Python bank",
|
||||
"we are using fakebank",
|
||||
&use_fakebank),
|
||||
#endif
|
||||
GNUNET_GETOPT_option_help ("taler-bank benchmark"),
|
||||
GNUNET_GETOPT_option_flag ('K',
|
||||
"linger",
|
||||
"linger around until key press",
|
||||
&linger),
|
||||
GNUNET_GETOPT_option_flag ('i',
|
||||
"incremental",
|
||||
"skip initializing and resetting the database",
|
||||
&incremental),
|
||||
GNUNET_GETOPT_option_string ('l',
|
||||
"logfile",
|
||||
"LF",
|
||||
"will log to file LF",
|
||||
&logfile),
|
||||
GNUNET_GETOPT_option_loglevel (&loglev),
|
||||
GNUNET_GETOPT_option_string ('m',
|
||||
"mode",
|
||||
"MODE",
|
||||
"run as bank, client or both",
|
||||
&mode_str),
|
||||
GNUNET_GETOPT_option_uint ('p',
|
||||
"worker-parallelism",
|
||||
"NPROCS",
|
||||
@ -807,11 +458,12 @@ main (int argc,
|
||||
"NRESERVES",
|
||||
"How many reserves per client we should create",
|
||||
&howmany_reserves),
|
||||
GNUNET_GETOPT_option_ulong ('s',
|
||||
"size",
|
||||
"HISTORY_SIZE",
|
||||
"Maximum history size kept in memory by the fakebank",
|
||||
&history_size),
|
||||
GNUNET_GETOPT_option_string (
|
||||
'u',
|
||||
"exchange-account-section",
|
||||
"SECTION",
|
||||
"use exchange bank account configuration from the given SECTION",
|
||||
&exchange_bank_section),
|
||||
GNUNET_GETOPT_option_version (PACKAGE_VERSION " " VCS_VERSION),
|
||||
GNUNET_GETOPT_option_verbose (&verbose),
|
||||
GNUNET_GETOPT_option_uint ('w',
|
||||
@ -821,6 +473,7 @@ main (int argc,
|
||||
&start_wirewatch),
|
||||
GNUNET_GETOPT_OPTION_END
|
||||
};
|
||||
struct GNUNET_TIME_Relative duration;
|
||||
|
||||
unsetenv ("XDG_DATA_HOME");
|
||||
unsetenv ("XDG_CONFIG_HOME");
|
||||
@ -833,35 +486,15 @@ main (int argc,
|
||||
GNUNET_free (cfg_filename);
|
||||
if (GNUNET_NO == result)
|
||||
return 0;
|
||||
return BAD_CLI_ARG;
|
||||
return EXIT_INVALIDARGUMENT;
|
||||
}
|
||||
if (NULL == exchange_bank_section)
|
||||
exchange_bank_section = "exchange-account-1";
|
||||
if (NULL == loglev)
|
||||
loglev = "INFO";
|
||||
GNUNET_log_setup ("taler-bank-benchmark",
|
||||
NULL == loglev ? "INFO" : loglev,
|
||||
loglev,
|
||||
logfile);
|
||||
if (history_size < 10)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"History size too small, this can hardly work\n");
|
||||
return BAD_CLI_ARG;
|
||||
}
|
||||
if (NULL == mode_str)
|
||||
mode = MODE_BOTH;
|
||||
else if (0 == strcasecmp (mode_str,
|
||||
"bank"))
|
||||
mode = MODE_BANK;
|
||||
else if (0 == strcasecmp (mode_str,
|
||||
"client"))
|
||||
mode = MODE_CLIENT;
|
||||
else if (0 == strcasecmp (mode_str,
|
||||
"both"))
|
||||
mode = MODE_BOTH;
|
||||
else
|
||||
{
|
||||
TALER_LOG_ERROR ("Unknown mode given: '%s'\n",
|
||||
mode_str);
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
}
|
||||
if (NULL == cfg_filename)
|
||||
cfg_filename = GNUNET_CONFIGURATION_default_filename ();
|
||||
if (NULL == cfg_filename)
|
||||
@ -877,7 +510,7 @@ main (int argc,
|
||||
{
|
||||
TALER_LOG_ERROR ("Could not parse configuration\n");
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
return EXIT_NOTCONFIGURED;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_currency (cfg,
|
||||
@ -885,52 +518,30 @@ main (int argc,
|
||||
{
|
||||
GNUNET_CONFIGURATION_destroy (cfg);
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
}
|
||||
if (MODE_BANK != mode)
|
||||
{
|
||||
if (howmany_clients > 10240)
|
||||
{
|
||||
TALER_LOG_ERROR ("-p option value given is too large\n");
|
||||
return BAD_CLI_ARG;
|
||||
}
|
||||
if (0 == howmany_clients)
|
||||
{
|
||||
TALER_LOG_ERROR ("-p option value must not be zero\n");
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CLI_ARG;
|
||||
}
|
||||
return EXIT_NOTCONFIGURED;
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGEDB_load_accounts (cfg,
|
||||
TALER_EXCHANGEDB_ALO_AUTHDATA
|
||||
| TALER_EXCHANGEDB_ALO_CREDIT))
|
||||
TALER_TESTING_get_credentials (
|
||||
cfg_filename,
|
||||
exchange_bank_section,
|
||||
use_fakebank
|
||||
? TALER_TESTING_BS_FAKEBANK
|
||||
: TALER_TESTING_BS_IBAN,
|
||||
&cred))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Configuration fails to provide exchange bank details\n");
|
||||
"Required bank credentials not given in configuration\n");
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
return EXIT_NOTCONFIGURED;
|
||||
}
|
||||
|
||||
exchange_bank_account
|
||||
= TALER_EXCHANGEDB_find_account_by_method ("x-taler-bank");
|
||||
if (NULL == exchange_bank_account)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"No bank account for `x-taler-bank` given in configuration\n");
|
||||
GNUNET_free (cfg_filename);
|
||||
return BAD_CONFIG_FILE;
|
||||
}
|
||||
result = parallel_benchmark ();
|
||||
TALER_EXCHANGEDB_unload_accounts ();
|
||||
GNUNET_CONFIGURATION_destroy (cfg);
|
||||
GNUNET_free (cfg_filename);
|
||||
struct GNUNET_TIME_Absolute start_time;
|
||||
|
||||
if (MODE_BANK == mode)
|
||||
{
|
||||
/* If we're the bank, we're done now. No need to print results. */
|
||||
return (GNUNET_OK == result) ? 0 : result;
|
||||
start_time = GNUNET_TIME_absolute_get ();
|
||||
result = parallel_benchmark ();
|
||||
duration = GNUNET_TIME_absolute_get_duration (start_time);
|
||||
}
|
||||
|
||||
if (GNUNET_OK == result)
|
||||
@ -958,7 +569,7 @@ main (int argc,
|
||||
tps);
|
||||
}
|
||||
fprintf (stdout,
|
||||
"CPU time: sys %llu user %llu\n", \
|
||||
"CPU time: sys %llu user %llu\n",
|
||||
(unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
|
||||
+ usage.ru_stime.tv_usec),
|
||||
(unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
|
||||
@ -969,5 +580,7 @@ main (int argc,
|
||||
GNUNET_array_grow (labels,
|
||||
label_len,
|
||||
0);
|
||||
GNUNET_CONFIGURATION_destroy (cfg);
|
||||
GNUNET_free (cfg_filename);
|
||||
return (GNUNET_OK == result) ? 0 : result;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,10 +3,10 @@
|
||||
[exchange-offline]
|
||||
|
||||
# Where do we store the offline master private key of the exchange?
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange-offline/master.priv
|
||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}exchange-offline/master.priv
|
||||
|
||||
# Where do we store the TOFU key material?
|
||||
SECM_TOFU_FILE = ${TALER_DATA_HOME}/exchange-offline/secm_tofus.pub
|
||||
SECM_TOFU_FILE = ${TALER_DATA_HOME}exchange-offline/secm_tofus.pub
|
||||
|
||||
# Base32-encoded public key of the RSA helper.
|
||||
# SECM_DENOM_PUBKEY =
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2020-2021 Taler Systems SA
|
||||
Copyright (C) 2020-2023 Taler Systems SA
|
||||
|
||||
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
|
||||
@ -174,7 +174,7 @@ static struct DenominationAddRequest *dar_tail;
|
||||
/**
|
||||
* Handle to the exchange, used to request /keys.
|
||||
*/
|
||||
static struct TALER_EXCHANGE_Handle *exchange;
|
||||
static struct TALER_EXCHANGE_GetKeysHandle *exchange;
|
||||
|
||||
|
||||
/**
|
||||
@ -219,7 +219,7 @@ do_shutdown (void *cls)
|
||||
}
|
||||
if (NULL != exchange)
|
||||
{
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
TALER_EXCHANGE_get_keys_cancel (exchange);
|
||||
exchange = NULL;
|
||||
}
|
||||
if (NULL != nxt)
|
||||
@ -388,14 +388,15 @@ load_offline_key (int do_create)
|
||||
* add operation result.
|
||||
*
|
||||
* @param cls closure with a `struct DenominationAddRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param adr response data
|
||||
*/
|
||||
static void
|
||||
denomination_add_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_AuditorAddDenominationResponse *adr)
|
||||
{
|
||||
struct DenominationAddRequest *dar = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &adr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -644,30 +645,24 @@ do_upload (char *const *args)
|
||||
* a particular exchange and what keys the exchange is using.
|
||||
*
|
||||
* @param cls closure with the `char **` remaining args
|
||||
* @param hr HTTP response data
|
||||
* @param keys information about the various keys used
|
||||
* by the exchange, NULL if /keys failed
|
||||
* @param compat protocol compatibility information
|
||||
* @param kr response data
|
||||
* @param keys key data from the exchange
|
||||
*/
|
||||
static void
|
||||
keys_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_EXCHANGE_Keys *keys,
|
||||
enum TALER_EXCHANGE_VersionCompatibility compat)
|
||||
const struct TALER_EXCHANGE_KeysResponse *kr,
|
||||
struct TALER_EXCHANGE_Keys *keys)
|
||||
{
|
||||
char *const *args = cls;
|
||||
|
||||
(void) keys;
|
||||
(void) compat;
|
||||
switch (hr->http_status)
|
||||
exchange = NULL;
|
||||
switch (kr->hr.http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
if (! json_is_object (hr->reply))
|
||||
if (NULL == kr->hr.reply)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
exchange = NULL;
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_FAILURE;
|
||||
return;
|
||||
@ -676,11 +671,9 @@ keys_cb (
|
||||
default:
|
||||
fprintf (stderr,
|
||||
"Failed to download keys: %s (HTTP status: %u/%u)\n",
|
||||
hr->hint,
|
||||
hr->http_status,
|
||||
(unsigned int) hr->ec);
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
exchange = NULL;
|
||||
kr->hr.hint,
|
||||
kr->hr.http_status,
|
||||
(unsigned int) kr->hr.ec);
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_FAILURE;
|
||||
return;
|
||||
@ -689,7 +682,7 @@ keys_cb (
|
||||
GNUNET_JSON_pack_string ("operation",
|
||||
OP_INPUT_KEYS),
|
||||
GNUNET_JSON_pack_object_incref ("arguments",
|
||||
(json_t *) hr->reply));
|
||||
(json_t *) kr->hr.reply));
|
||||
if (NULL == args[0])
|
||||
{
|
||||
json_dumpf (in,
|
||||
@ -698,9 +691,8 @@ keys_cb (
|
||||
json_decref (in);
|
||||
in = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
exchange = NULL;
|
||||
next (args);
|
||||
TALER_EXCHANGE_keys_decref (keys);
|
||||
}
|
||||
|
||||
|
||||
@ -727,11 +719,11 @@ do_download (char *const *args)
|
||||
global_ret = EXIT_NOTCONFIGURED;
|
||||
return;
|
||||
}
|
||||
exchange = TALER_EXCHANGE_connect (ctx,
|
||||
exchange_url,
|
||||
&keys_cb,
|
||||
(void *) args,
|
||||
TALER_EXCHANGE_OPTION_END);
|
||||
exchange = TALER_EXCHANGE_get_keys (ctx,
|
||||
exchange_url,
|
||||
NULL,
|
||||
&keys_cb,
|
||||
(void *) args);
|
||||
GNUNET_free (exchange_url);
|
||||
}
|
||||
|
||||
@ -956,11 +948,11 @@ do_show (char *const *args)
|
||||
json_t *keys;
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
json_t *denomkeys;
|
||||
const json_t *denomkeys;
|
||||
struct TALER_MasterPublicKeyP mpub;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_array_const ("denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_public_key",
|
||||
&mpub),
|
||||
GNUNET_JSON_spec_end ()
|
||||
@ -1004,11 +996,9 @@ do_show (char *const *args)
|
||||
{
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
/* do NOT consume input if next argument is '-' */
|
||||
if ( (NULL != args[0]) &&
|
||||
@ -1145,10 +1135,10 @@ do_sign (char *const *args)
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
struct TALER_MasterPublicKeyP mpub;
|
||||
json_t *denomkeys;
|
||||
const json_t *denomkeys;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_array_const ("denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_public_key",
|
||||
&mpub),
|
||||
GNUNET_JSON_spec_end ()
|
||||
@ -1203,11 +1193,9 @@ do_sign (char *const *args)
|
||||
{
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
next (args);
|
||||
}
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_exchange_service.h"
|
||||
#include "taler_extensions.h"
|
||||
#include <regex.h>
|
||||
|
||||
|
||||
/**
|
||||
* Name of the input for the 'sign' and 'show' operation.
|
||||
@ -1119,14 +1121,15 @@ load_offline_key (int do_create)
|
||||
* Function called with information about the post revocation operation result.
|
||||
*
|
||||
* @param cls closure with a `struct DenomRevocationRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param dr response data
|
||||
*/
|
||||
static void
|
||||
denom_revocation_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
|
||||
{
|
||||
struct DenomRevocationRequest *drr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1208,14 +1211,15 @@ upload_denom_revocation (const char *exchange_url,
|
||||
* Function called with information about the post revocation operation result.
|
||||
*
|
||||
* @param cls closure with a `struct SignkeyRevocationRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param sr response data
|
||||
*/
|
||||
static void
|
||||
signkey_revocation_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
|
||||
{
|
||||
struct SignkeyRevocationRequest *srr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1297,13 +1301,15 @@ upload_signkey_revocation (const char *exchange_url,
|
||||
* Function called with information about the post auditor add operation result.
|
||||
*
|
||||
* @param cls closure with a `struct AuditorAddRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mer response data
|
||||
*/
|
||||
static void
|
||||
auditor_add_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
auditor_add_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *mer)
|
||||
{
|
||||
struct AuditorAddRequest *aar = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1397,13 +1403,15 @@ upload_auditor_add (const char *exchange_url,
|
||||
* Function called with information about the post auditor del operation result.
|
||||
*
|
||||
* @param cls closure with a `struct AuditorDelRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mdr response data
|
||||
*/
|
||||
static void
|
||||
auditor_del_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct
|
||||
TALER_EXCHANGE_ManagementAuditorDisableResponse *mdr)
|
||||
{
|
||||
struct AuditorDelRequest *adr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1489,13 +1497,14 @@ upload_auditor_del (const char *exchange_url,
|
||||
* Function called with information about the post wire add operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireAddRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param wer response data
|
||||
*/
|
||||
static void
|
||||
wire_add_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
|
||||
{
|
||||
struct WireAddRequest *war = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1533,10 +1542,21 @@ upload_wire_add (const char *exchange_url,
|
||||
struct GNUNET_TIME_Timestamp start_time;
|
||||
struct WireAddRequest *war;
|
||||
const char *err_name;
|
||||
const char *conversion_url = NULL;
|
||||
const json_t *debit_restrictions;
|
||||
const json_t *credit_restrictions;
|
||||
unsigned int err_line;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("conversion_url",
|
||||
&conversion_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_array_const ("debit_restrictions",
|
||||
&debit_restrictions),
|
||||
GNUNET_JSON_spec_array_const ("credit_restrictions",
|
||||
&credit_restrictions),
|
||||
GNUNET_JSON_spec_timestamp ("validity_start",
|
||||
&start_time),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
|
||||
@ -1599,6 +1619,9 @@ upload_wire_add (const char *exchange_url,
|
||||
TALER_EXCHANGE_management_enable_wire (ctx,
|
||||
exchange_url,
|
||||
payto_uri,
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
start_time,
|
||||
&master_sig_add,
|
||||
&master_sig_wire,
|
||||
@ -1614,13 +1637,14 @@ upload_wire_add (const char *exchange_url,
|
||||
* Function called with information about the post wire del operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireDelRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param wdres response data
|
||||
*/
|
||||
static void
|
||||
wire_del_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
|
||||
{
|
||||
struct WireDelRequest *wdr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1706,14 +1730,15 @@ upload_wire_del (const char *exchange_url,
|
||||
* Function called with information about the post wire fee operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireFeeRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param swr response data
|
||||
*/
|
||||
static void
|
||||
wire_fee_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *swr)
|
||||
{
|
||||
struct WireFeeRequest *wfr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1811,14 +1836,15 @@ upload_wire_fee (const char *exchange_url,
|
||||
* Function called with information about the post global fee operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireFeeRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param gr response data
|
||||
*/
|
||||
static void
|
||||
global_fee_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse *gr)
|
||||
{
|
||||
struct GlobalFeeRequest *gfr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1927,14 +1953,15 @@ upload_global_fee (const char *exchange_url,
|
||||
* Function called with information about the drain profits operation.
|
||||
*
|
||||
* @param cls closure with a `struct DrainProfitsRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mdr response data
|
||||
*/
|
||||
static void
|
||||
drain_profits_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
|
||||
{
|
||||
struct DrainProfitsRequest *dpr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2033,14 +2060,15 @@ upload_drain (const char *exchange_url,
|
||||
* Function called with information about the post upload keys operation result.
|
||||
*
|
||||
* @param cls closure with a `struct UploadKeysRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mr response data
|
||||
*/
|
||||
static void
|
||||
keys_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
|
||||
{
|
||||
struct UploadKeysRequest *ukr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2076,13 +2104,13 @@ upload_keys (const char *exchange_url,
|
||||
struct UploadKeysRequest *ukr;
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
json_t *denom_sigs;
|
||||
json_t *signkey_sigs;
|
||||
const json_t *denom_sigs;
|
||||
const json_t *signkey_sigs;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("denom_sigs",
|
||||
&denom_sigs),
|
||||
GNUNET_JSON_spec_json ("signkey_sigs",
|
||||
&signkey_sigs),
|
||||
GNUNET_JSON_spec_array_const ("denom_sigs",
|
||||
&denom_sigs),
|
||||
GNUNET_JSON_spec_array_const ("signkey_sigs",
|
||||
&signkey_sigs),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
bool ok = true;
|
||||
@ -2198,7 +2226,6 @@ upload_keys (const char *exchange_url,
|
||||
}
|
||||
GNUNET_free (pkd.sign_sigs);
|
||||
GNUNET_free (pkd.denom_sigs);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
}
|
||||
|
||||
|
||||
@ -2206,14 +2233,15 @@ upload_keys (const char *exchange_url,
|
||||
* Function called with information about the post upload extensions operation result.
|
||||
*
|
||||
* @param cls closure with a `struct UploadExtensionsRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param er response data
|
||||
*/
|
||||
static void
|
||||
extensions_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
|
||||
{
|
||||
struct UploadExtensionsRequest *uer = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2245,13 +2273,13 @@ upload_extensions (const char *exchange_url,
|
||||
size_t idx,
|
||||
const json_t *value)
|
||||
{
|
||||
json_t *extensions;
|
||||
const json_t *extensions;
|
||||
struct TALER_MasterSignatureP sig;
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("extensions",
|
||||
&extensions),
|
||||
GNUNET_JSON_spec_object_const ("extensions",
|
||||
&extensions),
|
||||
GNUNET_JSON_spec_fixed_auto ("extensions_sig",
|
||||
&sig),
|
||||
GNUNET_JSON_spec_end ()
|
||||
@ -2282,9 +2310,9 @@ upload_extensions (const char *exchange_url,
|
||||
struct TALER_ExtensionManifestsHashP h_manifests;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_JSON_extensions_manifests_hash (extensions, &h_manifests))
|
||||
TALER_JSON_extensions_manifests_hash (extensions,
|
||||
&h_manifests))
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"couldn't hash extensions' manifests\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
@ -2301,7 +2329,6 @@ upload_extensions (const char *exchange_url,
|
||||
&master_pub,
|
||||
&sig))
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"invalid signature for extensions\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
@ -2316,8 +2343,9 @@ upload_extensions (const char *exchange_url,
|
||||
.extensions = extensions,
|
||||
.extensions_sig = sig,
|
||||
};
|
||||
struct UploadExtensionsRequest *uer = GNUNET_new (struct
|
||||
UploadExtensionsRequest);
|
||||
struct UploadExtensionsRequest *uer
|
||||
= GNUNET_new (struct UploadExtensionsRequest);
|
||||
|
||||
uer->idx = idx;
|
||||
uer->h = TALER_EXCHANGE_management_post_extensions (
|
||||
ctx,
|
||||
@ -2329,7 +2357,6 @@ upload_extensions (const char *exchange_url,
|
||||
uer_tail,
|
||||
uer);
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
}
|
||||
|
||||
|
||||
@ -2337,14 +2364,15 @@ upload_extensions (const char *exchange_url,
|
||||
* Function called with information about the add partner operation.
|
||||
*
|
||||
* @param cls closure with a `struct PartnerAddRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param apr response data
|
||||
*/
|
||||
static void
|
||||
add_partner_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementAddPartnerResponse *apr)
|
||||
{
|
||||
struct PartnerAddRequest *par = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2447,14 +2475,15 @@ add_partner (const char *exchange_url,
|
||||
* Function called with information about the AML officer update operation.
|
||||
*
|
||||
* @param cls closure with a `struct AmlStaffRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param ar response data
|
||||
*/
|
||||
static void
|
||||
update_aml_officer_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
|
||||
{
|
||||
struct AmlStaffRequest *asr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2949,6 +2978,96 @@ do_del_auditor (char *const *args)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse account restriction.
|
||||
*
|
||||
* @param args the array of command-line arguments to process next
|
||||
* @param[in,out] restrictions JSON array to update
|
||||
* @return -1 on error, otherwise number of arguments from @a args that were used
|
||||
*/
|
||||
static int
|
||||
parse_restriction (char *const *args,
|
||||
json_t *restrictions)
|
||||
{
|
||||
if (NULL == args[0])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Restriction TYPE argument missing\n");
|
||||
return -1;
|
||||
}
|
||||
if (0 == strcmp (args[0],
|
||||
"deny"))
|
||||
{
|
||||
GNUNET_assert (0 ==
|
||||
json_array_append_new (
|
||||
restrictions,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("type",
|
||||
"deny"))));
|
||||
return 1;
|
||||
}
|
||||
if (0 == strcmp (args[0],
|
||||
"regex"))
|
||||
{
|
||||
json_t *i18n;
|
||||
json_error_t err;
|
||||
|
||||
if ( (NULL == args[1]) ||
|
||||
(NULL == args[2]) ||
|
||||
(NULL == args[3]) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
regex_t ex;
|
||||
|
||||
if (0 != regcomp (&ex,
|
||||
args[1],
|
||||
REG_NOSUB | REG_EXTENDED))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid regular expression `%s'\n",
|
||||
args[1]);
|
||||
return -1;
|
||||
}
|
||||
regfree (&ex);
|
||||
}
|
||||
|
||||
i18n = json_loads (args[3],
|
||||
JSON_REJECT_DUPLICATES,
|
||||
&err);
|
||||
if (NULL == i18n)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid JSON for restriction of type `regex': `%s` at %d\n",
|
||||
args[3],
|
||||
err.position);
|
||||
return -1;
|
||||
}
|
||||
GNUNET_assert (0 ==
|
||||
json_array_append_new (
|
||||
restrictions,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("type",
|
||||
"regex"),
|
||||
GNUNET_JSON_pack_string ("regex",
|
||||
args[1]),
|
||||
GNUNET_JSON_pack_string ("human_hint",
|
||||
args[2]),
|
||||
GNUNET_JSON_pack_object_steal ("human_hint_i18n",
|
||||
i18n)
|
||||
)));
|
||||
return 4;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Restriction TYPE `%s' unsupported\n",
|
||||
args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add wire account.
|
||||
*
|
||||
@ -2961,6 +3080,10 @@ do_add_wire (char *const *args)
|
||||
struct TALER_MasterSignatureP master_sig_add;
|
||||
struct TALER_MasterSignatureP master_sig_wire;
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
const char *conversion_url = NULL;
|
||||
json_t *debit_restrictions;
|
||||
json_t *credit_restrictions;
|
||||
unsigned int num_args = 1;
|
||||
|
||||
if (NULL != in)
|
||||
{
|
||||
@ -3011,24 +3134,101 @@ do_add_wire (char *const *args)
|
||||
}
|
||||
GNUNET_free (wire_method);
|
||||
}
|
||||
debit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != debit_restrictions);
|
||||
credit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != credit_restrictions);
|
||||
while (NULL != args[num_args])
|
||||
{
|
||||
if (0 == strcmp (args[num_args],
|
||||
"conversion-url"))
|
||||
{
|
||||
num_args++;
|
||||
conversion_url = args[num_args];
|
||||
if (NULL == conversion_url)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"'conversion-url' requires an argument\n");
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args++;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (args[num_args],
|
||||
"credit-restriction"))
|
||||
{
|
||||
int iret;
|
||||
|
||||
num_args++;
|
||||
iret = parse_restriction (&args[num_args],
|
||||
credit_restrictions);
|
||||
if (iret <= 0)
|
||||
{
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args += iret;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (args[num_args],
|
||||
"debit-restriction"))
|
||||
{
|
||||
int iret;
|
||||
|
||||
num_args++;
|
||||
iret = parse_restriction (&args[num_args],
|
||||
debit_restrictions);
|
||||
if (iret <= 0)
|
||||
{
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args += iret;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
TALER_exchange_offline_wire_add_sign (args[0],
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
now,
|
||||
&master_priv,
|
||||
&master_sig_add);
|
||||
TALER_exchange_wire_signature_make (args[0],
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
&master_priv,
|
||||
&master_sig_wire);
|
||||
output_operation (OP_ENABLE_WIRE,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("payto_uri",
|
||||
args[0]),
|
||||
GNUNET_JSON_pack_array_steal ("debit_restrictions",
|
||||
debit_restrictions),
|
||||
GNUNET_JSON_pack_array_steal ("credit_restrictions",
|
||||
credit_restrictions),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string ("conversion_url",
|
||||
conversion_url)),
|
||||
GNUNET_JSON_pack_timestamp ("validity_start",
|
||||
now),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig_add",
|
||||
&master_sig_add),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig_wire",
|
||||
&master_sig_wire)));
|
||||
next (args + 1);
|
||||
next (args + num_args);
|
||||
}
|
||||
|
||||
|
||||
@ -3198,14 +3398,28 @@ do_set_global_fee (char *const *args)
|
||||
(NULL == args[3]) ||
|
||||
(NULL == args[4]) ||
|
||||
(NULL == args[5]) ||
|
||||
(NULL == args[6]) ||
|
||||
( (1 != sscanf (args[0],
|
||||
"%u%c",
|
||||
&year,
|
||||
&dummy)) &&
|
||||
(0 != strcasecmp ("now",
|
||||
args[0])) ) ||
|
||||
(GNUNET_OK !=
|
||||
(NULL == args[6]) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
}
|
||||
if ( (1 != sscanf (args[0],
|
||||
"%u%c",
|
||||
&year,
|
||||
&dummy)) &&
|
||||
(0 != strcasecmp ("now",
|
||||
args[0])) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid YEAR given for 'global-fee' subcommand\n");
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
}
|
||||
if ( (GNUNET_OK !=
|
||||
TALER_string_to_amount (args[1],
|
||||
&fees.history)) ||
|
||||
(GNUNET_OK !=
|
||||
@ -3213,20 +3427,34 @@ do_set_global_fee (char *const *args)
|
||||
&fees.account)) ||
|
||||
(GNUNET_OK !=
|
||||
TALER_string_to_amount (args[3],
|
||||
&fees.purse)) ||
|
||||
(GNUNET_OK !=
|
||||
&fees.purse)) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid amount given for 'global-fee' subcommand\n");
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
}
|
||||
if ( (GNUNET_OK !=
|
||||
GNUNET_STRINGS_fancy_time_to_relative (args[4],
|
||||
&purse_timeout)) ||
|
||||
(GNUNET_OK !=
|
||||
GNUNET_STRINGS_fancy_time_to_relative (args[5],
|
||||
&history_expiration)) ||
|
||||
(1 != sscanf (args[6],
|
||||
"%u%c",
|
||||
&purse_account_limit,
|
||||
&dummy)) )
|
||||
&history_expiration)) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n");
|
||||
"Invalid delay given for 'global-fee' subcommand\n");
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
}
|
||||
if (1 != sscanf (args[6],
|
||||
"%u%c",
|
||||
&purse_account_limit,
|
||||
&dummy))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid purse account limit given for 'global-fee' subcommand\n");
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
@ -3643,18 +3871,15 @@ enable_aml_staff (char *const *args)
|
||||
* whether there are subsequent commands).
|
||||
*
|
||||
* @param cls closure with the `char **` remaining args
|
||||
* @param hr HTTP response data
|
||||
* @param keys information about the various keys used
|
||||
* by the exchange, NULL if /management/keys failed
|
||||
* @param mgr response data
|
||||
*/
|
||||
static void
|
||||
download_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_EXCHANGE_FutureKeys *keys)
|
||||
const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
|
||||
{
|
||||
char *const *args = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
|
||||
|
||||
(void) keys;
|
||||
mgkh = NULL;
|
||||
switch (hr->http_status)
|
||||
{
|
||||
@ -4258,15 +4483,15 @@ do_show (char *const *args)
|
||||
json_t *keys;
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
json_t *denomkeys;
|
||||
json_t *signkeys;
|
||||
const json_t *denomkeys;
|
||||
const json_t *signkeys;
|
||||
struct TALER_MasterPublicKeyP mpub;
|
||||
struct TALER_SecurityModulePublicKeySetP secmset;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("future_denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_json ("future_signkeys",
|
||||
&signkeys),
|
||||
GNUNET_JSON_spec_array_const ("future_denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_array_const ("future_signkeys",
|
||||
&signkeys),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_pub",
|
||||
&mpub),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
|
||||
@ -4311,7 +4536,6 @@ do_show (char *const *args)
|
||||
"Fatal: exchange uses different master key!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
@ -4320,7 +4544,6 @@ do_show (char *const *args)
|
||||
{
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
@ -4334,12 +4557,10 @@ do_show (char *const *args)
|
||||
{
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
json_decref (keys);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
next (args);
|
||||
}
|
||||
|
||||
@ -4659,15 +4880,15 @@ do_sign (char *const *args)
|
||||
json_t *keys;
|
||||
const char *err_name;
|
||||
unsigned int err_line;
|
||||
json_t *denomkeys;
|
||||
json_t *signkeys;
|
||||
const json_t *denomkeys;
|
||||
const json_t *signkeys;
|
||||
struct TALER_MasterPublicKeyP mpub;
|
||||
struct TALER_SecurityModulePublicKeySetP secmset;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_json ("future_denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_json ("future_signkeys",
|
||||
&signkeys),
|
||||
GNUNET_JSON_spec_array_const ("future_denoms",
|
||||
&denomkeys),
|
||||
GNUNET_JSON_spec_array_const ("future_signkeys",
|
||||
&signkeys),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_pub",
|
||||
&mpub),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
|
||||
@ -4714,7 +4935,6 @@ do_sign (char *const *args)
|
||||
"Fatal: exchange uses different master key!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
@ -4725,7 +4945,6 @@ do_sign (char *const *args)
|
||||
"Fatal: security module keys changed!\n");
|
||||
global_ret = EXIT_FAILURE;
|
||||
test_shutdown ();
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
@ -4749,7 +4968,6 @@ do_sign (char *const *args)
|
||||
test_shutdown ();
|
||||
json_decref (signkey_sig_array);
|
||||
json_decref (denomkey_sig_array);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
return;
|
||||
}
|
||||
@ -4761,7 +4979,6 @@ do_sign (char *const *args)
|
||||
GNUNET_JSON_pack_array_steal ("signkey_sigs",
|
||||
signkey_sig_array)));
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
json_decref (keys);
|
||||
next (args);
|
||||
}
|
||||
@ -5045,7 +5262,7 @@ work (void *cls)
|
||||
{
|
||||
.name = "enable-account",
|
||||
.help =
|
||||
"enable wire account of the exchange (payto-URI must be given as argument)",
|
||||
"enable wire account of the exchange (payto-URI must be given as argument; for optional argument see man page)",
|
||||
.cb = &do_add_wire
|
||||
},
|
||||
{
|
||||
|
@ -15,6 +15,8 @@ pkgcfg_DATA = \
|
||||
exchange.conf
|
||||
|
||||
# Programs
|
||||
bin_SCRIPTS = \
|
||||
taler-exchange-kyc-aml-pep-trigger.sh
|
||||
|
||||
bin_PROGRAMS = \
|
||||
taler-exchange-aggregator \
|
||||
@ -131,6 +133,7 @@ taler_exchange_httpd_SOURCES = \
|
||||
taler-exchange-httpd_age-withdraw.c taler-exchange-httpd_age-withdraw.h \
|
||||
taler-exchange-httpd_age-withdraw_reveal.c taler-exchange-httpd_age-withdraw_reveal.h \
|
||||
taler-exchange-httpd_common_deposit.c taler-exchange-httpd_common_deposit.h \
|
||||
taler-exchange-httpd_common_kyc.c taler-exchange-httpd_common_kyc.h \
|
||||
taler-exchange-httpd_config.c taler-exchange-httpd_config.h \
|
||||
taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \
|
||||
taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \
|
||||
@ -181,7 +184,6 @@ taler_exchange_httpd_SOURCES = \
|
||||
taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
|
||||
taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
|
||||
taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
|
||||
taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
|
||||
taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
|
||||
|
||||
taler_exchange_httpd_LDADD = \
|
||||
@ -227,4 +229,5 @@ EXTRA_DIST = \
|
||||
test_taler_exchange_httpd.get \
|
||||
test_taler_exchange_httpd.post \
|
||||
exchange.conf \
|
||||
$(bin_SCRIPTS) \
|
||||
$(check_SCRIPTS)
|
||||
|
@ -6,10 +6,23 @@
|
||||
# This must be adjusted to your actual installation.
|
||||
# MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||
|
||||
# Must be set to the threshold above which transactions
|
||||
# are flagged for AML review.
|
||||
# AML_THRESHOLD =
|
||||
|
||||
# Specifies a program (binary) to run on KYC attribute data to decide
|
||||
# whether we should immediately flag an account for AML review.
|
||||
# The KYC attribute data will be passed on standard-input.
|
||||
# Return non-zero to trigger AML review of the new user.
|
||||
KYC_AML_TRIGGER = true
|
||||
|
||||
# Attribute encryption key for storing attributes encrypted
|
||||
# in the database. Should be a high-entropy nonce.
|
||||
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
|
||||
|
||||
# Set to NO to disable rewards.
|
||||
ENABLE_REWARDS = YES
|
||||
|
||||
# How long do we allow /keys to be cached at most? The actual
|
||||
# limit is the minimum of this value and the first expected
|
||||
# significant change in /keys based on the expiration times.
|
||||
|
@ -303,17 +303,17 @@ parse_aggregator_config (void)
|
||||
(TALER_amount_is_zero (¤cy_round_unit)) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need non-zero amount in section `TALER' under `CURRENCY_ROUND_UNIT'\n");
|
||||
"Need non-zero amount in section `taler' under `CURRENCY_ROUND_UNIT'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_amount (cfg,
|
||||
"taler",
|
||||
"exchange",
|
||||
"AML_THRESHOLD",
|
||||
&aml_threshold))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
|
||||
"Need amount in section `exchange' under `AML_THRESHOLD'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
|
@ -312,10 +312,10 @@ expired_reserve_cb (void *cls,
|
||||
memset (&wtid,
|
||||
0,
|
||||
sizeof (wtid));
|
||||
memcpy (&wtid,
|
||||
reserve_pub,
|
||||
GNUNET_MIN (sizeof (wtid),
|
||||
sizeof (*reserve_pub)));
|
||||
GNUNET_memcpy (&wtid,
|
||||
reserve_pub,
|
||||
GNUNET_MIN (sizeof (wtid),
|
||||
sizeof (*reserve_pub)));
|
||||
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
|
||||
reserve_pub,
|
||||
now,
|
||||
|
@ -30,6 +30,8 @@
|
||||
#include "taler_kyclogic_lib.h"
|
||||
#include "taler_templating_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler-exchange-httpd_age-withdraw.h"
|
||||
#include "taler-exchange-httpd_age-withdraw_reveal.h"
|
||||
#include "taler-exchange-httpd_aml-decision.h"
|
||||
#include "taler-exchange-httpd_auditors.h"
|
||||
#include "taler-exchange-httpd_batch-deposit.h"
|
||||
@ -68,7 +70,6 @@
|
||||
#include "taler-exchange-httpd_reserves_status.h"
|
||||
#include "taler-exchange-httpd_terms.h"
|
||||
#include "taler-exchange-httpd_transfers_get.h"
|
||||
#include "taler-exchange-httpd_wire.h"
|
||||
#include "taler-exchange-httpd_withdraw.h"
|
||||
#include "taler_exchangedb_lib.h"
|
||||
#include "taler_exchangedb_plugin.h"
|
||||
@ -153,6 +154,16 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
||||
*/
|
||||
char *TEH_currency;
|
||||
|
||||
/**
|
||||
* Name of the KYC-AML-trigger evaluation binary.
|
||||
*/
|
||||
char *TEH_kyc_aml_trigger;
|
||||
|
||||
/**
|
||||
* Option set to #GNUNET_YES if rewards are enabled.
|
||||
*/
|
||||
int TEH_enable_rewards;
|
||||
|
||||
/**
|
||||
* What is the largest amount we allow a peer to
|
||||
* merge into a reserve before always triggering
|
||||
@ -542,7 +553,6 @@ handle_get_aml (struct TEH_RequestContext *rc,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
NULL);
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
|
||||
@ -562,6 +572,46 @@ handle_get_aml (struct TEH_RequestContext *rc,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/age-withdraw/$ACH/reveal" POST request. Parses the "ACH"
|
||||
* hash of the commitment from a previous call to
|
||||
* /reserves/$reserve_pub/age-withdraw
|
||||
*
|
||||
* @param rc request context
|
||||
* @param root uploaded JSON data
|
||||
* @param args array of additional options
|
||||
* @return MHD result code
|
||||
*/
|
||||
static MHD_RESULT
|
||||
handle_post_age_withdraw (struct TEH_RequestContext *rc,
|
||||
const json_t *root,
|
||||
const char *const args[2])
|
||||
{
|
||||
struct TALER_AgeWithdrawCommitmentHashP ach;
|
||||
|
||||
if (0 != strcmp ("reveal", args[1]))
|
||||
return r404 (rc->connection,
|
||||
args[1]);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[0],
|
||||
strlen (args[0]),
|
||||
&ach,
|
||||
sizeof (ach)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
|
||||
args[0]);
|
||||
}
|
||||
|
||||
return TEH_handler_age_withdraw_reveal (rc,
|
||||
&ach,
|
||||
root);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Signature of functions that handle operations on reserves.
|
||||
*
|
||||
@ -608,6 +658,10 @@ handle_post_reserves (struct TEH_RequestContext *rc,
|
||||
.op = "batch-withdraw",
|
||||
.handler = &TEH_handler_batch_withdraw
|
||||
},
|
||||
{
|
||||
.op = "age-withdraw",
|
||||
.handler = &TEH_handler_age_withdraw
|
||||
},
|
||||
{
|
||||
.op = "withdraw",
|
||||
.handler = &TEH_handler_withdraw
|
||||
@ -932,9 +986,9 @@ proceed_with_handler (struct TEH_RequestContext *rc,
|
||||
|
||||
/* Parse command-line arguments */
|
||||
/* make a copy of 'url' because 'strtok_r()' will modify */
|
||||
memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
GNUNET_memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
i = 0;
|
||||
args[i++] = strtok_r (d, "/", &sp);
|
||||
while ( (NULL != args[i - 1]) &&
|
||||
@ -1445,6 +1499,12 @@ handle_mhd_request (void *cls,
|
||||
.handler.post = &handle_post_reserves,
|
||||
.nargs = 2
|
||||
},
|
||||
{
|
||||
.url = "age-withdraw",
|
||||
.method = MHD_HTTP_METHOD_POST,
|
||||
.handler.post = &handle_post_age_withdraw,
|
||||
.nargs = 2
|
||||
},
|
||||
{
|
||||
.url = "reserves-attest",
|
||||
.method = MHD_HTTP_METHOD_GET,
|
||||
@ -1617,33 +1677,8 @@ handle_mhd_request (void *cls,
|
||||
if (0 == strcasecmp (method,
|
||||
MHD_HTTP_METHOD_POST))
|
||||
{
|
||||
const char *cl;
|
||||
|
||||
/* Maybe check for maximum upload size
|
||||
and refuse requests if they are just too big. */
|
||||
cl = MHD_lookup_connection_value (connection,
|
||||
MHD_HEADER_KIND,
|
||||
MHD_HTTP_HEADER_CONTENT_LENGTH);
|
||||
if (NULL != cl)
|
||||
{
|
||||
unsigned long long cv;
|
||||
char dummy;
|
||||
|
||||
if (1 != sscanf (cl,
|
||||
"%llu%c",
|
||||
&cv,
|
||||
&dummy))
|
||||
{
|
||||
/* Not valid HTTP request, just close connection. */
|
||||
GNUNET_break_op (0);
|
||||
return MHD_NO;
|
||||
}
|
||||
if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_request_too_large (connection);
|
||||
}
|
||||
}
|
||||
TALER_MHD_check_content_length (connection,
|
||||
TALER_MHD_REQUEST_BUFFER_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1870,6 +1905,17 @@ exchange_serve_process_config (void)
|
||||
"valid relative time expected");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
|
||||
"exchange",
|
||||
"KYC_AML_TRIGGER",
|
||||
&TEH_kyc_aml_trigger))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
"exchange",
|
||||
"KYC_AML_TRIGGER");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_currency (TEH_cfg,
|
||||
&TEH_currency))
|
||||
@ -1881,19 +1927,30 @@ exchange_serve_process_config (void)
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_config_get_amount (TEH_cfg,
|
||||
"taler",
|
||||
"exchange",
|
||||
"AML_THRESHOLD",
|
||||
&TEH_aml_threshold))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
|
||||
"Need amount in section `exchange' under `AML_THRESHOLD'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (0 != strcmp (TEH_currency,
|
||||
TEH_aml_threshold.currency))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Amount in section `TALER' under `AML_THRESHOLD' uses the wrong currency!\n");
|
||||
"Amount in section `exchange' under `AML_THRESHOLD' uses the wrong currency!\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
TEH_enable_rewards
|
||||
= GNUNET_CONFIGURATION_get_value_yesno (
|
||||
TEH_cfg,
|
||||
"exchange",
|
||||
"ENABLE_REWARDS");
|
||||
if (GNUNET_SYSERR == TEH_enable_rewards)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Need YES or NO in section `exchange' under `ENABLE_REWARDS'\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
@ -2215,6 +2272,7 @@ do_shutdown (void *cls)
|
||||
|
||||
mhd = TALER_MHD_daemon_stop ();
|
||||
TEH_resume_keys_requests (true);
|
||||
TEH_deposits_get_cleanup ();
|
||||
TEH_reserves_get_cleanup ();
|
||||
TEH_purses_get_cleanup ();
|
||||
TEH_kyc_check_cleanup ();
|
||||
|
@ -64,6 +64,11 @@ extern int TEH_check_invariants_flag;
|
||||
*/
|
||||
extern int TEH_allow_keys_timetravel;
|
||||
|
||||
/**
|
||||
* Option set to #GNUNET_YES if rewards are allowed.
|
||||
*/
|
||||
extern int TEH_enable_rewards;
|
||||
|
||||
/**
|
||||
* Main directory with revocation data.
|
||||
*/
|
||||
@ -97,6 +102,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
||||
*/
|
||||
extern char *TEH_currency;
|
||||
|
||||
/**
|
||||
* Name of the KYC-AML-trigger evaluation binary.
|
||||
*/
|
||||
extern char *TEH_kyc_aml_trigger;
|
||||
|
||||
/**
|
||||
* What is the largest amount we allow a peer to
|
||||
* merge into a reserve before always triggering
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user