Compare commits
1 Commits
master
...
master-bro
Author | SHA1 | Date | |
---|---|---|---|
e660a513fd |
@ -17,7 +17,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
AC_PREREQ([2.69])
|
AC_PREREQ([2.69])
|
||||||
AC_INIT([taler-exchange],[0.9.2],[taler-bug@gnunet.org])
|
AC_INIT([taler-exchange],[0.9.1],[taler-bug@gnunet.org])
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
AC_CONFIG_AUX_DIR([build-aux])
|
||||||
AC_CONFIG_SRCDIR([src/util/util.c])
|
AC_CONFIG_SRCDIR([src/util/util.c])
|
||||||
AC_CONFIG_HEADERS([taler_config.h])
|
AC_CONFIG_HEADERS([taler_config.h])
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit bd4e73b2ed06269fdee42eaad21acb5be8be9302
|
Subproject commit 832685b6a942a6ebbec8e1e5b8c33b6b85b0a727
|
@ -46,12 +46,6 @@
|
|||||||
<anchorfile>microhttpd.h</anchorfile>
|
<anchorfile>microhttpd.h</anchorfile>
|
||||||
<arglist></arglist>
|
<arglist></arglist>
|
||||||
</member>
|
</member>
|
||||||
<member kind="define">
|
|
||||||
<type>#define</type>
|
|
||||||
<name>MHD_HTTP_CONTENT_TOO_LARGE</name>
|
|
||||||
<anchorfile>microhttpd.h</anchorfile>
|
|
||||||
<arglist></arglist>
|
|
||||||
</member>
|
|
||||||
<member kind="define">
|
<member kind="define">
|
||||||
<type>#define</type>
|
<type>#define</type>
|
||||||
<name>MHD_HTTP_REQUEST_TIMEOUT</name>
|
<name>MHD_HTTP_REQUEST_TIMEOUT</name>
|
||||||
|
24
debian/changelog
vendored
24
debian/changelog
vendored
@ -1,27 +1,3 @@
|
|||||||
taler-exchange (0.9.2-3) unstable; urgency=low
|
|
||||||
|
|
||||||
* Improvements to timeout handling when DB is not available yet.
|
|
||||||
|
|
||||||
-- Florian Dold <dold@taler.net> Tue, 14 Mar 2023 12:30:15 +0100
|
|
||||||
|
|
||||||
taler-exchange (0.9.2-2) unstable; urgency=low
|
|
||||||
|
|
||||||
* Further improvements to Debian package.
|
|
||||||
|
|
||||||
-- Christian Grothoff <grothoff@gnu.org> Sat, 3 Mar 2023 23:50:12 +0200
|
|
||||||
|
|
||||||
taler-exchange (0.9.2-1) unstable; urgency=low
|
|
||||||
|
|
||||||
* Minor improvements to Debian package, also adds age-withdraw REST APIs.
|
|
||||||
|
|
||||||
-- Christian Grothoff <grothoff@gnu.org> Sat, 3 Mar 2023 13:50:12 +0200
|
|
||||||
|
|
||||||
taler-exchange (0.9.2) unstable; urgency=low
|
|
||||||
|
|
||||||
* Packaging latest release.
|
|
||||||
|
|
||||||
-- Christian Grothoff <grothoff@gnu.org> Tue, 21 Feb 2023 13:50:12 +0200
|
|
||||||
|
|
||||||
taler-exchange (0.9.1) unstable; urgency=low
|
taler-exchange (0.9.1) unstable; urgency=low
|
||||||
|
|
||||||
* Packaging latest release.
|
* Packaging latest release.
|
||||||
|
2
debian/etc-libtalerexchange/taler/taler.conf
vendored
2
debian/etc-libtalerexchange/taler/taler.conf
vendored
@ -30,8 +30,6 @@
|
|||||||
# systems is always rounded to this unit.
|
# systems is always rounded to this unit.
|
||||||
#currency_round_unit = KUDOS:0.01
|
#currency_round_unit = KUDOS:0.01
|
||||||
|
|
||||||
# Monthly amount that mandatorily triggers an AML check
|
|
||||||
#AML_THRESHOLD = KUDOS:10000000
|
|
||||||
|
|
||||||
[paths]
|
[paths]
|
||||||
|
|
||||||
|
@ -1,18 +1,7 @@
|
|||||||
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/ {
|
location /taler-auditor/ {
|
||||||
proxy_pass http://unix:/var/lib/taler-auditor/auditor.sock;
|
proxy_pass http://unix:/var/lib/taler-auditor/auditor.sock;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-Host "localhost";
|
proxy_set_header X-Forwarded-Host "example.com";
|
||||||
#proxy_set_header X-Forwarded-Proto "https";
|
proxy_set_header X-Forwarded-Proto "https";
|
||||||
}
|
|
||||||
}
|
}
|
@ -2,16 +2,13 @@ server {
|
|||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
|
|
||||||
server_name localhost;
|
#server_name example.com;
|
||||||
|
|
||||||
access_log /var/log/nginx/exchange.log;
|
|
||||||
error_log /var/log/nginx/exchange.err;
|
|
||||||
|
|
||||||
location /taler-exchange/ {
|
location /taler-exchange/ {
|
||||||
proxy_pass http://unix:/run/taler/exchange-httpd/exchange-http.sock:/;
|
proxy_pass http://unix:/run/taler/exchange-httpd/exchange-http.sock:/;
|
||||||
proxy_redirect off;
|
proxy_redirect off;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
proxy_set_header X-Forwarded-Host "localhost";
|
#proxy_set_header X-Forwarded-Host "example.com";
|
||||||
#proxy_set_header X-Forwarded-Proto "https";
|
#proxy_set_header X-Forwarded-Proto "https";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@
|
|||||||
# which you can get using `taler-exchange-offline setup`.
|
# which you can get using `taler-exchange-offline setup`.
|
||||||
# This is just an example, your key will be different!
|
# This is just an example, your key will be different!
|
||||||
# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG
|
# MASTER_PUBLIC_KEY = YE6Q6TR1EDB7FD0S68TGDZGF1P0GHJD2S0XVV8R2S62MYJ6HJ4ZG
|
||||||
# MASTER_PUBLIC_KEY =
|
MASTER_PUBLIC_KEY =
|
||||||
|
|
||||||
# Publicly visible base URL of the exchange.
|
# Publicly visible base URL of the exchange.
|
||||||
# BASE_URL = https://example.com/
|
# BASE_URL = https://example.com/
|
||||||
# BASE_URL =
|
BASE_URL =
|
||||||
|
|
||||||
# For your terms of service and privacy policy, you should specify
|
# For your terms of service and privacy policy, you should specify
|
||||||
# an Etag that must be updated whenever there are significant
|
# an Etag that must be updated whenever there are significant
|
||||||
@ -20,14 +20,12 @@
|
|||||||
# TERMS_ETAG =
|
# TERMS_ETAG =
|
||||||
# PRIVACY_ETAG =
|
# PRIVACY_ETAG =
|
||||||
|
|
||||||
SERVE = unix
|
|
||||||
UNIXPATH_MODE = 666
|
|
||||||
|
|
||||||
# Bank accounts used by the exchange should be specified here:
|
# Bank accounts used by the exchange should be specified here:
|
||||||
[exchange-account-1]
|
[exchange-account-1]
|
||||||
|
|
||||||
enable_credit = no
|
enable_credit = yes
|
||||||
enable_debit = no
|
enable_debit = yes
|
||||||
|
|
||||||
# Account identifier in the form of an RFC-8905 payto:// URI.
|
# Account identifier in the form of an RFC-8905 payto:// URI.
|
||||||
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
|
# For SEPA, looks like payto://sepa/$IBAN?receiver-name=$NAME
|
||||||
@ -36,4 +34,4 @@ payto_uri =
|
|||||||
|
|
||||||
# Credentials to access the account are in a separate
|
# Credentials to access the account are in a separate
|
||||||
# config file with restricted permissions.
|
# config file with restricted permissions.
|
||||||
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials-1.secret.conf
|
@inline-secret@ exchange-accountcredentials-1 ../secrets/exchange-accountcredentials.secret.conf
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
# Typically, there should only be a single line here, of the form:
|
# Typically, there should only be a single line here, of the form:
|
||||||
|
|
||||||
# CONFIG=postgres:///DATABASE
|
CONFIG=postgres:///DATABASE
|
||||||
|
|
||||||
# The details of the URI depend on where the database lives and how
|
# The details of the URI depend on where the database lives and how
|
||||||
# access control was configured.
|
# access control was configured.
|
||||||
|
1
debian/libtalerexchange.install
vendored
1
debian/libtalerexchange.install
vendored
@ -5,5 +5,6 @@ usr/share/taler/config.d/paths.conf
|
|||||||
usr/share/taler/config.d/taler.conf
|
usr/share/taler/config.d/taler.conf
|
||||||
debian/etc-libtalerexchange/* etc/
|
debian/etc-libtalerexchange/* etc/
|
||||||
usr/bin/taler-config
|
usr/bin/taler-config
|
||||||
|
usr/bin/taler-crypto-worker
|
||||||
usr/share/man/man5/taler.conf.5
|
usr/share/man/man5/taler.conf.5
|
||||||
usr/share/man/man1/taler-config*
|
usr/share/man/man1/taler-config*
|
||||||
|
13
debian/taler-exchange-offline.postinst
vendored
13
debian/taler-exchange-offline.postinst
vendored
@ -4,21 +4,20 @@ set -e
|
|||||||
|
|
||||||
. /usr/share/debconf/confmodule
|
. /usr/share/debconf/confmodule
|
||||||
|
|
||||||
|
TALER_HOME="/var/lib/taler"
|
||||||
|
|
||||||
case "${1}" in
|
case "${1}" in
|
||||||
configure)
|
configure)
|
||||||
|
|
||||||
if ! getent group taler-exchange-offline >/dev/null; then
|
if ! getent group taler-exchange-offline >/dev/null; then
|
||||||
addgroup --quiet taler-exchange-offline
|
addgroup --quiet --system taler-exchange-offline
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! getent passwd taler-exchange-offline >/dev/null; then
|
if ! getent passwd taler-exchange-offline >/dev/null; then
|
||||||
adduser --quiet \
|
adduser --quiet --system \
|
||||||
--disabled-password \
|
|
||||||
--system \
|
|
||||||
--shell /bin/bash \
|
|
||||||
--home /home/taler-exchange-offline \
|
|
||||||
--ingroup taler-exchange-offline \
|
--ingroup taler-exchange-offline \
|
||||||
taler-exchange-offline
|
--no-create-home \
|
||||||
|
--home ${TALER_HOME} taler-exchange-offline
|
||||||
fi
|
fi
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
5
debian/taler-exchange.postinst
vendored
5
debian/taler-exchange.postinst
vendored
@ -30,7 +30,6 @@ configure)
|
|||||||
if ! getent passwd ${_EUSERNAME} >/dev/null; then
|
if ! getent passwd ${_EUSERNAME} >/dev/null; then
|
||||||
adduser --quiet --system --no-create-home --ingroup ${_GROUPNAME} --home ${TALER_HOME} ${_EUSERNAME}
|
adduser --quiet --system --no-create-home --ingroup ${_GROUPNAME} --home ${TALER_HOME} ${_EUSERNAME}
|
||||||
adduser --quiet ${_EUSERNAME} ${_DBGROUPNAME}
|
adduser --quiet ${_EUSERNAME} ${_DBGROUPNAME}
|
||||||
adduser --quiet ${_EUSERNAME} ${_GROUPNAME}
|
|
||||||
fi
|
fi
|
||||||
if ! getent passwd ${_RSECUSERNAME} >/dev/null; then
|
if ! getent passwd ${_RSECUSERNAME} >/dev/null; then
|
||||||
adduser --quiet --system --no-create-home --ingroup ${_GROUPNAME} --home ${TALER_HOME} ${_RSECUSERNAME}
|
adduser --quiet --system --no-create-home --ingroup ${_GROUPNAME} --home ${TALER_HOME} ${_RSECUSERNAME}
|
||||||
@ -54,10 +53,10 @@ configure)
|
|||||||
adduser --quiet ${_AGGRUSERNAME} ${_DBGROUPNAME}
|
adduser --quiet ${_AGGRUSERNAME} ${_DBGROUPNAME}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! dpkg-statoverride --list /etc/taler/secrets/exchange-accountcredentials-1.secret.conf >/dev/null 2>&1; then
|
if ! dpkg-statoverride --list /etc/taler/secrets/exchange-accountcredentials.secret.conf >/dev/null 2>&1; then
|
||||||
dpkg-statoverride --add --update \
|
dpkg-statoverride --add --update \
|
||||||
${_WIREUSERNAME} root 460 \
|
${_WIREUSERNAME} root 460 \
|
||||||
/etc/taler/secrets/exchange-accountcredentials-1.secret.conf
|
/etc/taler/secrets/exchange-accountcredentials.secret.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! dpkg-statoverride --list /etc/taler/secrets/exchange-db.secret.conf >/dev/null 2>&1; then
|
if ! dpkg-statoverride --list /etc/taler/secrets/exchange-db.secret.conf >/dev/null 2>&1; then
|
||||||
|
@ -7,7 +7,7 @@ After=postgres.service
|
|||||||
User=taler-exchange-aggregator
|
User=taler-exchange-aggregator
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -15,4 +15,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
@ -6,7 +6,7 @@ PartOf=taler-exchange.target
|
|||||||
User=taler-exchange-aggregator
|
User=taler-exchange-aggregator
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-aggregator -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -14,4 +14,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
@ -7,7 +7,7 @@ After=network.target postgres.service
|
|||||||
User=taler-exchange-closer
|
User=taler-exchange-closer
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-closer -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -15,4 +15,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
@ -7,7 +7,7 @@ After=postgres.service
|
|||||||
User=taler-exchange-expire
|
User=taler-exchange-expire
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-expire -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-expire -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -15,4 +15,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
@ -8,19 +8,11 @@ PartOf=taler-exchange.target
|
|||||||
[Service]
|
[Service]
|
||||||
User=taler-exchange-httpd
|
User=taler-exchange-httpd
|
||||||
Type=simple
|
Type=simple
|
||||||
|
# Depending on the configuration, the service suicides and then
|
||||||
# Depending on the configuration, the service process kills itself and then
|
# needs to be restarted.
|
||||||
# needs to be restarted. Thus no significant delay on restarts.
|
|
||||||
Restart=always
|
Restart=always
|
||||||
|
# Do not dally on restarts.
|
||||||
RestartSec=1ms
|
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
|
ExecStart=/usr/bin/taler-exchange-httpd -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
|
@ -7,7 +7,7 @@ PartOf=taler-exchange.target
|
|||||||
User=taler-exchange-wire
|
User=taler-exchange-wire
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-transfer -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -15,4 +15,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
@ -7,8 +7,7 @@ PartOf=taler-exchange.target
|
|||||||
User=taler-exchange-wire
|
User=taler-exchange-wire
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
|
@ -7,7 +7,7 @@ PartOf=taler-exchange.target
|
|||||||
User=taler-exchange-wire
|
User=taler-exchange-wire
|
||||||
Type=simple
|
Type=simple
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=1s
|
RestartSec=100ms
|
||||||
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
ExecStart=/usr/bin/taler-exchange-wirewatch -c /etc/taler/taler.conf
|
||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardError=journal
|
StandardError=journal
|
||||||
@ -15,4 +15,3 @@ PrivateTmp=yes
|
|||||||
PrivateDevices=yes
|
PrivateDevices=yes
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
Slice=taler-exchange.slice
|
Slice=taler-exchange.slice
|
||||||
RuntimeMaxSec=3600s
|
|
||||||
|
3
debian/taler-exchange.tmpfiles
vendored
3
debian/taler-exchange.tmpfiles
vendored
@ -1,8 +1,7 @@
|
|||||||
#Type Path Mode UID GID Age Argument
|
#Type Path Mode UID GID Age Argument
|
||||||
d /run/taler/exchange-secmod-rsa 0755 taler-exchange-secmod-rsa taler-exchange-secmod - -
|
d /run/taler/exchange-secmod-rsa 0755 taler-exchange-secmod-rsa taler-exchange-secmod - -
|
||||||
d /run/taler/exchange-secmod-cs 0755 taler-exchange-secmod-cs taler-exchange-secmod - -
|
|
||||||
d /run/taler/exchange-secmod-eddsa 0755 taler-exchange-secmod-eddsa taler-exchange-secmod - -
|
d /run/taler/exchange-secmod-eddsa 0755 taler-exchange-secmod-eddsa taler-exchange-secmod - -
|
||||||
d /run/taler/exchange-httpd 0750 taler-exchange-httpd www-data - -
|
d /run/taler/exchange-httpd 0750 taler-exchange-httpd www-data - -
|
||||||
d /var/lib/taler/exchange-secmod-cs 0700 taler-exchange-secmod-cs taler-exchange-secmod - -
|
d /var/lib/taler/exchange-offline 0700 taler-exchange-offline taler-exchange-offline - -
|
||||||
d /var/lib/taler/exchange-secmod-rsa 0700 taler-exchange-secmod-rsa taler-exchange-secmod - -
|
d /var/lib/taler/exchange-secmod-rsa 0700 taler-exchange-secmod-rsa taler-exchange-secmod - -
|
||||||
d /var/lib/taler/exchange-secmod-eddsa 0700 taler-exchange-secmod-eddsa taler-exchange-secmod - -
|
d /var/lib/taler/exchange-secmod-eddsa 0700 taler-exchange-secmod-eddsa taler-exchange-secmod - -
|
||||||
|
@ -193,7 +193,7 @@ echo " DONE"
|
|||||||
|
|
||||||
echo -n "Setting up merchant"
|
echo -n "Setting up merchant"
|
||||||
|
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"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
|
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"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 " DONE"
|
echo " DONE"
|
||||||
@ -214,7 +214,7 @@ bash
|
|||||||
# {
|
# {
|
||||||
# amountToSpend: "TESTKUDOS:4",
|
# amountToSpend: "TESTKUDOS:4",
|
||||||
# amountToWithdraw: "TESTKUDOS:10",
|
# amountToWithdraw: "TESTKUDOS:10",
|
||||||
# bankAccessApiBaseUrl: $BANK_URL,
|
# bankBaseUrl: $BANK_URL,
|
||||||
# exchangeBaseUrl: $EXCHANGE_URL,
|
# exchangeBaseUrl: $EXCHANGE_URL,
|
||||||
# merchantBaseUrl: $MERCHANT_URL,
|
# merchantBaseUrl: $MERCHANT_URL,
|
||||||
# }' \
|
# }' \
|
||||||
|
@ -141,7 +141,6 @@ CONFIG = /research/taler/exchange/src/auditor/auditor-basedb.conf
|
|||||||
[taler]
|
[taler]
|
||||||
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
|
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
|
||||||
CURRENCY = TESTKUDOS
|
CURRENCY = TESTKUDOS
|
||||||
AML_THRESHOLD = TESTKUDOS:1000000
|
|
||||||
|
|
||||||
[merchantdb-postgres]
|
[merchantdb-postgres]
|
||||||
CONFIG = postgres:///auditor-basedb
|
CONFIG = postgres:///auditor-basedb
|
||||||
|
@ -398,7 +398,7 @@ echo " DONE"
|
|||||||
|
|
||||||
echo -n "Setting up 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
|
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["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 " DONE"
|
echo " DONE"
|
||||||
@ -411,7 +411,7 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'runI
|
|||||||
{
|
{
|
||||||
amountToSpend: "TESTKUDOS:4",
|
amountToSpend: "TESTKUDOS:4",
|
||||||
amountToWithdraw: "TESTKUDOS:10",
|
amountToWithdraw: "TESTKUDOS:10",
|
||||||
bankAccessApiBaseUrl: $BANK_URL,
|
bankBaseUrl: $BANK_URL,
|
||||||
exchangeBaseUrl: $EXCHANGE_URL,
|
exchangeBaseUrl: $EXCHANGE_URL,
|
||||||
merchantBaseUrl: $MERCHANT_URL,
|
merchantBaseUrl: $MERCHANT_URL,
|
||||||
}' \
|
}' \
|
||||||
|
@ -400,7 +400,7 @@ echo " DONE"
|
|||||||
# Setup merchant
|
# Setup merchant
|
||||||
echo -n "Setting up 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
|
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "payto_uris":["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
|
||||||
|
|
||||||
|
|
||||||
# run wallet CLI
|
# run wallet CLI
|
||||||
@ -410,7 +410,7 @@ taler-wallet-cli --no-throttle --wallet-db=$WALLET_DB api --expect-success 'with
|
|||||||
"$(jq -n '
|
"$(jq -n '
|
||||||
{
|
{
|
||||||
amount: "TESTKUDOS:8",
|
amount: "TESTKUDOS:8",
|
||||||
bankAccessApiBaseUrl: $BANK_URL,
|
bankBaseUrl: $BANK_URL,
|
||||||
exchangeBaseUrl: $EXCHANGE_URL,
|
exchangeBaseUrl: $EXCHANGE_URL,
|
||||||
}' \
|
}' \
|
||||||
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/" \
|
--arg BANK_URL "$BANK_URL/demobanks/default/access-api/" \
|
||||||
|
@ -271,9 +271,7 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
|
|||||||
const char *upload_data,
|
const char *upload_data,
|
||||||
size_t *upload_data_size)
|
size_t *upload_data_size)
|
||||||
{
|
{
|
||||||
struct TALER_AUDITORDB_DepositConfirmation dc = {
|
struct TALER_AUDITORDB_DepositConfirmation dc;
|
||||||
.refund_deadline = GNUNET_TIME_UNIT_ZERO_TS
|
|
||||||
};
|
|
||||||
struct TALER_AUDITORDB_ExchangeSigningKey es;
|
struct TALER_AUDITORDB_ExchangeSigningKey es;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
|
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
|
||||||
@ -284,10 +282,8 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
|
|||||||
&dc.h_wire),
|
&dc.h_wire),
|
||||||
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
||||||
&dc.exchange_timestamp),
|
&dc.exchange_timestamp),
|
||||||
GNUNET_JSON_spec_mark_optional (
|
|
||||||
GNUNET_JSON_spec_timestamp ("refund_deadline",
|
GNUNET_JSON_spec_timestamp ("refund_deadline",
|
||||||
&dc.refund_deadline),
|
&dc.refund_deadline),
|
||||||
NULL),
|
|
||||||
GNUNET_JSON_spec_timestamp ("wire_deadline",
|
GNUNET_JSON_spec_timestamp ("wire_deadline",
|
||||||
&dc.wire_deadline),
|
&dc.wire_deadline),
|
||||||
TALER_JSON_spec_amount ("amount_without_fee",
|
TALER_JSON_spec_amount ("amount_without_fee",
|
||||||
|
@ -885,9 +885,8 @@ handle_purse_decision (
|
|||||||
report_row_inconsistency ("purse-request",
|
report_row_inconsistency ("purse-request",
|
||||||
rowid,
|
rowid,
|
||||||
"purse fee higher than balance");
|
"purse fee higher than balance");
|
||||||
GNUNET_assert (GNUNET_OK ==
|
|
||||||
TALER_amount_set_zero (TALER_ARL_currency,
|
TALER_amount_set_zero (TALER_ARL_currency,
|
||||||
&balance_without_purse_fee));
|
&balance_without_purse_fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refunded)
|
if (refunded)
|
||||||
@ -1022,9 +1021,8 @@ verify_purse_balance (void *cls,
|
|||||||
report_row_inconsistency ("purse",
|
report_row_inconsistency ("purse",
|
||||||
0,
|
0,
|
||||||
"purse fee higher than balance");
|
"purse fee higher than balance");
|
||||||
GNUNET_assert (GNUNET_OK ==
|
|
||||||
TALER_amount_set_zero (TALER_ARL_currency,
|
TALER_amount_set_zero (TALER_ARL_currency,
|
||||||
&balance_without_purse_fee));
|
&balance_without_purse_fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 != TALER_amount_cmp (&ps->exchange_balance,
|
if (0 != TALER_amount_cmp (&ps->exchange_balance,
|
||||||
|
@ -1081,9 +1081,8 @@ handle_reserve_closed (
|
|||||||
}
|
}
|
||||||
if (NULL == payto_uri)
|
if (NULL == payto_uri)
|
||||||
{
|
{
|
||||||
if ( (NULL == rs->sender_account) ||
|
if (0 != strcmp (rs->sender_account,
|
||||||
(0 != strcmp (rs->sender_account,
|
receiver_account))
|
||||||
receiver_account)) )
|
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("reserves_close",
|
report_row_inconsistency ("reserves_close",
|
||||||
rowid,
|
rowid,
|
||||||
@ -1111,7 +1110,7 @@ handle_reserve_closed (
|
|||||||
rowid,
|
rowid,
|
||||||
"target account not verified, auditor does not know reserve");
|
"target account not verified, auditor does not know reserve");
|
||||||
}
|
}
|
||||||
else if (0 != strcmp (rs->sender_account,
|
if (0 != strcmp (rs->sender_account,
|
||||||
receiver_account))
|
receiver_account))
|
||||||
{
|
{
|
||||||
report_row_inconsistency ("reserves_close",
|
report_row_inconsistency ("reserves_close",
|
||||||
|
@ -96,7 +96,7 @@ function cleanup()
|
|||||||
function exit_cleanup()
|
function exit_cleanup()
|
||||||
{
|
{
|
||||||
echo "Running exit-cleanup"
|
echo "Running exit-cleanup"
|
||||||
if test ! -z "${POSTGRES_PATH:-}"
|
if test -z "${POSTGRES_PATH:-}"
|
||||||
then
|
then
|
||||||
echo "Stopping Postgres at ${POSTGRES_PATH}"
|
echo "Stopping Postgres at ${POSTGRES_PATH}"
|
||||||
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
|
${POSTGRES_PATH}/pg_ctl -D $TMPDIR -l /dev/null stop &> /dev/null || true
|
||||||
|
@ -248,15 +248,12 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
|
|||||||
{
|
{
|
||||||
if ( (0 < num_results) &&
|
if ( (0 < num_results) &&
|
||||||
(! GNUNET_TIME_relative_is_zero (timeout)) )
|
(! GNUNET_TIME_relative_is_zero (timeout)) )
|
||||||
/* 0 == start_row is implied, go with timeout into future */
|
|
||||||
GNUNET_snprintf (url,
|
GNUNET_snprintf (url,
|
||||||
sizeof (url),
|
sizeof (url),
|
||||||
"history/incoming?delta=%lld&long_poll_ms=%llu",
|
"history/incoming?delta=%lld&long_poll_ms=%llu",
|
||||||
(long long) num_results,
|
(long long) num_results,
|
||||||
tms);
|
tms);
|
||||||
else
|
else
|
||||||
/* Going back from current transaction or have no timeout;
|
|
||||||
hence timeout makes no sense */
|
|
||||||
GNUNET_snprintf (url,
|
GNUNET_snprintf (url,
|
||||||
sizeof (url),
|
sizeof (url),
|
||||||
"history/incoming?delta=%lld",
|
"history/incoming?delta=%lld",
|
||||||
@ -266,7 +263,6 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
|
|||||||
{
|
{
|
||||||
if ( (0 < num_results) &&
|
if ( (0 < num_results) &&
|
||||||
(! GNUNET_TIME_relative_is_zero (timeout)) )
|
(! GNUNET_TIME_relative_is_zero (timeout)) )
|
||||||
/* going forward from num_result */
|
|
||||||
GNUNET_snprintf (url,
|
GNUNET_snprintf (url,
|
||||||
sizeof (url),
|
sizeof (url),
|
||||||
"history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu",
|
"history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu",
|
||||||
@ -274,8 +270,6 @@ TALER_BANK_credit_history (struct GNUNET_CURL_Context *ctx,
|
|||||||
(unsigned long long) start_row,
|
(unsigned long long) start_row,
|
||||||
tms);
|
tms);
|
||||||
else
|
else
|
||||||
/* going backwards or have no timeout;
|
|
||||||
hence timeout makes no sense */
|
|
||||||
GNUNET_snprintf (url,
|
GNUNET_snprintf (url,
|
||||||
sizeof (url),
|
sizeof (url),
|
||||||
"history/incoming?delta=%lld&start=%llu",
|
"history/incoming?delta=%lld&start=%llu",
|
||||||
|
@ -400,148 +400,6 @@ struct Transaction
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called to clean up context of a connection.
|
|
||||||
*
|
|
||||||
* @param ctx context to clean up
|
|
||||||
*/
|
|
||||||
typedef void
|
|
||||||
(*ConnectionCleaner)(void *ctx);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Universal context we keep per connection.
|
|
||||||
*/
|
|
||||||
struct ConnectionContext
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Function we call upon completion to clean up.
|
|
||||||
*/
|
|
||||||
ConnectionCleaner ctx_cleaner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Request-handler specific context.
|
|
||||||
*/
|
|
||||||
void *ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the "base" structure for both the /history and the
|
|
||||||
* /history-range API calls.
|
|
||||||
*/
|
|
||||||
struct HistoryArgs
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bank account number of the requesting client.
|
|
||||||
*/
|
|
||||||
uint64_t account_number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Index of the starting transaction, exclusive (!).
|
|
||||||
*/
|
|
||||||
uint64_t start_idx;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Requested number of results and order
|
|
||||||
* (positive: ascending, negative: descending)
|
|
||||||
*/
|
|
||||||
int64_t delta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Timeout for long polling.
|
|
||||||
*/
|
|
||||||
struct GNUNET_TIME_Relative lp_timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true if starting point was given.
|
|
||||||
*/
|
|
||||||
bool have_start;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context we keep per history request.
|
|
||||||
*/
|
|
||||||
struct HistoryContext
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* When does this request time out.
|
|
||||||
*/
|
|
||||||
struct GNUNET_TIME_Absolute timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Client arguments for this request.
|
|
||||||
*/
|
|
||||||
struct HistoryArgs ha;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Account the request is about.
|
|
||||||
*/
|
|
||||||
struct Account *acc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Payto URI of the account.
|
|
||||||
*/
|
|
||||||
char *payto_uri;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JSON object we are building to return.
|
|
||||||
*/
|
|
||||||
json_t *history;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called to clean up a history context.
|
|
||||||
*
|
|
||||||
* @param cls a `struct HistoryContext *`
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
history_cleanup (void *cls)
|
|
||||||
{
|
|
||||||
struct HistoryContext *hc = cls;
|
|
||||||
|
|
||||||
GNUNET_free (hc->payto_uri);
|
|
||||||
json_decref (hc->history);
|
|
||||||
GNUNET_free (hc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context we keep per get withdrawal operation request.
|
|
||||||
*/
|
|
||||||
struct WithdrawContext
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* When does this request time out.
|
|
||||||
*/
|
|
||||||
struct GNUNET_TIME_Absolute timeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The withdrawal operation this is about.
|
|
||||||
*/
|
|
||||||
struct WithdrawalOperation *wo;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called to clean up a withdraw context.
|
|
||||||
*
|
|
||||||
* @param cls a `struct WithdrawContext *`
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
withdraw_cleanup (void *cls)
|
|
||||||
{
|
|
||||||
struct WithdrawContext *wc = cls;
|
|
||||||
|
|
||||||
GNUNET_free (wc);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle for the fake bank.
|
* Handle for the fake bank.
|
||||||
*/
|
*/
|
||||||
@ -711,6 +569,13 @@ struct TALER_FAKEBANK_Handle
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special address "con_cls" can point to to indicate that the handler has
|
||||||
|
* been called more than once already (was previously suspended).
|
||||||
|
*/
|
||||||
|
static int special_ptr;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task run whenever HTTP server operations are pending.
|
* Task run whenever HTTP server operations are pending.
|
||||||
*
|
*
|
||||||
@ -1718,14 +1583,15 @@ handle_mhd_completion_callback (void *cls,
|
|||||||
enum MHD_RequestTerminationCode toe)
|
enum MHD_RequestTerminationCode toe)
|
||||||
{
|
{
|
||||||
/* struct TALER_FAKEBANK_Handle *h = cls; */
|
/* struct TALER_FAKEBANK_Handle *h = cls; */
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
(void) cls;
|
(void) cls;
|
||||||
(void) connection;
|
(void) connection;
|
||||||
(void) toe;
|
(void) toe;
|
||||||
if (NULL == cc)
|
if (NULL == *con_cls)
|
||||||
return;
|
return;
|
||||||
cc->ctx_cleaner (cc->ctx);
|
if (&special_ptr == *con_cls)
|
||||||
GNUNET_free (cc);
|
return;
|
||||||
|
GNUNET_JSON_post_parser_cleanup (*con_cls);
|
||||||
|
*con_cls = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1737,7 +1603,7 @@ handle_mhd_completion_callback (void *cls,
|
|||||||
* @param account account into which to deposit the funds (credit)
|
* @param account account into which to deposit the funds (credit)
|
||||||
* @param upload_data request data
|
* @param upload_data request data
|
||||||
* @param upload_data_size size of @a upload_data in bytes
|
* @param upload_data_size size of @a upload_data in bytes
|
||||||
* @param con_cls closure for request (a `struct ConnectionContext *`)
|
* @param con_cls closure for request (a `struct Buffer *`)
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
@ -1748,21 +1614,14 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
|
|||||||
size_t *upload_data_size,
|
size_t *upload_data_size,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
enum GNUNET_JSON_PostResult pr;
|
||||||
json_t *json;
|
json_t *json;
|
||||||
uint64_t row_id;
|
uint64_t row_id;
|
||||||
struct GNUNET_TIME_Timestamp timestamp;
|
struct GNUNET_TIME_Timestamp timestamp;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
connection,
|
connection,
|
||||||
&cc->ctx,
|
con_cls,
|
||||||
upload_data,
|
upload_data,
|
||||||
upload_data_size,
|
upload_data_size,
|
||||||
&json);
|
&json);
|
||||||
@ -1877,7 +1736,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
|
|||||||
* @param account account making the transfer
|
* @param account account making the transfer
|
||||||
* @param upload_data request data
|
* @param upload_data request data
|
||||||
* @param upload_data_size size of @a upload_data in bytes
|
* @param upload_data_size size of @a upload_data in bytes
|
||||||
* @param con_cls closure for request (a `struct ConnectionContext *`)
|
* @param con_cls closure for request (a `struct Buffer *`)
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
@ -1888,21 +1747,14 @@ handle_transfer (struct TALER_FAKEBANK_Handle *h,
|
|||||||
size_t *upload_data_size,
|
size_t *upload_data_size,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
enum GNUNET_JSON_PostResult pr;
|
||||||
json_t *json;
|
json_t *json;
|
||||||
uint64_t row_id;
|
uint64_t row_id;
|
||||||
struct GNUNET_TIME_Timestamp ts;
|
struct GNUNET_TIME_Timestamp ts;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
connection,
|
connection,
|
||||||
&cc->ctx,
|
con_cls,
|
||||||
upload_data,
|
upload_data,
|
||||||
upload_data_size,
|
upload_data_size,
|
||||||
&json);
|
&json);
|
||||||
@ -2043,6 +1895,42 @@ handle_home_page (struct TALER_FAKEBANK_Handle *h,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the "base" structure for both the /history and the
|
||||||
|
* /history-range API calls.
|
||||||
|
*/
|
||||||
|
struct HistoryArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bank account number of the requesting client.
|
||||||
|
*/
|
||||||
|
uint64_t account_number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the starting transaction, exclusive (!).
|
||||||
|
*/
|
||||||
|
uint64_t start_idx;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requested number of results and order
|
||||||
|
* (positive: ascending, negative: descending)
|
||||||
|
*/
|
||||||
|
int64_t delta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout for long polling.
|
||||||
|
*/
|
||||||
|
struct GNUNET_TIME_Relative lp_timeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* true if starting point was given.
|
||||||
|
*/
|
||||||
|
bool have_start;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse URL history arguments, of _both_ APIs:
|
* Parse URL history arguments, of _both_ APIs:
|
||||||
* /history/incoming and /history/outgoing.
|
* /history/incoming and /history/outgoing.
|
||||||
@ -2288,7 +2176,7 @@ start_lp (struct TALER_FAKEBANK_Handle *h,
|
|||||||
* @param h the fakebank handle
|
* @param h the fakebank handle
|
||||||
* @param connection the connection
|
* @param connection the connection
|
||||||
* @param account which account the request is about
|
* @param account which account the request is about
|
||||||
* @param con_cls closure for request
|
* @param con_cls closure for request (NULL or &special_ptr)
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
||||||
@ -2296,106 +2184,87 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
const char *account,
|
const char *account,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
struct HistoryArgs ha;
|
||||||
struct HistoryContext *hc;
|
struct Account *acc;
|
||||||
struct Transaction *pos;
|
struct Transaction *pos;
|
||||||
|
json_t *history;
|
||||||
|
char *debit_payto;
|
||||||
enum GNUNET_GenericReturnValue ret;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &history_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
hc = GNUNET_new (struct HistoryContext);
|
|
||||||
cc->ctx = hc;
|
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Handling /history/outgoing connection %p\n",
|
"Handling /history/outgoing connection %p\n",
|
||||||
connection);
|
connection);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
(ret = parse_history_common_args (h,
|
(ret = parse_history_common_args (h,
|
||||||
connection,
|
connection,
|
||||||
&hc->ha)))
|
&ha)))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
|
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
|
||||||
}
|
}
|
||||||
GNUNET_assert (0 ==
|
if (&special_ptr == *con_cls)
|
||||||
pthread_mutex_lock (&h->big_lock));
|
ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
|
||||||
hc->acc = lookup_account (h,
|
acc = lookup_account (h,
|
||||||
account,
|
account,
|
||||||
NULL);
|
NULL);
|
||||||
if (NULL == hc->acc)
|
if (NULL == acc)
|
||||||
{
|
{
|
||||||
GNUNET_assert (0 ==
|
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_BANK_UNKNOWN_ACCOUNT,
|
TALER_EC_BANK_UNKNOWN_ACCOUNT,
|
||||||
account);
|
account);
|
||||||
}
|
}
|
||||||
GNUNET_asprintf (&hc->payto_uri,
|
GNUNET_asprintf (&debit_payto,
|
||||||
"payto://x-taler-bank/localhost/%s?receiver-name=%s",
|
"payto://x-taler-bank/localhost/%s?receiver-name=%s",
|
||||||
account,
|
account,
|
||||||
hc->acc->receiver_name);
|
acc->receiver_name);
|
||||||
/* New invariant: */
|
history = json_array ();
|
||||||
GNUNET_assert (0 == strcmp (hc->payto_uri,
|
if (NULL == history)
|
||||||
hc->acc->payto_uri));
|
|
||||||
hc->history = json_array ();
|
|
||||||
if (NULL == hc->history)
|
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_free (debit_payto);
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
return MHD_NO;
|
return MHD_NO;
|
||||||
}
|
}
|
||||||
hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hc = cc->ctx;
|
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_lock (&h->big_lock));
|
pthread_mutex_lock (&h->big_lock));
|
||||||
}
|
if (! ha.have_start)
|
||||||
|
|
||||||
if (! hc->ha.have_start)
|
|
||||||
{
|
{
|
||||||
pos = (0 > hc->ha.delta)
|
pos = (0 > ha.delta)
|
||||||
? hc->acc->out_tail
|
? acc->out_tail
|
||||||
: hc->acc->out_head;
|
: acc->out_head;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
|
struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
|
||||||
bool overflow;
|
bool overflow;
|
||||||
uint64_t dir;
|
uint64_t dir;
|
||||||
bool skip = true;
|
bool skip = true;
|
||||||
|
|
||||||
dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
|
dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
|
||||||
overflow = (t->row_id != hc->ha.start_idx);
|
overflow = (t->row_id != ha.start_idx);
|
||||||
/* If account does not match, linear scan for
|
/* If account does not match, linear scan for
|
||||||
first matching account. */
|
first matching account. */
|
||||||
while ( (! overflow) &&
|
while ( (! overflow) &&
|
||||||
(NULL != t) &&
|
(NULL != t) &&
|
||||||
(t->debit_account != hc->acc) )
|
(t->debit_account != acc) )
|
||||||
{
|
{
|
||||||
skip = false;
|
skip = false;
|
||||||
t = h->transactions[(t->row_id + dir) % h->ram_limit];
|
t = h->transactions[(t->row_id + dir) % h->ram_limit];
|
||||||
if ( (NULL != t) &&
|
if ( (NULL != t) &&
|
||||||
(t->row_id == hc->ha.start_idx) )
|
(t->row_id == ha.start_idx) )
|
||||||
overflow = true; /* full circle, give up! */
|
overflow = true; /* full circle, give up! */
|
||||||
}
|
}
|
||||||
if ( (NULL == t) ||
|
if ( (NULL == t) ||
|
||||||
overflow)
|
overflow)
|
||||||
{
|
{
|
||||||
/* FIXME: these conditions are unclear to me. */
|
if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
|
||||||
if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
|
(0 < ha.delta))
|
||||||
(0 < hc->ha.delta))
|
|
||||||
{
|
{
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
if (overflow)
|
if (overflow)
|
||||||
{
|
{
|
||||||
|
GNUNET_free (debit_payto);
|
||||||
return TALER_MHD_reply_with_ec (
|
return TALER_MHD_reply_with_ec (
|
||||||
connection,
|
connection,
|
||||||
TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
|
TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
|
||||||
@ -2403,36 +2272,35 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
}
|
}
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
if (h->in_shutdown)
|
*con_cls = &special_ptr;
|
||||||
{
|
|
||||||
GNUNET_assert (0 ==
|
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
start_lp (h,
|
start_lp (h,
|
||||||
connection,
|
connection,
|
||||||
hc->acc,
|
acc,
|
||||||
GNUNET_TIME_absolute_get_remaining (hc->timeout),
|
ha.lp_timeout,
|
||||||
LP_DEBIT,
|
LP_DEBIT,
|
||||||
NULL);
|
NULL);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
|
json_decref (history);
|
||||||
|
GNUNET_free (debit_payto);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
if (t->debit_account != hc->acc)
|
if (t->debit_account != acc)
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Invalid start specified, transaction %llu not with account %s!\n",
|
"Invalid start specified, transaction %llu not with account %s!\n",
|
||||||
(unsigned long long) hc->ha.start_idx,
|
(unsigned long long) ha.start_idx,
|
||||||
account);
|
account);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
|
GNUNET_free (debit_payto);
|
||||||
|
json_decref (history);
|
||||||
return MHD_NO;
|
return MHD_NO;
|
||||||
}
|
}
|
||||||
if (skip)
|
if (skip)
|
||||||
{
|
{
|
||||||
/* range is exclusive, skip the matching entry */
|
/* range is exclusive, skip the matching entry */
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = t->prev_out;
|
pos = t->prev_out;
|
||||||
else
|
else
|
||||||
pos = t->next_out;
|
pos = t->next_out;
|
||||||
@ -2445,9 +2313,9 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
if (NULL != pos)
|
if (NULL != pos)
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Returning %lld debit transactions starting (inclusive) from %llu\n",
|
"Returning %lld debit transactions starting (inclusive) from %llu\n",
|
||||||
(long long) hc->ha.delta,
|
(long long) ha.delta,
|
||||||
(unsigned long long) pos->row_id);
|
(unsigned long long) pos->row_id);
|
||||||
while ( (0 != hc->ha.delta) &&
|
while ( (0 != ha.delta) &&
|
||||||
(NULL != pos) )
|
(NULL != pos) )
|
||||||
{
|
{
|
||||||
json_t *trans;
|
json_t *trans;
|
||||||
@ -2459,9 +2327,9 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
"Unexpected CREDIT transaction #%llu for account `%s'\n",
|
"Unexpected CREDIT transaction #%llu for account `%s'\n",
|
||||||
(unsigned long long) pos->row_id,
|
(unsigned long long) pos->row_id,
|
||||||
account);
|
account);
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = pos->prev_in;
|
pos = pos->prev_in;
|
||||||
if (0 < hc->ha.delta)
|
if (0 < ha.delta)
|
||||||
pos = pos->next_in;
|
pos = pos->next_in;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2480,7 +2348,7 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
GNUNET_JSON_pack_string ("credit_account",
|
GNUNET_JSON_pack_string ("credit_account",
|
||||||
credit_payto),
|
credit_payto),
|
||||||
GNUNET_JSON_pack_string ("debit_account",
|
GNUNET_JSON_pack_string ("debit_account",
|
||||||
hc->payto_uri), // FIXME #7275: inefficient to return this here always!
|
debit_payto), // FIXME #7275: inefficient to return this here always!
|
||||||
GNUNET_JSON_pack_string ("exchange_base_url",
|
GNUNET_JSON_pack_string ("exchange_base_url",
|
||||||
pos->subject.debit.exchange_base_url),
|
pos->subject.debit.exchange_base_url),
|
||||||
GNUNET_JSON_pack_data_auto ("wtid",
|
GNUNET_JSON_pack_data_auto ("wtid",
|
||||||
@ -2488,55 +2356,51 @@ handle_debit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
GNUNET_assert (NULL != trans);
|
GNUNET_assert (NULL != trans);
|
||||||
GNUNET_free (credit_payto);
|
GNUNET_free (credit_payto);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
json_array_append_new (hc->history,
|
json_array_append_new (history,
|
||||||
trans));
|
trans));
|
||||||
if (hc->ha.delta > 0)
|
if (ha.delta > 0)
|
||||||
hc->ha.delta--;
|
ha.delta--;
|
||||||
else
|
else
|
||||||
hc->ha.delta++;
|
ha.delta++;
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = pos->prev_out;
|
pos = pos->prev_out;
|
||||||
if (0 < hc->ha.delta)
|
if (0 < ha.delta)
|
||||||
pos = pos->next_out;
|
pos = pos->next_out;
|
||||||
}
|
}
|
||||||
if ( (0 == json_array_size (hc->history)) &&
|
if ( (0 == json_array_size (history)) &&
|
||||||
(! h->in_shutdown) &&
|
(! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
|
||||||
(GNUNET_TIME_absolute_is_future (hc->timeout)) &&
|
(0 < ha.delta))
|
||||||
(0 < hc->ha.delta))
|
|
||||||
{
|
{
|
||||||
|
*con_cls = &special_ptr;
|
||||||
start_lp (h,
|
start_lp (h,
|
||||||
connection,
|
connection,
|
||||||
hc->acc,
|
acc,
|
||||||
GNUNET_TIME_absolute_get_remaining (hc->timeout),
|
ha.lp_timeout,
|
||||||
LP_DEBIT,
|
LP_DEBIT,
|
||||||
NULL);
|
NULL);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
|
json_decref (history);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
finish:
|
finish:
|
||||||
if (0 == json_array_size (hc->history))
|
if (0 == json_array_size (history))
|
||||||
{
|
{
|
||||||
GNUNET_break (h->in_shutdown ||
|
json_decref (history);
|
||||||
(! GNUNET_TIME_absolute_is_future (hc->timeout)));
|
|
||||||
return TALER_MHD_reply_static (connection,
|
return TALER_MHD_reply_static (connection,
|
||||||
MHD_HTTP_NO_CONTENT,
|
MHD_HTTP_NO_CONTENT,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
{
|
GNUNET_free (debit_payto);
|
||||||
json_t *h = hc->history;
|
|
||||||
|
|
||||||
hc->history = NULL;
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (connection,
|
return TALER_MHD_REPLY_JSON_PACK (connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_array_steal (
|
GNUNET_JSON_pack_array_steal (
|
||||||
"outgoing_transactions",
|
"outgoing_transactions",
|
||||||
h));
|
history));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2555,101 +2419,77 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
const char *account,
|
const char *account,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
struct HistoryArgs ha;
|
||||||
struct HistoryContext *hc;
|
struct Account *acc;
|
||||||
const struct Transaction *pos;
|
const struct Transaction *pos;
|
||||||
|
json_t *history;
|
||||||
|
const char *credit_payto;
|
||||||
enum GNUNET_GenericReturnValue ret;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &history_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
hc = GNUNET_new (struct HistoryContext);
|
|
||||||
cc->ctx = hc;
|
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Handling /history/incoming connection %p\n",
|
"Handling /history/incoming connection %p (%d)\n",
|
||||||
connection);
|
connection,
|
||||||
|
(*con_cls == &special_ptr));
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
(ret = parse_history_common_args (h,
|
(ret = parse_history_common_args (h,
|
||||||
connection,
|
connection,
|
||||||
&hc->ha)))
|
&ha)))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
|
return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
|
||||||
}
|
}
|
||||||
GNUNET_assert (0 ==
|
if (&special_ptr == *con_cls)
|
||||||
pthread_mutex_lock (&h->big_lock));
|
ha.lp_timeout = GNUNET_TIME_UNIT_ZERO;
|
||||||
hc->acc = lookup_account (h,
|
*con_cls = &special_ptr;
|
||||||
|
acc = lookup_account (h,
|
||||||
account,
|
account,
|
||||||
NULL);
|
NULL);
|
||||||
if (NULL == hc->acc)
|
if (NULL == acc)
|
||||||
{
|
{
|
||||||
GNUNET_assert (0 ==
|
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_BANK_UNKNOWN_ACCOUNT,
|
TALER_EC_BANK_UNKNOWN_ACCOUNT,
|
||||||
account);
|
account);
|
||||||
}
|
}
|
||||||
/* FIXME: was simply: acc->payto_uri -- same!? */
|
history = json_array ();
|
||||||
GNUNET_asprintf (&hc->payto_uri,
|
GNUNET_assert (NULL != history);
|
||||||
"payto://x-taler-bank/localhost/%s?receiver-name=%s",
|
credit_payto = acc->payto_uri;
|
||||||
account,
|
|
||||||
hc->acc->receiver_name);
|
|
||||||
GNUNET_assert (0 == strcmp (hc->payto_uri,
|
|
||||||
hc->acc->payto_uri));
|
|
||||||
hc->history = json_array ();
|
|
||||||
if (NULL == hc->history)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
GNUNET_assert (0 ==
|
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
return MHD_NO;
|
|
||||||
}
|
|
||||||
hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hc = cc->ctx;
|
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_lock (&h->big_lock));
|
pthread_mutex_lock (&h->big_lock));
|
||||||
}
|
if (! ha.have_start)
|
||||||
|
|
||||||
if (! hc->ha.have_start)
|
|
||||||
{
|
{
|
||||||
pos = (0 > hc->ha.delta)
|
pos = (0 > ha.delta)
|
||||||
? hc->acc->in_tail
|
? acc->in_tail
|
||||||
: hc->acc->in_head;
|
: acc->in_head;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
|
struct Transaction *t = h->transactions[ha.start_idx % h->ram_limit];
|
||||||
bool overflow;
|
bool overflow;
|
||||||
uint64_t dir;
|
uint64_t dir;
|
||||||
bool skip = true;
|
bool skip = true;
|
||||||
|
|
||||||
overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
|
overflow = ( (NULL != t) && (t->row_id != ha.start_idx) );
|
||||||
dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
|
dir = (0 > ha.delta) ? (h->ram_limit - 1) : 1;
|
||||||
/* If account does not match, linear scan for
|
/* If account does not match, linear scan for
|
||||||
first matching account. */
|
first matching account. */
|
||||||
while ( (! overflow) &&
|
while ( (! overflow) &&
|
||||||
(NULL != t) &&
|
(NULL != t) &&
|
||||||
(t->credit_account != hc->acc) )
|
(t->credit_account != acc) )
|
||||||
{
|
{
|
||||||
skip = false;
|
skip = false;
|
||||||
t = h->transactions[(t->row_id + dir) % h->ram_limit];
|
t = h->transactions[(t->row_id + dir) % h->ram_limit];
|
||||||
if ( (NULL != t) &&
|
if ( (NULL != t) &&
|
||||||
(t->row_id == hc->ha.start_idx) )
|
(t->row_id == ha.start_idx) )
|
||||||
overflow = true; /* full circle, give up! */
|
overflow = true; /* full circle, give up! */
|
||||||
}
|
}
|
||||||
if ( (NULL == t) ||
|
if ( (NULL == t) ||
|
||||||
overflow)
|
overflow)
|
||||||
{
|
{
|
||||||
/* FIXME: these conditions are unclear to me. */
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
|
"No transactions available, suspending request\n");
|
||||||
(0 < hc->ha.delta))
|
if (GNUNET_TIME_relative_is_zero (ha.lp_timeout) &&
|
||||||
|
(0 < ha.delta))
|
||||||
{
|
{
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
@ -2660,27 +2500,23 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
NULL);
|
NULL);
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
if (h->in_shutdown)
|
*con_cls = &special_ptr;
|
||||||
{
|
|
||||||
GNUNET_assert (0 ==
|
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
start_lp (h,
|
start_lp (h,
|
||||||
connection,
|
connection,
|
||||||
hc->acc,
|
acc,
|
||||||
GNUNET_TIME_absolute_get_remaining (hc->timeout),
|
ha.lp_timeout,
|
||||||
LP_CREDIT,
|
LP_CREDIT,
|
||||||
NULL);
|
NULL);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
|
json_decref (history);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
if (skip)
|
if (skip)
|
||||||
{
|
{
|
||||||
/* range from application is exclusive, skip the
|
/* range from application is exclusive, skip the
|
||||||
matching entry */
|
matching entry */
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = t->prev_in;
|
pos = t->prev_in;
|
||||||
else
|
else
|
||||||
pos = t->next_in;
|
pos = t->next_in;
|
||||||
@ -2693,9 +2529,9 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
if (NULL != pos)
|
if (NULL != pos)
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Returning %lld credit transactions starting (inclusive) from %llu\n",
|
"Returning %lld credit transactions starting (inclusive) from %llu\n",
|
||||||
(long long) hc->ha.delta,
|
(long long) ha.delta,
|
||||||
(unsigned long long) pos->row_id);
|
(unsigned long long) pos->row_id);
|
||||||
while ( (0 != hc->ha.delta) &&
|
while ( (0 != ha.delta) &&
|
||||||
(NULL != pos) )
|
(NULL != pos) )
|
||||||
{
|
{
|
||||||
json_t *trans;
|
json_t *trans;
|
||||||
@ -2706,9 +2542,9 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
"Unexpected DEBIT transaction #%llu for account `%s'\n",
|
"Unexpected DEBIT transaction #%llu for account `%s'\n",
|
||||||
(unsigned long long) pos->row_id,
|
(unsigned long long) pos->row_id,
|
||||||
account);
|
account);
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = pos->prev_in;
|
pos = pos->prev_in;
|
||||||
if (0 < hc->ha.delta)
|
if (0 < ha.delta)
|
||||||
pos = pos->next_in;
|
pos = pos->next_in;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2720,62 +2556,57 @@ handle_credit_history (struct TALER_FAKEBANK_Handle *h,
|
|||||||
TALER_JSON_pack_amount ("amount",
|
TALER_JSON_pack_amount ("amount",
|
||||||
&pos->amount),
|
&pos->amount),
|
||||||
GNUNET_JSON_pack_string ("credit_account",
|
GNUNET_JSON_pack_string ("credit_account",
|
||||||
hc->payto_uri), // FIXME #7275: inefficient to repeat this always here!
|
credit_payto), // FIXME #7275: inefficient to repeat this always here!
|
||||||
GNUNET_JSON_pack_string ("debit_account",
|
GNUNET_JSON_pack_string ("debit_account",
|
||||||
pos->debit_account->payto_uri),
|
pos->debit_account->payto_uri),
|
||||||
GNUNET_JSON_pack_data_auto ("reserve_pub",
|
GNUNET_JSON_pack_data_auto ("reserve_pub",
|
||||||
&pos->subject.credit.reserve_pub));
|
&pos->subject.credit.reserve_pub));
|
||||||
GNUNET_assert (NULL != trans);
|
GNUNET_assert (NULL != trans);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
json_array_append_new (hc->history,
|
json_array_append_new (history,
|
||||||
trans));
|
trans));
|
||||||
if (hc->ha.delta > 0)
|
if (ha.delta > 0)
|
||||||
hc->ha.delta--;
|
ha.delta--;
|
||||||
else
|
else
|
||||||
hc->ha.delta++;
|
ha.delta++;
|
||||||
if (0 > hc->ha.delta)
|
if (0 > ha.delta)
|
||||||
pos = pos->prev_in;
|
pos = pos->prev_in;
|
||||||
if (0 < hc->ha.delta)
|
if (0 < ha.delta)
|
||||||
pos = pos->next_in;
|
pos = pos->next_in;
|
||||||
}
|
}
|
||||||
if ( (0 == json_array_size (hc->history)) &&
|
if ( (0 == json_array_size (history)) &&
|
||||||
(! h->in_shutdown) &&
|
(! GNUNET_TIME_relative_is_zero (ha.lp_timeout)) &&
|
||||||
(GNUNET_TIME_absolute_is_future (hc->timeout)) &&
|
(0 < ha.delta))
|
||||||
(0 < hc->ha.delta))
|
|
||||||
{
|
{
|
||||||
|
*con_cls = &special_ptr;
|
||||||
start_lp (h,
|
start_lp (h,
|
||||||
connection,
|
connection,
|
||||||
hc->acc,
|
acc,
|
||||||
GNUNET_TIME_absolute_get_remaining (hc->timeout),
|
ha.lp_timeout,
|
||||||
LP_CREDIT,
|
LP_CREDIT,
|
||||||
NULL);
|
NULL);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
|
json_decref (history);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
finish:
|
finish:
|
||||||
if (0 == json_array_size (hc->history))
|
if (0 == json_array_size (history))
|
||||||
{
|
{
|
||||||
GNUNET_break (h->in_shutdown ||
|
json_decref (history);
|
||||||
(! GNUNET_TIME_absolute_is_future (hc->timeout)));
|
|
||||||
return TALER_MHD_reply_static (connection,
|
return TALER_MHD_reply_static (connection,
|
||||||
MHD_HTTP_NO_CONTENT,
|
MHD_HTTP_NO_CONTENT,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
{
|
|
||||||
json_t *h = hc->history;
|
|
||||||
|
|
||||||
hc->history = NULL;
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (connection,
|
return TALER_MHD_REPLY_JSON_PACK (connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_array_steal (
|
GNUNET_JSON_pack_array_steal (
|
||||||
"incoming_transactions",
|
"incoming_transactions",
|
||||||
h));
|
history));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2880,21 +2711,13 @@ get_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
|
|||||||
struct GNUNET_TIME_Relative lp,
|
struct GNUNET_TIME_Relative lp,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
struct WithdrawalOperation *wo;
|
||||||
struct WithdrawContext *wc;
|
|
||||||
|
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_lock (&h->big_lock));
|
pthread_mutex_lock (&h->big_lock));
|
||||||
if (NULL == cc)
|
wo = lookup_withdrawal_operation (h,
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &withdraw_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
wc = GNUNET_new (struct WithdrawContext);
|
|
||||||
cc->ctx = wc;
|
|
||||||
wc->wo = lookup_withdrawal_operation (h,
|
|
||||||
wopid);
|
wopid);
|
||||||
if (NULL == wc->wo)
|
if (NULL == wo)
|
||||||
{
|
{
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
@ -2903,16 +2726,10 @@ get_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
|
|||||||
TALER_EC_BANK_TRANSACTION_NOT_FOUND,
|
TALER_EC_BANK_TRANSACTION_NOT_FOUND,
|
||||||
wopid);
|
wopid);
|
||||||
}
|
}
|
||||||
wc->timeout = GNUNET_TIME_relative_to_absolute (lp);
|
if ( (NULL != *con_cls) ||
|
||||||
}
|
(GNUNET_TIME_relative_is_zero (lp)) ||
|
||||||
else
|
wo->confirmation_done ||
|
||||||
{
|
wo->aborted)
|
||||||
wc = cc->ctx;
|
|
||||||
}
|
|
||||||
if (GNUNET_TIME_absolute_is_past (wc->timeout) ||
|
|
||||||
h->in_shutdown ||
|
|
||||||
wc->wo->confirmation_done ||
|
|
||||||
wc->wo->aborted)
|
|
||||||
{
|
{
|
||||||
json_t *wt;
|
json_t *wt;
|
||||||
|
|
||||||
@ -2927,26 +2744,27 @@ get_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
|
|||||||
connection,
|
connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_bool ("aborted",
|
GNUNET_JSON_pack_bool ("aborted",
|
||||||
wc->wo->aborted),
|
wo->aborted),
|
||||||
GNUNET_JSON_pack_bool ("selection_done",
|
GNUNET_JSON_pack_bool ("selection_done",
|
||||||
wc->wo->selection_done),
|
wo->selection_done),
|
||||||
GNUNET_JSON_pack_bool ("transfer_done",
|
GNUNET_JSON_pack_bool ("transfer_done",
|
||||||
wc->wo->confirmation_done),
|
wo->confirmation_done),
|
||||||
GNUNET_JSON_pack_allow_null (
|
GNUNET_JSON_pack_allow_null (
|
||||||
GNUNET_JSON_pack_string ("suggested_exchange",
|
GNUNET_JSON_pack_string ("suggested_exchange",
|
||||||
h->exchange_url)),
|
h->exchange_url)),
|
||||||
TALER_JSON_pack_amount ("amount",
|
TALER_JSON_pack_amount ("amount",
|
||||||
&wc->wo->amount),
|
&wo->amount),
|
||||||
GNUNET_JSON_pack_array_steal ("wire_types",
|
GNUNET_JSON_pack_array_steal ("wire_types",
|
||||||
wt));
|
wt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*con_cls = &special_ptr;
|
||||||
start_lp (h,
|
start_lp (h,
|
||||||
connection,
|
connection,
|
||||||
wc->wo->debit_account,
|
wo->debit_account,
|
||||||
GNUNET_TIME_absolute_get_remaining (wc->timeout),
|
lp,
|
||||||
LP_WITHDRAW,
|
LP_WITHDRAW,
|
||||||
wc->wo);
|
wo);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
pthread_mutex_unlock (&h->big_lock));
|
pthread_mutex_unlock (&h->big_lock));
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
@ -3085,20 +2903,13 @@ post_withdrawal_operation (struct TALER_FAKEBANK_Handle *h,
|
|||||||
size_t *upload_data_size,
|
size_t *upload_data_size,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
enum GNUNET_JSON_PostResult pr;
|
||||||
json_t *json;
|
json_t *json;
|
||||||
MHD_RESULT res;
|
MHD_RESULT res;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
connection,
|
connection,
|
||||||
&cc->ctx,
|
con_cls,
|
||||||
upload_data,
|
upload_data,
|
||||||
upload_data_size,
|
upload_data_size,
|
||||||
&json);
|
&json);
|
||||||
@ -3483,20 +3294,13 @@ post_account_withdrawals_access (struct TALER_FAKEBANK_Handle *h,
|
|||||||
size_t *upload_data_size,
|
size_t *upload_data_size,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
enum GNUNET_JSON_PostResult pr;
|
||||||
json_t *json;
|
json_t *json;
|
||||||
MHD_RESULT res;
|
MHD_RESULT res;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
connection,
|
connection,
|
||||||
&cc->ctx,
|
con_cls,
|
||||||
upload_data,
|
upload_data,
|
||||||
upload_data_size,
|
upload_data_size,
|
||||||
&json);
|
&json);
|
||||||
@ -3563,20 +3367,13 @@ post_testing_register (struct TALER_FAKEBANK_Handle *h,
|
|||||||
size_t *upload_data_size,
|
size_t *upload_data_size,
|
||||||
void **con_cls)
|
void **con_cls)
|
||||||
{
|
{
|
||||||
struct ConnectionContext *cc = *con_cls;
|
|
||||||
enum GNUNET_JSON_PostResult pr;
|
enum GNUNET_JSON_PostResult pr;
|
||||||
json_t *json;
|
json_t *json;
|
||||||
MHD_RESULT res;
|
MHD_RESULT res;
|
||||||
|
|
||||||
if (NULL == cc)
|
|
||||||
{
|
|
||||||
cc = GNUNET_new (struct ConnectionContext);
|
|
||||||
cc->ctx_cleaner = &GNUNET_JSON_post_parser_cleanup;
|
|
||||||
*con_cls = cc;
|
|
||||||
}
|
|
||||||
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
|
||||||
connection,
|
connection,
|
||||||
&cc->ctx,
|
con_cls,
|
||||||
upload_data,
|
upload_data,
|
||||||
upload_data_size,
|
upload_data_size,
|
||||||
&json);
|
&json);
|
||||||
|
@ -28,7 +28,7 @@ DB = postgres
|
|||||||
# exchange (or the twister) is actually listening.
|
# exchange (or the twister) is actually listening.
|
||||||
base_url = "http://localhost:8081/"
|
base_url = "http://localhost:8081/"
|
||||||
|
|
||||||
WIREWATCH_IDLE_SLEEP_INTERVAL = 500 ms
|
WIREWATCH_IDLE_SLEEP_INTERVAL = 1500 ms
|
||||||
|
|
||||||
[exchange-offline]
|
[exchange-offline]
|
||||||
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||||
@ -51,11 +51,11 @@ MAX_DEBT = EUR:100000000000.0
|
|||||||
MAX_DEBT_BANK = EUR:1000000000000000.0
|
MAX_DEBT_BANK = EUR:1000000000000000.0
|
||||||
|
|
||||||
[benchmark]
|
[benchmark]
|
||||||
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42?receiver-name=user42
|
USER_PAYTO_URI = payto://x-taler-bank/localhost:8082/42
|
||||||
|
|
||||||
[exchange-account-2]
|
[exchange-account-2]
|
||||||
# What is the payto://-URL of the exchange (to generate wire response)
|
# What is the payto://-URL of the exchange (to generate wire response)
|
||||||
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange?receiver-name=Exchange"
|
PAYTO_URI = "payto://x-taler-bank/localhost:8082/Exchange"
|
||||||
enable_debit = YES
|
enable_debit = YES
|
||||||
enable_credit = YES
|
enable_credit = YES
|
||||||
|
|
||||||
|
@ -689,8 +689,6 @@ parallel_benchmark (void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* But be extra sure we did finish all shards by doing one more */
|
/* 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,
|
wirewatch[0] = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
"taler-exchange-wirewatch",
|
"taler-exchange-wirewatch",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
(C) 2014-2023 Taler Systems SA
|
(C) 2014-2020 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it
|
TALER is free software; you can redistribute it and/or modify it
|
||||||
under the terms of the GNU Affero General Public License as
|
under the terms of the GNU Affero General Public License as
|
||||||
|
@ -15,7 +15,8 @@ endif
|
|||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
taler-auditor-offline \
|
taler-auditor-offline \
|
||||||
taler-exchange-offline \
|
taler-exchange-offline \
|
||||||
taler-exchange-dbinit
|
taler-exchange-dbinit \
|
||||||
|
taler-crypto-worker
|
||||||
|
|
||||||
taler_exchange_offline_SOURCES = \
|
taler_exchange_offline_SOURCES = \
|
||||||
taler-exchange-offline.c
|
taler-exchange-offline.c
|
||||||
@ -59,6 +60,19 @@ taler_exchange_dbinit_CPPFLAGS = \
|
|||||||
-I$(top_srcdir)/src/pq/ \
|
-I$(top_srcdir)/src/pq/ \
|
||||||
$(POSTGRESQL_CPPFLAGS)
|
$(POSTGRESQL_CPPFLAGS)
|
||||||
|
|
||||||
|
taler_crypto_worker_SOURCES = \
|
||||||
|
taler-crypto-worker.c
|
||||||
|
taler_crypto_worker_LDADD = \
|
||||||
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
|
-lgnunetutil \
|
||||||
|
-lgnunetjson \
|
||||||
|
-ljansson \
|
||||||
|
$(LIBGCRYPT_LIBS) \
|
||||||
|
$(XLIB)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Testcases
|
# Testcases
|
||||||
|
|
||||||
|
459
src/exchange-tools/taler-crypto-worker.c
Normal file
459
src/exchange-tools/taler-crypto-worker.c
Normal file
@ -0,0 +1,459 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
Copyright (C) 2014-2021 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
|
||||||
|
Foundation; either version 3, or (at your option) any later version.
|
||||||
|
|
||||||
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along with
|
||||||
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file exchange-tools/taler-crypto-worker.c
|
||||||
|
* @brief Standalone process to perform various cryptographic operations.
|
||||||
|
* @author Florian Dold
|
||||||
|
*/
|
||||||
|
#include "platform.h"
|
||||||
|
#include "taler_util.h"
|
||||||
|
#include <gnunet/gnunet_json_lib.h>
|
||||||
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
|
#include "taler_error_codes.h"
|
||||||
|
#include "taler_json_lib.h"
|
||||||
|
#include "taler_signatures.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value from main().
|
||||||
|
*/
|
||||||
|
static int global_ret;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main function that will be run under the GNUnet scheduler.
|
||||||
|
*
|
||||||
|
* @param cls closure
|
||||||
|
* @param args remaining command-line arguments
|
||||||
|
* @param cfgfile name of the configuration file used (for saving, can be NULL!)
|
||||||
|
* @param cfg configuration
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
run (void *cls,
|
||||||
|
char *const *args,
|
||||||
|
const char *cfgfile,
|
||||||
|
const struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||||
|
{
|
||||||
|
(void) cls;
|
||||||
|
(void) args;
|
||||||
|
(void) cfgfile;
|
||||||
|
(void) cfg;
|
||||||
|
|
||||||
|
json_t *req;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"started crypto worker\n");
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
const char *op;
|
||||||
|
const json_t *args;
|
||||||
|
req = json_loadf (stdin, JSON_DISABLE_EOF_CHECK, NULL);
|
||||||
|
if (NULL == req)
|
||||||
|
{
|
||||||
|
if (feof (stdin))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"end of input\n");
|
||||||
|
global_ret = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"invalid JSON\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
op = json_string_value (json_object_get (req,
|
||||||
|
"op"));
|
||||||
|
if (! op)
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"no op specified\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
args = json_object_get (req, "args");
|
||||||
|
if (! args)
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"no args specified\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"got request\n");
|
||||||
|
if (0 == strcmp ("eddsa_get_public",
|
||||||
|
op))
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EddsaPublicKey eddsa_pub;
|
||||||
|
struct GNUNET_CRYPTO_EddsaPrivateKey eddsa_priv;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("eddsa_priv",
|
||||||
|
&eddsa_priv),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK != GNUNET_JSON_parse (args,
|
||||||
|
spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&eddsa_priv,
|
||||||
|
&eddsa_pub);
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("eddsa_pub",
|
||||||
|
&eddsa_pub)
|
||||||
|
);
|
||||||
|
json_dumpf (resp, stdout, JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("ecdhe_get_public",
|
||||||
|
op))
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EcdhePublicKey ecdhe_pub;
|
||||||
|
struct GNUNET_CRYPTO_EcdhePrivateKey ecdhe_priv;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("ecdhe_priv",
|
||||||
|
&ecdhe_priv),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK != GNUNET_JSON_parse (args,
|
||||||
|
spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_CRYPTO_ecdhe_key_get_public (&ecdhe_priv,
|
||||||
|
&ecdhe_pub);
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("ecdhe_pub",
|
||||||
|
&ecdhe_pub)
|
||||||
|
);
|
||||||
|
json_dumpf (resp, stdout, JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("eddsa_verify",
|
||||||
|
op))
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EddsaPublicKey pub;
|
||||||
|
struct GNUNET_CRYPTO_EddsaSignature sig;
|
||||||
|
struct GNUNET_CRYPTO_EccSignaturePurpose *msg;
|
||||||
|
size_t msg_size;
|
||||||
|
enum GNUNET_GenericReturnValue verify_ret;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification eddsa_verify_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("pub",
|
||||||
|
&pub),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("sig",
|
||||||
|
&sig),
|
||||||
|
GNUNET_JSON_spec_varsize ("msg",
|
||||||
|
(void **) &msg,
|
||||||
|
&msg_size),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK != GNUNET_JSON_parse (args,
|
||||||
|
eddsa_verify_spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
verify_ret = GNUNET_CRYPTO_eddsa_verify_ (
|
||||||
|
ntohl (msg->purpose),
|
||||||
|
msg,
|
||||||
|
&sig,
|
||||||
|
&pub);
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_bool ("valid",
|
||||||
|
GNUNET_OK == verify_ret));
|
||||||
|
json_dumpf (resp, stdout, JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (eddsa_verify_spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("kx_ecdhe_eddsa",
|
||||||
|
op))
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EcdhePrivateKey priv;
|
||||||
|
struct GNUNET_CRYPTO_EddsaPublicKey pub;
|
||||||
|
struct GNUNET_HashCode key_material;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification kx_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("eddsa_pub",
|
||||||
|
&pub),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("ecdhe_priv",
|
||||||
|
&priv),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (args,
|
||||||
|
kx_spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
GNUNET_CRYPTO_ecdh_eddsa (&priv,
|
||||||
|
&pub,
|
||||||
|
&key_material));
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("h",
|
||||||
|
&key_material)
|
||||||
|
);
|
||||||
|
json_dumpf (resp,
|
||||||
|
stdout,
|
||||||
|
JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (kx_spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("eddsa_sign",
|
||||||
|
op))
|
||||||
|
{
|
||||||
|
struct GNUNET_CRYPTO_EddsaSignature sig;
|
||||||
|
struct GNUNET_CRYPTO_EccSignaturePurpose *msg;
|
||||||
|
struct GNUNET_CRYPTO_EddsaPrivateKey priv;
|
||||||
|
size_t msg_size;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification eddsa_sign_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("priv",
|
||||||
|
&priv),
|
||||||
|
GNUNET_JSON_spec_varsize ("msg",
|
||||||
|
(void **) &msg,
|
||||||
|
&msg_size),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (args,
|
||||||
|
eddsa_sign_spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
GNUNET_CRYPTO_eddsa_sign_ (
|
||||||
|
&priv,
|
||||||
|
msg,
|
||||||
|
&sig));
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("sig", &sig)
|
||||||
|
);
|
||||||
|
json_dumpf (resp, stdout,
|
||||||
|
JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (eddsa_sign_spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("setup_refresh_planchet", op))
|
||||||
|
{
|
||||||
|
struct TALER_TransferSecretP transfer_secret;
|
||||||
|
uint32_t coin_index;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification setup_refresh_planchet_spec[] = {
|
||||||
|
GNUNET_JSON_spec_uint32 ("coin_index",
|
||||||
|
&coin_index),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("transfer_secret",
|
||||||
|
&transfer_secret),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||||
|
struct TALER_CoinSpendPrivateKeyP coin_priv;
|
||||||
|
struct TALER_PlanchetMasterSecretP ps;
|
||||||
|
struct TALER_ExchangeWithdrawValues alg_values = {
|
||||||
|
// FIXME: also allow CS
|
||||||
|
.cipher = TALER_DENOMINATION_RSA,
|
||||||
|
};
|
||||||
|
union TALER_DenominationBlindingKeyP dbk;
|
||||||
|
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (args,
|
||||||
|
setup_refresh_planchet_spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TALER_transfer_secret_to_planchet_secret (&transfer_secret,
|
||||||
|
coin_index,
|
||||||
|
&ps);
|
||||||
|
TALER_planchet_setup_coin_priv (&ps,
|
||||||
|
&alg_values,
|
||||||
|
&coin_priv);
|
||||||
|
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv.eddsa_priv,
|
||||||
|
&coin_pub.eddsa_pub);
|
||||||
|
TALER_planchet_blinding_secret_create (&ps,
|
||||||
|
&alg_values,
|
||||||
|
&dbk);
|
||||||
|
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_auto ("coin_priv", &coin_priv),
|
||||||
|
GNUNET_JSON_pack_data_auto ("coin_pub", &coin_pub),
|
||||||
|
GNUNET_JSON_pack_data_auto ("blinding_key", &dbk.rsa_bks)
|
||||||
|
);
|
||||||
|
json_dumpf (resp, stdout, JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (setup_refresh_planchet_spec);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (0 == strcmp ("rsa_blind", op))
|
||||||
|
{
|
||||||
|
struct GNUNET_HashCode hm;
|
||||||
|
struct GNUNET_CRYPTO_RsaBlindingKeySecret bks;
|
||||||
|
void *pub_enc;
|
||||||
|
size_t pub_enc_size;
|
||||||
|
int success;
|
||||||
|
struct GNUNET_CRYPTO_RsaPublicKey *pub;
|
||||||
|
void *blinded_buf;
|
||||||
|
size_t blinded_size;
|
||||||
|
json_t *resp;
|
||||||
|
struct GNUNET_JSON_Specification rsa_blind_spec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("hm",
|
||||||
|
&hm),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("bks",
|
||||||
|
&bks),
|
||||||
|
GNUNET_JSON_spec_varsize ("pub",
|
||||||
|
&pub_enc,
|
||||||
|
&pub_enc_size),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
GNUNET_JSON_parse (args,
|
||||||
|
rsa_blind_spec,
|
||||||
|
NULL,
|
||||||
|
NULL))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"malformed op args\n");
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pub = GNUNET_CRYPTO_rsa_public_key_decode (pub_enc,
|
||||||
|
pub_enc_size);
|
||||||
|
success = GNUNET_CRYPTO_rsa_blind (&hm,
|
||||||
|
&bks,
|
||||||
|
pub,
|
||||||
|
&blinded_buf,
|
||||||
|
&blinded_size);
|
||||||
|
|
||||||
|
if (GNUNET_YES == success)
|
||||||
|
{
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_data_varsize ("blinded", blinded_buf, blinded_size),
|
||||||
|
GNUNET_JSON_pack_bool ("success", true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resp = GNUNET_JSON_PACK (
|
||||||
|
GNUNET_JSON_pack_bool ("success", false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
json_dumpf (resp, stdout, JSON_COMPACT);
|
||||||
|
printf ("\n");
|
||||||
|
fflush (stdout);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"sent response\n");
|
||||||
|
GNUNET_JSON_parse_free (rsa_blind_spec);
|
||||||
|
GNUNET_free (blinded_buf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"unsupported operation '%s'\n",
|
||||||
|
op);
|
||||||
|
global_ret = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entry point.
|
||||||
|
*
|
||||||
|
* @param argc number of arguments in @a argv
|
||||||
|
* @param argv command-line arguments
|
||||||
|
* @return 0 on normal termination
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
main (int argc,
|
||||||
|
char **argv)
|
||||||
|
{
|
||||||
|
struct GNUNET_GETOPT_CommandLineOption options[] = {
|
||||||
|
GNUNET_GETOPT_OPTION_END
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* force linker to link against libtalerutil; if we do
|
||||||
|
not do this, the linker may "optimize" libtalerutil
|
||||||
|
away and skip #TALER_OS_init(), which we do need */
|
||||||
|
TALER_OS_init ();
|
||||||
|
ret = GNUNET_PROGRAM_run (argc, argv,
|
||||||
|
"taler-crypto-worker",
|
||||||
|
"Execute cryptographic operations read from stdin",
|
||||||
|
options,
|
||||||
|
&run,
|
||||||
|
NULL);
|
||||||
|
if (GNUNET_NO == ret)
|
||||||
|
return 0;
|
||||||
|
if (GNUNET_SYSERR == ret)
|
||||||
|
return 1;
|
||||||
|
return global_ret;
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2020-2023 Taler Systems SA
|
Copyright (C) 2020, 2021, 2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
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
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -112,16 +112,6 @@
|
|||||||
*/
|
*/
|
||||||
#define OP_DRAIN_PROFITS "exchange-drain-profits-0"
|
#define OP_DRAIN_PROFITS "exchange-drain-profits-0"
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup AML staff.
|
|
||||||
*/
|
|
||||||
#define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup partner exchange for wad transfers.
|
|
||||||
*/
|
|
||||||
#define OP_ADD_PARTNER "exchange-add-partner-0"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our private key, initialized in #load_offline_key().
|
* Our private key, initialized in #load_offline_key().
|
||||||
*/
|
*/
|
||||||
@ -508,62 +498,6 @@ struct UploadExtensionsRequest
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data structure for AML staff requests.
|
|
||||||
*/
|
|
||||||
struct AmlStaffRequest
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kept in a DLL.
|
|
||||||
*/
|
|
||||||
struct AmlStaffRequest *next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kept in a DLL.
|
|
||||||
*/
|
|
||||||
struct AmlStaffRequest *prev;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operation handle.
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array index of the associated command.
|
|
||||||
*/
|
|
||||||
size_t idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data structure for partner add requests.
|
|
||||||
*/
|
|
||||||
struct PartnerAddRequest
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kept in a DLL.
|
|
||||||
*/
|
|
||||||
struct PartnerAddRequest *next;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Kept in a DLL.
|
|
||||||
*/
|
|
||||||
struct PartnerAddRequest *prev;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operation handle.
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGE_ManagementAddPartner *h;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array index of the associated command.
|
|
||||||
*/
|
|
||||||
size_t idx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Next work item to perform.
|
* Next work item to perform.
|
||||||
*/
|
*/
|
||||||
@ -574,27 +508,6 @@ static struct GNUNET_SCHEDULER_Task *nxt;
|
|||||||
*/
|
*/
|
||||||
static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh;
|
static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh;
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active AML staff change requests.
|
|
||||||
*/
|
|
||||||
static struct AmlStaffRequest *asr_head;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active AML staff change requests.
|
|
||||||
*/
|
|
||||||
static struct AmlStaffRequest *asr_tail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active partner add requests.
|
|
||||||
*/
|
|
||||||
static struct PartnerAddRequest *par_head;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Active partner add requests.
|
|
||||||
*/
|
|
||||||
static struct PartnerAddRequest *par_tail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Active denomiantion revocation requests.
|
* Active denomiantion revocation requests.
|
||||||
*/
|
*/
|
||||||
@ -716,36 +629,6 @@ do_shutdown (void *cls)
|
|||||||
{
|
{
|
||||||
(void) cls;
|
(void) cls;
|
||||||
|
|
||||||
{
|
|
||||||
struct AmlStaffRequest *asr;
|
|
||||||
|
|
||||||
while (NULL != (asr = asr_head))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Aborting incomplete AML staff update #%u\n",
|
|
||||||
(unsigned int) asr->idx);
|
|
||||||
TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h);
|
|
||||||
GNUNET_CONTAINER_DLL_remove (asr_head,
|
|
||||||
asr_tail,
|
|
||||||
asr);
|
|
||||||
GNUNET_free (asr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct PartnerAddRequest *par;
|
|
||||||
|
|
||||||
while (NULL != (par = par_head))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Aborting incomplete partner add request #%u\n",
|
|
||||||
(unsigned int) par->idx);
|
|
||||||
TALER_EXCHANGE_management_add_partner_cancel (par->h);
|
|
||||||
GNUNET_CONTAINER_DLL_remove (par_head,
|
|
||||||
par_tail,
|
|
||||||
par);
|
|
||||||
GNUNET_free (par);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
struct DenomRevocationRequest *drr;
|
struct DenomRevocationRequest *drr;
|
||||||
|
|
||||||
@ -959,8 +842,6 @@ static void
|
|||||||
test_shutdown (void)
|
test_shutdown (void)
|
||||||
{
|
{
|
||||||
if ( (NULL == drr_head) &&
|
if ( (NULL == drr_head) &&
|
||||||
(NULL == par_head) &&
|
|
||||||
(NULL == asr_head) &&
|
|
||||||
(NULL == srr_head) &&
|
(NULL == srr_head) &&
|
||||||
(NULL == aar_head) &&
|
(NULL == aar_head) &&
|
||||||
(NULL == adr_head) &&
|
(NULL == adr_head) &&
|
||||||
@ -2333,221 +2214,6 @@ 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
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
add_partner_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
|
||||||
{
|
|
||||||
struct PartnerAddRequest *par = cls;
|
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Upload failed for command %u with status %u: %s (%s)\n",
|
|
||||||
(unsigned int) par->idx,
|
|
||||||
hr->http_status,
|
|
||||||
TALER_ErrorCode_get_hint (hr->ec),
|
|
||||||
hr->hint);
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
GNUNET_CONTAINER_DLL_remove (par_head,
|
|
||||||
par_tail,
|
|
||||||
par);
|
|
||||||
GNUNET_free (par);
|
|
||||||
test_shutdown ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add partner action.
|
|
||||||
*
|
|
||||||
* @param exchange_url base URL of the exchange
|
|
||||||
* @param idx index of the operation we are performing (for logging)
|
|
||||||
* @param value arguments for add partner
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
add_partner (const char *exchange_url,
|
|
||||||
size_t idx,
|
|
||||||
const json_t *value)
|
|
||||||
{
|
|
||||||
struct TALER_MasterPublicKeyP partner_pub;
|
|
||||||
struct GNUNET_TIME_Timestamp start_date;
|
|
||||||
struct GNUNET_TIME_Timestamp end_date;
|
|
||||||
struct GNUNET_TIME_Relative wad_frequency;
|
|
||||||
struct TALER_Amount wad_fee;
|
|
||||||
const char *partner_base_url;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
struct PartnerAddRequest *par;
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("partner_pub",
|
|
||||||
&partner_pub),
|
|
||||||
TALER_JSON_spec_amount ("wad_fee",
|
|
||||||
currency,
|
|
||||||
&wad_fee),
|
|
||||||
GNUNET_JSON_spec_relative_time ("wad_frequency",
|
|
||||||
&wad_frequency),
|
|
||||||
GNUNET_JSON_spec_timestamp ("start_date",
|
|
||||||
&start_date),
|
|
||||||
GNUNET_JSON_spec_timestamp ("end_date",
|
|
||||||
&end_date),
|
|
||||||
GNUNET_JSON_spec_string ("partner_base_url",
|
|
||||||
&partner_base_url),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
|
||||||
&master_sig),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
const char *err_name;
|
|
||||||
unsigned int err_line;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_JSON_parse (value,
|
|
||||||
spec,
|
|
||||||
&err_name,
|
|
||||||
&err_line))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Invalid input to add partner: %s#%u at %u (skipping)\n",
|
|
||||||
err_name,
|
|
||||||
err_line,
|
|
||||||
(unsigned int) idx);
|
|
||||||
json_dumpf (value,
|
|
||||||
stderr,
|
|
||||||
JSON_INDENT (2));
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
test_shutdown ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
par = GNUNET_new (struct PartnerAddRequest);
|
|
||||||
par->idx = idx;
|
|
||||||
par->h =
|
|
||||||
TALER_EXCHANGE_management_add_partner (ctx,
|
|
||||||
exchange_url,
|
|
||||||
&partner_pub,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
wad_frequency,
|
|
||||||
&wad_fee,
|
|
||||||
partner_base_url,
|
|
||||||
&master_sig,
|
|
||||||
&add_partner_cb,
|
|
||||||
par);
|
|
||||||
GNUNET_CONTAINER_DLL_insert (par_head,
|
|
||||||
par_tail,
|
|
||||||
par);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called with information about the AML officer update operation.
|
|
||||||
*
|
|
||||||
* @param cls closure with a `struct AmlStaffRequest`
|
|
||||||
* @param hr HTTP response data
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
update_aml_officer_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
|
||||||
{
|
|
||||||
struct AmlStaffRequest *asr = cls;
|
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Upload failed for command %u with status %u: %s (%s)\n",
|
|
||||||
(unsigned int) asr->idx,
|
|
||||||
hr->http_status,
|
|
||||||
TALER_ErrorCode_get_hint (hr->ec),
|
|
||||||
hr->hint);
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
GNUNET_CONTAINER_DLL_remove (asr_head,
|
|
||||||
asr_tail,
|
|
||||||
asr);
|
|
||||||
GNUNET_free (asr);
|
|
||||||
test_shutdown ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Upload AML staff action.
|
|
||||||
*
|
|
||||||
* @param exchange_url base URL of the exchange
|
|
||||||
* @param idx index of the operation we are performing (for logging)
|
|
||||||
* @param value arguments for AML staff change
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
update_aml_staff (const char *exchange_url,
|
|
||||||
size_t idx,
|
|
||||||
const json_t *value)
|
|
||||||
{
|
|
||||||
struct TALER_AmlOfficerPublicKeyP officer_pub;
|
|
||||||
const char *officer_name;
|
|
||||||
struct GNUNET_TIME_Timestamp change_date;
|
|
||||||
bool is_active;
|
|
||||||
bool read_only;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
struct AmlStaffRequest *asr;
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("officer_pub",
|
|
||||||
&officer_pub),
|
|
||||||
GNUNET_JSON_spec_timestamp ("change_date",
|
|
||||||
&change_date),
|
|
||||||
GNUNET_JSON_spec_bool ("is_active",
|
|
||||||
&is_active),
|
|
||||||
GNUNET_JSON_spec_bool ("read_only",
|
|
||||||
&read_only),
|
|
||||||
GNUNET_JSON_spec_string ("officer_name",
|
|
||||||
&officer_name),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
|
||||||
&master_sig),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
const char *err_name;
|
|
||||||
unsigned int err_line;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_JSON_parse (value,
|
|
||||||
spec,
|
|
||||||
&err_name,
|
|
||||||
&err_line))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Invalid input to AML staff update: %s#%u at %u (skipping)\n",
|
|
||||||
err_name,
|
|
||||||
err_line,
|
|
||||||
(unsigned int) idx);
|
|
||||||
json_dumpf (value,
|
|
||||||
stderr,
|
|
||||||
JSON_INDENT (2));
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
test_shutdown ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
asr = GNUNET_new (struct AmlStaffRequest);
|
|
||||||
asr->idx = idx;
|
|
||||||
asr->h =
|
|
||||||
TALER_EXCHANGE_management_update_aml_officer (ctx,
|
|
||||||
exchange_url,
|
|
||||||
&officer_pub,
|
|
||||||
officer_name,
|
|
||||||
change_date,
|
|
||||||
is_active,
|
|
||||||
read_only,
|
|
||||||
&master_sig,
|
|
||||||
&update_aml_officer_cb,
|
|
||||||
asr);
|
|
||||||
GNUNET_CONTAINER_DLL_insert (asr_head,
|
|
||||||
asr_tail,
|
|
||||||
asr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform uploads based on the JSON in #out.
|
* Perform uploads based on the JSON in #out.
|
||||||
*
|
*
|
||||||
@ -2601,14 +2267,6 @@ trigger_upload (const char *exchange_url)
|
|||||||
.key = OP_EXTENSIONS,
|
.key = OP_EXTENSIONS,
|
||||||
.cb = &upload_extensions
|
.cb = &upload_extensions
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.key = OP_UPDATE_AML_STAFF,
|
|
||||||
.cb = &update_aml_staff
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.key = OP_ADD_PARTNER,
|
|
||||||
.cb = &add_partner
|
|
||||||
},
|
|
||||||
/* array termination */
|
/* array termination */
|
||||||
{
|
{
|
||||||
.key = NULL
|
.key = NULL
|
||||||
@ -3382,261 +3040,6 @@ do_drain (char *const *args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add partner.
|
|
||||||
*
|
|
||||||
* @param args the array of command-line arguments to process next;
|
|
||||||
* args[0] must be the partner's master public key, args[1] the partner's
|
|
||||||
* API base URL, args[2] the wad fee, args[3] the wad frequency, and
|
|
||||||
* args[4] the year (including possibly 'now')
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
do_add_partner (char *const *args)
|
|
||||||
{
|
|
||||||
struct TALER_MasterPublicKeyP partner_pub;
|
|
||||||
struct GNUNET_TIME_Timestamp start_date;
|
|
||||||
struct GNUNET_TIME_Timestamp end_date;
|
|
||||||
struct GNUNET_TIME_Relative wad_frequency;
|
|
||||||
struct TALER_Amount wad_fee;
|
|
||||||
const char *partner_base_url;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
char dummy;
|
|
||||||
unsigned int year;
|
|
||||||
|
|
||||||
if (NULL != in)
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Downloaded data was not consumed, not adding partner\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( (NULL == args[0]) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
|
||||||
strlen (args[0]),
|
|
||||||
&partner_pub,
|
|
||||||
sizeof (partner_pub))) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"You must specify the partner master public key as first argument for this subcommand\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( (NULL == args[1]) ||
|
|
||||||
(0 != strncmp ("http",
|
|
||||||
args[1],
|
|
||||||
strlen ("http"))) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"You must specify the partner's base URL as the 2nd argument to this subcommand\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
partner_base_url = args[1];
|
|
||||||
if ( (NULL == args[2]) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
TALER_string_to_amount (args[2],
|
|
||||||
&wad_fee)) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Invalid amount `%s' specified for wad fee of partner\n",
|
|
||||||
args[2]);
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( (NULL == args[3]) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_fancy_time_to_relative (args[3],
|
|
||||||
&wad_frequency)) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Invalid wad frequency `%s' specified for add partner\n",
|
|
||||||
args[3]);
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( (NULL == args[4]) ||
|
|
||||||
( (1 != sscanf (args[4],
|
|
||||||
"%u%c",
|
|
||||||
&year,
|
|
||||||
&dummy)) &&
|
|
||||||
(0 != strcasecmp ("now",
|
|
||||||
args[4])) ) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Invalid year `%s' specified for add partner\n",
|
|
||||||
args[4]);
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (0 == strcasecmp ("now",
|
|
||||||
args[4]))
|
|
||||||
year = GNUNET_TIME_get_current_year ();
|
|
||||||
start_date = GNUNET_TIME_absolute_to_timestamp (
|
|
||||||
GNUNET_TIME_year_to_time (year));
|
|
||||||
end_date = GNUNET_TIME_absolute_to_timestamp (
|
|
||||||
GNUNET_TIME_year_to_time (year + 1));
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
load_offline_key (GNUNET_NO))
|
|
||||||
return;
|
|
||||||
TALER_exchange_offline_partner_details_sign (&partner_pub,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
wad_frequency,
|
|
||||||
&wad_fee,
|
|
||||||
partner_base_url,
|
|
||||||
&master_priv,
|
|
||||||
&master_sig);
|
|
||||||
output_operation (OP_ADD_PARTNER,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_string ("partner_base_url",
|
|
||||||
partner_base_url),
|
|
||||||
GNUNET_JSON_pack_time_rel ("wad_frequency",
|
|
||||||
wad_frequency),
|
|
||||||
GNUNET_JSON_pack_timestamp ("start_date",
|
|
||||||
start_date),
|
|
||||||
GNUNET_JSON_pack_timestamp ("end_date",
|
|
||||||
end_date),
|
|
||||||
GNUNET_JSON_pack_data_auto ("partner_pub",
|
|
||||||
&partner_pub),
|
|
||||||
GNUNET_JSON_pack_data_auto ("master_sig",
|
|
||||||
&master_sig)));
|
|
||||||
next (args + 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable or disable AML staff.
|
|
||||||
*
|
|
||||||
* @param is_active true to enable, false to disable
|
|
||||||
* @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
do_set_aml_staff (bool is_active,
|
|
||||||
char *const *args)
|
|
||||||
{
|
|
||||||
struct TALER_AmlOfficerPublicKeyP officer_pub;
|
|
||||||
const char *officer_name;
|
|
||||||
bool read_only;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
struct GNUNET_TIME_Timestamp now
|
|
||||||
= GNUNET_TIME_timestamp_get ();
|
|
||||||
|
|
||||||
if (NULL != in)
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Downloaded data was not consumed, not updating AML staff status\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_FAILURE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( (NULL == args[0]) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
|
||||||
strlen (args[0]),
|
|
||||||
&officer_pub,
|
|
||||||
sizeof (officer_pub))) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"You must specify the AML officer's public key as first argument for this subcommand\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (NULL == args[1])
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"You must specify the officer's legal name as the 2nd argument to this subcommand\n");
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
officer_name = args[1];
|
|
||||||
if (is_active)
|
|
||||||
{
|
|
||||||
if ( (NULL == args[2]) ||
|
|
||||||
( (0 != strcmp (args[2],
|
|
||||||
"ro")) &&
|
|
||||||
(0 != strcmp (args[2],
|
|
||||||
"rw")) ) )
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"You must specify 'ro' or 'rw' (and not `%s') for the access level\n",
|
|
||||||
args[2]);
|
|
||||||
test_shutdown ();
|
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
read_only = (0 == strcmp (args[2],
|
|
||||||
"ro"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
read_only = true;
|
|
||||||
}
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
load_offline_key (GNUNET_NO))
|
|
||||||
return;
|
|
||||||
TALER_exchange_offline_aml_officer_status_sign (&officer_pub,
|
|
||||||
officer_name,
|
|
||||||
now,
|
|
||||||
is_active,
|
|
||||||
read_only,
|
|
||||||
&master_priv,
|
|
||||||
&master_sig);
|
|
||||||
output_operation (OP_UPDATE_AML_STAFF,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_string ("officer_name",
|
|
||||||
officer_name),
|
|
||||||
GNUNET_JSON_pack_timestamp ("change_date",
|
|
||||||
now),
|
|
||||||
GNUNET_JSON_pack_bool ("is_active",
|
|
||||||
is_active),
|
|
||||||
GNUNET_JSON_pack_bool ("read_only",
|
|
||||||
read_only),
|
|
||||||
GNUNET_JSON_pack_data_auto ("officer_pub",
|
|
||||||
&officer_pub),
|
|
||||||
GNUNET_JSON_pack_data_auto ("master_sig",
|
|
||||||
&master_sig)));
|
|
||||||
next (args + (is_active ? 3 : 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable AML staff.
|
|
||||||
*
|
|
||||||
* @param args the array of command-line arguments to process next;
|
|
||||||
* args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
disable_aml_staff (char *const *args)
|
|
||||||
{
|
|
||||||
do_set_aml_staff (false,
|
|
||||||
args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable AML staff.
|
|
||||||
*
|
|
||||||
* @param args the array of command-line arguments to process next;
|
|
||||||
* args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only)
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
enable_aml_staff (char *const *args)
|
|
||||||
{
|
|
||||||
do_set_aml_staff (true,
|
|
||||||
args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with information about future keys. Dumps the JSON output
|
* Function called with information about future keys. Dumps the JSON output
|
||||||
* (on success), either into an internal buffer or to stdout (depending on
|
* (on success), either into an internal buffer or to stdout (depending on
|
||||||
@ -5072,24 +4475,6 @@ work (void *cls)
|
|||||||
"drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)",
|
"drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)",
|
||||||
.cb = &do_drain
|
.cb = &do_drain
|
||||||
},
|
},
|
||||||
{
|
|
||||||
.name = "add-partner",
|
|
||||||
.help =
|
|
||||||
"add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)",
|
|
||||||
.cb = &do_add_partner
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "aml-enable",
|
|
||||||
.help =
|
|
||||||
"enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)",
|
|
||||||
.cb = &enable_aml_staff
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.name = "aml-disable",
|
|
||||||
.help =
|
|
||||||
"disable AML staff member (staff member public key and legal name must be given as arguments)",
|
|
||||||
.cb = &disable_aml_staff
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
.name = "upload",
|
.name = "upload",
|
||||||
.help =
|
.help =
|
||||||
|
@ -123,15 +123,9 @@ taler_exchange_wirewatch_LDADD = \
|
|||||||
taler_exchange_httpd_SOURCES = \
|
taler_exchange_httpd_SOURCES = \
|
||||||
taler-exchange-httpd.c taler-exchange-httpd.h \
|
taler-exchange-httpd.c taler-exchange-httpd.h \
|
||||||
taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
|
taler-exchange-httpd_auditors.c taler-exchange-httpd_auditors.h \
|
||||||
taler-exchange-httpd_aml-decision.c taler-exchange-httpd_aml-decision.h \
|
|
||||||
taler-exchange-httpd_aml-decision-get.c \
|
|
||||||
taler-exchange-httpd_aml-decisions-get.c \
|
|
||||||
taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \
|
taler-exchange-httpd_batch-deposit.c taler-exchange-httpd_batch-deposit.h \
|
||||||
taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \
|
taler-exchange-httpd_batch-withdraw.c taler-exchange-httpd_batch-withdraw.h \
|
||||||
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_deposit.c taler-exchange-httpd_common_deposit.h \
|
||||||
taler-exchange-httpd_config.c taler-exchange-httpd_config.h \
|
|
||||||
taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \
|
taler-exchange-httpd_contract.c taler-exchange-httpd_contract.h \
|
||||||
taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \
|
taler-exchange-httpd_csr.c taler-exchange-httpd_csr.h \
|
||||||
taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
|
taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
|
||||||
@ -145,14 +139,12 @@ taler_exchange_httpd_SOURCES = \
|
|||||||
taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \
|
taler-exchange-httpd_kyc-webhook.c taler-exchange-httpd_kyc-webhook.h \
|
||||||
taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
|
taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
|
||||||
taler-exchange-httpd_management.h \
|
taler-exchange-httpd_management.h \
|
||||||
taler-exchange-httpd_management_aml-officers.c \
|
|
||||||
taler-exchange-httpd_management_auditors.c \
|
taler-exchange-httpd_management_auditors.c \
|
||||||
taler-exchange-httpd_management_auditors_AP_disable.c \
|
taler-exchange-httpd_management_auditors_AP_disable.c \
|
||||||
taler-exchange-httpd_management_denominations_HDP_revoke.c \
|
taler-exchange-httpd_management_denominations_HDP_revoke.c \
|
||||||
taler-exchange-httpd_management_drain.c \
|
taler-exchange-httpd_management_drain.c \
|
||||||
taler-exchange-httpd_management_extensions.c \
|
taler-exchange-httpd_management_extensions.c \
|
||||||
taler-exchange-httpd_management_global_fees.c \
|
taler-exchange-httpd_management_global_fees.c \
|
||||||
taler-exchange-httpd_management_partners.c \
|
|
||||||
taler-exchange-httpd_management_post_keys.c \
|
taler-exchange-httpd_management_post_keys.c \
|
||||||
taler-exchange-httpd_management_signkey_EP_revoke.c \
|
taler-exchange-httpd_management_signkey_EP_revoke.c \
|
||||||
taler-exchange-httpd_management_wire_enable.c \
|
taler-exchange-httpd_management_wire_enable.c \
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
# This must be adjusted to your actual installation.
|
# This must be adjusted to your actual installation.
|
||||||
# MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
# MASTER_PUBLIC_KEY = 98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
|
||||||
|
|
||||||
# Attribute encryption key for storing attributes encrypted
|
|
||||||
# in the database. Should be a high-entropy nonce.
|
|
||||||
ATTRIBUTE_ENCRYPTION_KEY = SET_ME_PLEASE
|
|
||||||
|
|
||||||
# How long do we allow /keys to be cached at most? The actual
|
# How long do we allow /keys to be cached at most? The actual
|
||||||
# limit is the minimum of this value and the first expected
|
# limit is the minimum of this value and the first expected
|
||||||
# significant change in /keys based on the expiration times.
|
# significant change in /keys based on the expiration times.
|
||||||
@ -19,7 +15,7 @@ MAX_KEYS_CACHING = forever
|
|||||||
# After how many requests should the exchange auto-restart
|
# After how many requests should the exchange auto-restart
|
||||||
# (to address potential issues with memory fragmentation)?
|
# (to address potential issues with memory fragmentation)?
|
||||||
# If this option is not specified, auto-restarting is disabled.
|
# If this option is not specified, auto-restarting is disabled.
|
||||||
# MAX_REQUESTS = 100000
|
# MAX_REQUESTS = 10000000
|
||||||
|
|
||||||
# How to access our database
|
# How to access our database
|
||||||
DB = postgres
|
DB = postgres
|
||||||
|
@ -148,12 +148,6 @@ struct Shard
|
|||||||
*/
|
*/
|
||||||
static struct TALER_Amount currency_round_unit;
|
static struct TALER_Amount currency_round_unit;
|
||||||
|
|
||||||
/**
|
|
||||||
* What is the largest amount we transfer before triggering
|
|
||||||
* an AML check?
|
|
||||||
*/
|
|
||||||
static struct TALER_Amount aml_threshold;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* What is the base URL of this exchange? Used in the
|
* What is the base URL of this exchange? Used in the
|
||||||
* wire transfer subjects so that merchants and governments
|
* wire transfer subjects so that merchants and governments
|
||||||
@ -300,20 +294,11 @@ parse_aggregator_config (void)
|
|||||||
"taler",
|
"taler",
|
||||||
"CURRENCY_ROUND_UNIT",
|
"CURRENCY_ROUND_UNIT",
|
||||||
¤cy_round_unit)) ||
|
¤cy_round_unit)) ||
|
||||||
(TALER_amount_is_zero (¤cy_round_unit)) )
|
( (0 != currency_round_unit.fraction) &&
|
||||||
|
(0 != currency_round_unit.value) ) )
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Need non-zero amount in section `TALER' under `CURRENCY_ROUND_UNIT'\n");
|
"Need non-zero value in section `TALER' under `CURRENCY_ROUND_UNIT'\n");
|
||||||
return GNUNET_SYSERR;
|
|
||||||
}
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_config_get_amount (cfg,
|
|
||||||
"taler",
|
|
||||||
"AML_THRESHOLD",
|
|
||||||
&aml_threshold))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Need amount in section `TALER' under `AML_THRESHOLD'\n");
|
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,22 +481,16 @@ return_relevant_amounts (void *cls,
|
|||||||
static bool
|
static bool
|
||||||
kyc_satisfied (struct AggregationUnit *au_active)
|
kyc_satisfied (struct AggregationUnit *au_active)
|
||||||
{
|
{
|
||||||
char *requirement;
|
const char *requirement;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
requirement = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
|
TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
|
||||||
&au_active->h_payto,
|
&au_active->h_payto,
|
||||||
db_plugin->select_satisfied_kyc_processes,
|
db_plugin->select_satisfied_kyc_processes,
|
||||||
db_plugin->cls,
|
db_plugin->cls,
|
||||||
&return_relevant_amounts,
|
&return_relevant_amounts,
|
||||||
(void *) au_active,
|
(void *) au_active);
|
||||||
&requirement);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (NULL == requirement)
|
if (NULL == requirement)
|
||||||
return true;
|
return true;
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
@ -535,114 +514,6 @@ kyc_satisfied (struct AggregationUnit *au_active)
|
|||||||
"Legitimization process %llu started\n",
|
"Legitimization process %llu started\n",
|
||||||
(unsigned long long) au_active->requirement_row);
|
(unsigned long long) au_active->requirement_row);
|
||||||
}
|
}
|
||||||
GNUNET_free (requirement);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called on each @a amount that was found to
|
|
||||||
* be relevant for an AML check.
|
|
||||||
*
|
|
||||||
* @param cls closure with the `struct TALER_Amount *` where we store the sum
|
|
||||||
* @param amount encountered transaction amount
|
|
||||||
* @param date when was the amount encountered
|
|
||||||
* @return #GNUNET_OK to continue to iterate,
|
|
||||||
* #GNUNET_NO to abort iteration
|
|
||||||
* #GNUNET_SYSERR on internal error (also abort itaration)
|
|
||||||
*/
|
|
||||||
static enum GNUNET_GenericReturnValue
|
|
||||||
sum_for_aml (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_Amount *amount,
|
|
||||||
struct GNUNET_TIME_Absolute date)
|
|
||||||
{
|
|
||||||
struct TALER_Amount *sum = cls;
|
|
||||||
|
|
||||||
(void) date;
|
|
||||||
if (0 >
|
|
||||||
TALER_amount_add (sum,
|
|
||||||
sum,
|
|
||||||
amount))
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
return GNUNET_SYSERR;
|
|
||||||
}
|
|
||||||
return GNUNET_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if AML is required for a transfer to @a h_payto.
|
|
||||||
*
|
|
||||||
* @param[in,out] au_active aggregation unit to check for
|
|
||||||
* @return true if AML checks are satisfied
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
aml_satisfied (struct AggregationUnit *au_active)
|
|
||||||
{
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
struct TALER_Amount total;
|
|
||||||
struct TALER_Amount threshold;
|
|
||||||
enum TALER_AmlDecisionState decision;
|
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
|
||||||
|
|
||||||
total = au_active->final_amount;
|
|
||||||
qs = db_plugin->select_aggregation_amounts_for_kyc_check (
|
|
||||||
db_plugin->cls,
|
|
||||||
&au_active->h_payto,
|
|
||||||
GNUNET_TIME_absolute_subtract (GNUNET_TIME_absolute_get (),
|
|
||||||
GNUNET_TIME_UNIT_MONTHS),
|
|
||||||
&sum_for_aml,
|
|
||||||
&total);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
qs = db_plugin->select_aml_threshold (db_plugin->cls,
|
|
||||||
&au_active->h_payto,
|
|
||||||
&decision,
|
|
||||||
&kyc,
|
|
||||||
&threshold);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
{
|
|
||||||
threshold = aml_threshold; /* use default */
|
|
||||||
decision = TALER_AML_NORMAL;
|
|
||||||
}
|
|
||||||
switch (decision)
|
|
||||||
{
|
|
||||||
case TALER_AML_NORMAL:
|
|
||||||
if (0 >= TALER_amount_cmp (&total,
|
|
||||||
&threshold))
|
|
||||||
{
|
|
||||||
/* total <= threshold, do nothing */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
qs = db_plugin->trigger_aml_process (db_plugin->cls,
|
|
||||||
&au_active->h_payto,
|
|
||||||
&total);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case TALER_AML_PENDING:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"AML already pending, doing nothing\n");
|
|
||||||
return false;
|
|
||||||
case TALER_AML_FROZEN:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Account frozen, doing nothing\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
GNUNET_assert (0);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -772,8 +643,7 @@ do_aggregate (struct AggregationUnit *au)
|
|||||||
TALER_amount_round_down (&au->final_amount,
|
TALER_amount_round_down (&au->final_amount,
|
||||||
¤cy_round_unit)) ||
|
¤cy_round_unit)) ||
|
||||||
(TALER_amount_is_zero (&au->final_amount)) ||
|
(TALER_amount_is_zero (&au->final_amount)) ||
|
||||||
(! kyc_satisfied (au)) ||
|
(! kyc_satisfied (au)) )
|
||||||
(! aml_satisfied (au)) )
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Not ready for wire transfer (%d/%s)\n",
|
"Not ready for wire transfer (%d/%s)\n",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014-2023 Taler Systems SA
|
Copyright (C) 2014-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -30,11 +30,9 @@
|
|||||||
#include "taler_kyclogic_lib.h"
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_templating_lib.h"
|
#include "taler_templating_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_aml-decision.h"
|
|
||||||
#include "taler-exchange-httpd_auditors.h"
|
#include "taler-exchange-httpd_auditors.h"
|
||||||
#include "taler-exchange-httpd_batch-deposit.h"
|
#include "taler-exchange-httpd_batch-deposit.h"
|
||||||
#include "taler-exchange-httpd_batch-withdraw.h"
|
#include "taler-exchange-httpd_batch-withdraw.h"
|
||||||
#include "taler-exchange-httpd_config.h"
|
|
||||||
#include "taler-exchange-httpd_contract.h"
|
#include "taler-exchange-httpd_contract.h"
|
||||||
#include "taler-exchange-httpd_csr.h"
|
#include "taler-exchange-httpd_csr.h"
|
||||||
#include "taler-exchange-httpd_deposit.h"
|
#include "taler-exchange-httpd_deposit.h"
|
||||||
@ -80,11 +78,6 @@
|
|||||||
*/
|
*/
|
||||||
#define UNIX_BACKLOG 50
|
#define UNIX_BACKLOG 50
|
||||||
|
|
||||||
/**
|
|
||||||
* How often will we try to connect to the database before giving up?
|
|
||||||
*/
|
|
||||||
#define MAX_DB_RETRIES 5
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Above what request latency do we start to log?
|
* Above what request latency do we start to log?
|
||||||
*/
|
*/
|
||||||
@ -138,11 +131,6 @@ struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
|
|||||||
*/
|
*/
|
||||||
struct TALER_MasterPublicKeyP TEH_master_public_key;
|
struct TALER_MasterPublicKeyP TEH_master_public_key;
|
||||||
|
|
||||||
/**
|
|
||||||
* Key used to encrypt KYC attribute data in our database.
|
|
||||||
*/
|
|
||||||
struct TALER_AttributeEncryptionKeyP TEH_attribute_key;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our DB plugin. (global)
|
* Our DB plugin. (global)
|
||||||
*/
|
*/
|
||||||
@ -153,13 +141,6 @@ struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
|||||||
*/
|
*/
|
||||||
char *TEH_currency;
|
char *TEH_currency;
|
||||||
|
|
||||||
/**
|
|
||||||
* What is the largest amount we allow a peer to
|
|
||||||
* merge into a reserve before always triggering
|
|
||||||
* an AML check?
|
|
||||||
*/
|
|
||||||
struct TALER_Amount TEH_aml_threshold;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our base URL.
|
* Our base URL.
|
||||||
*/
|
*/
|
||||||
@ -341,227 +322,6 @@ handle_post_coins (struct TEH_RequestContext *rc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature of functions that handle operations
|
|
||||||
* authorized by AML officers.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param officer_pub the public key of the AML officer
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
typedef MHD_RESULT
|
|
||||||
(*AmlOpPostHandler)(struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a "/aml/$OFFICER_PUB/$OP" POST request. Parses the "officer_pub"
|
|
||||||
* EdDSA key of the officer and demultiplexes based on $OP.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @param args array of additional options
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
static MHD_RESULT
|
|
||||||
handle_post_aml (struct TEH_RequestContext *rc,
|
|
||||||
const json_t *root,
|
|
||||||
const char *const args[2])
|
|
||||||
{
|
|
||||||
struct TALER_AmlOfficerPublicKeyP officer_pub;
|
|
||||||
static const struct
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Name of the operation (args[1])
|
|
||||||
*/
|
|
||||||
const char *op;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to call to perform the operation.
|
|
||||||
*/
|
|
||||||
AmlOpPostHandler handler;
|
|
||||||
|
|
||||||
} h[] = {
|
|
||||||
{
|
|
||||||
.op = "decision",
|
|
||||||
.handler = &TEH_handler_post_aml_decision
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.op = NULL,
|
|
||||||
.handler = NULL
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
|
||||||
strlen (args[0]),
|
|
||||||
&officer_pub,
|
|
||||||
sizeof (officer_pub)))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
|
|
||||||
args[0]);
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; NULL != h[i].op; i++)
|
|
||||||
if (0 == strcmp (h[i].op,
|
|
||||||
args[1]))
|
|
||||||
return h[i].handler (rc,
|
|
||||||
&officer_pub,
|
|
||||||
root);
|
|
||||||
return r404 (rc->connection,
|
|
||||||
args[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature of functions that handle operations
|
|
||||||
* authorized by AML officers.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param officer_pub the public key of the AML officer
|
|
||||||
* @param args remaining arguments
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
typedef MHD_RESULT
|
|
||||||
(*AmlOpGetHandler)(struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const char *const args[]);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a "/aml/$OFFICER_PUB/$OP" GET request. Parses the "officer_pub"
|
|
||||||
* EdDSA key of the officer, checks the authentication signature, and
|
|
||||||
* demultiplexes based on $OP.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param args array of additional options
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
static MHD_RESULT
|
|
||||||
handle_get_aml (struct TEH_RequestContext *rc,
|
|
||||||
const char *const args[])
|
|
||||||
{
|
|
||||||
struct TALER_AmlOfficerPublicKeyP officer_pub;
|
|
||||||
static const struct
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Name of the operation (args[1])
|
|
||||||
*/
|
|
||||||
const char *op;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to call to perform the operation.
|
|
||||||
*/
|
|
||||||
AmlOpGetHandler handler;
|
|
||||||
|
|
||||||
} h[] = {
|
|
||||||
{
|
|
||||||
.op = "decisions",
|
|
||||||
.handler = &TEH_handler_aml_decisions_get
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.op = "decision",
|
|
||||||
.handler = &TEH_handler_aml_decision_get
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.op = NULL,
|
|
||||||
.handler = NULL
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (NULL == args[0])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
|
|
||||||
"argument missing");
|
|
||||||
}
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
|
||||||
strlen (args[0]),
|
|
||||||
&officer_pub,
|
|
||||||
sizeof (officer_pub)))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
|
|
||||||
args[0]);
|
|
||||||
}
|
|
||||||
if (NULL == args[1])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_NOT_FOUND,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
|
|
||||||
"AML GET operations must specify an operation identifier");
|
|
||||||
}
|
|
||||||
{
|
|
||||||
const char *sig_hdr;
|
|
||||||
struct TALER_AmlOfficerSignatureP officer_sig;
|
|
||||||
|
|
||||||
sig_hdr = MHD_lookup_connection_value (rc->connection,
|
|
||||||
MHD_HEADER_KIND,
|
|
||||||
TALER_AML_OFFICER_SIGNATURE_HEADER);
|
|
||||||
if ( (NULL == sig_hdr) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (sig_hdr,
|
|
||||||
strlen (sig_hdr),
|
|
||||||
&officer_sig,
|
|
||||||
sizeof (officer_sig))) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
TALER_officer_aml_query_verify (&officer_pub,
|
|
||||||
&officer_sig)) )
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID,
|
|
||||||
sig_hdr);
|
|
||||||
}
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = TEH_plugin->test_aml_officer (TEH_plugin->cls,
|
|
||||||
&officer_pub);
|
|
||||||
switch (qs)
|
|
||||||
{
|
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
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,
|
|
||||||
NULL);
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; NULL != h[i].op; i++)
|
|
||||||
if (0 == strcmp (h[i].op,
|
|
||||||
args[1]))
|
|
||||||
return h[i].handler (rc,
|
|
||||||
&officer_pub,
|
|
||||||
&args[2]);
|
|
||||||
return r404 (rc->connection,
|
|
||||||
args[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature of functions that handle operations on reserves.
|
* Signature of functions that handle operations on reserves.
|
||||||
*
|
*
|
||||||
@ -1130,8 +890,6 @@ handle_post_management (struct TEH_RequestContext *rc,
|
|||||||
&exchange_pub,
|
&exchange_pub,
|
||||||
root);
|
root);
|
||||||
}
|
}
|
||||||
/* FIXME-STYLE: all of the following can likely be nicely combined
|
|
||||||
into an array-based dispatcher to deduplicate the logic... */
|
|
||||||
if (0 == strcmp (args[0],
|
if (0 == strcmp (args[0],
|
||||||
"keys"))
|
"keys"))
|
||||||
{
|
{
|
||||||
@ -1209,30 +967,6 @@ handle_post_management (struct TEH_RequestContext *rc,
|
|||||||
return TEH_handler_management_post_drain (rc->connection,
|
return TEH_handler_management_post_drain (rc->connection,
|
||||||
root);
|
root);
|
||||||
}
|
}
|
||||||
if (0 == strcmp (args[0],
|
|
||||||
"aml-officers"))
|
|
||||||
{
|
|
||||||
if (NULL != args[1])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return r404 (rc->connection,
|
|
||||||
"/management/aml-officers/*");
|
|
||||||
}
|
|
||||||
return TEH_handler_management_aml_officers (rc->connection,
|
|
||||||
root);
|
|
||||||
}
|
|
||||||
if (0 == strcmp (args[0],
|
|
||||||
"partners"))
|
|
||||||
{
|
|
||||||
if (NULL != args[1])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return r404 (rc->connection,
|
|
||||||
"/management/partners/*");
|
|
||||||
}
|
|
||||||
return TEH_handler_management_partners (rc->connection,
|
|
||||||
root);
|
|
||||||
}
|
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return r404 (rc->connection,
|
return r404 (rc->connection,
|
||||||
"/management/*");
|
"/management/*");
|
||||||
@ -1377,12 +1111,6 @@ handle_mhd_request (void *cls,
|
|||||||
.method = MHD_HTTP_METHOD_GET,
|
.method = MHD_HTTP_METHOD_GET,
|
||||||
.handler.get = &handler_seed
|
.handler.get = &handler_seed
|
||||||
},
|
},
|
||||||
/* Configuration */
|
|
||||||
{
|
|
||||||
.url = "config",
|
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
|
||||||
.handler.get = &TEH_handler_config
|
|
||||||
},
|
|
||||||
/* Performance metrics */
|
/* Performance metrics */
|
||||||
{
|
{
|
||||||
.url = "metrics",
|
.url = "metrics",
|
||||||
@ -1561,22 +1289,6 @@ handle_mhd_request (void *cls,
|
|||||||
.nargs = 4,
|
.nargs = 4,
|
||||||
.nargs_is_upper_bound = true
|
.nargs_is_upper_bound = true
|
||||||
},
|
},
|
||||||
/* AML endpoints */
|
|
||||||
{
|
|
||||||
.url = "aml",
|
|
||||||
.method = MHD_HTTP_METHOD_GET,
|
|
||||||
.handler.get = &handle_get_aml,
|
|
||||||
.nargs = 4,
|
|
||||||
.nargs_is_upper_bound = true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.url = "aml",
|
|
||||||
.method = MHD_HTTP_METHOD_POST,
|
|
||||||
.handler.post = &handle_post_aml,
|
|
||||||
.nargs = 2
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/* mark end of list */
|
/* mark end of list */
|
||||||
{
|
{
|
||||||
.url = NULL
|
.url = NULL
|
||||||
@ -1639,13 +1351,10 @@ handle_mhd_request (void *cls,
|
|||||||
return MHD_NO;
|
return MHD_NO;
|
||||||
}
|
}
|
||||||
if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
|
if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_request_too_large (connection);
|
return TALER_MHD_reply_request_too_large (connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GNUNET_async_scope_enter (&rc->async_scope_id,
|
GNUNET_async_scope_enter (&rc->async_scope_id,
|
||||||
&old_scope);
|
&old_scope);
|
||||||
@ -1879,23 +1588,6 @@ exchange_serve_process_config (void)
|
|||||||
"CURRENCY");
|
"CURRENCY");
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_config_get_amount (TEH_cfg,
|
|
||||||
"taler",
|
|
||||||
"AML_THRESHOLD",
|
|
||||||
&TEH_aml_threshold))
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Need amount in section `TALER' 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");
|
|
||||||
return GNUNET_SYSERR;
|
|
||||||
}
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
|
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
|
||||||
"exchange",
|
"exchange",
|
||||||
@ -1944,46 +1636,17 @@ exchange_serve_process_config (void)
|
|||||||
GNUNET_free (master_public_key_str);
|
GNUNET_free (master_public_key_str);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Launching exchange with public key `%s'...\n",
|
|
||||||
master_public_key_str);
|
|
||||||
GNUNET_free (master_public_key_str);
|
GNUNET_free (master_public_key_str);
|
||||||
}
|
}
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Launching exchange with public key `%s'...\n",
|
||||||
|
GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
|
||||||
|
|
||||||
{
|
if (NULL ==
|
||||||
char *attr_enc_key_str;
|
(TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg)))
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
|
|
||||||
"exchange",
|
|
||||||
"ATTRIBUTE_ENCRYPTION_KEY",
|
|
||||||
&attr_enc_key_str))
|
|
||||||
{
|
|
||||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"exchange",
|
|
||||||
"ATTRIBUTE_ENCRYPTION_KEY");
|
|
||||||
return GNUNET_SYSERR;
|
|
||||||
}
|
|
||||||
GNUNET_CRYPTO_hash (attr_enc_key_str,
|
|
||||||
strlen (attr_enc_key_str),
|
|
||||||
&TEH_attribute_key.hash);
|
|
||||||
GNUNET_free (attr_enc_key_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i<MAX_DB_RETRIES; i++)
|
|
||||||
{
|
|
||||||
TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg);
|
|
||||||
if (NULL != TEH_plugin)
|
|
||||||
break;
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
|
||||||
"Failed to connect to DB, will try again %u times\n",
|
|
||||||
MAX_DB_RETRIES - i);
|
|
||||||
sleep (1);
|
|
||||||
}
|
|
||||||
if (NULL == TEH_plugin)
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Failed to initialize DB subsystem. Giving up.\n");
|
"Failed to initialize DB subsystem\n");
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
|
@ -82,11 +82,6 @@ extern bool TEH_suicide;
|
|||||||
*/
|
*/
|
||||||
extern struct TALER_MasterPublicKeyP TEH_master_public_key;
|
extern struct TALER_MasterPublicKeyP TEH_master_public_key;
|
||||||
|
|
||||||
/**
|
|
||||||
* Key used to encrypt KYC attribute data in our database.
|
|
||||||
*/
|
|
||||||
extern struct TALER_AttributeEncryptionKeyP TEH_attribute_key;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our DB plugin.
|
* Our DB plugin.
|
||||||
*/
|
*/
|
||||||
@ -97,13 +92,6 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
|
|||||||
*/
|
*/
|
||||||
extern char *TEH_currency;
|
extern char *TEH_currency;
|
||||||
|
|
||||||
/**
|
|
||||||
* What is the largest amount we allow a peer to
|
|
||||||
* merge into a reserve before always triggering
|
|
||||||
* an AML check?
|
|
||||||
*/
|
|
||||||
extern struct TALER_Amount TEH_aml_threshold;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our (externally visible) base URL.
|
* Our (externally visible) base URL.
|
||||||
*/
|
*/
|
||||||
@ -225,7 +213,7 @@ struct TEH_RequestHandler
|
|||||||
*
|
*
|
||||||
* @param rc context for the request
|
* @param rc context for the request
|
||||||
* @param json uploaded JSON data
|
* @param json uploaded JSON data
|
||||||
* @param args array of arguments, needs to be of length @e nargs
|
* @param args array of arguments, needs to be of length @e args_expected
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
MHD_RESULT
|
MHD_RESULT
|
||||||
@ -237,7 +225,7 @@ struct TEH_RequestHandler
|
|||||||
* Function to call to handle DELETE requests.
|
* Function to call to handle DELETE requests.
|
||||||
*
|
*
|
||||||
* @param rc context for the request
|
* @param rc context for the request
|
||||||
* @param args array of arguments, needs to be of length @e nargs
|
* @param args array of arguments, needs to be of length @e args_expected
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
MHD_RESULT
|
MHD_RESULT
|
||||||
@ -246,6 +234,17 @@ struct TEH_RequestHandler
|
|||||||
|
|
||||||
} handler;
|
} handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of arguments this handler expects in the @a args array.
|
||||||
|
*/
|
||||||
|
unsigned int nargs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the number of arguments given in @e nargs only an upper bound,
|
||||||
|
* and calling with fewer arguments could be OK?
|
||||||
|
*/
|
||||||
|
bool nargs_is_upper_bound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mime type to use in reply (hint, can be NULL).
|
* Mime type to use in reply (hint, can be NULL).
|
||||||
*/
|
*/
|
||||||
@ -265,17 +264,6 @@ struct TEH_RequestHandler
|
|||||||
* Default response code. 0 for none provided.
|
* Default response code. 0 for none provided.
|
||||||
*/
|
*/
|
||||||
unsigned int response_code;
|
unsigned int response_code;
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of arguments this handler expects in the @a args array.
|
|
||||||
*/
|
|
||||||
unsigned int nargs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the number of arguments given in @e nargs only an upper bound,
|
|
||||||
* and calling with fewer arguments could be OK?
|
|
||||||
*/
|
|
||||||
bool nargs_is_upper_bound;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,395 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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
|
|
||||||
published by the Free Software Foundation; either version 3,
|
|
||||||
or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
||||||
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General
|
|
||||||
Public License along with TALER; see the file COPYING. If not,
|
|
||||||
see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_age-withdraw.c
|
|
||||||
* @brief Handle /reserves/$RESERVE_PUB/age-withdraw requests
|
|
||||||
* @author Özgür Kesim
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_kyclogic_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler-exchange-httpd_age-withdraw.h"
|
|
||||||
#include "taler-exchange-httpd_responses.h"
|
|
||||||
#include "taler-exchange-httpd_keys.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a response to a "age-withdraw" request.
|
|
||||||
*
|
|
||||||
* @param connection the connection to send the response to
|
|
||||||
* @param ach value the client committed to
|
|
||||||
* @param noreveal_index which index will the client not have to reveal
|
|
||||||
* @return a MHD status code
|
|
||||||
*/
|
|
||||||
static MHD_RESULT
|
|
||||||
reply_age_withdraw_success (
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const struct TALER_AgeWithdrawCommitmentHashP *ach,
|
|
||||||
uint32_t noreveal_index)
|
|
||||||
{
|
|
||||||
struct TALER_ExchangePublicKeyP pub;
|
|
||||||
struct TALER_ExchangeSignatureP sig;
|
|
||||||
enum TALER_ErrorCode ec =
|
|
||||||
TALER_exchange_online_age_withdraw_confirmation_sign (
|
|
||||||
&TEH_keys_exchange_sign_,
|
|
||||||
ach,
|
|
||||||
noreveal_index,
|
|
||||||
&pub,
|
|
||||||
&sig);
|
|
||||||
|
|
||||||
if (TALER_EC_NONE != ec)
|
|
||||||
return TALER_MHD_reply_with_ec (connection,
|
|
||||||
ec,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
GNUNET_JSON_pack_uint64 ("noreveal_index",
|
|
||||||
noreveal_index),
|
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_sig",
|
|
||||||
&sig),
|
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
|
||||||
&pub));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context for #age_withdraw_transaction.
|
|
||||||
*/
|
|
||||||
struct AgeWithdrawContext
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* KYC status for the operation.
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash of the wire source URL, needed when kyc is needed.
|
|
||||||
*/
|
|
||||||
struct TALER_PaytoHashP h_payto;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The data from the age-withdraw request
|
|
||||||
*/
|
|
||||||
struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current time for the DB transaction.
|
|
||||||
*/
|
|
||||||
struct GNUNET_TIME_Timestamp now;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called to iterate over KYC-relevant
|
|
||||||
* transaction amounts for a particular time range.
|
|
||||||
* Called within a database transaction, so must
|
|
||||||
* not start a new one.
|
|
||||||
*
|
|
||||||
* @param cls closure, identifies the event type and
|
|
||||||
* account to iterate over events for
|
|
||||||
* @param limit maximum time-range for which events
|
|
||||||
* should be fetched (timestamp in the past)
|
|
||||||
* @param cb function to call on each event found,
|
|
||||||
* events must be returned in reverse chronological
|
|
||||||
* order
|
|
||||||
* @param cb_cls closure for @a cb
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
age_withdraw_amount_cb (void *cls,
|
|
||||||
struct GNUNET_TIME_Absolute limit,
|
|
||||||
TALER_EXCHANGEDB_KycAmountCallback cb,
|
|
||||||
void *cb_cls)
|
|
||||||
{
|
|
||||||
struct AgeWithdrawContext *awc = cls;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Signaling amount %s for KYC check during age-withdrawal\n",
|
|
||||||
TALER_amount2s (&awc->commitment.amount_with_fee));
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
cb (cb_cls,
|
|
||||||
&awc->commitment.amount_with_fee,
|
|
||||||
awc->now.abs_time))
|
|
||||||
return;
|
|
||||||
qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (TEH_plugin->cls,
|
|
||||||
&awc->h_payto,
|
|
||||||
limit,
|
|
||||||
cb,
|
|
||||||
cb_cls);
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Got %d additional transactions for this age-withdrawal and limit %llu\n",
|
|
||||||
qs,
|
|
||||||
(unsigned long long) limit.abs_value_us);
|
|
||||||
GNUNET_break (qs >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function implementing age withdraw transaction. Runs the
|
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
|
||||||
* logic MUST NOT queue a MHD response. IF it returns an hard error,
|
|
||||||
* the transaction logic MUST queue a MHD response and set @a mhd_ret.
|
|
||||||
* IF it returns the soft error code, the function MAY be called again
|
|
||||||
* to retry and MUST not queue a MHD response.
|
|
||||||
*
|
|
||||||
* Note that "awc->commitment.sig" is set before entering this function as we
|
|
||||||
* signed before entering the transaction.
|
|
||||||
*
|
|
||||||
* @param cls a `struct AgeWithdrawContext *`
|
|
||||||
* @param connection MHD request which triggered the transaction
|
|
||||||
* @param[out] mhd_ret set to MHD response status for @a connection,
|
|
||||||
* if transaction failed (!)
|
|
||||||
* @return transaction status
|
|
||||||
*/
|
|
||||||
static enum GNUNET_DB_QueryStatus
|
|
||||||
age_withdraw_transaction (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
MHD_RESULT *mhd_ret)
|
|
||||||
{
|
|
||||||
struct AgeWithdrawContext *awc = cls;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
bool found = false;
|
|
||||||
bool balance_ok = false;
|
|
||||||
uint64_t ruuid;
|
|
||||||
|
|
||||||
awc->now = GNUNET_TIME_timestamp_get ();
|
|
||||||
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
|
||||||
&awc->commitment.reserve_pub,
|
|
||||||
&awc->h_payto);
|
|
||||||
if (qs < 0)
|
|
||||||
return qs;
|
|
||||||
|
|
||||||
/* If no results, reserve was created by merge,
|
|
||||||
in which case no KYC check is required as the
|
|
||||||
merge already did that. */
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
|
||||||
{
|
|
||||||
char *kyc_required;
|
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_AGE_WITHDRAW,
|
|
||||||
&awc->h_payto,
|
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
|
||||||
TEH_plugin->cls,
|
|
||||||
&age_withdraw_amount_cb,
|
|
||||||
awc,
|
|
||||||
&kyc_required);
|
|
||||||
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL != kyc_required)
|
|
||||||
{
|
|
||||||
/* insert KYC requirement into DB! */
|
|
||||||
awc->kyc.ok = false;
|
|
||||||
return TEH_plugin->insert_kyc_requirement_for_account (
|
|
||||||
TEH_plugin->cls,
|
|
||||||
kyc_required,
|
|
||||||
&awc->h_payto,
|
|
||||||
&awc->kyc.requirement_row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
awc->kyc.ok = true;
|
|
||||||
qs = TEH_plugin->do_age_withdraw (TEH_plugin->cls,
|
|
||||||
&awc->commitment,
|
|
||||||
&found,
|
|
||||||
&balance_ok,
|
|
||||||
&ruuid);
|
|
||||||
if (0 > qs)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"do_age_withdraw");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
else if (! found)
|
|
||||||
{
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_NOT_FOUND,
|
|
||||||
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
|
|
||||||
NULL);
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
else if (! balance_ok)
|
|
||||||
{
|
|
||||||
TEH_plugin->rollback (TEH_plugin->cls);
|
|
||||||
*mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
|
|
||||||
connection,
|
|
||||||
TALER_EC_EXCHANGE_AGE_WITHDRAW_INSUFFICIENT_FUNDS,
|
|
||||||
&awc->commitment.amount_with_fee,
|
|
||||||
&awc->commitment.reserve_pub);
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
|
||||||
TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW]++;
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the @a rc is replayed and we already have an
|
|
||||||
* answer. If so, replay the existing answer and return the
|
|
||||||
* HTTP response.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param[in,out] awc parsed request data
|
|
||||||
* @param[out] mret HTTP status, set if we return true
|
|
||||||
* @return true if the request is idempotent with an existing request
|
|
||||||
* false if we did not find the request in the DB and did not set @a mret
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
request_is_idempotent (struct TEH_RequestContext *rc,
|
|
||||||
struct AgeWithdrawContext *awc,
|
|
||||||
MHD_RESULT *mret)
|
|
||||||
{
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
struct TALER_EXCHANGEDB_AgeWithdrawCommitment commitment;
|
|
||||||
|
|
||||||
qs = TEH_plugin->get_age_withdraw_info (TEH_plugin->cls,
|
|
||||||
&awc->commitment.reserve_pub,
|
|
||||||
&awc->commitment.h_commitment,
|
|
||||||
&commitment);
|
|
||||||
if (0 > qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mret = TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"get_age_withdraw_info");
|
|
||||||
return true; /* well, kind-of */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* generate idempotent reply */
|
|
||||||
TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_AGE_WITHDRAW]++;
|
|
||||||
*mret = reply_age_withdraw_success (rc->connection,
|
|
||||||
&commitment.h_commitment,
|
|
||||||
commitment.noreveal_index);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
|
||||||
const json_t *root)
|
|
||||||
{
|
|
||||||
MHD_RESULT mhd_ret;
|
|
||||||
struct AgeWithdrawContext awc = {0};
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
|
||||||
&awc.commitment.reserve_sig),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("h_commitment",
|
|
||||||
&awc.commitment.h_commitment),
|
|
||||||
TALER_JSON_spec_amount ("amount",
|
|
||||||
TEH_currency,
|
|
||||||
&awc.commitment.amount_with_fee),
|
|
||||||
GNUNET_JSON_spec_uint16 ("max_age",
|
|
||||||
&awc.commitment.max_age),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
|
|
||||||
awc.commitment.reserve_pub = *reserve_pub;
|
|
||||||
|
|
||||||
|
|
||||||
/* Parse the JSON body */
|
|
||||||
{
|
|
||||||
enum GNUNET_GenericReturnValue res;
|
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (rc->connection,
|
|
||||||
root,
|
|
||||||
spec);
|
|
||||||
if (GNUNET_OK != res)
|
|
||||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
/* If request was made before successfully, return the previous answer */
|
|
||||||
if (request_is_idempotent (rc,
|
|
||||||
&awc,
|
|
||||||
&mhd_ret))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Verify the signature of the request body with the reserve key */
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_wallet_age_withdraw_verify (&awc.commitment.h_commitment,
|
|
||||||
&awc.commitment.amount_with_fee,
|
|
||||||
awc.commitment.max_age,
|
|
||||||
&awc.commitment.reserve_pub,
|
|
||||||
&awc.commitment.reserve_sig))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
mhd_ret = TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_FORBIDDEN,
|
|
||||||
TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
|
|
||||||
NULL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run the transaction */
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TEH_DB_run_transaction (rc->connection,
|
|
||||||
"run age withdraw",
|
|
||||||
TEH_MT_REQUEST_AGE_WITHDRAW,
|
|
||||||
&mhd_ret,
|
|
||||||
&age_withdraw_transaction,
|
|
||||||
&awc))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Clean up and send back final response */
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
|
|
||||||
if (! awc.kyc.ok)
|
|
||||||
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
|
||||||
&awc.h_payto,
|
|
||||||
&awc.kyc);
|
|
||||||
|
|
||||||
return reply_age_withdraw_success (rc->connection,
|
|
||||||
&awc.commitment.h_commitment,
|
|
||||||
awc.commitment.noreveal_index);
|
|
||||||
} while(0);
|
|
||||||
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return mhd_ret;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_age-withdraw.c */
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_age-withdraw.h
|
|
||||||
* @brief Handle /reserve/$RESERVE_PUB/age-withdraw requests
|
|
||||||
* @author Özgür Kesim
|
|
||||||
*/
|
|
||||||
#ifndef TALER_EXCHANGE_HTTPD_AGE_WITHDRAW_H
|
|
||||||
#define TALER_EXCHANGE_HTTPD_AGE_WITHDRAW_H
|
|
||||||
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a "/reserves/$RESERVE_PUB/age-withdraw" request.
|
|
||||||
*
|
|
||||||
* Parses the batch of commitments to withdraw age restricted coins, and checks
|
|
||||||
* that the signature "reserve_sig" makes this a valid withdrawal request from
|
|
||||||
* the specified reserve. If the request is valid, the response contains a
|
|
||||||
* noreveal_index which the client has to use for the subsequent call to
|
|
||||||
* /age-withdraw/$ACH/reveal.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @param reserve_pub public key of the reserve
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_age_withdraw (struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_age-withdraw_reveal.h
|
|
||||||
* @brief Handle /age-withdraw/$ACH/reveal requests
|
|
||||||
* @author Özgür Kesim
|
|
||||||
*/
|
|
||||||
#ifndef TALER_EXCHANGE_HTTPD_AGE_WITHDRAW_H
|
|
||||||
#define TALER_EXCHANGE_HTTPD_AGE_WITHDRAW_H
|
|
||||||
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a "/age-withdraw/$ACH/reveal" request.
|
|
||||||
*
|
|
||||||
* The client got a noreveal_index in response to a previous request
|
|
||||||
* /reserve/$RESERVE_PUB/age-withdraw. It now has to reveal all n*(kappa-1)
|
|
||||||
* coin's private keys (except for the noreveal_index), from which all other
|
|
||||||
* coin-relevant data (blinding, age restriction, nonce) is derived from.
|
|
||||||
*
|
|
||||||
* The exchange computes those values, ensures that the maximum age is
|
|
||||||
* correctly applied, calculates the hash of the blinded envelopes, and -
|
|
||||||
* together with the non-disclosed blinded envelopes - compares the hash of
|
|
||||||
* those against the original commitment $ACH.
|
|
||||||
*
|
|
||||||
* If all those checks and the used denominations turn out to be correct, the
|
|
||||||
* exchange signs all blinded envelopes with their appropriate denomination
|
|
||||||
* keys.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @param ach commitment to the age restricted coints from the age-withdraw request
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_age_withdraw_reveal (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AgeWithdrawCommitmentHashP *ach,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_aml-decision-get.c
|
|
||||||
* @brief Return summary information about AML decision
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler_signatures.h"
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
#include "taler_exchangedb_plugin.h"
|
|
||||||
#include "taler-exchange-httpd_aml-decision.h"
|
|
||||||
#include "taler-exchange-httpd_metrics.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of records we return per request.
|
|
||||||
*/
|
|
||||||
#define MAX_RECORDS 1024
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback with KYC attributes about a particular user.
|
|
||||||
*
|
|
||||||
* @param[in,out] cls closure with a `json_t *` array to update
|
|
||||||
* @param h_payto account for which the attribute data is stored
|
|
||||||
* @param provider_section provider that must be checked
|
|
||||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
|
||||||
* digits can be 0 if exact day, month or year are unknown
|
|
||||||
* @param collection_time when was the data collected
|
|
||||||
* @param expiration_time when does the data expire
|
|
||||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
|
||||||
* @param enc_attributes encrypted attribute data
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
kyc_attribute_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
|
||||||
const char *provider_section,
|
|
||||||
const char *birthdate,
|
|
||||||
struct GNUNET_TIME_Timestamp collection_time,
|
|
||||||
struct GNUNET_TIME_Timestamp expiration_time,
|
|
||||||
size_t enc_attributes_size,
|
|
||||||
const void *enc_attributes)
|
|
||||||
{
|
|
||||||
json_t *kyc_attributes = cls;
|
|
||||||
json_t *attributes;
|
|
||||||
|
|
||||||
attributes = TALER_CRYPTO_kyc_attributes_decrypt (&TEH_attribute_key,
|
|
||||||
enc_attributes,
|
|
||||||
enc_attributes_size);
|
|
||||||
GNUNET_break (NULL != attributes);
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_array_append (
|
|
||||||
kyc_attributes,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_string ("provider_section",
|
|
||||||
provider_section),
|
|
||||||
GNUNET_JSON_pack_timestamp ("collection_time",
|
|
||||||
collection_time),
|
|
||||||
GNUNET_JSON_pack_timestamp ("expiration_time",
|
|
||||||
expiration_time),
|
|
||||||
GNUNET_JSON_pack_allow_null (
|
|
||||||
GNUNET_JSON_pack_object_steal ("attributes",
|
|
||||||
attributes))
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return historic AML decision(s).
|
|
||||||
*
|
|
||||||
* @param[in,out] cls closure with a `json_t *` array to update
|
|
||||||
* @param new_threshold new monthly threshold that would trigger an AML check
|
|
||||||
* @param new_state AML decision status
|
|
||||||
* @param decision_time when was the decision made
|
|
||||||
* @param justification human-readable text justifying the decision
|
|
||||||
* @param decider_pub public key of the staff member
|
|
||||||
* @param decider_sig signature of the staff member
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
aml_history_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_Amount *new_threshold,
|
|
||||||
enum TALER_AmlDecisionState new_state,
|
|
||||||
struct GNUNET_TIME_Timestamp decision_time,
|
|
||||||
const char *justification,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *decider_pub,
|
|
||||||
const struct TALER_AmlOfficerSignatureP *decider_sig)
|
|
||||||
{
|
|
||||||
json_t *aml_history = cls;
|
|
||||||
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_array_append (
|
|
||||||
aml_history,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_data_auto ("decider_pub",
|
|
||||||
decider_pub),
|
|
||||||
GNUNET_JSON_pack_string ("justification",
|
|
||||||
justification),
|
|
||||||
TALER_JSON_pack_amount ("new_threshold",
|
|
||||||
new_threshold),
|
|
||||||
GNUNET_JSON_pack_int64 ("new_state",
|
|
||||||
new_state),
|
|
||||||
GNUNET_JSON_pack_timestamp ("decision_time",
|
|
||||||
decision_time)
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_aml_decision_get (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const char *const args[])
|
|
||||||
{
|
|
||||||
struct TALER_PaytoHashP h_payto;
|
|
||||||
|
|
||||||
if ( (NULL == args[0]) ||
|
|
||||||
(GNUNET_OK !=
|
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
|
||||||
strlen (args[0]),
|
|
||||||
&h_payto,
|
|
||||||
sizeof (h_payto))) )
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
|
||||||
"h_payto");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL != args[1])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
|
||||||
args[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
json_t *aml_history;
|
|
||||||
json_t *kyc_attributes;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
bool none = false;
|
|
||||||
|
|
||||||
aml_history = json_array ();
|
|
||||||
GNUNET_assert (NULL != aml_history);
|
|
||||||
qs = TEH_plugin->select_aml_history (TEH_plugin->cls,
|
|
||||||
&h_payto,
|
|
||||||
&aml_history_cb,
|
|
||||||
aml_history);
|
|
||||||
switch (qs)
|
|
||||||
{
|
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
|
||||||
json_decref (aml_history);
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
NULL);
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
|
||||||
none = true;
|
|
||||||
break;
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
|
||||||
none = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
kyc_attributes = json_array ();
|
|
||||||
GNUNET_assert (NULL != kyc_attributes);
|
|
||||||
qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
|
|
||||||
&h_payto,
|
|
||||||
&kyc_attribute_cb,
|
|
||||||
kyc_attributes);
|
|
||||||
switch (qs)
|
|
||||||
{
|
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
|
||||||
json_decref (aml_history);
|
|
||||||
json_decref (kyc_attributes);
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
NULL);
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
|
||||||
if (none)
|
|
||||||
{
|
|
||||||
json_decref (aml_history);
|
|
||||||
json_decref (kyc_attributes);
|
|
||||||
return TALER_MHD_reply_static (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
GNUNET_JSON_pack_array_steal ("aml_history",
|
|
||||||
aml_history),
|
|
||||||
GNUNET_JSON_pack_array_steal ("kyc_attributes",
|
|
||||||
kyc_attributes));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_aml-decision_get.c */
|
|
@ -1,374 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_aml-decision.c
|
|
||||||
* @brief Handle request about an AML decision.
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <gnunet/gnunet_json_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler_kyclogic_lib.h"
|
|
||||||
#include "taler_signatures.h"
|
|
||||||
#include "taler-exchange-httpd_responses.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closure for #make_aml_decision()
|
|
||||||
*/
|
|
||||||
struct DecisionContext
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Justification given for the decision.
|
|
||||||
*/
|
|
||||||
const char *justification;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When was the decision taken.
|
|
||||||
*/
|
|
||||||
struct GNUNET_TIME_Timestamp decision_time;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New threshold for revising the decision.
|
|
||||||
*/
|
|
||||||
struct TALER_Amount new_threshold;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash of payto://-URI of affected account.
|
|
||||||
*/
|
|
||||||
struct TALER_PaytoHashP h_payto;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* New AML state.
|
|
||||||
*/
|
|
||||||
enum TALER_AmlDecisionState new_state;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signature affirming the decision.
|
|
||||||
*/
|
|
||||||
struct TALER_AmlOfficerSignatureP officer_sig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public key of the AML officer.
|
|
||||||
*/
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* KYC requirements imposed, NULL for none.
|
|
||||||
*/
|
|
||||||
json_t *kyc_requirements;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function implementing AML decision database transaction.
|
|
||||||
*
|
|
||||||
* Runs the transaction logic; IF it returns a non-error code, the
|
|
||||||
* transaction logic MUST NOT queue a MHD response. IF it returns an hard
|
|
||||||
* error, the transaction logic MUST queue a MHD response and set @a mhd_ret.
|
|
||||||
* IF it returns the soft error code, the function MAY be called again to
|
|
||||||
* retry and MUST not queue a MHD response.
|
|
||||||
*
|
|
||||||
* @param cls closure with a `struct DecisionContext`
|
|
||||||
* @param connection MHD request which triggered the transaction
|
|
||||||
* @param[out] mhd_ret set to MHD response status for @a connection,
|
|
||||||
* if transaction failed (!)
|
|
||||||
* @return transaction status
|
|
||||||
*/
|
|
||||||
static enum GNUNET_DB_QueryStatus
|
|
||||||
make_aml_decision (void *cls,
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
MHD_RESULT *mhd_ret)
|
|
||||||
{
|
|
||||||
struct DecisionContext *dc = cls;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
struct GNUNET_TIME_Timestamp last_date;
|
|
||||||
bool invalid_officer;
|
|
||||||
uint64_t requirement_row = 0;
|
|
||||||
|
|
||||||
if ( (NULL != dc->kyc_requirements) &&
|
|
||||||
(0 != json_array_size (dc->kyc_requirements)) )
|
|
||||||
{
|
|
||||||
char *res = NULL;
|
|
||||||
size_t idx;
|
|
||||||
json_t *req;
|
|
||||||
bool satisfied;
|
|
||||||
|
|
||||||
json_array_foreach (dc->kyc_requirements, idx, req)
|
|
||||||
{
|
|
||||||
const char *r = json_string_value (req);
|
|
||||||
|
|
||||||
if (NULL == res)
|
|
||||||
{
|
|
||||||
res = GNUNET_strdup (r);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
char *tmp;
|
|
||||||
|
|
||||||
GNUNET_asprintf (&tmp,
|
|
||||||
"%s %s",
|
|
||||||
res,
|
|
||||||
r);
|
|
||||||
GNUNET_free (res);
|
|
||||||
res = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
json_t *kyc_details = NULL;
|
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_check_satisfied (
|
|
||||||
&res,
|
|
||||||
&dc->h_payto,
|
|
||||||
&kyc_details,
|
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
|
||||||
TEH_plugin->cls,
|
|
||||||
&satisfied);
|
|
||||||
json_decref (kyc_details);
|
|
||||||
}
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"select_satisfied_kyc_processes");
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (! satisfied)
|
|
||||||
{
|
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
|
||||||
TEH_plugin->cls,
|
|
||||||
res,
|
|
||||||
&dc->h_payto,
|
|
||||||
&requirement_row);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GNUNET_free (res);
|
|
||||||
}
|
|
||||||
|
|
||||||
qs = TEH_plugin->insert_aml_decision (TEH_plugin->cls,
|
|
||||||
&dc->h_payto,
|
|
||||||
&dc->new_threshold,
|
|
||||||
dc->new_state,
|
|
||||||
dc->decision_time,
|
|
||||||
dc->justification,
|
|
||||||
dc->kyc_requirements,
|
|
||||||
requirement_row,
|
|
||||||
dc->officer_pub,
|
|
||||||
&dc->officer_sig,
|
|
||||||
&invalid_officer,
|
|
||||||
&last_date);
|
|
||||||
if (qs <= 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_aml_decision");
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (invalid_officer)
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_FORBIDDEN,
|
|
||||||
TALER_EC_EXCHANGE_AML_DECISION_INVALID_OFFICER,
|
|
||||||
NULL);
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
if (GNUNET_TIME_timestamp_cmp (last_date,
|
|
||||||
>=,
|
|
||||||
dc->decision_time))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_CONFLICT,
|
|
||||||
TALER_EC_EXCHANGE_AML_DECISION_MORE_RECENT_PRESENT,
|
|
||||||
NULL);
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_post_aml_decision (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const json_t *root)
|
|
||||||
{
|
|
||||||
struct MHD_Connection *connection = rc->connection;
|
|
||||||
struct DecisionContext dc = {
|
|
||||||
.officer_pub = officer_pub
|
|
||||||
};
|
|
||||||
uint32_t new_state32;
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("officer_sig",
|
|
||||||
&dc.officer_sig),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("h_payto",
|
|
||||||
&dc.h_payto),
|
|
||||||
TALER_JSON_spec_amount ("new_threshold",
|
|
||||||
TEH_currency,
|
|
||||||
&dc.new_threshold),
|
|
||||||
GNUNET_JSON_spec_string ("justification",
|
|
||||||
&dc.justification),
|
|
||||||
GNUNET_JSON_spec_timestamp ("decision_time",
|
|
||||||
&dc.decision_time),
|
|
||||||
GNUNET_JSON_spec_uint32 ("new_state",
|
|
||||||
&new_state32),
|
|
||||||
GNUNET_JSON_spec_mark_optional (
|
|
||||||
GNUNET_JSON_spec_json ("kyc_requirements",
|
|
||||||
&dc.kyc_requirements),
|
|
||||||
NULL),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
enum GNUNET_GenericReturnValue res;
|
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (connection,
|
|
||||||
root,
|
|
||||||
spec);
|
|
||||||
if (GNUNET_SYSERR == res)
|
|
||||||
return MHD_NO; /* hard failure */
|
|
||||||
if (GNUNET_NO == res)
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return MHD_YES; /* failure */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dc.new_state = (enum TALER_AmlDecisionState) new_state32;
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_officer_aml_decision_verify (dc.justification,
|
|
||||||
dc.decision_time,
|
|
||||||
&dc.new_threshold,
|
|
||||||
&dc.h_payto,
|
|
||||||
dc.new_state,
|
|
||||||
dc.kyc_requirements,
|
|
||||||
dc.officer_pub,
|
|
||||||
&dc.officer_sig))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_FORBIDDEN,
|
|
||||||
TALER_EC_EXCHANGE_AML_DECISION_ADD_SIGNATURE_INVALID,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL != dc.kyc_requirements)
|
|
||||||
{
|
|
||||||
size_t index;
|
|
||||||
json_t *elem;
|
|
||||||
|
|
||||||
if (! json_is_array (dc.kyc_requirements))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
|
||||||
"kyc_requirements must be an array");
|
|
||||||
}
|
|
||||||
|
|
||||||
json_array_foreach (dc.kyc_requirements, index, elem)
|
|
||||||
{
|
|
||||||
const char *val;
|
|
||||||
|
|
||||||
if (! json_is_string (elem))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
|
||||||
"kyc_requirements array members must be strings");
|
|
||||||
}
|
|
||||||
val = json_string_value (elem);
|
|
||||||
if (GNUNET_SYSERR ==
|
|
||||||
TALER_KYCLOGIC_check_satisfiable (val))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_EXCHANGE_AML_DECISION_UNKNOWN_CHECK,
|
|
||||||
val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
MHD_RESULT mhd_ret;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TEH_DB_run_transaction (connection,
|
|
||||||
"make-aml-decision",
|
|
||||||
TEH_MT_REQUEST_OTHER,
|
|
||||||
&mhd_ret,
|
|
||||||
&make_aml_decision,
|
|
||||||
&dc))
|
|
||||||
{
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return mhd_ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GNUNET_JSON_parse_free (spec);
|
|
||||||
return TALER_MHD_reply_static (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_aml-decision.c */
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_aml-decision.h
|
|
||||||
* @brief Handle /aml/$OFFICER_PUB/decision requests
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#ifndef TALER_EXCHANGE_HTTPD_AML_DECISION_H
|
|
||||||
#define TALER_EXCHANGE_HTTPD_AML_DECISION_H
|
|
||||||
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a POST "/aml/$OFFICER_PUB/decision" request. Parses the decision
|
|
||||||
* details, checks the signatures and if appropriately authorized executes
|
|
||||||
* the decision.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param officer_pub public key of the AML officer who made the request
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_post_aml_decision (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a GET "/aml/$OFFICER_PUB/decisions/$STATE" request. Parses the request
|
|
||||||
* details, checks the signatures and if appropriately authorized returns
|
|
||||||
* the matching decisions.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param officer_pub public key of the AML officer who made the request
|
|
||||||
* @param args GET arguments (should be the state)
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_aml_decisions_get (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const char *const args[]);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a GET "/aml/$OFFICER_PUB/decision/$H_PAYTO" request. Parses the request
|
|
||||||
* details, checks the signatures and if appropriately authorized returns
|
|
||||||
* the AML history and KYC attributes for the account.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param officer_pub public key of the AML officer who made the request
|
|
||||||
* @param args GET arguments (should be one)
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_aml_decision_get (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const char *const args[]);
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,211 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_aml-decisions-get.c
|
|
||||||
* @brief Return summary information about AML decisions
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler_signatures.h"
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
#include "taler_exchangedb_plugin.h"
|
|
||||||
#include "taler-exchange-httpd_aml-decision.h"
|
|
||||||
#include "taler-exchange-httpd_metrics.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of records we return per request.
|
|
||||||
*/
|
|
||||||
#define MAX_RECORDS 1024
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return AML status.
|
|
||||||
*
|
|
||||||
* @param cls closure
|
|
||||||
* @param row_id current row in AML status table
|
|
||||||
* @param h_payto account for which the attribute data is stored
|
|
||||||
* @param threshold currently monthly threshold that would trigger an AML check
|
|
||||||
* @param status what is the current AML decision
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
record_cb (
|
|
||||||
void *cls,
|
|
||||||
uint64_t row_id,
|
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
|
||||||
const struct TALER_Amount *threshold,
|
|
||||||
enum TALER_AmlDecisionState status)
|
|
||||||
{
|
|
||||||
json_t *records = cls;
|
|
||||||
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_array_append (
|
|
||||||
records,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_data_auto ("h_payto",
|
|
||||||
h_payto),
|
|
||||||
GNUNET_JSON_pack_int64 ("current_state",
|
|
||||||
status),
|
|
||||||
TALER_JSON_pack_amount ("threshold",
|
|
||||||
threshold),
|
|
||||||
GNUNET_JSON_pack_int64 ("rowid",
|
|
||||||
row_id)
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_aml_decisions_get (
|
|
||||||
struct TEH_RequestContext *rc,
|
|
||||||
const struct TALER_AmlOfficerPublicKeyP *officer_pub,
|
|
||||||
const char *const args[])
|
|
||||||
{
|
|
||||||
enum TALER_AmlDecisionState decision;
|
|
||||||
int delta = -20;
|
|
||||||
unsigned long long start = INT64_MAX;
|
|
||||||
const char *state_str = args[0];
|
|
||||||
|
|
||||||
if (NULL == state_str)
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
|
||||||
args[0]);
|
|
||||||
}
|
|
||||||
if (0 == strcmp (state_str,
|
|
||||||
"pending"))
|
|
||||||
decision = TALER_AML_PENDING;
|
|
||||||
else if (0 == strcmp (state_str,
|
|
||||||
"frozen"))
|
|
||||||
decision = TALER_AML_FROZEN;
|
|
||||||
else if (0 == strcmp (state_str,
|
|
||||||
"normal"))
|
|
||||||
decision = TALER_AML_NORMAL;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
|
||||||
state_str);
|
|
||||||
}
|
|
||||||
if (NULL != args[1])
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
|
||||||
args[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
const char *p;
|
|
||||||
|
|
||||||
p = MHD_lookup_connection_value (rc->connection,
|
|
||||||
MHD_GET_ARGUMENT_KIND,
|
|
||||||
"start");
|
|
||||||
if (NULL != p)
|
|
||||||
{
|
|
||||||
char dummy;
|
|
||||||
|
|
||||||
if (1 != sscanf (p,
|
|
||||||
"%llu%c",
|
|
||||||
&start,
|
|
||||||
&dummy))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
|
||||||
"start");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p = MHD_lookup_connection_value (rc->connection,
|
|
||||||
MHD_GET_ARGUMENT_KIND,
|
|
||||||
"delta");
|
|
||||||
if (NULL != p)
|
|
||||||
{
|
|
||||||
char dummy;
|
|
||||||
|
|
||||||
if (1 != sscanf (p,
|
|
||||||
"%d%c",
|
|
||||||
&delta,
|
|
||||||
&dummy))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_BAD_REQUEST,
|
|
||||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
|
||||||
"delta");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
json_t *records;
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
records = json_array ();
|
|
||||||
GNUNET_assert (NULL != records);
|
|
||||||
if (INT_MIN == delta)
|
|
||||||
delta = INT_MIN + 1;
|
|
||||||
qs = TEH_plugin->select_aml_process (TEH_plugin->cls,
|
|
||||||
decision,
|
|
||||||
start,
|
|
||||||
GNUNET_MIN (MAX_RECORDS,
|
|
||||||
delta > 0
|
|
||||||
? delta
|
|
||||||
: -delta),
|
|
||||||
delta > 0,
|
|
||||||
&record_cb,
|
|
||||||
records);
|
|
||||||
switch (qs)
|
|
||||||
{
|
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
|
||||||
json_decref (records);
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
NULL);
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
|
||||||
return TALER_MHD_reply_static (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
GNUNET_JSON_pack_array_steal ("records",
|
|
||||||
records));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_aml-decisions_get.c */
|
|
@ -233,7 +233,8 @@ again:
|
|||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
|
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
|
||||||
bdc->exchange_timestamp),
|
bdc->exchange_timestamp),
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
GNUNET_JSON_pack_data_auto (
|
||||||
|
"exchange_pub",
|
||||||
&pub),
|
&pub),
|
||||||
GNUNET_JSON_pack_array_steal ("exchange_sigs",
|
GNUNET_JSON_pack_array_steal ("exchange_sigs",
|
||||||
arr));
|
arr));
|
||||||
@ -267,12 +268,13 @@ batch_deposit_transaction (void *cls,
|
|||||||
* insert or update the record. */
|
* insert or update the record. */
|
||||||
if (dc->has_policy)
|
if (dc->has_policy)
|
||||||
{
|
{
|
||||||
qs = TEH_plugin->persist_policy_details (
|
qs = TEH_plugin->persist_policy_details (TEH_plugin->cls,
|
||||||
TEH_plugin->cls,
|
|
||||||
&dc->policy_details,
|
&dc->policy_details,
|
||||||
&dc->policy_details_serial_id,
|
&dc->policy_details_serial_id,
|
||||||
&dc->policy_details.accumulated_total,
|
&dc->policy_details.
|
||||||
&dc->policy_details.fulfillment_state);
|
accumulated_total,
|
||||||
|
&dc->policy_details.
|
||||||
|
fulfillment_state);
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
@ -293,7 +295,7 @@ batch_deposit_transaction (void *cls,
|
|||||||
deposit,
|
deposit,
|
||||||
known_coin_id,
|
known_coin_id,
|
||||||
&dc->h_payto,
|
&dc->h_payto,
|
||||||
dc->has_policy
|
(dc->has_policy)
|
||||||
? &dc->policy_details_serial_id
|
? &dc->policy_details_serial_id
|
||||||
: NULL,
|
: NULL,
|
||||||
&dc->exchange_timestamp,
|
&dc->exchange_timestamp,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014-2023 Taler Systems SA
|
Copyright (C) 2014-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify
|
TALER is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
@ -77,11 +77,6 @@ struct BatchWithdrawContext
|
|||||||
*/
|
*/
|
||||||
const struct TALER_ReservePublicKeyP *reserve_pub;
|
const struct TALER_ReservePublicKeyP *reserve_pub;
|
||||||
|
|
||||||
/**
|
|
||||||
* request context
|
|
||||||
*/
|
|
||||||
const struct TEH_RequestContext *rc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KYC status of the reserve used for the operation.
|
* KYC status of the reserve used for the operation.
|
||||||
*/
|
*/
|
||||||
@ -113,11 +108,6 @@ struct BatchWithdrawContext
|
|||||||
*/
|
*/
|
||||||
unsigned int planchets_length;
|
unsigned int planchets_length;
|
||||||
|
|
||||||
/**
|
|
||||||
* AML decision, #TALER_AML_NORMAL if we may proceed.
|
|
||||||
*/
|
|
||||||
enum TALER_AmlDecisionState aml_decision;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -160,127 +150,6 @@ batch_withdraw_amount_cb (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called on each @a amount that was found to
|
|
||||||
* be relevant for the AML check as it was merged into
|
|
||||||
* the reserve.
|
|
||||||
*
|
|
||||||
* @param cls `struct TALER_Amount *` to total up the amounts
|
|
||||||
* @param amount encountered transaction amount
|
|
||||||
* @param date when was the amount encountered
|
|
||||||
* @return #GNUNET_OK to continue to iterate,
|
|
||||||
* #GNUNET_NO to abort iteration
|
|
||||||
* #GNUNET_SYSERR on internal error (also abort itaration)
|
|
||||||
*/
|
|
||||||
static enum GNUNET_GenericReturnValue
|
|
||||||
aml_amount_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_Amount *amount,
|
|
||||||
struct GNUNET_TIME_Absolute date)
|
|
||||||
{
|
|
||||||
struct TALER_Amount *total = cls;
|
|
||||||
|
|
||||||
GNUNET_assert (0 <=
|
|
||||||
TALER_amount_add (total,
|
|
||||||
total,
|
|
||||||
amount));
|
|
||||||
return GNUNET_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates our final (successful) response.
|
|
||||||
*
|
|
||||||
* @param rc request context
|
|
||||||
* @param wc operation context
|
|
||||||
* @return MHD queue status
|
|
||||||
*/
|
|
||||||
static MHD_RESULT
|
|
||||||
generate_reply_success (const struct TEH_RequestContext *rc,
|
|
||||||
const struct BatchWithdrawContext *wc)
|
|
||||||
{
|
|
||||||
json_t *sigs;
|
|
||||||
|
|
||||||
if (! wc->kyc.ok)
|
|
||||||
{
|
|
||||||
/* KYC required */
|
|
||||||
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
|
||||||
&wc->h_payto,
|
|
||||||
&wc->kyc);
|
|
||||||
}
|
|
||||||
if (TALER_AML_NORMAL != wc->aml_decision)
|
|
||||||
return TEH_RESPONSE_reply_aml_blocked (rc->connection,
|
|
||||||
wc->aml_decision);
|
|
||||||
|
|
||||||
sigs = json_array ();
|
|
||||||
GNUNET_assert (NULL != sigs);
|
|
||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
|
||||||
{
|
|
||||||
struct PlanchetContext *pc = &wc->planchets[i];
|
|
||||||
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_array_append_new (
|
|
||||||
sigs,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
TALER_JSON_pack_blinded_denom_sig (
|
|
||||||
"ev_sig",
|
|
||||||
&pc->collectable.sig))));
|
|
||||||
}
|
|
||||||
TEH_METRICS_batch_withdraw_num_coins += wc->planchets_length;
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
GNUNET_JSON_pack_array_steal ("ev_sigs",
|
|
||||||
sigs));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the @a wc is replayed and we already have an
|
|
||||||
* answer. If so, replay the existing answer and return the
|
|
||||||
* HTTP response.
|
|
||||||
*
|
|
||||||
* @param wc parsed request data
|
|
||||||
* @param[out] mret HTTP status, set if we return true
|
|
||||||
* @return true if the request is idempotent with an existing request
|
|
||||||
* false if we did not find the request in the DB and did not set @a mret
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
check_request_idempotent (const struct BatchWithdrawContext *wc,
|
|
||||||
MHD_RESULT *mret)
|
|
||||||
{
|
|
||||||
const struct TEH_RequestContext *rc = wc->rc;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
|
||||||
{
|
|
||||||
struct PlanchetContext *pc = &wc->planchets[i];
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
|
||||||
&pc->h_coin_envelope,
|
|
||||||
&pc->collectable);
|
|
||||||
if (0 > qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mret = TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"get_withdraw_info");
|
|
||||||
return true; /* well, kind-of */
|
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
/* generate idempotent reply */
|
|
||||||
TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++;
|
|
||||||
*mret = generate_reply_success (rc,
|
|
||||||
wc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function implementing withdraw transaction. Runs the
|
* Function implementing withdraw transaction. Runs the
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
@ -308,116 +177,14 @@ batch_withdraw_transaction (void *cls,
|
|||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
bool balance_ok = false;
|
bool balance_ok = false;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
char *kyc_required;
|
const char *kyc_required;
|
||||||
struct TALER_PaytoHashP reserve_h_payto;
|
|
||||||
|
|
||||||
wc->now = GNUNET_TIME_timestamp_get ();
|
wc->now = GNUNET_TIME_timestamp_get ();
|
||||||
/* Do AML check: compute total merged amount and check
|
|
||||||
against applicable AML threshold */
|
|
||||||
{
|
|
||||||
char *reserve_payto;
|
|
||||||
|
|
||||||
reserve_payto = TALER_reserve_make_payto (TEH_base_url,
|
|
||||||
wc->reserve_pub);
|
|
||||||
TALER_payto_hash (reserve_payto,
|
|
||||||
&reserve_h_payto);
|
|
||||||
GNUNET_free (reserve_payto);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct TALER_Amount merge_amount;
|
|
||||||
struct TALER_Amount threshold;
|
|
||||||
struct GNUNET_TIME_Absolute now_minus_one_month;
|
|
||||||
|
|
||||||
now_minus_one_month
|
|
||||||
= GNUNET_TIME_absolute_subtract (wc->now.abs_time,
|
|
||||||
GNUNET_TIME_UNIT_MONTHS);
|
|
||||||
GNUNET_assert (GNUNET_OK ==
|
|
||||||
TALER_amount_set_zero (TEH_currency,
|
|
||||||
&merge_amount));
|
|
||||||
qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
now_minus_one_month,
|
|
||||||
&aml_amount_cb,
|
|
||||||
&merge_amount);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"select_merge_amounts_for_kyc_check");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
&wc->aml_decision,
|
|
||||||
&wc->kyc,
|
|
||||||
&threshold);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"select_aml_threshold");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
{
|
|
||||||
threshold = TEH_aml_threshold; /* use default */
|
|
||||||
wc->aml_decision = TALER_AML_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (wc->aml_decision)
|
|
||||||
{
|
|
||||||
case TALER_AML_NORMAL:
|
|
||||||
if (0 >= TALER_amount_cmp (&merge_amount,
|
|
||||||
&threshold))
|
|
||||||
{
|
|
||||||
/* merge_amount <= threshold, continue withdraw below */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
wc->aml_decision = TALER_AML_PENDING;
|
|
||||||
qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
&merge_amount);
|
|
||||||
if (qs <= 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"trigger_aml_process");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
case TALER_AML_PENDING:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"AML already pending, doing nothing\n");
|
|
||||||
return qs;
|
|
||||||
case TALER_AML_FROZEN:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Account frozen, doing nothing\n");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the money came from a wire transfer */
|
|
||||||
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
||||||
wc->reserve_pub,
|
wc->reserve_pub,
|
||||||
&wc->h_payto);
|
&wc->h_payto);
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"reserves_get_origin");
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
{
|
{
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
@ -426,44 +193,22 @@ batch_withdraw_transaction (void *cls,
|
|||||||
NULL);
|
NULL);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
kyc_required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
||||||
&wc->h_payto,
|
&wc->h_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&batch_withdraw_amount_cb,
|
&batch_withdraw_amount_cb,
|
||||||
wc,
|
wc);
|
||||||
&kyc_required);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (NULL != kyc_required)
|
if (NULL != kyc_required)
|
||||||
{
|
{
|
||||||
/* insert KYC requirement into DB! */
|
/* insert KYC requirement into DB! */
|
||||||
wc->kyc.ok = false;
|
wc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
kyc_required,
|
kyc_required,
|
||||||
&wc->h_payto,
|
&wc->h_payto,
|
||||||
&wc->kyc.requirement_row);
|
&wc->kyc.requirement_row);
|
||||||
GNUNET_free (kyc_required);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
wc->kyc.ok = true;
|
wc->kyc.ok = true;
|
||||||
qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
|
qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
|
||||||
@ -476,13 +221,10 @@ batch_withdraw_transaction (void *cls,
|
|||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"update_reserve_batch_withdraw");
|
"update_reserve_batch_withdraw");
|
||||||
}
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
if (! found)
|
if (! found)
|
||||||
@ -546,18 +288,12 @@ batch_withdraw_transaction (void *cls,
|
|||||||
if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
|
if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
|
||||||
(conflict) )
|
(conflict) )
|
||||||
{
|
{
|
||||||
if (! check_request_idempotent (wc,
|
|
||||||
mhd_ret))
|
|
||||||
{
|
|
||||||
/* We do not support *some* of the coins of the request being
|
|
||||||
idempotent while others being fresh. */
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||||
"Idempotent coin in batch, not allowed. Aborting.\n");
|
"Idempotent coin in batch, not allowed. Aborting.\n");
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_CONFLICT,
|
MHD_HTTP_CONFLICT,
|
||||||
TALER_EC_EXCHANGE_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET,
|
TALER_EC_EXCHANGE_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
if (nonce_reuse)
|
if (nonce_reuse)
|
||||||
@ -575,6 +311,96 @@ batch_withdraw_transaction (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates our final (successful) response.
|
||||||
|
*
|
||||||
|
* @param rc request context
|
||||||
|
* @param wc operation context
|
||||||
|
* @return MHD queue status
|
||||||
|
*/
|
||||||
|
static MHD_RESULT
|
||||||
|
generate_reply_success (const struct TEH_RequestContext *rc,
|
||||||
|
const struct BatchWithdrawContext *wc)
|
||||||
|
{
|
||||||
|
json_t *sigs;
|
||||||
|
|
||||||
|
if (! wc->kyc.ok)
|
||||||
|
{
|
||||||
|
/* KYC required */
|
||||||
|
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
||||||
|
&wc->h_payto,
|
||||||
|
&wc->kyc);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigs = json_array ();
|
||||||
|
GNUNET_assert (NULL != sigs);
|
||||||
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
|
{
|
||||||
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
|
|
||||||
|
GNUNET_assert (
|
||||||
|
0 ==
|
||||||
|
json_array_append_new (
|
||||||
|
sigs,
|
||||||
|
GNUNET_JSON_PACK (
|
||||||
|
TALER_JSON_pack_blinded_denom_sig (
|
||||||
|
"ev_sig",
|
||||||
|
&pc->collectable.sig))));
|
||||||
|
}
|
||||||
|
TEH_METRICS_batch_withdraw_num_coins += wc->planchets_length;
|
||||||
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
|
rc->connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
GNUNET_JSON_pack_array_steal ("ev_sigs",
|
||||||
|
sigs));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the @a rc is replayed and we already have an
|
||||||
|
* answer. If so, replay the existing answer and return the
|
||||||
|
* HTTP response.
|
||||||
|
*
|
||||||
|
* @param rc request context
|
||||||
|
* @param wc parsed request data
|
||||||
|
* @param[out] mret HTTP status, set if we return true
|
||||||
|
* @return true if the request is idempotent with an existing request
|
||||||
|
* false if we did not find the request in the DB and did not set @a mret
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
check_request_idempotent (const struct TEH_RequestContext *rc,
|
||||||
|
const struct BatchWithdrawContext *wc,
|
||||||
|
MHD_RESULT *mret)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
|
{
|
||||||
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
|
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
||||||
|
&pc->h_coin_envelope,
|
||||||
|
&pc->collectable);
|
||||||
|
if (0 > qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||||
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
*mret = TALER_MHD_reply_with_error (rc->connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
|
"get_withdraw_info");
|
||||||
|
return true; /* well, kind-of */
|
||||||
|
}
|
||||||
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* generate idempotent reply */
|
||||||
|
TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++;
|
||||||
|
*mret = generate_reply_success (rc,
|
||||||
|
wc);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The request was parsed successfully. Prepare
|
* The request was parsed successfully. Prepare
|
||||||
* our side for the main DB transaction.
|
* our side for the main DB transaction.
|
||||||
@ -702,7 +528,8 @@ parse_planchets (const struct TEH_RequestContext *rc,
|
|||||||
ksh = TEH_keys_get_state ();
|
ksh = TEH_keys_get_state ();
|
||||||
if (NULL == ksh)
|
if (NULL == ksh)
|
||||||
{
|
{
|
||||||
if (! check_request_idempotent (wc,
|
if (! check_request_idempotent (rc,
|
||||||
|
wc,
|
||||||
&mret))
|
&mret))
|
||||||
{
|
{
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
@ -723,7 +550,8 @@ parse_planchets (const struct TEH_RequestContext *rc,
|
|||||||
NULL);
|
NULL);
|
||||||
if (NULL == dk)
|
if (NULL == dk)
|
||||||
{
|
{
|
||||||
if (! check_request_idempotent (wc,
|
if (! check_request_idempotent (rc,
|
||||||
|
wc,
|
||||||
&mret))
|
&mret))
|
||||||
{
|
{
|
||||||
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
|
return TEH_RESPONSE_reply_unknown_denom_pub_hash (
|
||||||
@ -735,7 +563,8 @@ parse_planchets (const struct TEH_RequestContext *rc,
|
|||||||
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
|
if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
|
||||||
{
|
{
|
||||||
/* This denomination is past the expiration time for withdraws */
|
/* This denomination is past the expiration time for withdraws */
|
||||||
if (! check_request_idempotent (wc,
|
if (! check_request_idempotent (rc,
|
||||||
|
wc,
|
||||||
&mret))
|
&mret))
|
||||||
{
|
{
|
||||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||||
@ -759,7 +588,8 @@ parse_planchets (const struct TEH_RequestContext *rc,
|
|||||||
if (dk->recoup_possible)
|
if (dk->recoup_possible)
|
||||||
{
|
{
|
||||||
/* This denomination has been revoked */
|
/* This denomination has been revoked */
|
||||||
if (! check_request_idempotent (wc,
|
if (! check_request_idempotent (rc,
|
||||||
|
wc,
|
||||||
&mret))
|
&mret))
|
||||||
{
|
{
|
||||||
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
return TEH_RESPONSE_reply_expired_denom_pub_hash (
|
||||||
@ -839,10 +669,7 @@ TEH_handler_batch_withdraw (struct TEH_RequestContext *rc,
|
|||||||
const struct TALER_ReservePublicKeyP *reserve_pub,
|
const struct TALER_ReservePublicKeyP *reserve_pub,
|
||||||
const json_t *root)
|
const json_t *root)
|
||||||
{
|
{
|
||||||
struct BatchWithdrawContext wc = {
|
struct BatchWithdrawContext wc;
|
||||||
.reserve_pub = reserve_pub,
|
|
||||||
.rc = rc
|
|
||||||
};
|
|
||||||
json_t *planchets;
|
json_t *planchets;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_json ("planchets",
|
GNUNET_JSON_spec_json ("planchets",
|
||||||
@ -850,9 +677,13 @@ TEH_handler_batch_withdraw (struct TEH_RequestContext *rc,
|
|||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
memset (&wc,
|
||||||
|
0,
|
||||||
|
sizeof (wc));
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_set_zero (TEH_currency,
|
TALER_amount_set_zero (TEH_currency,
|
||||||
&wc.batch_total));
|
&wc.batch_total));
|
||||||
|
wc.reserve_pub = reserve_pub;
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue res;
|
enum GNUNET_GenericReturnValue res;
|
||||||
|
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 2015-2021 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_config.c
|
|
||||||
* @brief Handle /config requests
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_json_lib.h>
|
|
||||||
#include "taler_dbevents.h"
|
|
||||||
#include "taler-exchange-httpd_config.h"
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_kyclogic_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include <jansson.h>
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_config (struct TEH_RequestContext *rc,
|
|
||||||
const char *const args[])
|
|
||||||
{
|
|
||||||
static struct MHD_Response *resp;
|
|
||||||
|
|
||||||
if (NULL == resp)
|
|
||||||
{
|
|
||||||
resp = TALER_MHD_MAKE_JSON_PACK (
|
|
||||||
GNUNET_JSON_pack_array_steal ("supported_kyc_requirements",
|
|
||||||
TALER_KYCLOGIC_get_satisfiable ()),
|
|
||||||
GNUNET_JSON_pack_string ("currency",
|
|
||||||
TEH_currency),
|
|
||||||
GNUNET_JSON_pack_string ("name",
|
|
||||||
"taler-exchange"),
|
|
||||||
GNUNET_JSON_pack_string ("version",
|
|
||||||
EXCHANGE_PROTOCOL_VERSION));
|
|
||||||
}
|
|
||||||
return MHD_queue_response (rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
resp);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_config.c */
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
(C) 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
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_config.h
|
|
||||||
* @brief headers for /config handler
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#ifndef TALER_EXCHANGE_HTTPD_CONFIG_H
|
|
||||||
#define TALER_EXCHANGE_HTTPD_CONFIG_H
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include "taler-exchange-httpd.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Taler protocol version in the format CURRENT:REVISION:AGE
|
|
||||||
* as used by GNU libtool. See
|
|
||||||
* https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
|
|
||||||
*
|
|
||||||
* Please be very careful when updating and follow
|
|
||||||
* https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
|
||||||
* precisely. Note that this version has NOTHING to do with the
|
|
||||||
* release version, and the format is NOT the same that semantic
|
|
||||||
* versioning uses either.
|
|
||||||
*
|
|
||||||
* When changing this version, you likely want to also update
|
|
||||||
* #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
|
|
||||||
* exchange_api_handle.c!
|
|
||||||
*
|
|
||||||
* Returned via both /config and /keys endpoints.
|
|
||||||
*/
|
|
||||||
#define EXCHANGE_PROTOCOL_VERSION "14:0:2"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages a /config call.
|
|
||||||
*
|
|
||||||
* @param rc context of the handler
|
|
||||||
* @param[in,out] args remaining arguments (ignored)
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_config (struct TEH_RequestContext *rc,
|
|
||||||
const char *const args[]);
|
|
||||||
|
|
||||||
#endif
|
|
@ -234,10 +234,12 @@ TEH_handler_csr_withdraw (struct TEH_RequestContext *rc,
|
|||||||
.cipher = TALER_DENOMINATION_CS
|
.cipher = TALER_DENOMINATION_CS
|
||||||
};
|
};
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_fixed_auto ("nonce",
|
GNUNET_JSON_spec_fixed ("nonce",
|
||||||
&nonce),
|
&nonce,
|
||||||
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
sizeof (struct TALER_CsNonce)),
|
||||||
&denom_pub_hash),
|
GNUNET_JSON_spec_fixed ("denom_pub_hash",
|
||||||
|
&denom_pub_hash,
|
||||||
|
sizeof (struct TALER_DenominationHashP)),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
struct TEH_DenominationKey *dk;
|
struct TEH_DenominationKey *dk;
|
||||||
@ -331,11 +333,17 @@ TEH_handler_csr_withdraw (struct TEH_RequestContext *rc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
{
|
||||||
rc->connection,
|
json_t *csr_obj;
|
||||||
MHD_HTTP_OK,
|
|
||||||
|
csr_obj = GNUNET_JSON_PACK (
|
||||||
TALER_JSON_pack_exchange_withdraw_values ("ewv",
|
TALER_JSON_pack_exchange_withdraw_values ("ewv",
|
||||||
&ewv));
|
&ewv));
|
||||||
|
GNUNET_assert (NULL != csr_obj);
|
||||||
|
return TALER_MHD_reply_json_steal (rc->connection,
|
||||||
|
csr_obj,
|
||||||
|
MHD_HTTP_OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,11 +90,6 @@ struct DepositWtidContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||||
|
|
||||||
/**
|
|
||||||
* AML status information for the receiving account.
|
|
||||||
*/
|
|
||||||
enum TALER_AmlDecisionState aml_decision;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending
|
* Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending
|
||||||
* (and the above were not set).
|
* (and the above were not set).
|
||||||
@ -133,7 +128,6 @@ reply_deposit_details (
|
|||||||
&pub,
|
&pub,
|
||||||
&sig)))
|
&sig)))
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_ec (connection,
|
return TALER_MHD_reply_with_ec (connection,
|
||||||
ec,
|
ec,
|
||||||
NULL);
|
NULL);
|
||||||
@ -190,8 +184,7 @@ deposits_get_transaction (void *cls,
|
|||||||
&ctx->execution_time,
|
&ctx->execution_time,
|
||||||
&ctx->coin_contribution,
|
&ctx->coin_contribution,
|
||||||
&fee,
|
&fee,
|
||||||
&ctx->kyc,
|
&ctx->kyc);
|
||||||
&ctx->aml_decision);
|
|
||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
@ -264,8 +257,6 @@ handle_track_transaction_request (
|
|||||||
NULL)
|
NULL)
|
||||||
: GNUNET_JSON_pack_uint64 ("requirement_row",
|
: GNUNET_JSON_pack_uint64 ("requirement_row",
|
||||||
ctx->kyc.requirement_row)),
|
ctx->kyc.requirement_row)),
|
||||||
GNUNET_JSON_pack_uint64 ("aml_decision",
|
|
||||||
(uint32_t) ctx->aml_decision),
|
|
||||||
GNUNET_JSON_pack_bool ("kyc_ok",
|
GNUNET_JSON_pack_bool ("kyc_ok",
|
||||||
ctx->kyc.ok),
|
ctx->kyc.ok),
|
||||||
GNUNET_JSON_pack_timestamp ("execution_time",
|
GNUNET_JSON_pack_timestamp ("execution_time",
|
||||||
|
@ -150,11 +150,12 @@ extension_update_event_cb (void *cls,
|
|||||||
{
|
{
|
||||||
TEH_age_restriction_enabled = true;
|
TEH_age_restriction_enabled = true;
|
||||||
TEH_age_restriction_config = *conf;
|
TEH_age_restriction_config = *conf;
|
||||||
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"[age restriction] DB event has changed the config to %s with mask: %s\n",
|
"[age restriction] DB event has changed the config to %s with mask: %s\n",
|
||||||
TEH_age_restriction_enabled ? "enabled": "DISABLED",
|
TEH_age_restriction_enabled ? "enabled": "DISABLED",
|
||||||
TALER_age_mask_to_string (&conf->mask));
|
TALER_age_mask_to_string (&conf->mask));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, call TEH_keys_update_states in order to refresh the cached
|
// Finally, call TEH_keys_update_states in order to refresh the cached
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include "taler_kyclogic_lib.h"
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_dbevents.h"
|
#include "taler_dbevents.h"
|
||||||
#include "taler-exchange-httpd.h"
|
#include "taler-exchange-httpd.h"
|
||||||
#include "taler-exchange-httpd_config.h"
|
|
||||||
#include "taler-exchange-httpd_keys.h"
|
#include "taler-exchange-httpd_keys.h"
|
||||||
#include "taler-exchange-httpd_responses.h"
|
#include "taler-exchange-httpd_responses.h"
|
||||||
#include "taler_exchangedb_plugin.h"
|
#include "taler_exchangedb_plugin.h"
|
||||||
@ -45,6 +44,24 @@
|
|||||||
#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES
|
#define KEYS_TIMEOUT GNUNET_TIME_UNIT_MINUTES
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Taler protocol version in the format CURRENT:REVISION:AGE
|
||||||
|
* as used by GNU libtool. See
|
||||||
|
* https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
|
||||||
|
*
|
||||||
|
* Please be very careful when updating and follow
|
||||||
|
* https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
|
||||||
|
* precisely. Note that this version has NOTHING to do with the
|
||||||
|
* release version, and the format is NOT the same that semantic
|
||||||
|
* versioning uses either.
|
||||||
|
*
|
||||||
|
* When changing this version, you likely want to also update
|
||||||
|
* #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
|
||||||
|
* exchange_api_handle.c!
|
||||||
|
*/
|
||||||
|
#define EXCHANGE_PROTOCOL_VERSION "14:0:2"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about a denomination on offer by the denomination helper.
|
* Information about a denomination on offer by the denomination helper.
|
||||||
*/
|
*/
|
||||||
@ -738,7 +755,7 @@ free_denom_cb (void *cls,
|
|||||||
* @param value the `struct HelperSignkey` to release
|
* @param value the `struct HelperSignkey` to release
|
||||||
* @return #GNUNET_OK (continue to iterate)
|
* @return #GNUNET_OK (continue to iterate)
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_GenericReturnValue
|
static int
|
||||||
free_esign_cb (void *cls,
|
free_esign_cb (void *cls,
|
||||||
const struct GNUNET_PeerIdentity *pid,
|
const struct GNUNET_PeerIdentity *pid,
|
||||||
void *value)
|
void *value)
|
||||||
@ -1905,7 +1922,6 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
|||||||
json_t *extensions = json_object ();
|
json_t *extensions = json_object ();
|
||||||
bool has_extensions = false;
|
bool has_extensions = false;
|
||||||
|
|
||||||
GNUNET_assert (NULL != extensions);
|
|
||||||
/* Fill in the configurations of the enabled extensions */
|
/* Fill in the configurations of the enabled extensions */
|
||||||
for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
|
for (const struct TALER_Extensions *iter = TALER_extensions_get_head ();
|
||||||
NULL != iter && NULL != iter->extension;
|
NULL != iter && NULL != iter->extension;
|
||||||
@ -1939,7 +1955,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
|
|||||||
json_t *sig;
|
json_t *sig;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = json_object_set_new (
|
r = json_object_set (
|
||||||
keys,
|
keys,
|
||||||
"extensions",
|
"extensions",
|
||||||
extensions);
|
extensions);
|
||||||
@ -2259,7 +2275,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
|
|
||||||
if (age_restricted)
|
if (age_restricted)
|
||||||
{
|
{
|
||||||
int r = json_object_set_new (group->json,
|
int r = json_object_set (group->json,
|
||||||
"age_mask",
|
"age_mask",
|
||||||
json_integer (meta.age_mask.bits));
|
json_integer (meta.age_mask.bits));
|
||||||
GNUNET_assert (0 == r);
|
GNUNET_assert (0 == r);
|
||||||
@ -2269,9 +2285,8 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
list = json_array ();
|
list = json_array ();
|
||||||
GNUNET_assert (NULL != list);
|
GNUNET_assert (NULL != list);
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
json_object_set_new (group->json,
|
json_object_set (group->json, denoms_key, list));
|
||||||
denoms_key,
|
|
||||||
list));
|
|
||||||
GNUNET_assert (
|
GNUNET_assert (
|
||||||
GNUNET_OK ==
|
GNUNET_OK ==
|
||||||
GNUNET_CONTAINER_multihashmap_put (denominations_by_group,
|
GNUNET_CONTAINER_multihashmap_put (denominations_by_group,
|
||||||
@ -2355,7 +2370,7 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
{
|
{
|
||||||
/* Add the XOR over all hashes of denominations in this group to the group */
|
/* Add the XOR over all hashes of denominations in this group to the group */
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
json_object_set_new (
|
json_object_set (
|
||||||
group->json,
|
group->json,
|
||||||
"hash",
|
"hash",
|
||||||
GNUNET_JSON_PACK (
|
GNUNET_JSON_PACK (
|
||||||
@ -2377,10 +2392,9 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
|
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
|
||||||
|
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_CONTAINER_heap_destroy (heap);
|
GNUNET_CONTAINER_heap_destroy (heap);
|
||||||
@ -2404,7 +2418,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
"Failed to generate key response data for %s\n",
|
"Failed to generate key response data for %s\n",
|
||||||
GNUNET_TIME_timestamp2s (last_cpd));
|
GNUNET_TIME_timestamp2s (last_cpd));
|
||||||
json_decref (denoms);
|
json_decref (denoms);
|
||||||
json_decref (grouped_denominations);
|
|
||||||
json_decref (sctx.signkeys);
|
json_decref (sctx.signkeys);
|
||||||
json_decref (recoup);
|
json_decref (recoup);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
@ -2417,7 +2430,6 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
"No denomination keys available. Refusing to generate /keys response.\n");
|
"No denomination keys available. Refusing to generate /keys response.\n");
|
||||||
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
||||||
}
|
}
|
||||||
json_decref (grouped_denominations);
|
|
||||||
json_decref (sctx.signkeys);
|
json_decref (sctx.signkeys);
|
||||||
json_decref (recoup);
|
json_decref (recoup);
|
||||||
json_decref (denoms);
|
json_decref (denoms);
|
||||||
@ -3617,7 +3629,6 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
|
|||||||
if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) &&
|
if ( (GNUNET_is_zero (&denom_rsa_sm_pub)) &&
|
||||||
(GNUNET_is_zero (&denom_cs_sm_pub)) )
|
(GNUNET_is_zero (&denom_cs_sm_pub)) )
|
||||||
{
|
{
|
||||||
/* Either IPC failed, or neither helper had any denominations configured. */
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_BAD_GATEWAY,
|
MHD_HTTP_BAD_GATEWAY,
|
||||||
TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
|
TALER_EC_EXCHANGE_DENOMINATION_HELPER_UNAVAILABLE,
|
||||||
@ -3630,6 +3641,7 @@ TEH_keys_management_get_keys_handler (const struct TEH_RequestHandler *rh,
|
|||||||
TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE,
|
TALER_EC_EXCHANGE_SIGNKEY_HELPER_UNAVAILABLE,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
// then a secmod helper is not yet running and we should return an MHD_HTTP_BAD_GATEWAY!
|
||||||
GNUNET_assert (NULL != fbc.denoms);
|
GNUNET_assert (NULL != fbc.denoms);
|
||||||
GNUNET_assert (NULL != fbc.signkeys);
|
GNUNET_assert (NULL != fbc.signkeys);
|
||||||
GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys,
|
GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers->denom_keys,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2021-2023 Taler Systems SA
|
Copyright (C) 2021-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -112,11 +112,6 @@ struct KycPoller
|
|||||||
*/
|
*/
|
||||||
const char *section_name;
|
const char *section_name;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set to AML status of the account.
|
|
||||||
*/
|
|
||||||
enum TALER_AmlDecisionState aml_status;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to error encountered with KYC logic, if any.
|
* Set to error encountered with KYC logic, if any.
|
||||||
*/
|
*/
|
||||||
@ -238,7 +233,7 @@ initiate_cb (
|
|||||||
kyp->ih = NULL;
|
kyp->ih = NULL;
|
||||||
kyp->ih_done = true;
|
kyp->ih_done = true;
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"KYC initiation completed with ec=%d (%s)\n",
|
"KYC initiation completed with status %d (%s)\n",
|
||||||
ec,
|
ec,
|
||||||
(TALER_EC_NONE == ec)
|
(TALER_EC_NONE == ec)
|
||||||
? redirect_url
|
? redirect_url
|
||||||
@ -302,13 +297,11 @@ kyc_check (void *cls,
|
|||||||
enum GNUNET_GenericReturnValue ret;
|
enum GNUNET_GenericReturnValue ret;
|
||||||
struct TALER_PaytoHashP h_payto;
|
struct TALER_PaytoHashP h_payto;
|
||||||
char *requirements;
|
char *requirements;
|
||||||
bool satisfied;
|
|
||||||
|
|
||||||
qs = TEH_plugin->lookup_kyc_requirement_by_row (
|
qs = TEH_plugin->lookup_kyc_requirement_by_row (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
kyp->requirement_row,
|
kyp->requirement_row,
|
||||||
&requirements,
|
&requirements,
|
||||||
&kyp->aml_status,
|
|
||||||
&h_payto);
|
&h_payto);
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
{
|
{
|
||||||
@ -337,26 +330,12 @@ kyc_check (void *cls,
|
|||||||
GNUNET_free (requirements);
|
GNUNET_free (requirements);
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
qs = TALER_KYCLOGIC_check_satisfied (
|
if (TALER_KYCLOGIC_check_satisfied (
|
||||||
&requirements,
|
requirements,
|
||||||
&h_payto,
|
&h_payto,
|
||||||
&kyp->kyc_details,
|
&kyp->kyc_details,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls))
|
||||||
&satisfied);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
GNUNET_free (requirements);
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
if (satisfied)
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"KYC requirements `%s' already satisfied\n",
|
"KYC requirements `%s' already satisfied\n",
|
||||||
@ -395,17 +374,6 @@ kyc_check (void *cls,
|
|||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
&kyp->process_row);
|
&kyp->process_row);
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_process");
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Initiating KYC check with logic %s\n",
|
"Initiating KYC check with logic %s\n",
|
||||||
kyp->ih_logic->name);
|
kyp->ih_logic->name);
|
||||||
@ -586,17 +554,6 @@ TEH_handler_kyc_check (
|
|||||||
if ( (NULL == kyp->ih) &&
|
if ( (NULL == kyp->ih) &&
|
||||||
(! kyp->kyc_required) )
|
(! kyp->kyc_required) )
|
||||||
{
|
{
|
||||||
if (TALER_AML_NORMAL != kyp->aml_status)
|
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"KYC is OK, but AML active: %d\n",
|
|
||||||
(int) kyp->aml_status);
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
|
||||||
GNUNET_JSON_pack_uint64 ("aml_status",
|
|
||||||
kyp->aml_status));
|
|
||||||
}
|
|
||||||
/* KYC not required */
|
/* KYC not required */
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"KYC not required %llu\n",
|
"KYC not required %llu\n",
|
||||||
@ -645,8 +602,6 @@ TEH_handler_kyc_check (
|
|||||||
return TALER_MHD_REPLY_JSON_PACK (
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
MHD_HTTP_ACCEPTED,
|
MHD_HTTP_ACCEPTED,
|
||||||
GNUNET_JSON_pack_uint64 ("aml_status",
|
|
||||||
kyp->aml_status),
|
|
||||||
GNUNET_JSON_pack_string ("kyc_url",
|
GNUNET_JSON_pack_string ("kyc_url",
|
||||||
kyp->kyc_url));
|
kyp->kyc_url));
|
||||||
}
|
}
|
||||||
@ -684,8 +639,6 @@ TEH_handler_kyc_check (
|
|||||||
&sig),
|
&sig),
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
||||||
&pub),
|
&pub),
|
||||||
GNUNET_JSON_pack_uint64 ("aml_status",
|
|
||||||
kyp->aml_status),
|
|
||||||
GNUNET_JSON_pack_object_incref ("kyc_details",
|
GNUNET_JSON_pack_object_incref ("kyc_details",
|
||||||
kyp->kyc_details),
|
kyp->kyc_details),
|
||||||
GNUNET_JSON_pack_timestamp ("now",
|
GNUNET_JSON_pack_timestamp ("now",
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "taler_attributes.h"
|
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_kyclogic_lib.h"
|
#include "taler_kyclogic_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
@ -170,7 +169,6 @@ TEH_kyc_proof_cleanup (void)
|
|||||||
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
|
* @param provider_user_id set to user ID at the provider, or NULL if not supported or unknown
|
||||||
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
||||||
* @param expiration until when is the KYC check valid
|
* @param expiration until when is the KYC check valid
|
||||||
* @param attributes user attributes returned by the provider
|
|
||||||
* @param http_status HTTP status code of @a response
|
* @param http_status HTTP status code of @a response
|
||||||
* @param[in] response to return to the HTTP client
|
* @param[in] response to return to the HTTP client
|
||||||
*/
|
*/
|
||||||
@ -181,7 +179,6 @@ proof_cb (
|
|||||||
const char *provider_user_id,
|
const char *provider_user_id,
|
||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
struct GNUNET_TIME_Absolute expiration,
|
struct GNUNET_TIME_Absolute expiration,
|
||||||
const json_t *attributes,
|
|
||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
struct MHD_Response *response)
|
struct MHD_Response *response)
|
||||||
{
|
{
|
||||||
@ -196,41 +193,7 @@ proof_cb (
|
|||||||
if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
|
if (TALER_KYCLOGIC_STATUS_SUCCESS == status)
|
||||||
{
|
{
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
size_t eas;
|
|
||||||
void *ea;
|
|
||||||
const char *birthdate;
|
|
||||||
struct GNUNET_ShortHashCode kyc_prox;
|
|
||||||
|
|
||||||
TALER_CRYPTO_attributes_to_kyc_prox (attributes,
|
|
||||||
&kyc_prox);
|
|
||||||
birthdate = json_string_value (json_object_get (attributes,
|
|
||||||
TALER_ATTRIBUTE_BIRTHDATE));
|
|
||||||
TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
|
|
||||||
attributes,
|
|
||||||
&ea,
|
|
||||||
&eas);
|
|
||||||
qs = TEH_plugin->insert_kyc_attributes (
|
|
||||||
TEH_plugin->cls,
|
|
||||||
&kpc->h_payto,
|
|
||||||
&kyc_prox,
|
|
||||||
kpc->provider_section,
|
|
||||||
birthdate,
|
|
||||||
GNUNET_TIME_timestamp_get (),
|
|
||||||
GNUNET_TIME_absolute_to_timestamp (expiration),
|
|
||||||
eas,
|
|
||||||
ea);
|
|
||||||
GNUNET_free (ea);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
if (NULL != response)
|
|
||||||
MHD_destroy_response (response);
|
|
||||||
kpc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
|
|
||||||
kpc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_attributes");
|
|
||||||
GNUNET_async_scope_restore (&old_scope);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls,
|
qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls,
|
||||||
kpc->process_row,
|
kpc->process_row,
|
||||||
kpc->provider_section,
|
kpc->provider_section,
|
||||||
|
@ -54,7 +54,7 @@ struct KycRequestContext
|
|||||||
/**
|
/**
|
||||||
* Name of the required check.
|
* Name of the required check.
|
||||||
*/
|
*/
|
||||||
char *required;
|
const char *required;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,34 +109,22 @@ wallet_kyc_check (void *cls,
|
|||||||
struct KycRequestContext *krc = cls;
|
struct KycRequestContext *krc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
krc->required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
TALER_KYCLOGIC_KYC_TRIGGER_WALLET_BALANCE,
|
||||||
&krc->h_payto,
|
&krc->h_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&balance_iterator,
|
&balance_iterator,
|
||||||
krc,
|
krc);
|
||||||
&krc->required);
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
if (qs < 0)
|
"KYC check required at %s is `%s'\n",
|
||||||
{
|
TALER_amount2s (&krc->balance),
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
krc->required);
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (NULL == krc->required)
|
if (NULL == krc->required)
|
||||||
{
|
{
|
||||||
krc->kyc.ok = true;
|
krc->kyc.ok = true;
|
||||||
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"KYC check required at %s is `%s'\n",
|
|
||||||
TALER_amount2s (&krc->balance),
|
|
||||||
krc->required);
|
|
||||||
krc->kyc.ok = false;
|
krc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (TEH_plugin->cls,
|
qs = TEH_plugin->insert_kyc_requirement_for_account (TEH_plugin->cls,
|
||||||
krc->required,
|
krc->required,
|
||||||
@ -237,7 +225,6 @@ TEH_handler_kyc_wallet (
|
|||||||
NULL,
|
NULL,
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
GNUNET_free (krc.required);
|
|
||||||
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
||||||
&krc.h_payto,
|
&krc.h_payto,
|
||||||
&krc.kyc);
|
&krc.kyc);
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include "taler_attributes.h"
|
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler_kyclogic_lib.h"
|
#include "taler_kyclogic_lib.h"
|
||||||
@ -141,7 +140,8 @@ TEH_kyc_webhook_cleanup (void)
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with the result of a KYC webhook operation.
|
* Function called with the result of a webhook
|
||||||
|
* operation.
|
||||||
*
|
*
|
||||||
* Note that the "decref" for the @a response
|
* Note that the "decref" for the @a response
|
||||||
* will be done by the plugin.
|
* will be done by the plugin.
|
||||||
@ -154,7 +154,6 @@ TEH_kyc_webhook_cleanup (void)
|
|||||||
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
* @param provider_legitimization_id set to legitimization process ID at the provider, or NULL if not supported or unknown
|
||||||
* @param status KYC status
|
* @param status KYC status
|
||||||
* @param expiration until when is the KYC check valid
|
* @param expiration until when is the KYC check valid
|
||||||
* @param attributes user attributes returned by the provider
|
|
||||||
* @param http_status HTTP status code of @a response
|
* @param http_status HTTP status code of @a response
|
||||||
* @param[in] response to return to the HTTP client
|
* @param[in] response to return to the HTTP client
|
||||||
*/
|
*/
|
||||||
@ -168,7 +167,6 @@ webhook_finished_cb (
|
|||||||
const char *provider_legitimization_id,
|
const char *provider_legitimization_id,
|
||||||
enum TALER_KYCLOGIC_KycStatus status,
|
enum TALER_KYCLOGIC_KycStatus status,
|
||||||
struct GNUNET_TIME_Absolute expiration,
|
struct GNUNET_TIME_Absolute expiration,
|
||||||
const json_t *attributes,
|
|
||||||
unsigned int http_status,
|
unsigned int http_status,
|
||||||
struct MHD_Response *response)
|
struct MHD_Response *response)
|
||||||
{
|
{
|
||||||
@ -181,39 +179,7 @@ webhook_finished_cb (
|
|||||||
/* _successfully_ resumed case */
|
/* _successfully_ resumed case */
|
||||||
{
|
{
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
size_t eas;
|
|
||||||
void *ea;
|
|
||||||
const char *birthdate;
|
|
||||||
struct GNUNET_ShortHashCode kyc_prox;
|
|
||||||
|
|
||||||
TALER_CRYPTO_attributes_to_kyc_prox (attributes,
|
|
||||||
&kyc_prox);
|
|
||||||
birthdate = json_string_value (json_object_get (attributes,
|
|
||||||
TALER_ATTRIBUTE_BIRTHDATE));
|
|
||||||
TALER_CRYPTO_kyc_attributes_encrypt (&TEH_attribute_key,
|
|
||||||
attributes,
|
|
||||||
&ea,
|
|
||||||
&eas);
|
|
||||||
qs = TEH_plugin->insert_kyc_attributes (
|
|
||||||
TEH_plugin->cls,
|
|
||||||
account_id,
|
|
||||||
&kyc_prox,
|
|
||||||
provider_section,
|
|
||||||
birthdate,
|
|
||||||
GNUNET_TIME_timestamp_get (),
|
|
||||||
GNUNET_TIME_absolute_to_timestamp (expiration),
|
|
||||||
eas,
|
|
||||||
ea);
|
|
||||||
GNUNET_free (ea);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
kwh->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_attributes");
|
|
||||||
kwh->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
|
|
||||||
kwh_resume (kwh);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls,
|
qs = TEH_plugin->update_kyc_process_by_row (TEH_plugin->cls,
|
||||||
process_row,
|
process_row,
|
||||||
provider_section,
|
provider_section,
|
||||||
@ -296,12 +262,11 @@ handler_kyc_webhook_generic (
|
|||||||
rc->rh_ctx = kwh;
|
rc->rh_ctx = kwh;
|
||||||
rc->rh_cleaner = &clean_kwh;
|
rc->rh_cleaner = &clean_kwh;
|
||||||
|
|
||||||
if ( (NULL == args[0]) ||
|
if (GNUNET_OK !=
|
||||||
(GNUNET_OK !=
|
|
||||||
TALER_KYCLOGIC_lookup_logic (args[0],
|
TALER_KYCLOGIC_lookup_logic (args[0],
|
||||||
&kwh->plugin,
|
&kwh->plugin,
|
||||||
&kwh->pd,
|
&kwh->pd,
|
||||||
&kwh->provider_section)) )
|
&kwh->provider_section))
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"KYC logic `%s' unknown (check KYC provider configuration)\n",
|
"KYC logic `%s' unknown (check KYC provider configuration)\n",
|
||||||
@ -309,7 +274,7 @@ handler_kyc_webhook_generic (
|
|||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
|
TALER_EC_EXCHANGE_KYC_GENERIC_LOGIC_UNKNOWN,
|
||||||
args[0]);
|
"$NAME");
|
||||||
}
|
}
|
||||||
kwh->wh = kwh->plugin->webhook (kwh->plugin->cls,
|
kwh->wh = kwh->plugin->webhook (kwh->plugin->cls,
|
||||||
kwh->pd,
|
kwh->pd,
|
||||||
|
@ -174,32 +174,6 @@ TEH_handler_management_post_drain (
|
|||||||
const json_t *root);
|
const json_t *root);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a POST "/management/aml-officers" request.
|
|
||||||
*
|
|
||||||
* @param connection the MHD connection to handle
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_management_aml_officers (
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handle a POST "/management/partners" request.
|
|
||||||
*
|
|
||||||
* @param connection the MHD connection to handle
|
|
||||||
* @param root uploaded JSON data
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_management_partners (
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const json_t *root);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize extension configuration handling.
|
* Initialize extension configuration handling.
|
||||||
*
|
*
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_management_aml-officers.c
|
|
||||||
* @brief Handle request to update AML officer status
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <gnunet/gnunet_json_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler_signatures.h"
|
|
||||||
#include "taler-exchange-httpd_management.h"
|
|
||||||
#include "taler-exchange-httpd_responses.h"
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* How often do we try the DB operation at most?
|
|
||||||
*/
|
|
||||||
#define MAX_RETRIES 10
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_management_aml_officers (
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const json_t *root)
|
|
||||||
{
|
|
||||||
struct TALER_AmlOfficerPublicKeyP officer_pub;
|
|
||||||
const char *officer_name;
|
|
||||||
struct GNUNET_TIME_Timestamp change_date;
|
|
||||||
bool is_active;
|
|
||||||
bool read_only;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("officer_pub",
|
|
||||||
&officer_pub),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
|
||||||
&master_sig),
|
|
||||||
GNUNET_JSON_spec_bool ("is_active",
|
|
||||||
&is_active),
|
|
||||||
GNUNET_JSON_spec_bool ("read_only",
|
|
||||||
&read_only),
|
|
||||||
GNUNET_JSON_spec_string ("officer_name",
|
|
||||||
&officer_name),
|
|
||||||
GNUNET_JSON_spec_timestamp ("change_date",
|
|
||||||
&change_date),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
enum GNUNET_GenericReturnValue res;
|
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (connection,
|
|
||||||
root,
|
|
||||||
spec);
|
|
||||||
if (GNUNET_SYSERR == res)
|
|
||||||
return MHD_NO; /* hard failure */
|
|
||||||
if (GNUNET_NO == res)
|
|
||||||
return MHD_YES; /* failure */
|
|
||||||
}
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_exchange_offline_aml_officer_status_verify (
|
|
||||||
&officer_pub,
|
|
||||||
officer_name,
|
|
||||||
change_date,
|
|
||||||
is_active,
|
|
||||||
read_only,
|
|
||||||
&TEH_master_public_key,
|
|
||||||
&master_sig))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_FORBIDDEN,
|
|
||||||
TALER_EC_EXCHANGE_MANAGEMENT_UPDATE_AML_OFFICER_SIGNATURE_INVALID,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
struct GNUNET_TIME_Timestamp last_date;
|
|
||||||
unsigned int retries_left = MAX_RETRIES;
|
|
||||||
|
|
||||||
do {
|
|
||||||
qs = TEH_plugin->insert_aml_officer (TEH_plugin->cls,
|
|
||||||
&officer_pub,
|
|
||||||
&master_sig,
|
|
||||||
officer_name,
|
|
||||||
is_active,
|
|
||||||
read_only,
|
|
||||||
change_date,
|
|
||||||
&last_date);
|
|
||||||
if (0 == --retries_left)
|
|
||||||
break;
|
|
||||||
} while (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_aml_officer");
|
|
||||||
}
|
|
||||||
if (GNUNET_TIME_timestamp_cmp (last_date,
|
|
||||||
>,
|
|
||||||
change_date))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_CONFLICT,
|
|
||||||
TALER_EC_EXCHANGE_MANAGEMENT_AML_OFFICERS_MORE_RECENT_PRESENT,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TALER_MHD_reply_static (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_management_aml-officers.c */
|
|
@ -1,132 +0,0 @@
|
|||||||
/*
|
|
||||||
This file is part of TALER
|
|
||||||
Copyright (C) 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 published by the Free Software
|
|
||||||
Foundation; either version 3, or (at your option) any later version.
|
|
||||||
|
|
||||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License along with
|
|
||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* @file taler-exchange-httpd_management_partners.c
|
|
||||||
* @brief Handle request to add exchange partner
|
|
||||||
* @author Christian Grothoff
|
|
||||||
*/
|
|
||||||
#include "platform.h"
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
|
||||||
#include <gnunet/gnunet_json_lib.h>
|
|
||||||
#include <jansson.h>
|
|
||||||
#include <microhttpd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include "taler_json_lib.h"
|
|
||||||
#include "taler_mhd_lib.h"
|
|
||||||
#include "taler_signatures.h"
|
|
||||||
#include "taler-exchange-httpd_management.h"
|
|
||||||
#include "taler-exchange-httpd_responses.h"
|
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_handler_management_partners (
|
|
||||||
struct MHD_Connection *connection,
|
|
||||||
const json_t *root)
|
|
||||||
{
|
|
||||||
struct TALER_MasterPublicKeyP partner_pub;
|
|
||||||
struct GNUNET_TIME_Timestamp start_date;
|
|
||||||
struct GNUNET_TIME_Timestamp end_date;
|
|
||||||
struct GNUNET_TIME_Relative wad_frequency;
|
|
||||||
struct TALER_Amount wad_fee;
|
|
||||||
const char *partner_base_url;
|
|
||||||
struct TALER_MasterSignatureP master_sig;
|
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("partner_pub",
|
|
||||||
&partner_pub),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
|
||||||
&master_sig),
|
|
||||||
GNUNET_JSON_spec_string ("partner_base_url",
|
|
||||||
&partner_base_url),
|
|
||||||
TALER_JSON_spec_amount ("wad_fee",
|
|
||||||
TEH_currency,
|
|
||||||
&wad_fee),
|
|
||||||
GNUNET_JSON_spec_timestamp ("start_date",
|
|
||||||
&start_date),
|
|
||||||
GNUNET_JSON_spec_timestamp ("end_date",
|
|
||||||
&end_date),
|
|
||||||
GNUNET_JSON_spec_relative_time ("wad_frequency",
|
|
||||||
&wad_frequency),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
|
||||||
enum GNUNET_GenericReturnValue res;
|
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (connection,
|
|
||||||
root,
|
|
||||||
spec);
|
|
||||||
if (GNUNET_SYSERR == res)
|
|
||||||
return MHD_NO; /* hard failure */
|
|
||||||
if (GNUNET_NO == res)
|
|
||||||
return MHD_YES; /* failure */
|
|
||||||
}
|
|
||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
|
||||||
if (GNUNET_OK !=
|
|
||||||
TALER_exchange_offline_partner_details_verify (
|
|
||||||
&partner_pub,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
wad_frequency,
|
|
||||||
&wad_fee,
|
|
||||||
partner_base_url,
|
|
||||||
&TEH_master_public_key,
|
|
||||||
&master_sig))
|
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_FORBIDDEN,
|
|
||||||
TALER_EC_EXCHANGE_MANAGEMENT_ADD_PARTNER_SIGNATURE_INVALID,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = TEH_plugin->insert_partner (TEH_plugin->cls,
|
|
||||||
&partner_pub,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
wad_frequency,
|
|
||||||
&wad_fee,
|
|
||||||
partner_base_url,
|
|
||||||
&master_sig);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"add_partner");
|
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
{
|
|
||||||
/* FIXME: check for idempotency! */
|
|
||||||
return TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_CONFLICT,
|
|
||||||
TALER_EC_EXCHANGE_MANAGEMENT_ADD_PARTNER_DATA_CONFLICT,
|
|
||||||
NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TALER_MHD_reply_static (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_NO_CONTENT,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_management_partners.c */
|
|
@ -34,20 +34,18 @@ enum TEH_MetricTypeRequest
|
|||||||
TEH_MT_REQUEST_OTHER = 0,
|
TEH_MT_REQUEST_OTHER = 0,
|
||||||
TEH_MT_REQUEST_DEPOSIT = 1,
|
TEH_MT_REQUEST_DEPOSIT = 1,
|
||||||
TEH_MT_REQUEST_WITHDRAW = 2,
|
TEH_MT_REQUEST_WITHDRAW = 2,
|
||||||
TEH_MT_REQUEST_AGE_WITHDRAW = 3,
|
TEH_MT_REQUEST_MELT = 3,
|
||||||
TEH_MT_REQUEST_MELT = 4,
|
TEH_MT_REQUEST_PURSE_CREATE = 4,
|
||||||
TEH_MT_REQUEST_PURSE_CREATE = 5,
|
TEH_MT_REQUEST_PURSE_MERGE = 5,
|
||||||
TEH_MT_REQUEST_PURSE_MERGE = 6,
|
TEH_MT_REQUEST_RESERVE_PURSE = 6,
|
||||||
TEH_MT_REQUEST_RESERVE_PURSE = 7,
|
TEH_MT_REQUEST_PURSE_DEPOSIT = 7,
|
||||||
TEH_MT_REQUEST_PURSE_DEPOSIT = 8,
|
TEH_MT_REQUEST_IDEMPOTENT_DEPOSIT = 8,
|
||||||
TEH_MT_REQUEST_IDEMPOTENT_DEPOSIT = 9,
|
TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW = 9,
|
||||||
TEH_MT_REQUEST_IDEMPOTENT_WITHDRAW = 10,
|
TEH_MT_REQUEST_IDEMPOTENT_MELT = 10,
|
||||||
TEH_MT_REQUEST_IDEMPOTENT_AGE_WITHDRAW = 11,
|
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 11,
|
||||||
TEH_MT_REQUEST_IDEMPOTENT_MELT = 12,
|
TEH_MT_REQUEST_BATCH_DEPOSIT = 12,
|
||||||
TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW = 13,
|
TEH_MT_REQUEST_POLICY_FULFILLMENT = 13,
|
||||||
TEH_MT_REQUEST_BATCH_DEPOSIT = 14,
|
TEH_MT_REQUEST_COUNT = 14 /* MUST BE LAST! */
|
||||||
TEH_MT_REQUEST_POLICY_FULFILLMENT = 15,
|
|
||||||
TEH_MT_REQUEST_COUNT = 16 /* MUST BE LAST! */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,12 +55,10 @@ enum TEH_MetricTypeSuccess
|
|||||||
{
|
{
|
||||||
TEH_MT_SUCCESS_DEPOSIT = 0,
|
TEH_MT_SUCCESS_DEPOSIT = 0,
|
||||||
TEH_MT_SUCCESS_WITHDRAW = 1,
|
TEH_MT_SUCCESS_WITHDRAW = 1,
|
||||||
TEH_MT_SUCCESS_AGE_WITHDRAW = 2,
|
TEH_MT_SUCCESS_BATCH_WITHDRAW = 2,
|
||||||
TEH_MT_SUCCESS_BATCH_WITHDRAW = 3,
|
TEH_MT_SUCCESS_MELT = 3,
|
||||||
TEH_MT_SUCCESS_MELT = 4,
|
TEH_MT_SUCCESS_REFRESH_REVEAL = 4,
|
||||||
TEH_MT_SUCCESS_REFRESH_REVEAL = 5,
|
TEH_MT_SUCCESS_COUNT = 5 /* MUST BE LAST! */
|
||||||
TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL = 6,
|
|
||||||
TEH_MT_SUCCESS_COUNT = 7 /* MUST BE LAST! */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -435,8 +435,6 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc,
|
|||||||
&exchange_sig),
|
&exchange_sig),
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
||||||
&exchange_pub),
|
&exchange_pub),
|
||||||
GNUNET_JSON_pack_timestamp ("purse_expiration",
|
|
||||||
gc->purse_expiration),
|
|
||||||
GNUNET_JSON_pack_allow_null (
|
GNUNET_JSON_pack_allow_null (
|
||||||
GNUNET_JSON_pack_timestamp ("merge_timestamp",
|
GNUNET_JSON_pack_timestamp ("merge_timestamp",
|
||||||
gc->merge_timestamp)),
|
gc->merge_timestamp)),
|
||||||
|
@ -280,47 +280,23 @@ merge_transaction (void *cls,
|
|||||||
bool in_conflict = true;
|
bool in_conflict = true;
|
||||||
bool no_balance = true;
|
bool no_balance = true;
|
||||||
bool no_partner = true;
|
bool no_partner = true;
|
||||||
char *required;
|
const char *required;
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
|
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
|
||||||
&pcc->h_payto,
|
&pcc->h_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&amount_iterator,
|
&amount_iterator,
|
||||||
pcc,
|
pcc);
|
||||||
&required);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret =
|
|
||||||
TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (NULL != required)
|
if (NULL != required)
|
||||||
{
|
{
|
||||||
pcc->kyc.ok = false;
|
pcc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
required,
|
required,
|
||||||
&pcc->h_payto,
|
&pcc->h_payto,
|
||||||
&pcc->kyc.requirement_row);
|
&pcc->kyc.requirement_row);
|
||||||
GNUNET_free (required);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret
|
|
||||||
= TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
pcc->kyc.ok = true;
|
pcc->kyc.ok = true;
|
||||||
qs = TEH_plugin->do_purse_merge (
|
qs = TEH_plugin->do_purse_merge (
|
||||||
@ -338,7 +314,8 @@ merge_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
return qs;
|
return qs;
|
||||||
GNUNET_break (0);
|
TALER_LOG_WARNING (
|
||||||
|
"Failed to store merge purse information in database\n");
|
||||||
*mhd_ret =
|
*mhd_ret =
|
||||||
TALER_MHD_reply_with_error (connection,
|
TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
@ -773,17 +773,12 @@ clean_age:
|
|||||||
NULL);
|
NULL);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
||||||
{
|
|
||||||
rrcs[i].coin_sig = bss[i];
|
rrcs[i].coin_sig = bss[i];
|
||||||
rrcs[i].blinded_planchet = rcds[i].blinded_planchet;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
"Signatures ready, starting DB interaction\n");
|
"Signatures ready, starting DB interaction\n");
|
||||||
|
|
||||||
|
|
||||||
for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
|
for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
|
||||||
{
|
{
|
||||||
bool changed;
|
bool changed;
|
||||||
@ -800,7 +795,12 @@ clean_age:
|
|||||||
NULL);
|
NULL);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
||||||
|
{
|
||||||
|
struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
|
||||||
|
|
||||||
|
rrc->blinded_planchet = rcds[i].blinded_planchet;
|
||||||
|
}
|
||||||
qs = TEH_plugin->insert_refresh_reveal (
|
qs = TEH_plugin->insert_refresh_reveal (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
melt_serial_id,
|
melt_serial_id,
|
||||||
|
@ -76,14 +76,14 @@ struct ReserveAttestContext
|
|||||||
struct TALER_ReserveSignatureP reserve_sig;
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attributes we are affirming. JSON object.
|
* Attributes we are affirming.
|
||||||
*/
|
*/
|
||||||
json_t *json_attest;
|
json_t *json_attest;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Database error codes encountered.
|
* Error code encountered in interaction with KYC provider.
|
||||||
*/
|
*/
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum TALER_ErrorCode ec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true if we did not find the reserve.
|
* Set to true if we did not find the reserve.
|
||||||
@ -140,11 +140,7 @@ reply_reserve_attest_success (struct MHD_Connection *connection,
|
|||||||
&exchange_sig),
|
&exchange_sig),
|
||||||
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
GNUNET_JSON_pack_data_auto ("exchange_pub",
|
||||||
&exchange_pub),
|
&exchange_pub),
|
||||||
GNUNET_JSON_pack_timestamp ("exchange_timestamp",
|
GNUNET_JSON_pack_array_steal ("attest",
|
||||||
now),
|
|
||||||
GNUNET_JSON_pack_timestamp ("expiration_time",
|
|
||||||
rhc->etime),
|
|
||||||
GNUNET_JSON_pack_object_steal ("attributes",
|
|
||||||
rhc->json_attest));
|
rhc->json_attest));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,36 +152,37 @@ reply_reserve_attest_success (struct MHD_Connection *connection,
|
|||||||
* set based on the details requested by the client.
|
* set based on the details requested by the client.
|
||||||
*
|
*
|
||||||
* @param cls our `struct ReserveAttestContext *`
|
* @param cls our `struct ReserveAttestContext *`
|
||||||
* @param h_payto account for which the attribute data is stored
|
* @param provider_section KYC provider configuration section
|
||||||
* @param provider_section provider that must be checked
|
* @param provider_user_id UID at a provider (can be NULL)
|
||||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
* @param legi_id legitimization process ID (can be NULL)
|
||||||
* digits can be 0 if exact day, month or year are unknown
|
|
||||||
* @param collection_time when was the data collected
|
|
||||||
* @param expiration_time when does the data expire
|
|
||||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
|
||||||
* @param enc_attributes encrypted attribute data
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
kyc_process_cb (void *cls,
|
kyc_process_cb (void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
|
||||||
const char *provider_section,
|
const char *provider_section,
|
||||||
const char *birthdate,
|
const char *provider_user_id,
|
||||||
struct GNUNET_TIME_Timestamp collection_time,
|
const char *legi_id)
|
||||||
struct GNUNET_TIME_Timestamp expiration_time,
|
|
||||||
size_t enc_attributes_size,
|
|
||||||
const void *enc_attributes)
|
|
||||||
{
|
{
|
||||||
struct ReserveAttestContext *rsc = cls;
|
struct ReserveAttestContext *rsc = cls;
|
||||||
|
struct GNUNET_TIME_Timestamp etime;
|
||||||
json_t *attrs;
|
json_t *attrs;
|
||||||
json_t *val;
|
|
||||||
const char *name;
|
|
||||||
bool match = false;
|
bool match = false;
|
||||||
|
|
||||||
if (GNUNET_TIME_absolute_is_past (expiration_time.abs_time))
|
rsc->ec = TALER_KYCLOGIC_user_to_attributes (provider_section,
|
||||||
|
provider_user_id,
|
||||||
|
legi_id,
|
||||||
|
&etime,
|
||||||
|
&attrs);
|
||||||
|
if (TALER_EC_NONE != rsc->ec)
|
||||||
return;
|
return;
|
||||||
attrs = TALER_CRYPTO_kyc_attributes_decrypt (&TEH_attribute_key,
|
if (GNUNET_TIME_absolute_is_past (etime.abs_time))
|
||||||
enc_attributes,
|
{
|
||||||
enc_attributes_size);
|
json_decref (attrs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
json_t *val;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
json_object_foreach (attrs, name, val)
|
json_object_foreach (attrs, name, val)
|
||||||
{
|
{
|
||||||
bool requested = false;
|
bool requested = false;
|
||||||
@ -205,22 +202,18 @@ kyc_process_cb (void *cls,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! requested)
|
if (! requested)
|
||||||
{
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
|
||||||
"Skipping attribute `%s': not requested\n",
|
|
||||||
name);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
match = true;
|
match = true;
|
||||||
GNUNET_assert (0 ==
|
GNUNET_assert (0 ==
|
||||||
json_object_set (rsc->json_attest, /* NOT set_new! */
|
json_object_set (rsc->json_attest, /* NOT set_new! */
|
||||||
name,
|
name,
|
||||||
val));
|
val));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
json_decref (attrs);
|
json_decref (attrs);
|
||||||
if (! match)
|
if (! match)
|
||||||
return;
|
return;
|
||||||
rsc->etime = GNUNET_TIME_timestamp_min (expiration_time,
|
rsc->etime = GNUNET_TIME_timestamp_min (etime,
|
||||||
rsc->etime);
|
rsc->etime);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,9 +241,9 @@ reserve_attest_transaction (void *cls,
|
|||||||
struct ReserveAttestContext *rsc = cls;
|
struct ReserveAttestContext *rsc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
rsc->json_attest = json_object ();
|
rsc->json_attest = json_array ();
|
||||||
GNUNET_assert (NULL != rsc->json_attest);
|
GNUNET_assert (NULL != rsc->json_attest);
|
||||||
qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
|
qs = TEH_plugin->iterate_kyc_reference (TEH_plugin->cls,
|
||||||
&rsc->h_payto,
|
&rsc->h_payto,
|
||||||
&kyc_process_cb,
|
&kyc_process_cb,
|
||||||
rsc);
|
rsc);
|
||||||
@ -262,7 +255,7 @@ reserve_attest_transaction (void *cls,
|
|||||||
= TALER_MHD_reply_with_error (connection,
|
= TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"select_kyc_attributes");
|
"iterate_kyc_reference");
|
||||||
return qs;
|
return qs;
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
@ -380,8 +373,16 @@ TEH_handler_reserves_attest (struct TEH_RequestContext *rc,
|
|||||||
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
|
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
|
||||||
args[0]);
|
args[0]);
|
||||||
}
|
}
|
||||||
return reply_reserve_attest_success (rc->connection,
|
if (TALER_EC_NONE != rsc.ec)
|
||||||
|
{
|
||||||
|
json_decref (rsc.json_attest);
|
||||||
|
return TALER_MHD_reply_with_ec (rc->connection,
|
||||||
|
rsc.ec,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
mhd_ret = reply_reserve_attest_success (rc->connection,
|
||||||
&rsc);
|
&rsc);
|
||||||
|
return mhd_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -178,13 +178,14 @@ reserve_close_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
struct ReserveCloseContext *rcc = cls;
|
struct ReserveCloseContext *rcc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
struct TALER_Amount balance;
|
||||||
char *payto_uri = NULL;
|
char *payto_uri = NULL;
|
||||||
const struct TALER_WireFeeSet *wf;
|
const struct TALER_WireFeeSet *wf;
|
||||||
|
|
||||||
qs = TEH_plugin->select_reserve_close_info (
|
qs = TEH_plugin->select_reserve_close_info (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
rcc->reserve_pub,
|
rcc->reserve_pub,
|
||||||
&rcc->balance,
|
&balance,
|
||||||
&payto_uri);
|
&payto_uri);
|
||||||
switch (qs)
|
switch (qs)
|
||||||
{
|
{
|
||||||
@ -225,34 +226,19 @@ reserve_close_transaction (void *cls,
|
|||||||
(0 != strcmp (payto_uri,
|
(0 != strcmp (payto_uri,
|
||||||
rcc->payto_uri)) ) )
|
rcc->payto_uri)) ) )
|
||||||
{
|
{
|
||||||
/* KYC check may be needed: we're not returning
|
const char *kyc_needed;
|
||||||
the money to the account that funded the reserve
|
|
||||||
in the first place. */
|
|
||||||
char *kyc_needed;
|
|
||||||
|
|
||||||
TALER_payto_hash (rcc->payto_uri,
|
TALER_payto_hash (rcc->payto_uri,
|
||||||
&rcc->kyc_payto);
|
&rcc->kyc_payto);
|
||||||
rcc->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
rcc->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
kyc_needed
|
||||||
|
= TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE,
|
TALER_KYCLOGIC_KYC_TRIGGER_RESERVE_CLOSE,
|
||||||
&rcc->kyc_payto,
|
&rcc->kyc_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&amount_it,
|
&amount_it,
|
||||||
rcc,
|
rcc);
|
||||||
&kyc_needed);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret
|
|
||||||
= TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"iterate_reserve_close_info");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (rcc->qs < 0)
|
if (rcc->qs < 0)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == rcc->qs)
|
if (GNUNET_DB_STATUS_SOFT_ERROR == rcc->qs)
|
||||||
@ -265,26 +251,12 @@ reserve_close_transaction (void *cls,
|
|||||||
"iterate_reserve_close_info");
|
"iterate_reserve_close_info");
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
if (NULL != kyc_needed)
|
|
||||||
{
|
|
||||||
rcc->kyc.ok = false;
|
rcc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
kyc_needed,
|
kyc_needed,
|
||||||
&rcc->kyc_payto,
|
&rcc->kyc_payto,
|
||||||
&rcc->kyc.requirement_row);
|
&rcc->kyc.requirement_row);
|
||||||
GNUNET_free (kyc_needed);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret
|
|
||||||
= TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rcc->kyc.ok = true;
|
rcc->kyc.ok = true;
|
||||||
@ -312,7 +284,7 @@ reserve_close_transaction (void *cls,
|
|||||||
|
|
||||||
if (0 >
|
if (0 >
|
||||||
TALER_amount_subtract (&rcc->wire_amount,
|
TALER_amount_subtract (&rcc->wire_amount,
|
||||||
&rcc->balance,
|
&balance,
|
||||||
&wf->closing))
|
&wf->closing))
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
@ -330,7 +302,7 @@ reserve_close_transaction (void *cls,
|
|||||||
payto_uri,
|
payto_uri,
|
||||||
&rcc->reserve_sig,
|
&rcc->reserve_sig,
|
||||||
rcc->timestamp,
|
rcc->timestamp,
|
||||||
&rcc->balance,
|
&balance,
|
||||||
&wf->closing);
|
&wf->closing);
|
||||||
GNUNET_free (payto_uri);
|
GNUNET_free (payto_uri);
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014-2023 Taler Systems SA
|
Copyright (C) 2014-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -62,16 +62,6 @@ struct ReservePoller
|
|||||||
*/
|
*/
|
||||||
struct GNUNET_TIME_Absolute timeout;
|
struct GNUNET_TIME_Absolute timeout;
|
||||||
|
|
||||||
/**
|
|
||||||
* Public key of the reserve the inquiry is about.
|
|
||||||
*/
|
|
||||||
struct TALER_ReservePublicKeyP reserve_pub;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Balance of the reserve, set in the callback.
|
|
||||||
*/
|
|
||||||
struct TALER_Amount balance;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if we are still suspended.
|
* True if we are still suspended.
|
||||||
*/
|
*/
|
||||||
@ -94,10 +84,13 @@ static struct ReservePoller *rp_tail;
|
|||||||
void
|
void
|
||||||
TEH_reserves_get_cleanup ()
|
TEH_reserves_get_cleanup ()
|
||||||
{
|
{
|
||||||
for (struct ReservePoller *rp = rp_head;
|
struct ReservePoller *rp;
|
||||||
NULL != rp;
|
|
||||||
rp = rp->next)
|
while (NULL != (rp = rp_head))
|
||||||
{
|
{
|
||||||
|
GNUNET_CONTAINER_DLL_remove (rp_head,
|
||||||
|
rp_tail,
|
||||||
|
rp);
|
||||||
if (rp->suspended)
|
if (rp->suspended)
|
||||||
{
|
{
|
||||||
rp->suspended = false;
|
rp->suspended = false;
|
||||||
@ -122,14 +115,11 @@ rp_cleanup (struct TEH_RequestContext *rc)
|
|||||||
if (NULL != rp->eh)
|
if (NULL != rp->eh)
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Cancelling DB event listening on cleanup (odd unless during shutdown)\n");
|
"Cancelling DB event listening\n");
|
||||||
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
|
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
|
||||||
rp->eh);
|
rp->eh);
|
||||||
rp->eh = NULL;
|
rp->eh = NULL;
|
||||||
}
|
}
|
||||||
GNUNET_CONTAINER_DLL_remove (rp_head,
|
|
||||||
rp_tail,
|
|
||||||
rp);
|
|
||||||
GNUNET_free (rp);
|
GNUNET_free (rp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,15 +137,26 @@ db_event_cb (void *cls,
|
|||||||
const void *extra,
|
const void *extra,
|
||||||
size_t extra_size)
|
size_t extra_size)
|
||||||
{
|
{
|
||||||
struct ReservePoller *rp = cls;
|
struct TEH_RequestContext *rc = cls;
|
||||||
|
struct ReservePoller *rp = rc->rh_ctx;
|
||||||
struct GNUNET_AsyncScopeSave old_scope;
|
struct GNUNET_AsyncScopeSave old_scope;
|
||||||
|
|
||||||
(void) extra;
|
(void) extra;
|
||||||
(void) extra_size;
|
(void) extra_size;
|
||||||
|
if (NULL == rp)
|
||||||
|
return; /* event triggered while main transaction
|
||||||
|
was still running */
|
||||||
if (! rp->suspended)
|
if (! rp->suspended)
|
||||||
return; /* might get multiple wake-up events */
|
return; /* might get multiple wake-up events */
|
||||||
TEH_check_invariants ();
|
|
||||||
rp->suspended = false;
|
rp->suspended = false;
|
||||||
|
GNUNET_async_scope_enter (&rc->async_scope_id,
|
||||||
|
&old_scope);
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Resuming from long-polling on reserve\n");
|
||||||
|
TEH_check_invariants ();
|
||||||
|
GNUNET_CONTAINER_DLL_remove (rp_head,
|
||||||
|
rp_tail,
|
||||||
|
rp);
|
||||||
MHD_resume_connection (rp->connection);
|
MHD_resume_connection (rp->connection);
|
||||||
TALER_MHD_daemon_trigger ();
|
TALER_MHD_daemon_trigger ();
|
||||||
TEH_check_invariants ();
|
TEH_check_invariants ();
|
||||||
@ -163,29 +164,85 @@ db_event_cb (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure for #reserve_history_transaction.
|
||||||
|
*/
|
||||||
|
struct ReserveHistoryContext
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Public key of the reserve the inquiry is about.
|
||||||
|
*/
|
||||||
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Balance of the reserve, set in the callback.
|
||||||
|
*/
|
||||||
|
struct TALER_Amount balance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to true if we did not find the reserve.
|
||||||
|
*/
|
||||||
|
bool not_found;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function implementing /reserves/ GET transaction.
|
||||||
|
* Execute a /reserves/ GET. Given the public key of a reserve,
|
||||||
|
* return the associated transaction history. Runs the
|
||||||
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
|
* logic MUST NOT queue a MHD response. IF it returns an hard error,
|
||||||
|
* the transaction logic MUST queue a MHD response and set @a mhd_ret.
|
||||||
|
* IF it returns the soft error code, the function MAY be called again
|
||||||
|
* to retry and MUST not queue a MHD response.
|
||||||
|
*
|
||||||
|
* @param cls a `struct ReserveHistoryContext *`
|
||||||
|
* @param connection MHD request which triggered the transaction
|
||||||
|
* @param[out] mhd_ret set to MHD response status for @a connection,
|
||||||
|
* if transaction failed (!)
|
||||||
|
* @return transaction status
|
||||||
|
*/
|
||||||
|
static enum GNUNET_DB_QueryStatus
|
||||||
|
reserve_balance_transaction (void *cls,
|
||||||
|
struct MHD_Connection *connection,
|
||||||
|
MHD_RESULT *mhd_ret)
|
||||||
|
{
|
||||||
|
struct ReserveHistoryContext *rsc = cls;
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
|
qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
|
||||||
|
&rsc->reserve_pub,
|
||||||
|
&rsc->balance);
|
||||||
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
*mhd_ret
|
||||||
|
= TALER_MHD_reply_with_error (connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
|
"get_reserve_balance");
|
||||||
|
}
|
||||||
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
|
rsc->not_found = true;
|
||||||
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||||
|
rsc->not_found = false;
|
||||||
|
return qs;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
MHD_RESULT
|
||||||
TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
||||||
const char *const args[1])
|
const char *const args[1])
|
||||||
{
|
{
|
||||||
struct ReservePoller *rp = rc->rh_ctx;
|
struct ReserveHistoryContext rsc;
|
||||||
|
struct GNUNET_TIME_Relative timeout = GNUNET_TIME_UNIT_ZERO;
|
||||||
|
struct GNUNET_DB_EventHandler *eh = NULL;
|
||||||
|
|
||||||
if (NULL == rp)
|
|
||||||
{
|
|
||||||
struct GNUNET_TIME_Relative timeout
|
|
||||||
= GNUNET_TIME_UNIT_ZERO;
|
|
||||||
|
|
||||||
rp = GNUNET_new (struct ReservePoller);
|
|
||||||
rp->connection = rc->connection;
|
|
||||||
rc->rh_ctx = rp;
|
|
||||||
rc->rh_cleaner = &rp_cleanup;
|
|
||||||
GNUNET_CONTAINER_DLL_insert (rp_head,
|
|
||||||
rp_tail,
|
|
||||||
rp);
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_STRINGS_string_to_data (args[0],
|
GNUNET_STRINGS_string_to_data (args[0],
|
||||||
strlen (args[0]),
|
strlen (args[0]),
|
||||||
&rp->reserve_pub,
|
&rsc.reserve_pub,
|
||||||
sizeof (rp->reserve_pub)))
|
sizeof (rsc.reserve_pub)))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
@ -220,58 +277,47 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
|||||||
timeout_ms);
|
timeout_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
|
if ( (! GNUNET_TIME_relative_is_zero (timeout)) &&
|
||||||
}
|
(NULL == rc->rh_ctx) )
|
||||||
|
|
||||||
if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
|
|
||||||
(NULL == rp->eh) )
|
|
||||||
{
|
{
|
||||||
struct TALER_ReserveEventP rep = {
|
struct TALER_ReserveEventP rep = {
|
||||||
.header.size = htons (sizeof (rep)),
|
.header.size = htons (sizeof (rep)),
|
||||||
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
|
.header.type = htons (TALER_DBEVENT_EXCHANGE_RESERVE_INCOMING),
|
||||||
.reserve_pub = rp->reserve_pub
|
.reserve_pub = rsc.reserve_pub
|
||||||
};
|
};
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Starting DB event listening until %s\n",
|
"Starting DB event listening\n");
|
||||||
GNUNET_TIME_absolute2s (rp->timeout));
|
eh = TEH_plugin->event_listen (TEH_plugin->cls,
|
||||||
rp->eh = TEH_plugin->event_listen (
|
timeout,
|
||||||
TEH_plugin->cls,
|
|
||||||
GNUNET_TIME_absolute_get_remaining (rp->timeout),
|
|
||||||
&rep.header,
|
&rep.header,
|
||||||
&db_event_cb,
|
&db_event_cb,
|
||||||
rp);
|
rc);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
MHD_RESULT mhd_ret;
|
||||||
|
|
||||||
qs = TEH_plugin->get_reserve_balance (TEH_plugin->cls,
|
if (GNUNET_OK !=
|
||||||
&rp->reserve_pub,
|
TEH_DB_run_transaction (rc->connection,
|
||||||
&rp->balance);
|
"get reserve balance",
|
||||||
switch (qs)
|
TEH_MT_REQUEST_OTHER,
|
||||||
|
&mhd_ret,
|
||||||
|
&reserve_balance_transaction,
|
||||||
|
&rsc))
|
||||||
{
|
{
|
||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
if (NULL != eh)
|
||||||
GNUNET_break (0); /* single-shot query should never have soft-errors */
|
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
eh);
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
return mhd_ret;
|
||||||
TALER_EC_GENERIC_DB_SOFT_FAILURE,
|
}
|
||||||
"get_reserve_balance");
|
}
|
||||||
case GNUNET_DB_STATUS_HARD_ERROR:
|
/* generate proper response */
|
||||||
GNUNET_break (0);
|
if (rsc.not_found)
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
{
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
struct ReservePoller *rp = rc->rh_ctx;
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"get_reserve_balance");
|
if ( (NULL != rp) ||
|
||||||
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
(GNUNET_TIME_relative_is_zero (timeout)) )
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Got reserve balance of %s\n",
|
|
||||||
TALER_amount2s (&rp->balance));
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
TALER_JSON_pack_amount ("balance",
|
|
||||||
&rp->balance));
|
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
|
||||||
if (! GNUNET_TIME_absolute_is_future (rp->timeout))
|
|
||||||
{
|
{
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
@ -280,16 +326,29 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
|||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Long-polling on reserve for %s\n",
|
"Long-polling on reserve for %s\n",
|
||||||
GNUNET_STRINGS_relative_time_to_string (
|
GNUNET_STRINGS_relative_time_to_string (timeout,
|
||||||
GNUNET_TIME_absolute_get_remaining (rp->timeout),
|
GNUNET_YES));
|
||||||
true));
|
rp = GNUNET_new (struct ReservePoller);
|
||||||
|
rp->connection = rc->connection;
|
||||||
|
rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
|
||||||
|
rp->eh = eh;
|
||||||
|
rc->rh_ctx = rp;
|
||||||
|
rc->rh_cleaner = &rp_cleanup;
|
||||||
rp->suspended = true;
|
rp->suspended = true;
|
||||||
|
GNUNET_CONTAINER_DLL_insert (rp_head,
|
||||||
|
rp_tail,
|
||||||
|
rp);
|
||||||
MHD_suspend_connection (rc->connection);
|
MHD_suspend_connection (rc->connection);
|
||||||
return MHD_YES;
|
return MHD_YES;
|
||||||
}
|
}
|
||||||
}
|
if (NULL != eh)
|
||||||
GNUNET_break (0);
|
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
|
||||||
return MHD_NO;
|
eh);
|
||||||
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
|
rc->connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
TALER_JSON_pack_amount ("balance",
|
||||||
|
&rsc.balance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +50,11 @@ struct ReserveAttestContext
|
|||||||
*/
|
*/
|
||||||
json_t *attributes;
|
json_t *attributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error code encountered in interaction with KYC provider.
|
||||||
|
*/
|
||||||
|
enum TALER_ErrorCode ec;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true if we did not find the reserve.
|
* Set to true if we did not find the reserve.
|
||||||
*/
|
*/
|
||||||
@ -62,35 +67,32 @@ struct ReserveAttestContext
|
|||||||
* legitimization processes for the given user.
|
* legitimization processes for the given user.
|
||||||
*
|
*
|
||||||
* @param cls our `struct ReserveAttestContext *`
|
* @param cls our `struct ReserveAttestContext *`
|
||||||
* @param h_payto account for which the attribute data is stored
|
* @param provider_section KYC provider configuration section
|
||||||
* @param provider_section provider that must be checked
|
* @param provider_user_id UID at a provider (can be NULL)
|
||||||
* @param birthdate birthdate of user, in format YYYY-MM-DD; can be NULL;
|
* @param legi_id legitimization process ID (can be NULL)
|
||||||
* digits can be 0 if exact day, month or year are unknown
|
|
||||||
* @param collection_time when was the data collected
|
|
||||||
* @param expiration_time when does the data expire
|
|
||||||
* @param enc_attributes_size number of bytes in @a enc_attributes
|
|
||||||
* @param enc_attributes encrypted attribute data
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
kyc_process_cb (void *cls,
|
kyc_process_cb (void *cls,
|
||||||
const struct TALER_PaytoHashP *h_payto,
|
|
||||||
const char *provider_section,
|
const char *provider_section,
|
||||||
const char *birthdate,
|
const char *provider_user_id,
|
||||||
struct GNUNET_TIME_Timestamp collection_time,
|
const char *legi_id)
|
||||||
struct GNUNET_TIME_Timestamp expiration_time,
|
|
||||||
size_t enc_attributes_size,
|
|
||||||
const void *enc_attributes)
|
|
||||||
{
|
{
|
||||||
struct ReserveAttestContext *rsc = cls;
|
struct ReserveAttestContext *rsc = cls;
|
||||||
|
struct GNUNET_TIME_Timestamp etime;
|
||||||
json_t *attrs;
|
json_t *attrs;
|
||||||
|
|
||||||
|
rsc->ec = TALER_KYCLOGIC_user_to_attributes (provider_section,
|
||||||
|
provider_user_id,
|
||||||
|
legi_id,
|
||||||
|
&etime,
|
||||||
|
&attrs);
|
||||||
|
if (TALER_EC_NONE != rsc->ec)
|
||||||
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
json_t *val;
|
json_t *val;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
if (GNUNET_TIME_absolute_is_past (expiration_time.abs_time))
|
|
||||||
return;
|
|
||||||
attrs = TALER_CRYPTO_kyc_attributes_decrypt (&TEH_attribute_key,
|
|
||||||
enc_attributes,
|
|
||||||
enc_attributes_size);
|
|
||||||
json_object_foreach (attrs, name, val)
|
json_object_foreach (attrs, name, val)
|
||||||
{
|
{
|
||||||
bool duplicate = false;
|
bool duplicate = false;
|
||||||
@ -113,6 +115,7 @@ kyc_process_cb (void *cls,
|
|||||||
json_string (name)));
|
json_string (name)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -141,7 +144,7 @@ reserve_attest_transaction (void *cls,
|
|||||||
|
|
||||||
rsc->attributes = json_array ();
|
rsc->attributes = json_array ();
|
||||||
GNUNET_assert (NULL != rsc->attributes);
|
GNUNET_assert (NULL != rsc->attributes);
|
||||||
qs = TEH_plugin->select_kyc_attributes (TEH_plugin->cls,
|
qs = TEH_plugin->iterate_kyc_reference (TEH_plugin->cls,
|
||||||
&rsc->h_payto,
|
&rsc->h_payto,
|
||||||
&kyc_process_cb,
|
&kyc_process_cb,
|
||||||
rsc);
|
rsc);
|
||||||
@ -210,7 +213,6 @@ TEH_handler_reserves_get_attest (struct TEH_RequestContext *rc,
|
|||||||
&rsc))
|
&rsc))
|
||||||
{
|
{
|
||||||
json_decref (rsc.attributes);
|
json_decref (rsc.attributes);
|
||||||
rsc.attributes = NULL;
|
|
||||||
return mhd_ret;
|
return mhd_ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,16 +220,22 @@ TEH_handler_reserves_get_attest (struct TEH_RequestContext *rc,
|
|||||||
if (rsc.not_found)
|
if (rsc.not_found)
|
||||||
{
|
{
|
||||||
json_decref (rsc.attributes);
|
json_decref (rsc.attributes);
|
||||||
rsc.attributes = NULL;
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_NOT_FOUND,
|
MHD_HTTP_NOT_FOUND,
|
||||||
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
|
TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
|
||||||
args[0]);
|
args[0]);
|
||||||
}
|
}
|
||||||
|
if (TALER_EC_NONE != rsc.ec)
|
||||||
|
{
|
||||||
|
json_decref (rsc.attributes);
|
||||||
|
return TALER_MHD_reply_with_ec (rc->connection,
|
||||||
|
rsc.ec,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
MHD_HTTP_OK,
|
MHD_HTTP_OK,
|
||||||
GNUNET_JSON_pack_array_steal ("details",
|
GNUNET_JSON_pack_object_steal ("attributes",
|
||||||
rsc.attributes));
|
rsc.attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,47 +189,24 @@ purse_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
struct ReservePurseContext *rpc = cls;
|
struct ReservePurseContext *rpc = cls;
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
char *required;
|
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
const char *required;
|
||||||
|
|
||||||
|
required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
|
TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
|
||||||
&rpc->h_payto,
|
&rpc->h_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&amount_iterator,
|
&amount_iterator,
|
||||||
rpc,
|
rpc);
|
||||||
&required);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
|
||||||
return qs;
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret =
|
|
||||||
TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
|
||||||
}
|
|
||||||
if (NULL != required)
|
if (NULL != required)
|
||||||
{
|
{
|
||||||
rpc->kyc.ok = false;
|
rpc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
required,
|
required,
|
||||||
&rpc->h_payto,
|
&rpc->h_payto,
|
||||||
&rpc->kyc.requirement_row);
|
&rpc->kyc.requirement_row);
|
||||||
GNUNET_free (required);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret
|
|
||||||
= TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
rpc->kyc.ok = true;
|
rpc->kyc.ok = true;
|
||||||
|
|
||||||
@ -253,7 +230,8 @@ purse_transaction (void *cls,
|
|||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
return qs;
|
return qs;
|
||||||
GNUNET_break (0);
|
TALER_LOG_WARNING (
|
||||||
|
"Failed to store purse purse information in database\n");
|
||||||
*mhd_ret =
|
*mhd_ret =
|
||||||
TALER_MHD_reply_with_error (connection,
|
TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014-2023 Taler Systems SA
|
Copyright (C) 2014-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify it under the
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -1142,29 +1142,4 @@ TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection,
|
|
||||||
enum TALER_AmlDecisionState status)
|
|
||||||
{
|
|
||||||
enum TALER_ErrorCode ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
|
|
||||||
|
|
||||||
switch (status)
|
|
||||||
{
|
|
||||||
case TALER_AML_NORMAL:
|
|
||||||
GNUNET_break (0);
|
|
||||||
return MHD_NO;
|
|
||||||
case TALER_AML_PENDING:
|
|
||||||
ec = TALER_EC_EXCHANGE_GENERIC_AML_PENDING;
|
|
||||||
break;
|
|
||||||
case TALER_AML_FROZEN:
|
|
||||||
ec = TALER_EC_EXCHANGE_GENERIC_AML_FROZEN;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
connection,
|
|
||||||
MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS,
|
|
||||||
TALER_JSON_pack_ec (ec));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* end of taler-exchange-httpd_responses.c */
|
/* end of taler-exchange-httpd_responses.c */
|
||||||
|
@ -91,19 +91,6 @@ TEH_RESPONSE_reply_kyc_required (struct MHD_Connection *connection,
|
|||||||
const struct TALER_EXCHANGEDB_KycStatus *kyc);
|
const struct TALER_EXCHANGEDB_KycStatus *kyc);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send information that an AML process is blocking
|
|
||||||
* the operation right now.
|
|
||||||
*
|
|
||||||
* @param connection connection to the client
|
|
||||||
* @param status current AML status
|
|
||||||
* @return MHD result code
|
|
||||||
*/
|
|
||||||
MHD_RESULT
|
|
||||||
TEH_RESPONSE_reply_aml_blocked (struct MHD_Connection *connection,
|
|
||||||
enum TALER_AmlDecisionState status);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send assertion that the given denomination key hash
|
* Send assertion that the given denomination key hash
|
||||||
* is not usable (typically expired) at this time.
|
* is not usable (typically expired) at this time.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
This file is part of TALER
|
||||||
Copyright (C) 2014-2023 Taler Systems SA
|
Copyright (C) 2014-2022 Taler Systems SA
|
||||||
|
|
||||||
TALER is free software; you can redistribute it and/or modify
|
TALER is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as
|
it under the terms of the GNU Affero General Public License as
|
||||||
@ -61,21 +61,16 @@ struct WithdrawContext
|
|||||||
struct TALER_EXCHANGEDB_KycStatus kyc;
|
struct TALER_EXCHANGEDB_KycStatus kyc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hash of the payto-URI representing the account
|
* Hash of the payto-URI representing the reserve
|
||||||
* from which the money was put into the reserve.
|
* from which we are withdrawing.
|
||||||
*/
|
*/
|
||||||
struct TALER_PaytoHashP h_account_payto;
|
struct TALER_PaytoHashP h_payto;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current time for the DB transaction.
|
* Current time for the DB transaction.
|
||||||
*/
|
*/
|
||||||
struct GNUNET_TIME_Timestamp now;
|
struct GNUNET_TIME_Timestamp now;
|
||||||
|
|
||||||
/**
|
|
||||||
* AML decision, #TALER_AML_NORMAL if we may proceed.
|
|
||||||
*/
|
|
||||||
enum TALER_AmlDecisionState aml_decision;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -113,7 +108,7 @@ withdraw_amount_cb (void *cls,
|
|||||||
return;
|
return;
|
||||||
qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
|
qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&wc->h_account_payto,
|
&wc->h_payto,
|
||||||
limit,
|
limit,
|
||||||
cb,
|
cb,
|
||||||
cb_cls);
|
cb_cls);
|
||||||
@ -125,34 +120,6 @@ withdraw_amount_cb (void *cls,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function called on each @a amount that was found to
|
|
||||||
* be relevant for the AML check as it was merged into
|
|
||||||
* the reserve.
|
|
||||||
*
|
|
||||||
* @param cls `struct TALER_Amount *` to total up the amounts
|
|
||||||
* @param amount encountered transaction amount
|
|
||||||
* @param date when was the amount encountered
|
|
||||||
* @return #GNUNET_OK to continue to iterate,
|
|
||||||
* #GNUNET_NO to abort iteration
|
|
||||||
* #GNUNET_SYSERR on internal error (also abort itaration)
|
|
||||||
*/
|
|
||||||
static enum GNUNET_GenericReturnValue
|
|
||||||
aml_amount_cb (
|
|
||||||
void *cls,
|
|
||||||
const struct TALER_Amount *amount,
|
|
||||||
struct GNUNET_TIME_Absolute date)
|
|
||||||
{
|
|
||||||
struct TALER_Amount *total = cls;
|
|
||||||
|
|
||||||
GNUNET_assert (0 <=
|
|
||||||
TALER_amount_add (total,
|
|
||||||
total,
|
|
||||||
amount));
|
|
||||||
return GNUNET_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function implementing withdraw transaction. Runs the
|
* Function implementing withdraw transaction. Runs the
|
||||||
* transaction logic; IF it returns a non-error code, the transaction
|
* transaction logic; IF it returns a non-error code, the transaction
|
||||||
@ -183,153 +150,36 @@ withdraw_transaction (void *cls,
|
|||||||
uint64_t ruuid;
|
uint64_t ruuid;
|
||||||
const struct TALER_CsNonce *nonce;
|
const struct TALER_CsNonce *nonce;
|
||||||
const struct TALER_BlindedPlanchet *bp;
|
const struct TALER_BlindedPlanchet *bp;
|
||||||
struct TALER_PaytoHashP reserve_h_payto;
|
|
||||||
|
|
||||||
wc->now = GNUNET_TIME_timestamp_get ();
|
wc->now = GNUNET_TIME_timestamp_get ();
|
||||||
/* Do AML check: compute total merged amount and check
|
|
||||||
against applicable AML threshold */
|
|
||||||
{
|
|
||||||
char *reserve_payto;
|
|
||||||
|
|
||||||
reserve_payto = TALER_reserve_make_payto (TEH_base_url,
|
|
||||||
&wc->collectable.reserve_pub);
|
|
||||||
TALER_payto_hash (reserve_payto,
|
|
||||||
&reserve_h_payto);
|
|
||||||
GNUNET_free (reserve_payto);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
struct TALER_Amount merge_amount;
|
|
||||||
struct TALER_Amount threshold;
|
|
||||||
struct GNUNET_TIME_Absolute now_minus_one_month;
|
|
||||||
|
|
||||||
now_minus_one_month
|
|
||||||
= GNUNET_TIME_absolute_subtract (wc->now.abs_time,
|
|
||||||
GNUNET_TIME_UNIT_MONTHS);
|
|
||||||
GNUNET_assert (GNUNET_OK ==
|
|
||||||
TALER_amount_set_zero (TEH_currency,
|
|
||||||
&merge_amount));
|
|
||||||
qs = TEH_plugin->select_merge_amounts_for_kyc_check (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
now_minus_one_month,
|
|
||||||
&aml_amount_cb,
|
|
||||||
&merge_amount);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"select_merge_amounts_for_kyc_check");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
qs = TEH_plugin->select_aml_threshold (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
&wc->aml_decision,
|
|
||||||
&wc->kyc,
|
|
||||||
&threshold);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"select_aml_threshold");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
{
|
|
||||||
threshold = TEH_aml_threshold; /* use default */
|
|
||||||
wc->aml_decision = TALER_AML_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (wc->aml_decision)
|
|
||||||
{
|
|
||||||
case TALER_AML_NORMAL:
|
|
||||||
if (0 >= TALER_amount_cmp (&merge_amount,
|
|
||||||
&threshold))
|
|
||||||
{
|
|
||||||
/* merge_amount <= threshold, continue withdraw below */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
wc->aml_decision = TALER_AML_PENDING;
|
|
||||||
qs = TEH_plugin->trigger_aml_process (TEH_plugin->cls,
|
|
||||||
&reserve_h_payto,
|
|
||||||
&merge_amount);
|
|
||||||
if (qs <= 0)
|
|
||||||
{
|
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"trigger_aml_process");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
case TALER_AML_PENDING:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"AML already pending, doing nothing\n");
|
|
||||||
return qs;
|
|
||||||
case TALER_AML_FROZEN:
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
|
||||||
"Account frozen, doing nothing\n");
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the money came from a wire transfer */
|
|
||||||
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
|
||||||
&wc->collectable.reserve_pub,
|
&wc->collectable.reserve_pub,
|
||||||
&wc->h_account_payto);
|
&wc->h_payto);
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
return qs;
|
return qs;
|
||||||
/* If no results, reserve was created by merge, in which case no KYC check
|
/* If no results, reserve was created by merge,
|
||||||
is required as the merge already did that. */
|
in which case no KYC check is required as the
|
||||||
|
merge already did that. */
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
|
||||||
{
|
{
|
||||||
char *kyc_required;
|
const char *kyc_required;
|
||||||
|
|
||||||
qs = TALER_KYCLOGIC_kyc_test_required (
|
kyc_required = TALER_KYCLOGIC_kyc_test_required (
|
||||||
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
|
||||||
&wc->h_account_payto,
|
&wc->h_payto,
|
||||||
TEH_plugin->select_satisfied_kyc_processes,
|
TEH_plugin->select_satisfied_kyc_processes,
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
&withdraw_amount_cb,
|
&withdraw_amount_cb,
|
||||||
wc,
|
wc);
|
||||||
&kyc_required);
|
|
||||||
if (qs < 0)
|
|
||||||
{
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
|
||||||
"kyc_test_required");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
|
||||||
if (NULL != kyc_required)
|
if (NULL != kyc_required)
|
||||||
{
|
{
|
||||||
/* insert KYC requirement into DB! */
|
/* insert KYC requirement into DB! */
|
||||||
wc->kyc.ok = false;
|
wc->kyc.ok = false;
|
||||||
qs = TEH_plugin->insert_kyc_requirement_for_account (
|
return TEH_plugin->insert_kyc_requirement_for_account (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
kyc_required,
|
kyc_required,
|
||||||
&wc->h_account_payto,
|
&wc->h_payto,
|
||||||
&wc->kyc.requirement_row);
|
&wc->kyc.requirement_row);
|
||||||
GNUNET_free (kyc_required);
|
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
|
||||||
TALER_EC_GENERIC_DB_STORE_FAILED,
|
|
||||||
"insert_kyc_requirement_for_account");
|
|
||||||
}
|
|
||||||
return qs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wc->kyc.ok = true;
|
wc->kyc.ok = true;
|
||||||
@ -348,13 +198,10 @@ withdraw_transaction (void *cls,
|
|||||||
if (0 > qs)
|
if (0 > qs)
|
||||||
{
|
{
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
{
|
|
||||||
GNUNET_break (0);
|
|
||||||
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
*mhd_ret = TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
"do_withdraw");
|
"do_withdraw");
|
||||||
}
|
|
||||||
return qs;
|
return qs;
|
||||||
}
|
}
|
||||||
if (! found)
|
if (! found)
|
||||||
@ -652,13 +499,8 @@ TEH_handler_withdraw (struct TEH_RequestContext *rc,
|
|||||||
|
|
||||||
if (! wc.kyc.ok)
|
if (! wc.kyc.ok)
|
||||||
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
return TEH_RESPONSE_reply_kyc_required (rc->connection,
|
||||||
&wc.h_account_payto,
|
&wc.h_payto,
|
||||||
&wc.kyc);
|
&wc.kyc);
|
||||||
|
|
||||||
if (TALER_AML_NORMAL != wc.aml_decision)
|
|
||||||
return TEH_RESPONSE_reply_aml_blocked (rc->connection,
|
|
||||||
wc.aml_decision);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
MHD_RESULT ret;
|
MHD_RESULT ret;
|
||||||
|
|
||||||
|
@ -57,12 +57,6 @@ static struct TALER_BANK_CreditHistoryHandle *hh;
|
|||||||
*/
|
*/
|
||||||
static bool hh_returned_data;
|
static bool hh_returned_data;
|
||||||
|
|
||||||
/**
|
|
||||||
* Set to true if the request for history did not
|
|
||||||
* succeed because the account was unknown.
|
|
||||||
*/
|
|
||||||
static bool hh_account_404;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When did we start the last @e hh request?
|
* When did we start the last @e hh request?
|
||||||
*/
|
*/
|
||||||
@ -478,9 +472,9 @@ transaction_completed (void)
|
|||||||
GNUNET_SCHEDULER_shutdown ();
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (! (hh_returned_data || hh_account_404) )
|
if (! hh_returned_data)
|
||||||
{
|
{
|
||||||
/* Enforce long-polling delay even if the server ignored it
|
/* Enforce long polling delay even if the server ignored it
|
||||||
and returned earlier */
|
and returned earlier */
|
||||||
struct GNUNET_TIME_Relative latency;
|
struct GNUNET_TIME_Relative latency;
|
||||||
struct GNUNET_TIME_Relative left;
|
struct GNUNET_TIME_Relative left;
|
||||||
@ -488,17 +482,8 @@ transaction_completed (void)
|
|||||||
latency = GNUNET_TIME_absolute_get_duration (hh_start_time);
|
latency = GNUNET_TIME_absolute_get_duration (hh_start_time);
|
||||||
left = GNUNET_TIME_relative_subtract (longpoll_timeout,
|
left = GNUNET_TIME_relative_subtract (longpoll_timeout,
|
||||||
latency);
|
latency);
|
||||||
if (! (test_mode ||
|
|
||||||
GNUNET_TIME_relative_is_zero (left)) )
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR, // WARNING,
|
|
||||||
"Server did not respect long-polling, enforcing client-side by sleeping for %s\n",
|
|
||||||
GNUNET_TIME_relative2s (left,
|
|
||||||
true));
|
|
||||||
delayed_until = GNUNET_TIME_relative_to_absolute (left);
|
delayed_until = GNUNET_TIME_relative_to_absolute (left);
|
||||||
}
|
}
|
||||||
if (hh_account_404)
|
|
||||||
delayed_until = GNUNET_TIME_relative_to_absolute (
|
|
||||||
GNUNET_TIME_UNIT_MILLISECONDS);
|
|
||||||
if (test_mode)
|
if (test_mode)
|
||||||
delayed_until = GNUNET_TIME_UNIT_ZERO_ABS;
|
delayed_until = GNUNET_TIME_UNIT_ZERO_ABS;
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
@ -510,12 +495,395 @@ transaction_completed (void)
|
|||||||
* We got incoming transaction details from the bank. Add them
|
* We got incoming transaction details from the bank. Add them
|
||||||
* to the database.
|
* to the database.
|
||||||
*
|
*
|
||||||
* @param wrap_size desired bulk insert size
|
|
||||||
* @param details array of transaction details
|
* @param details array of transaction details
|
||||||
* @param details_length length of the @a details array
|
* @param details_length length of the @a details array
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
process_reply (unsigned int wrap_size,
|
process_reply (const struct TALER_BANK_CreditDetails *details,
|
||||||
|
unsigned int details_length)
|
||||||
|
{
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
bool shard_done;
|
||||||
|
uint64_t lroff = latest_row_off;
|
||||||
|
|
||||||
|
if (0 == details_length)
|
||||||
|
{
|
||||||
|
/* Server should have used 204, not 200! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
transaction_completed ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hh_returned_data = true;
|
||||||
|
/* check serial IDs for range constraints */
|
||||||
|
for (unsigned int i = 0; i<details_length; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_BANK_CreditDetails *cd = &details[i];
|
||||||
|
|
||||||
|
if (cd->serial_id < lroff)
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Serial ID %llu not monotonic (got %llu before). Failing!\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
(unsigned long long) lroff);
|
||||||
|
db_plugin->rollback (db_plugin->cls);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cd->serial_id > shard_end)
|
||||||
|
{
|
||||||
|
/* we are *past* the current shard (likely because the serial_id of the
|
||||||
|
shard_end happens to not exist in the DB). So commit and stop this
|
||||||
|
iteration! */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Serial ID %llu past shard end at %llu, ending iteration early!\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
(unsigned long long) shard_end);
|
||||||
|
details_length = i;
|
||||||
|
progress = true;
|
||||||
|
lroff = cd->serial_id - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lroff = cd->serial_id;
|
||||||
|
}
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
db_plugin->start_read_committed (db_plugin->cls,
|
||||||
|
"wirewatch check for incoming wire transfers"))
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Failed to start database transaction!\n");
|
||||||
|
global_ret = EXIT_FAILURE;
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
started_transaction = true;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Importing %u transactions\n",
|
||||||
|
details_length);
|
||||||
|
for (unsigned int i = 0; i<details_length; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_BANK_CreditDetails *cd = &details[i];
|
||||||
|
|
||||||
|
/* FIXME #7276: Consider using Postgres multi-valued insert here,
|
||||||
|
for up to 15x speed-up according to
|
||||||
|
https://dba.stackexchange.com/questions/224989/multi-row-insert-vs-transactional-single-row-inserts#225006
|
||||||
|
(Note: this may require changing both the
|
||||||
|
plugin API as well as modifying how this function is called.) */
|
||||||
|
qs = db_plugin->reserves_in_insert (db_plugin->cls,
|
||||||
|
&cd->reserve_pub,
|
||||||
|
&cd->amount,
|
||||||
|
cd->execution_date,
|
||||||
|
cd->debit_account_uri,
|
||||||
|
ai->section_name,
|
||||||
|
cd->serial_id);
|
||||||
|
switch (qs)
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error for reserves_in_insert. Rolling back.\n");
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
/* Either wirewatch was freshly started after the system was
|
||||||
|
shutdown and we're going over an incomplete shard again
|
||||||
|
after being restarted, or the shard lock period was too
|
||||||
|
short (number of workers set incorrectly?) and a 2nd
|
||||||
|
wirewatcher has been stealing our work while we are still
|
||||||
|
at it. */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Attempted to import transaction %llu (%s) twice. "
|
||||||
|
"This should happen rarely (if not, ask for support).\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
job_name);
|
||||||
|
break;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Imported transaction %llu.",
|
||||||
|
(unsigned long long) cd->serial_id);
|
||||||
|
/* normal case */
|
||||||
|
progress = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
latest_row_off = lroff;
|
||||||
|
shard_done = (shard_end <= latest_row_off);
|
||||||
|
if (shard_done)
|
||||||
|
{
|
||||||
|
/* shard is complete, mark this as well */
|
||||||
|
qs = db_plugin->complete_shard (db_plugin->cls,
|
||||||
|
job_name,
|
||||||
|
shard_start,
|
||||||
|
shard_end);
|
||||||
|
switch (qs)
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error for complete_shard. Rolling back.\n");
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
GNUNET_break (0);
|
||||||
|
/* Not expected, but let's just continue */
|
||||||
|
break;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
/* normal case */
|
||||||
|
progress = true;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Completed shard %s (%llu,%llu] after %s\n",
|
||||||
|
job_name,
|
||||||
|
(unsigned long long) shard_start,
|
||||||
|
(unsigned long long) shard_end,
|
||||||
|
GNUNET_STRINGS_relative_time_to_string (
|
||||||
|
GNUNET_TIME_absolute_get_duration (shard_start_time),
|
||||||
|
true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! progress)
|
||||||
|
{
|
||||||
|
db_plugin->rollback (db_plugin->cls);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Committing %s progress (%llu,%llu] at %llu\n (%s)",
|
||||||
|
job_name,
|
||||||
|
(unsigned long long) shard_start,
|
||||||
|
(unsigned long long) shard_end,
|
||||||
|
(unsigned long long) latest_row_off,
|
||||||
|
shard_done
|
||||||
|
? "shard done"
|
||||||
|
: "shard incomplete");
|
||||||
|
qs = db_plugin->commit (db_plugin->cls);
|
||||||
|
switch (qs)
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
/* reduce transaction size to reduce rollback probability */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error on commit. Reducing transaction size.\n");
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
started_transaction = false;
|
||||||
|
/* normal case */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shard_done)
|
||||||
|
{
|
||||||
|
shard_delay = GNUNET_TIME_absolute_get_duration (shard_start_time);
|
||||||
|
shard_open = false;
|
||||||
|
transaction_completed ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_assert (NULL == task);
|
||||||
|
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got incoming transaction details from the bank. Add them
|
||||||
|
* to the database.
|
||||||
|
*
|
||||||
|
* @param details array of transaction details
|
||||||
|
* @param details_length length of the @a details array
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
process_reply_batched (const struct TALER_BANK_CreditDetails *details,
|
||||||
|
unsigned int details_length)
|
||||||
|
{
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
bool shard_done;
|
||||||
|
uint64_t lroff = latest_row_off;
|
||||||
|
|
||||||
|
if (0 == details_length)
|
||||||
|
{
|
||||||
|
/* Server should have used 204, not 200! */
|
||||||
|
GNUNET_break_op (0);
|
||||||
|
transaction_completed ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* check serial IDs for range constraints */
|
||||||
|
for (unsigned int i = 0; i<details_length; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_BANK_CreditDetails *cd = &details[i];
|
||||||
|
|
||||||
|
if (cd->serial_id < lroff)
|
||||||
|
{
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
|
"Serial ID %llu not monotonic (got %llu before). Failing!\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
(unsigned long long) lroff);
|
||||||
|
db_plugin->rollback (db_plugin->cls);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cd->serial_id > shard_end)
|
||||||
|
{
|
||||||
|
/* we are *past* the current shard (likely because the serial_id of the
|
||||||
|
shard_end happens to not exist in the DB). So commit and stop this
|
||||||
|
iteration! */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Serial ID %llu past shard end at %llu, ending iteration early!\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
(unsigned long long) shard_end);
|
||||||
|
details_length = i;
|
||||||
|
progress = true;
|
||||||
|
lroff = cd->serial_id - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lroff = cd->serial_id;
|
||||||
|
}
|
||||||
|
if (0 != details_length)
|
||||||
|
{
|
||||||
|
enum GNUNET_DB_QueryStatus qss[details_length];
|
||||||
|
struct TALER_EXCHANGEDB_ReserveInInfo reserves[details_length];
|
||||||
|
|
||||||
|
hh_returned_data = true;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Importing %u transactions\n",
|
||||||
|
details_length);
|
||||||
|
for (unsigned int i = 0; i<details_length; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_BANK_CreditDetails *cd = &details[i];
|
||||||
|
struct TALER_EXCHANGEDB_ReserveInInfo *res = &reserves[i];
|
||||||
|
|
||||||
|
res->reserve_pub = &cd->reserve_pub;
|
||||||
|
res->balance = &cd->amount;
|
||||||
|
res->execution_time = cd->execution_date;
|
||||||
|
res->sender_account_details = cd->debit_account_uri;
|
||||||
|
res->exchange_account_name = ai->section_name;
|
||||||
|
res->wire_reference = cd->serial_id;
|
||||||
|
}
|
||||||
|
qs = db_plugin->batch_reserves_in_insert (db_plugin->cls,
|
||||||
|
reserves,
|
||||||
|
details_length,
|
||||||
|
qss);
|
||||||
|
switch (qs)
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error for batch_reserves_in_insert. Rolling back.\n");
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (unsigned int i = 0; i<details_length; i++)
|
||||||
|
{
|
||||||
|
const struct TALER_BANK_CreditDetails *cd = &details[i];
|
||||||
|
|
||||||
|
switch (qss[i])
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error for batch_reserves_in_insert(%u). Rolling back.\n",
|
||||||
|
i);
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
/* Either wirewatch was freshly started after the system was
|
||||||
|
shutdown and we're going over an incomplete shard again
|
||||||
|
after being restarted, or the shard lock period was too
|
||||||
|
short (number of workers set incorrectly?) and a 2nd
|
||||||
|
wirewatcher has been stealing our work while we are still
|
||||||
|
at it. */
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Attempted to import transaction %llu (%s) twice. "
|
||||||
|
"This should happen rarely (if not, ask for support).\n",
|
||||||
|
(unsigned long long) cd->serial_id,
|
||||||
|
job_name);
|
||||||
|
break;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Imported transaction %llu.",
|
||||||
|
(unsigned long long) cd->serial_id);
|
||||||
|
/* normal case */
|
||||||
|
progress = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
latest_row_off = lroff;
|
||||||
|
shard_done = (shard_end <= latest_row_off);
|
||||||
|
if (shard_done)
|
||||||
|
{
|
||||||
|
/* shard is complete, mark this as well */
|
||||||
|
qs = db_plugin->complete_shard (db_plugin->cls,
|
||||||
|
job_name,
|
||||||
|
shard_start,
|
||||||
|
shard_end);
|
||||||
|
switch (qs)
|
||||||
|
{
|
||||||
|
case GNUNET_DB_STATUS_HARD_ERROR:
|
||||||
|
GNUNET_break (0);
|
||||||
|
GNUNET_SCHEDULER_shutdown ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Got DB soft error for complete_shard. Rolling back.\n");
|
||||||
|
handle_soft_error ();
|
||||||
|
return;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
|
GNUNET_break (0);
|
||||||
|
/* Not expected, but let's just continue */
|
||||||
|
break;
|
||||||
|
case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
|
||||||
|
/* normal case */
|
||||||
|
progress = true;
|
||||||
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
|
"Completed shard %s (%llu,%llu] after %s\n",
|
||||||
|
job_name,
|
||||||
|
(unsigned long long) shard_start,
|
||||||
|
(unsigned long long) shard_end,
|
||||||
|
GNUNET_STRINGS_relative_time_to_string (
|
||||||
|
GNUNET_TIME_absolute_get_duration (shard_start_time),
|
||||||
|
true));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shard_delay = GNUNET_TIME_absolute_get_duration (shard_start_time);
|
||||||
|
shard_open = false;
|
||||||
|
transaction_completed ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GNUNET_assert (NULL == task);
|
||||||
|
task = GNUNET_SCHEDULER_add_now (&continue_with_shard,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We got incoming transaction details from the bank. Add them
|
||||||
|
* to the database.
|
||||||
|
*
|
||||||
|
* @param batch_size desired batch size
|
||||||
|
* @param details array of transaction details
|
||||||
|
* @param details_length length of the @a details array
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
process_reply_batched2 (unsigned int batch_size,
|
||||||
const struct TALER_BANK_CreditDetails *details,
|
const struct TALER_BANK_CreditDetails *details,
|
||||||
unsigned int details_length)
|
unsigned int details_length)
|
||||||
{
|
{
|
||||||
@ -582,10 +950,10 @@ process_reply (unsigned int wrap_size,
|
|||||||
res->exchange_account_name = ai->section_name;
|
res->exchange_account_name = ai->section_name;
|
||||||
res->wire_reference = cd->serial_id;
|
res->wire_reference = cd->serial_id;
|
||||||
}
|
}
|
||||||
qs = db_plugin->reserves_in_insert (db_plugin->cls,
|
qs = db_plugin->batch2_reserves_in_insert (db_plugin->cls,
|
||||||
reserves,
|
reserves,
|
||||||
details_length,
|
details_length,
|
||||||
wrap_size,
|
batch_size,
|
||||||
qss);
|
qss);
|
||||||
switch (qs)
|
switch (qs)
|
||||||
{
|
{
|
||||||
@ -596,7 +964,7 @@ process_reply (unsigned int wrap_size,
|
|||||||
case GNUNET_DB_STATUS_SOFT_ERROR:
|
case GNUNET_DB_STATUS_SOFT_ERROR:
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Got DB soft error for batch2_reserves_in_insert (%u). Rolling back.\n",
|
"Got DB soft error for batch2_reserves_in_insert (%u). Rolling back.\n",
|
||||||
wrap_size);
|
batch_size);
|
||||||
handle_soft_error ();
|
handle_soft_error ();
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
@ -701,44 +1069,56 @@ static void
|
|||||||
history_cb (void *cls,
|
history_cb (void *cls,
|
||||||
const struct TALER_BANK_CreditHistoryResponse *reply)
|
const struct TALER_BANK_CreditHistoryResponse *reply)
|
||||||
{
|
{
|
||||||
static int wrap_size = -2;
|
static int batch_mode = -2;
|
||||||
|
|
||||||
(void) cls;
|
(void) cls;
|
||||||
if (-2 == wrap_size)
|
if (-2 == batch_mode)
|
||||||
{
|
{
|
||||||
const char *mode = getenv ("TALER_WIREWATCH_WARP_SIZE");
|
const char *mode = getenv ("TALER_USE_BATCH");
|
||||||
char dummy;
|
char dummy;
|
||||||
|
|
||||||
if ( (NULL == mode) ||
|
if ( (NULL == mode) ||
|
||||||
(1 != sscanf (mode,
|
(1 != sscanf (mode,
|
||||||
"%d%c",
|
"%d%c",
|
||||||
&wrap_size,
|
&batch_mode,
|
||||||
&dummy)) )
|
&dummy)) )
|
||||||
{
|
{
|
||||||
if (NULL != mode)
|
if (NULL != mode)
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Bad batch mode `%s' specified\n",
|
"Bad batch mode `%s' specified\n",
|
||||||
mode);
|
mode);
|
||||||
wrap_size = 8; /* maximum supported is currently 8 */
|
batch_mode = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
hh = NULL;
|
hh = NULL;
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"History request returned with HTTP status %u\n",
|
"History request returned with HTTP status %u\n",
|
||||||
reply->http_status);
|
reply->http_status);
|
||||||
switch (reply->http_status)
|
switch (reply->http_status)
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
process_reply (wrap_size,
|
switch (batch_mode)
|
||||||
|
{
|
||||||
|
case -1:
|
||||||
|
process_reply (reply->details.success.details,
|
||||||
|
reply->details.success.details_length);
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
process_reply_batched (reply->details.success.details,
|
||||||
|
reply->details.success.details_length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
process_reply_batched2 ((unsigned int) batch_mode,
|
||||||
reply->details.success.details,
|
reply->details.success.details,
|
||||||
reply->details.success.details_length);
|
reply->details.success.details_length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case MHD_HTTP_NO_CONTENT:
|
case MHD_HTTP_NO_CONTENT:
|
||||||
transaction_completed ();
|
transaction_completed ();
|
||||||
return;
|
return;
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
hh_account_404 = true;
|
|
||||||
if (ignore_account_404)
|
if (ignore_account_404)
|
||||||
{
|
{
|
||||||
transaction_completed ();
|
transaction_completed ();
|
||||||
@ -773,11 +1153,10 @@ continue_with_shard (void *cls)
|
|||||||
shard_end - latest_row_off);
|
shard_end - latest_row_off);
|
||||||
GNUNET_assert (NULL == hh);
|
GNUNET_assert (NULL == hh);
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Requesting credit history starting from %llu\n",
|
"Requesting credit history staring from %llu\n",
|
||||||
(unsigned long long) latest_row_off);
|
(unsigned long long) latest_row_off);
|
||||||
hh_start_time = GNUNET_TIME_absolute_get ();
|
hh_start_time = GNUNET_TIME_absolute_get ();
|
||||||
hh_returned_data = false;
|
hh_returned_data = false;
|
||||||
hh_account_404 = false;
|
|
||||||
hh = TALER_BANK_credit_history (ctx,
|
hh = TALER_BANK_credit_history (ctx,
|
||||||
ai->auth,
|
ai->auth,
|
||||||
latest_row_off,
|
latest_row_off,
|
||||||
@ -874,17 +1253,6 @@ lock_shard (void *cls)
|
|||||||
job_name,
|
job_name,
|
||||||
GNUNET_STRINGS_relative_time_to_string (rdelay,
|
GNUNET_STRINGS_relative_time_to_string (rdelay,
|
||||||
true));
|
true));
|
||||||
#if 1
|
|
||||||
if (GNUNET_TIME_relative_cmp (rdelay,
|
|
||||||
>,
|
|
||||||
GNUNET_TIME_UNIT_SECONDS))
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
|
||||||
"Delay would have been for %s\n",
|
|
||||||
GNUNET_TIME_relative2s (rdelay,
|
|
||||||
true));
|
|
||||||
rdelay = GNUNET_TIME_relative_min (rdelay,
|
|
||||||
GNUNET_TIME_UNIT_SECONDS);
|
|
||||||
#endif
|
|
||||||
delayed_until = GNUNET_TIME_relative_to_absolute (rdelay);
|
delayed_until = GNUNET_TIME_relative_to_absolute (rdelay);
|
||||||
}
|
}
|
||||||
GNUNET_assert (NULL == task);
|
GNUNET_assert (NULL == task);
|
||||||
@ -897,7 +1265,7 @@ lock_shard (void *cls)
|
|||||||
job_name,
|
job_name,
|
||||||
GNUNET_STRINGS_relative_time_to_string (
|
GNUNET_STRINGS_relative_time_to_string (
|
||||||
wirewatch_idle_sleep_interval,
|
wirewatch_idle_sleep_interval,
|
||||||
true));
|
GNUNET_YES));
|
||||||
delayed_until = GNUNET_TIME_relative_to_absolute (
|
delayed_until = GNUNET_TIME_relative_to_absolute (
|
||||||
wirewatch_idle_sleep_interval);
|
wirewatch_idle_sleep_interval);
|
||||||
shard_open = false;
|
shard_open = false;
|
||||||
|
@ -7,7 +7,6 @@ TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/${USER:-}/taler-system-runtime/
|
|||||||
# Currency supported by the exchange (can only be one)
|
# Currency supported by the exchange (can only be one)
|
||||||
CURRENCY = EUR
|
CURRENCY = EUR
|
||||||
CURRENCY_ROUND_UNIT = EUR:0.01
|
CURRENCY_ROUND_UNIT = EUR:0.01
|
||||||
AML_THRESHOLD = EUR:1000000
|
|
||||||
|
|
||||||
[auditor]
|
[auditor]
|
||||||
TINY_AMOUNT = EUR:0.01
|
TINY_AMOUNT = EUR:0.01
|
||||||
|
18
src/exchangedb/.gitignore
vendored
18
src/exchangedb/.gitignore
vendored
@ -1,15 +1,15 @@
|
|||||||
|
test-exchangedb-auditors
|
||||||
|
test-exchangedb-denomkeys
|
||||||
|
test-exchangedb-fees
|
||||||
test-exchangedb-postgres
|
test-exchangedb-postgres
|
||||||
|
test-exchangedb-signkeys
|
||||||
|
test-perf-taler-exchangedb
|
||||||
bench-db-postgres
|
bench-db-postgres
|
||||||
perf_deposits_get_ready-postgres
|
shard-drop0001.sqltest-exchangedb-by-j-postgres
|
||||||
perf_get_link_data-postgres
|
test-exchangedb-by-j-postgres
|
||||||
perf_reserves_in_insert-postgres
|
perf-exchangedb-reserves-in-insert-postgres
|
||||||
perf_select_refunds_by_coin-postgres
|
|
||||||
exchange-0002.sql
|
exchange-0002.sql
|
||||||
procedures.sql
|
procedures.sql
|
||||||
exchange-0003.sql
|
exchange-0003.sql
|
||||||
perf-exchangedb-reserves-in-insert-postgres
|
|
||||||
test-exchangedb-batch-reserves-in-insert-postgres
|
test-exchangedb-batch-reserves-in-insert-postgres
|
||||||
test-exchangedb-by-j-postgres
|
test-exchangedb-populate-table-postgres
|
||||||
test-exchangedb-populate-link-data-postgres
|
|
||||||
test-exchangedb-populate-ready-deposit-postgres
|
|
||||||
test-exchangedb-populate-select-refunds-by-coin-postgres
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
--
|
--
|
||||||
-- This file is part of TALER
|
-- This file is part of TALER
|
||||||
-- Copyright (C) 2014--2023 Taler Systems SA
|
-- Copyright (C) 2014--2022 Taler Systems SA
|
||||||
--
|
--
|
||||||
-- TALER is free software; you can redistribute it and/or modify it under the
|
-- 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
|
-- terms of the GNU General Public License as published by the Free Software
|
||||||
@ -116,22 +116,6 @@ BEGIN
|
|||||||
',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key'
|
',ADD CONSTRAINT ' || table_name || '_coin_pub_merchant_pub_h_contract_terms_key'
|
||||||
' UNIQUE (coin_pub, merchant_pub, h_contract_terms)'
|
' UNIQUE (coin_pub, merchant_pub, h_contract_terms)'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
|
||||||
'CREATE INDEX ' || table_name || '_by_ready '
|
|
||||||
'ON ' || table_name || ' '
|
|
||||||
'(wire_deadline ASC'
|
|
||||||
',shard ASC'
|
|
||||||
',coin_pub'
|
|
||||||
') WHERE NOT (done OR policy_blocked);'
|
|
||||||
);
|
|
||||||
EXECUTE FORMAT (
|
|
||||||
'CREATE INDEX ' || table_name || '_for_matching '
|
|
||||||
'ON ' || table_name || ' '
|
|
||||||
'(refund_deadline ASC'
|
|
||||||
',merchant_pub'
|
|
||||||
',coin_pub'
|
|
||||||
') WHERE NOT (done OR policy_blocked);'
|
|
||||||
);
|
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
@ -415,5 +399,29 @@ INSERT INTO exchange_tables
|
|||||||
,'exchange-0002'
|
,'exchange-0002'
|
||||||
,'foreign'
|
,'foreign'
|
||||||
,TRUE
|
,TRUE
|
||||||
,FALSE)
|
,FALSE),
|
||||||
;
|
('deposits_by_ready'
|
||||||
|
,'exchange-0002'
|
||||||
|
,'create'
|
||||||
|
,TRUE
|
||||||
|
,TRUE),
|
||||||
|
('deposits_by_ready'
|
||||||
|
,'exchange-0002'
|
||||||
|
,'constrain'
|
||||||
|
,TRUE
|
||||||
|
,TRUE),
|
||||||
|
('deposits_for_matching'
|
||||||
|
,'exchange-0002'
|
||||||
|
,'create'
|
||||||
|
,TRUE
|
||||||
|
,TRUE),
|
||||||
|
('deposits_for_matching'
|
||||||
|
,'exchange-0002'
|
||||||
|
,'constrain'
|
||||||
|
,TRUE
|
||||||
|
,TRUE),
|
||||||
|
('deposits'
|
||||||
|
,'exchange-0002'
|
||||||
|
,'master'
|
||||||
|
,TRUE
|
||||||
|
,FALSE);
|
||||||
|
@ -25,7 +25,6 @@ CREATE TABLE partners
|
|||||||
,wad_fee_frac INT4 NOT NULL
|
,wad_fee_frac INT4 NOT NULL
|
||||||
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
|
,master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)
|
||||||
,partner_base_url TEXT NOT NULL
|
,partner_base_url TEXT NOT NULL
|
||||||
,PRIMARY KEY (partner_master_pub, start_date)
|
|
||||||
);
|
);
|
||||||
COMMENT ON TABLE partners
|
COMMENT ON TABLE partners
|
||||||
IS 'exchanges we do wad transfers to';
|
IS 'exchanges we do wad transfers to';
|
||||||
|
@ -62,9 +62,6 @@ BEGIN
|
|||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
--
|
|
||||||
-- FIXME: Add comment for link_sig
|
|
||||||
--
|
|
||||||
PERFORM comment_partitioned_column(
|
PERFORM comment_partitioned_column(
|
||||||
'envelope of the new coin to be signed'
|
'envelope of the new coin to be signed'
|
||||||
,'coin_ev'
|
,'coin_ev'
|
||||||
|
@ -26,14 +26,12 @@ BEGIN
|
|||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE IF NOT EXISTS %I'
|
'CREATE TABLE IF NOT EXISTS %I'
|
||||||
'(aml_history_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
'(aml_history_serial_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
||||||
',h_payto BYTEA CHECK (LENGTH(h_payto)=32)'
|
',h_payto BYTEA PRIMARY KEY CHECK (LENGTH(h_payto)=32)'
|
||||||
',new_threshold_val INT8 NOT NULL DEFAULT(0)'
|
',new_threshold_val INT8 NOT NULL DEFAULT(0)'
|
||||||
',new_threshold_frac INT4 NOT NULL DEFAULT(0)'
|
',new_threshold_frac INT4 NOT NULL DEFAULT(0)'
|
||||||
',new_status INT4 NOT NULL DEFAULT(0)'
|
',new_status INT4 NOT NULL DEFAULT(0)'
|
||||||
',decision_time INT8 NOT NULL DEFAULT(0)'
|
',decision_time INT8 NOT NULL DEFAULT(0)'
|
||||||
',justification VARCHAR NOT NULL'
|
',justification VARCHAR NOT NULL'
|
||||||
',kyc_requirements VARCHAR'
|
|
||||||
',kyc_req_row INT8 NOT NULL DEFAULT(0)'
|
|
||||||
',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)'
|
',decider_pub BYTEA CHECK (LENGTH(decider_pub)=32)'
|
||||||
',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)'
|
',decider_sig BYTEA CHECK (LENGTH(decider_sig)=64)'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
@ -82,18 +80,6 @@ BEGIN
|
|||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
PERFORM comment_partitioned_column(
|
|
||||||
'Additional KYC requirements imposed by the AML staff member. Serialized JSON array of strings.'
|
|
||||||
,'kyc_requirements'
|
|
||||||
,table_name
|
|
||||||
,partition_suffix
|
|
||||||
);
|
|
||||||
PERFORM comment_partitioned_column(
|
|
||||||
'Row in the KYC table for this KYC requirement, 0 for none.'
|
|
||||||
,'kyc_req_row'
|
|
||||||
,table_name
|
|
||||||
,partition_suffix
|
|
||||||
);
|
|
||||||
PERFORM comment_partitioned_column(
|
PERFORM comment_partitioned_column(
|
||||||
'Signature key of the staff member affirming the AML decision; of type AML_DECISION'
|
'Signature key of the staff member affirming the AML decision; of type AML_DECISION'
|
||||||
,'decider_sig'
|
,'decider_sig'
|
||||||
@ -124,10 +110,11 @@ BEGIN
|
|||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'CREATE INDEX ' || table_name || '_main_index '
|
'CREATE INDEX ' || table_name || '_main_index '
|
||||||
'ON ' || table_name || ' '
|
'ON ' || table_name || ' '
|
||||||
'(h_payto, decision_time DESC);'
|
'(h_payto ASC, decision_time ASC);'
|
||||||
);
|
);
|
||||||
END $$;
|
END $$;
|
||||||
|
|
||||||
|
-- FIXME: also have INSERT on AML decisions to update AML status!
|
||||||
|
|
||||||
INSERT INTO exchange_tables
|
INSERT INTO exchange_tables
|
||||||
(name
|
(name
|
||||||
|
@ -30,7 +30,6 @@ BEGIN
|
|||||||
',threshold_val INT8 NOT NULL DEFAULT(0)'
|
',threshold_val INT8 NOT NULL DEFAULT(0)'
|
||||||
',threshold_frac INT4 NOT NULL DEFAULT(0)'
|
',threshold_frac INT4 NOT NULL DEFAULT(0)'
|
||||||
',status INT4 NOT NULL DEFAULT(0)'
|
',status INT4 NOT NULL DEFAULT(0)'
|
||||||
',kyc_requirement INT8 NOT NULL DEFAULT(0)'
|
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
,'PARTITION BY HASH (h_payto)'
|
,'PARTITION BY HASH (h_payto)'
|
||||||
|
@ -32,7 +32,7 @@ BEGIN
|
|||||||
',birthdate VARCHAR'
|
',birthdate VARCHAR'
|
||||||
',collection_time INT8 NOT NULL'
|
',collection_time INT8 NOT NULL'
|
||||||
',expiration_time INT8 NOT NULL'
|
',expiration_time INT8 NOT NULL'
|
||||||
',encrypted_attributes BYTEA NOT NULL'
|
',encrypted_attributes VARCHAR NOT NULL'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
,'PARTITION BY HASH (h_payto)'
|
,'PARTITION BY HASH (h_payto)'
|
||||||
|
@ -14,25 +14,26 @@
|
|||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE FUNCTION create_table_age_withdraw_commitments(
|
CREATE FUNCTION create_table_withdraw_age_commitments(
|
||||||
IN partition_suffix VARCHAR DEFAULT NULL
|
IN partition_suffix VARCHAR DEFAULT NULL
|
||||||
)
|
)
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE %I'
|
'CREATE TABLE %I'
|
||||||
'(age_withdraw_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
'(withdraw_age_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
||||||
',h_commitment BYTEA CHECK (LENGTH(h_commitment)=64)'
|
',h_commitment BYTEA PRIMARY KEY CHECK (LENGTH(h_commitment)=64)'
|
||||||
',amount_with_fee_val INT8 NOT NULL'
|
',amount_with_fee_val INT8 NOT NULL'
|
||||||
',amount_with_fee_frac INT4 NOT NULL'
|
',amount_with_fee_frac INT4 NOT NULL'
|
||||||
',max_age INT2 NOT NULL'
|
',max_age_group INT2 NOT NULL'
|
||||||
',reserve_pub BYTEA CHECK (LENGTH(reserve_pub)=32)'
|
',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
|
||||||
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
|
',reserve_sig BYTEA CHECK (LENGTH(reserve_sig)=64)'
|
||||||
',noreveal_index INT4 NOT NULL'
|
',noreveal_index INT4 NOT NULL'
|
||||||
|
',timestamp INT8 NOT NULL'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
,'PARTITION BY HASH (reserve_pub)'
|
,'PARTITION BY HASH (reserve_pub)'
|
||||||
@ -50,8 +51,8 @@ BEGIN
|
|||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
PERFORM comment_partitioned_column(
|
PERFORM comment_partitioned_column(
|
||||||
'The maximum age (in years) that the client commits to with this request'
|
'The maximum age group that the client commits to with this request'
|
||||||
,'max_age'
|
,'max_age_group'
|
||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
@ -73,44 +74,46 @@ BEGIN
|
|||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
|
PERFORM comment_partitioned_column(
|
||||||
|
'Timestamp with the time when the withdraw-age request was received by the exchange'
|
||||||
|
,'timestamp'
|
||||||
|
,table_name
|
||||||
|
,partition_suffix
|
||||||
|
);
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
CREATE FUNCTION constrain_table_age_withdraw_commitments(
|
CREATE FUNCTION constrain_table_withdraw_age_commitments(
|
||||||
IN partition_suffix VARCHAR
|
IN partition_suffix VARCHAR
|
||||||
)
|
)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
table_name = concat_ws('_', table_name, partition_suffix);
|
table_name = concat_ws('_', table_name, partition_suffix);
|
||||||
|
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD PRIMARY KEY (h_commitment);'
|
' ADD PRIMARY KEY (h_commitment, reserve_pub);'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_h_commitment_reserve_pub_key'
|
' ADD CONSTRAINT ' || table_name || '_withdraw_age_commitment_id_key'
|
||||||
' UNIQUE (h_commitment, reserve_pub);'
|
' UNIQUE (withdraw_age_commitment_id);'
|
||||||
);
|
|
||||||
EXECUTE FORMAT (
|
|
||||||
'ALTER TABLE ' || table_name ||
|
|
||||||
' ADD CONSTRAINT ' || table_name || '_age_withdraw_commitment_id_key'
|
|
||||||
' UNIQUE (age_withdraw_commitment_id);'
|
|
||||||
);
|
);
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
CREATE FUNCTION foreign_table_age_withdraw_commitments()
|
CREATE FUNCTION foreign_table_withdraw_age_commitments()
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
@ -129,6 +132,18 @@ INSERT INTO exchange_tables
|
|||||||
,partitioned
|
,partitioned
|
||||||
,by_range)
|
,by_range)
|
||||||
VALUES
|
VALUES
|
||||||
('age_withdraw_commitments', 'exchange-0003', 'create', TRUE ,FALSE),
|
('withdraw_age_commitments'
|
||||||
('age_withdraw_commitments', 'exchange-0003', 'constrain',TRUE ,FALSE),
|
,'exchange-0003'
|
||||||
('age_withdraw_commitments', 'exchange-0003', 'foreign', TRUE ,FALSE);
|
,'create'
|
||||||
|
,TRUE
|
||||||
|
,FALSE),
|
||||||
|
('withdraw_age_commitments'
|
||||||
|
,'exchange-0003'
|
||||||
|
,'constrain'
|
||||||
|
,TRUE
|
||||||
|
,FALSE),
|
||||||
|
('withdraw_age_commitments'
|
||||||
|
,'exchange-0003'
|
||||||
|
,'foreign'
|
||||||
|
,TRUE
|
||||||
|
,FALSE);
|
@ -14,24 +14,22 @@
|
|||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE FUNCTION create_table_age_withdraw_revealed_coins(
|
CREATE FUNCTION create_table_withdraw_age_reveals(
|
||||||
IN partition_suffix VARCHAR DEFAULT NULL
|
IN partition_suffix VARCHAR DEFAULT NULL
|
||||||
)
|
)
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
table_name VARCHAR DEFAULT 'withdraw_age_reveals';
|
||||||
BEGIN
|
BEGIN
|
||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE %I'
|
'CREATE TABLE %I'
|
||||||
'(age_withdraw_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
|
'(withdraw_age_reveals_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
|
||||||
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=64)'
|
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=32)'
|
||||||
',freshcoin_index INT4 NOT NULL'
|
',freshcoin_index INT4 NOT NULL'
|
||||||
',denominations_serial INT8 NOT NULL'
|
',denominations_serial INT8 NOT NULL'
|
||||||
',coin_ev BYTEA NOT NULL'
|
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=32)'
|
||||||
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=64)'
|
|
||||||
',ev_sig BYTEA NOT NULL'
|
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
,'PARTITION BY HASH (h_commitment)'
|
,'PARTITION BY HASH (h_commitment)'
|
||||||
@ -61,41 +59,29 @@ BEGIN
|
|||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
PERFORM comment_partitioned_column(
|
PERFORM comment_partitioned_column(
|
||||||
'Envelope of the new coin to be signed'
|
'Hash of the blinded coins'
|
||||||
,'coin_ev'
|
|
||||||
,table_name
|
|
||||||
,partition_suffix
|
|
||||||
);
|
|
||||||
PERFORM comment_partitioned_column(
|
|
||||||
'Hash of the envelope of the new coin to be signed (for lookups)'
|
|
||||||
,'h_coin_ev'
|
,'h_coin_ev'
|
||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
PERFORM comment_partitioned_column(
|
|
||||||
'Exchange signature over the envelope'
|
|
||||||
,'ev_sig'
|
|
||||||
,table_name
|
|
||||||
,partition_suffix
|
|
||||||
);
|
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE FUNCTION constrain_table_age_withdraw_revealed_coins(
|
CREATE FUNCTION constrain_table_withdraw_age_reveals(
|
||||||
IN partition_suffix VARCHAR
|
IN partition_suffix VARCHAR
|
||||||
)
|
)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
table_name VARCHAR DEFAULT 'withdraw_age_reveals';
|
||||||
BEGIN
|
BEGIN
|
||||||
table_name = concat_ws('_', table_name, partition_suffix);
|
table_name = concat_ws('_', table_name, partition_suffix);
|
||||||
|
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_age_withdraw_revealed_coins_id_key'
|
' ADD CONSTRAINT ' || table_name || '_withdraw_age_reveals_id_key'
|
||||||
' UNIQUE (age_withdraw_revealed_coins_id);'
|
' UNIQUE (withdraw_age_reveals_id);'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
@ -105,18 +91,18 @@ BEGIN
|
|||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE FUNCTION foreign_table_age_withdraw_revealed_coins()
|
CREATE FUNCTION foreign_table_withdraw_age_reveals()
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
table_name VARCHAR DEFAULT 'withdraw_age_reveals';
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
|
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
|
||||||
' FOREIGN KEY (h_commitment)'
|
' FOREIGN KEY (h_commitment)'
|
||||||
' REFERENCES age_withdraw_commitments (h_commitment) ON DELETE CASCADE;'
|
' REFERENCES withdraw_age_commitments (h_commitment) ON DELETE CASCADE;'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
@ -135,17 +121,17 @@ INSERT INTO exchange_tables
|
|||||||
,partitioned
|
,partitioned
|
||||||
,by_range)
|
,by_range)
|
||||||
VALUES
|
VALUES
|
||||||
('age_withdraw_revealed_coins'
|
('withdraw_age_reveals'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'create'
|
,'create'
|
||||||
,TRUE
|
,TRUE
|
||||||
,FALSE),
|
,FALSE),
|
||||||
('age_withdraw_revealed_coins'
|
('withdraw_age_reveals'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'constrain'
|
,'constrain'
|
||||||
,TRUE
|
,TRUE
|
||||||
,FALSE),
|
,FALSE),
|
||||||
('age_withdraw_revealed_coins'
|
('withdraw_age_reveals'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'foreign'
|
,'foreign'
|
||||||
,TRUE
|
,TRUE
|
@ -96,7 +96,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
|
|||||||
pg_get_drain_profit.h pg_get_drain_profit.c \
|
pg_get_drain_profit.h pg_get_drain_profit.c \
|
||||||
pg_get_purse_deposit.h pg_get_purse_deposit.c \
|
pg_get_purse_deposit.h pg_get_purse_deposit.c \
|
||||||
pg_insert_contract.h pg_insert_contract.c \
|
pg_insert_contract.h pg_insert_contract.c \
|
||||||
pg_select_aml_threshold.h pg_select_aml_threshold.c \
|
|
||||||
pg_select_contract.h pg_select_contract.c \
|
pg_select_contract.h pg_select_contract.c \
|
||||||
pg_select_purse_merge.h pg_select_purse_merge.c \
|
pg_select_purse_merge.h pg_select_purse_merge.c \
|
||||||
pg_select_contract_by_purse.h pg_select_contract_by_purse.c \
|
pg_select_contract_by_purse.h pg_select_contract_by_purse.c \
|
||||||
@ -115,8 +114,6 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
|
|||||||
pg_drain_kyc_alert.h pg_drain_kyc_alert.c \
|
pg_drain_kyc_alert.h pg_drain_kyc_alert.c \
|
||||||
pg_reserves_in_insert.h pg_reserves_in_insert.c \
|
pg_reserves_in_insert.h pg_reserves_in_insert.c \
|
||||||
pg_get_withdraw_info.h pg_get_withdraw_info.c \
|
pg_get_withdraw_info.h pg_get_withdraw_info.c \
|
||||||
pg_get_age_withdraw_info.c pg_get_age_withdraw_info.h \
|
|
||||||
pg_batch_ensure_coin_known.h pg_batch_ensure_coin_known.c \
|
|
||||||
pg_do_batch_withdraw.h pg_do_batch_withdraw.c \
|
pg_do_batch_withdraw.h pg_do_batch_withdraw.c \
|
||||||
pg_get_policy_details.h pg_get_policy_details.c \
|
pg_get_policy_details.h pg_get_policy_details.c \
|
||||||
pg_persist_policy_details.h pg_persist_policy_details.c \
|
pg_persist_policy_details.h pg_persist_policy_details.c \
|
||||||
@ -139,7 +136,7 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
|
|||||||
pg_select_similar_kyc_attributes.h pg_select_similar_kyc_attributes.c \
|
pg_select_similar_kyc_attributes.h pg_select_similar_kyc_attributes.c \
|
||||||
pg_select_kyc_attributes.h pg_select_kyc_attributes.c \
|
pg_select_kyc_attributes.h pg_select_kyc_attributes.c \
|
||||||
pg_insert_aml_officer.h pg_insert_aml_officer.c \
|
pg_insert_aml_officer.h pg_insert_aml_officer.c \
|
||||||
pg_test_aml_officer.h pg_test_aml_officer.c \
|
pg_update_aml_officer.h pg_update_aml_officer.c \
|
||||||
pg_lookup_aml_officer.h pg_lookup_aml_officer.c \
|
pg_lookup_aml_officer.h pg_lookup_aml_officer.c \
|
||||||
pg_trigger_aml_process.h pg_trigger_aml_process.c \
|
pg_trigger_aml_process.h pg_trigger_aml_process.c \
|
||||||
pg_select_aml_process.h pg_select_aml_process.c \
|
pg_select_aml_process.h pg_select_aml_process.c \
|
||||||
@ -258,6 +255,8 @@ libtaler_plugin_exchangedb_postgres_la_SOURCES = \
|
|||||||
pg_select_purse_deposits_above_serial_id.h pg_select_purse_deposits_above_serial_id.c \
|
pg_select_purse_deposits_above_serial_id.h pg_select_purse_deposits_above_serial_id.c \
|
||||||
pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \
|
pg_select_account_merges_above_serial_id.h pg_select_account_merges_above_serial_id.c \
|
||||||
pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \
|
pg_select_all_purse_decisions_above_serial_id.h pg_select_all_purse_decisions_above_serial_id.c \
|
||||||
|
pg_batch_reserves_in_insert.h pg_batch_reserves_in_insert.c \
|
||||||
|
pg_batch2_reserves_in_insert.h pg_batch2_reserves_in_insert.c \
|
||||||
pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h
|
pg_select_reserve_open_above_serial_id.c pg_select_reserve_open_above_serial_id.h
|
||||||
libtaler_plugin_exchangedb_postgres_la_LIBADD = \
|
libtaler_plugin_exchangedb_postgres_la_LIBADD = \
|
||||||
$(LTLIBINTL)
|
$(LTLIBINTL)
|
||||||
@ -292,19 +291,24 @@ libtalerexchangedb_la_LDFLAGS = \
|
|||||||
|
|
||||||
|
|
||||||
check_PROGRAMS = \
|
check_PROGRAMS = \
|
||||||
test-exchangedb-postgres
|
test-exchangedb-postgres \
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
|
||||||
bench-db-postgres\
|
bench-db-postgres\
|
||||||
perf_get_link_data-postgres\
|
perf-exchangedb-reserves-in-insert-postgres\
|
||||||
perf_select_refunds_by_coin-postgres\
|
test-exchangedb-by-j-postgres\
|
||||||
perf_reserves_in_insert-postgres \
|
test-exchangedb-batch-reserves-in-insert-postgres\
|
||||||
perf_deposits_get_ready-postgres
|
test-exchangedb-populate-table-postgres\
|
||||||
|
test-exchangedb-populate-link-data-postgres\
|
||||||
|
test-exchangedb-populate-ready-deposit-postgres
|
||||||
|
|
||||||
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
|
AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
|
||||||
TESTS = \
|
TESTS = \
|
||||||
$(check_PROGRAMS)
|
test-exchangedb-postgres\
|
||||||
|
test-exchangedb-by-j-postgres\
|
||||||
|
perf-exchangedb-reserves-in-insert-postgres\
|
||||||
|
test-exchangedb-batch-reserves-in-insert-postgres\
|
||||||
|
test-exchangedb-populate-table-postgres\
|
||||||
|
test-exchangedb-populate-link-data-postgres\
|
||||||
|
test-exchangedb-populate-ready-deposit-postgres
|
||||||
test_exchangedb_postgres_SOURCES = \
|
test_exchangedb_postgres_SOURCES = \
|
||||||
test_exchangedb.c
|
test_exchangedb.c
|
||||||
test_exchangedb_postgres_LDADD = \
|
test_exchangedb_postgres_LDADD = \
|
||||||
@ -317,6 +321,32 @@ test_exchangedb_postgres_LDADD = \
|
|||||||
-lgnunetutil \
|
-lgnunetutil \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
|
test_exchangedb_by_j_postgres_SOURCES = \
|
||||||
|
test_exchangedb_by_j.c
|
||||||
|
test_exchangedb_by_j_postgres_LDADD = \
|
||||||
|
libtalerexchangedb.la \
|
||||||
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
|
$(top_builddir)/src/pq/libtalerpq.la \
|
||||||
|
-ljansson \
|
||||||
|
-lgnunetjson \
|
||||||
|
-lgnunetutil \
|
||||||
|
-lm \
|
||||||
|
$(XLIB)
|
||||||
|
|
||||||
|
|
||||||
|
perf_exchangedb_reserves_in_insert_postgres_SOURCES = \
|
||||||
|
perf_exchangedb_reserves_in_insert.c
|
||||||
|
perf_exchangedb_reserves_in_insert_postgres_LDADD = \
|
||||||
|
libtalerexchangedb.la \
|
||||||
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
|
$(top_builddir)/src/pq/libtalerpq.la \
|
||||||
|
-ljansson \
|
||||||
|
-lgnunetjson \
|
||||||
|
-lgnunetutil \
|
||||||
|
$(XLIB)
|
||||||
|
|
||||||
bench_db_postgres_SOURCES = \
|
bench_db_postgres_SOURCES = \
|
||||||
bench_db.c
|
bench_db.c
|
||||||
bench_db_postgres_LDADD = \
|
bench_db_postgres_LDADD = \
|
||||||
@ -327,9 +357,21 @@ bench_db_postgres_LDADD = \
|
|||||||
-lgnunetutil \
|
-lgnunetutil \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
perf_reserves_in_insert_postgres_SOURCES = \
|
test_exchangedb_batch_reserves_in_insert_postgres_SOURCES = \
|
||||||
perf_reserves_in_insert.c
|
test_exchangedb_batch_reserves_in_insert.c
|
||||||
perf_reserves_in_insert_postgres_LDADD = \
|
test_exchangedb_batch_reserves_in_insert_postgres_LDADD = \
|
||||||
|
libtalerexchangedb.la \
|
||||||
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
|
$(top_builddir)/src/pq/libtalerpq.la \
|
||||||
|
-ljansson \
|
||||||
|
-lgnunetjson \
|
||||||
|
-lgnunetutil \
|
||||||
|
$(XLIB)
|
||||||
|
|
||||||
|
test_exchangedb_populate_table_postgres_SOURCES = \
|
||||||
|
test_exchangedb_populate_table.c
|
||||||
|
test_exchangedb_populate_table_postgres_LDADD = \
|
||||||
libtalerexchangedb.la \
|
libtalerexchangedb.la \
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
@ -340,9 +382,9 @@ perf_reserves_in_insert_postgres_LDADD = \
|
|||||||
-lm \
|
-lm \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
perf_select_refunds_by_coin_postgres_SOURCES = \
|
test_exchangedb_populate_link_data_postgres_SOURCES = \
|
||||||
perf_select_refunds_by_coin.c
|
test_exchangedb_populate_link_data.c
|
||||||
perf_select_refunds_by_coin_postgres_LDADD = \
|
test_exchangedb_populate_link_data_postgres_LDADD = \
|
||||||
libtalerexchangedb.la \
|
libtalerexchangedb.la \
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
@ -353,9 +395,9 @@ perf_select_refunds_by_coin_postgres_LDADD = \
|
|||||||
-lm \
|
-lm \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
perf_get_link_data_postgres_SOURCES = \
|
test_exchangedb_populate_ready_deposit_postgres_SOURCES = \
|
||||||
perf_get_link_data.c
|
test_exchangedb_populate_ready_deposit.c
|
||||||
perf_get_link_data_postgres_LDADD = \
|
test_exchangedb_populate_ready_deposit_postgres_LDADD = \
|
||||||
libtalerexchangedb.la \
|
libtalerexchangedb.la \
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
$(top_builddir)/src/json/libtalerjson.la \
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
@ -366,19 +408,5 @@ perf_get_link_data_postgres_LDADD = \
|
|||||||
-lm \
|
-lm \
|
||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
perf_deposits_get_ready_postgres_SOURCES = \
|
|
||||||
perf_deposits_get_ready.c
|
|
||||||
perf_deposits_get_ready_postgres_LDADD = \
|
|
||||||
libtalerexchangedb.la \
|
|
||||||
$(top_builddir)/src/json/libtalerjson.la \
|
|
||||||
$(top_builddir)/src/util/libtalerutil.la \
|
|
||||||
$(top_builddir)/src/pq/libtalerpq.la \
|
|
||||||
-ljansson \
|
|
||||||
-lgnunetjson \
|
|
||||||
-lgnunetutil \
|
|
||||||
-lm \
|
|
||||||
$(XLIB)
|
|
||||||
|
|
||||||
|
|
||||||
EXTRA_test_exchangedb_postgres_DEPENDENCIES = \
|
EXTRA_test_exchangedb_postgres_DEPENDENCIES = \
|
||||||
libtaler_plugin_exchangedb_postgres.la
|
libtaler_plugin_exchangedb_postgres.la
|
||||||
|
@ -25,8 +25,6 @@ SET search_path TO exchange;
|
|||||||
#include "0003-aml_status.sql"
|
#include "0003-aml_status.sql"
|
||||||
#include "0003-aml_staff.sql"
|
#include "0003-aml_staff.sql"
|
||||||
#include "0003-aml_history.sql"
|
#include "0003-aml_history.sql"
|
||||||
#include "0003-age_withdraw_commitments.sql"
|
|
||||||
#include "0003-age_withdraw_reveals.sql"
|
|
||||||
|
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
184
src/exchangedb/exchange_do_batch2_reserves_in_insert.sql
Normal file
184
src/exchangedb/exchange_do_batch2_reserves_in_insert.sql
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
--
|
||||||
|
-- This file is part of TALER
|
||||||
|
-- Copyright (C) 2014--2022 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
|
||||||
|
-- Foundation; either version 3, or (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License along with
|
||||||
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
--
|
||||||
|
CREATE OR REPLACE FUNCTION exchange_do_batch2_reserves_insert(
|
||||||
|
IN in_reserve_pub BYTEA,
|
||||||
|
IN in_expiration_date INT8,
|
||||||
|
IN in_gc_date INT8,
|
||||||
|
IN in_wire_ref INT8,
|
||||||
|
IN in_credit_val INT8,
|
||||||
|
IN in_credit_frac INT4,
|
||||||
|
IN in_exchange_account_name VARCHAR,
|
||||||
|
IN in_exectution_date INT8,
|
||||||
|
IN in_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in_payto_uri VARCHAR,
|
||||||
|
IN in_reserve_expiration INT8,
|
||||||
|
IN in_notify text,
|
||||||
|
IN in2_notify text,
|
||||||
|
IN in2_reserve_pub BYTEA,
|
||||||
|
IN in2_wire_ref INT8,
|
||||||
|
IN in2_credit_val INT8,
|
||||||
|
IN in2_credit_frac INT4,
|
||||||
|
IN in2_exchange_account_name VARCHAR,
|
||||||
|
IN in2_exectution_date INT8,
|
||||||
|
IN in2_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in2_payto_uri VARCHAR,
|
||||||
|
IN in2_reserve_expiration INT8,
|
||||||
|
OUT out_reserve_found BOOLEAN,
|
||||||
|
OUT out_reserve_found2 BOOLEAN,
|
||||||
|
OUT transaction_duplicate BOOLEAN,
|
||||||
|
OUT transaction_duplicate2 BOOLEAN,
|
||||||
|
OUT ruuid INT8,
|
||||||
|
OUT ruuid2 INT8)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
curs_reserve_exist REFCURSOR;
|
||||||
|
DECLARE
|
||||||
|
curs_transaction_exist refcursor;
|
||||||
|
DECLARE
|
||||||
|
i RECORD;
|
||||||
|
DECLARE
|
||||||
|
r RECORD;
|
||||||
|
DECLARE
|
||||||
|
k INT8;
|
||||||
|
BEGIN
|
||||||
|
transaction_duplicate=TRUE;
|
||||||
|
transaction_duplicate2=TRUE;
|
||||||
|
out_reserve_found = TRUE;
|
||||||
|
out_reserve_found2 = TRUE;
|
||||||
|
ruuid=0;
|
||||||
|
ruuid2=0;
|
||||||
|
k=0;
|
||||||
|
INSERT INTO wire_targets
|
||||||
|
(wire_target_h_payto
|
||||||
|
,payto_uri)
|
||||||
|
VALUES
|
||||||
|
(in_wire_source_h_payto
|
||||||
|
,in_payto_uri),
|
||||||
|
(in2_wire_source_h_payto
|
||||||
|
,in2_payto_uri)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
OPEN curs_reserve_exist FOR
|
||||||
|
WITH reserve_changes AS (
|
||||||
|
INSERT INTO reserves
|
||||||
|
(reserve_pub
|
||||||
|
,current_balance_val
|
||||||
|
,current_balance_frac
|
||||||
|
,expiration_date
|
||||||
|
,gc_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_uuid,reserve_pub)
|
||||||
|
SELECT * FROM reserve_changes;
|
||||||
|
WHILE k < 2 LOOP
|
||||||
|
FETCH FROM curs_reserve_exist INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid = i.reserve_uuid;
|
||||||
|
IF in_reserve_pub <> in2_reserve_pub
|
||||||
|
THEN
|
||||||
|
out_reserve_found = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
out_reserve_found2 = FALSE;
|
||||||
|
ruuid2 = i.reserve_uuid;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
k=k+1;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE curs_reserve_exist;
|
||||||
|
|
||||||
|
PERFORM pg_notify(in_notify, NULL);
|
||||||
|
PERFORM pg_notify(in2_notify, NULL);
|
||||||
|
|
||||||
|
OPEN curs_transaction_exist FOR
|
||||||
|
WITH reserve_in_exist AS (
|
||||||
|
INSERT INTO reserves_in
|
||||||
|
(reserve_pub
|
||||||
|
,wire_reference
|
||||||
|
,credit_val
|
||||||
|
,credit_frac
|
||||||
|
,exchange_account_section
|
||||||
|
,wire_source_h_payto
|
||||||
|
,execution_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_wire_ref
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_exchange_account_name
|
||||||
|
,in_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_wire_ref
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in2_exchange_account_name
|
||||||
|
,in2_wire_source_h_payto
|
||||||
|
,in_expiration_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_pub)
|
||||||
|
SELECT * FROM reserve_in_exist;
|
||||||
|
FETCH FROM curs_transaction_exist INTO r;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
FETCH FROM curs_transaction_exist INTO r;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
/* IF transaction_duplicate
|
||||||
|
OR transaction_duplicate2
|
||||||
|
THEN
|
||||||
|
CLOSE curs_transaction_exist;
|
||||||
|
ROLLBACK;
|
||||||
|
RETURN;
|
||||||
|
END IF;*/
|
||||||
|
CLOSE curs_transaction_exist;
|
||||||
|
RETURN;
|
||||||
|
END $$;
|
||||||
|
|
284
src/exchangedb/exchange_do_batch4_reserves_in_insert.sql
Normal file
284
src/exchangedb/exchange_do_batch4_reserves_in_insert.sql
Normal file
@ -0,0 +1,284 @@
|
|||||||
|
--
|
||||||
|
-- This file is part of TALER
|
||||||
|
-- Copyright (C) 2014--2022 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
|
||||||
|
-- Foundation; either version 3, or (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License along with
|
||||||
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
--
|
||||||
|
CREATE OR REPLACE FUNCTION exchange_do_batch4_reserves_insert(
|
||||||
|
IN in_reserve_pub BYTEA,
|
||||||
|
IN in_expiration_date INT8,
|
||||||
|
IN in_gc_date INT8,
|
||||||
|
IN in_wire_ref INT8,
|
||||||
|
IN in_credit_val INT8,
|
||||||
|
IN in_credit_frac INT4,
|
||||||
|
IN in_exchange_account_name VARCHAR,
|
||||||
|
IN in_exectution_date INT8,
|
||||||
|
IN in_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in_payto_uri VARCHAR,
|
||||||
|
IN in_reserve_expiration INT8,
|
||||||
|
IN in_notify text,
|
||||||
|
IN in2_notify text,
|
||||||
|
IN in3_notify text,
|
||||||
|
IN in4_notify text,
|
||||||
|
IN in2_reserve_pub BYTEA,
|
||||||
|
IN in2_wire_ref INT8,
|
||||||
|
IN in2_credit_val INT8,
|
||||||
|
IN in2_credit_frac INT4,
|
||||||
|
IN in2_exchange_account_name VARCHAR,
|
||||||
|
IN in2_exectution_date INT8,
|
||||||
|
IN in2_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in2_payto_uri VARCHAR,
|
||||||
|
IN in2_reserve_expiration INT8,
|
||||||
|
IN in3_reserve_pub BYTEA,
|
||||||
|
IN in3_wire_ref INT8,
|
||||||
|
IN in3_credit_val INT8,
|
||||||
|
IN in3_credit_frac INT4,
|
||||||
|
IN in3_exchange_account_name VARCHAR,
|
||||||
|
IN in3_exectution_date INT8,
|
||||||
|
IN in3_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in3_payto_uri VARCHAR,
|
||||||
|
IN in3_reserve_expiration INT8,
|
||||||
|
IN in4_reserve_pub BYTEA,
|
||||||
|
IN in4_wire_ref INT8,
|
||||||
|
IN in4_credit_val INT8,
|
||||||
|
IN in4_credit_frac INT4,
|
||||||
|
IN in4_exchange_account_name VARCHAR,
|
||||||
|
IN in4_exectution_date INT8,
|
||||||
|
IN in4_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in4_payto_uri VARCHAR,
|
||||||
|
IN in4_reserve_expiration INT8,
|
||||||
|
OUT out_reserve_found BOOLEAN,
|
||||||
|
OUT out_reserve_found2 BOOLEAN,
|
||||||
|
OUT out_reserve_found3 BOOLEAN,
|
||||||
|
OUT out_reserve_found4 BOOLEAN,
|
||||||
|
OUT transaction_duplicate BOOLEAN,
|
||||||
|
OUT transaction_duplicate2 BOOLEAN,
|
||||||
|
OUT transaction_duplicate3 BOOLEAN,
|
||||||
|
OUT transaction_duplicate4 BOOLEAN,
|
||||||
|
OUT ruuid INT8,
|
||||||
|
OUT ruuid2 INT8,
|
||||||
|
OUT ruuid3 INT8,
|
||||||
|
OUT ruuid4 INT8)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
curs_reserve_exist refcursor;
|
||||||
|
DECLARE
|
||||||
|
k INT8;
|
||||||
|
DECLARE
|
||||||
|
curs_transaction_exist refcursor;
|
||||||
|
DECLARE
|
||||||
|
i RECORD;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
--INITIALIZATION
|
||||||
|
transaction_duplicate=TRUE;
|
||||||
|
transaction_duplicate2=TRUE;
|
||||||
|
transaction_duplicate3=TRUE;
|
||||||
|
transaction_duplicate4=TRUE;
|
||||||
|
out_reserve_found = TRUE;
|
||||||
|
out_reserve_found2 = TRUE;
|
||||||
|
out_reserve_found3 = TRUE;
|
||||||
|
out_reserve_found4 = TRUE;
|
||||||
|
ruuid=0;
|
||||||
|
ruuid2=0;
|
||||||
|
ruuid3=0;
|
||||||
|
ruuid4=0;
|
||||||
|
k=0;
|
||||||
|
--SIMPLE INSERT ON CONFLICT DO NOTHING
|
||||||
|
INSERT INTO wire_targets
|
||||||
|
(wire_target_h_payto
|
||||||
|
,payto_uri)
|
||||||
|
VALUES
|
||||||
|
(in_wire_source_h_payto
|
||||||
|
,in_payto_uri),
|
||||||
|
(in2_wire_source_h_payto
|
||||||
|
,in2_payto_uri),
|
||||||
|
(in3_wire_source_h_payto
|
||||||
|
,in3_payto_uri),
|
||||||
|
(in4_wire_source_h_payto
|
||||||
|
,in4_payto_uri)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
OPEN curs_reserve_exist FOR
|
||||||
|
WITH reserve_changes AS (
|
||||||
|
INSERT INTO reserves
|
||||||
|
(reserve_pub
|
||||||
|
,current_balance_val
|
||||||
|
,current_balance_frac
|
||||||
|
,expiration_date
|
||||||
|
,gc_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in3_reserve_pub
|
||||||
|
,in3_credit_val
|
||||||
|
,in3_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in4_reserve_pub
|
||||||
|
,in4_credit_val
|
||||||
|
,in4_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_uuid,reserve_pub)
|
||||||
|
SELECT * FROM reserve_changes;
|
||||||
|
|
||||||
|
WHILE k < 4 LOOP
|
||||||
|
FETCH FROM curs_reserve_exist INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid = i.reserve_uuid;
|
||||||
|
IF in_reserve_pub
|
||||||
|
NOT IN (in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid2 = i.reserve_uuid;
|
||||||
|
IF in2_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in3_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid3 = i.reserve_uuid;
|
||||||
|
IF in3_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in4_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found3 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in4_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid4 = i.reserve_uuid;
|
||||||
|
IF in4_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found4 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
k=k+1;
|
||||||
|
END LOOP;
|
||||||
|
CLOSE curs_reserve_exist;
|
||||||
|
|
||||||
|
|
||||||
|
PERFORM pg_notify(in_notify, NULL);
|
||||||
|
PERFORM pg_notify(in2_notify, NULL);
|
||||||
|
PERFORM pg_notify(in3_notify, NULL);
|
||||||
|
PERFORM pg_notify(in4_notify, NULL);
|
||||||
|
|
||||||
|
k=0;
|
||||||
|
OPEN curs_transaction_exist FOR
|
||||||
|
WITH reserve_in_changes AS (
|
||||||
|
INSERT INTO reserves_in
|
||||||
|
(reserve_pub
|
||||||
|
,wire_reference
|
||||||
|
,credit_val
|
||||||
|
,credit_frac
|
||||||
|
,exchange_account_section
|
||||||
|
,wire_source_h_payto
|
||||||
|
,execution_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_wire_ref
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_exchange_account_name
|
||||||
|
,in_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_wire_ref
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in2_exchange_account_name
|
||||||
|
,in2_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in3_reserve_pub
|
||||||
|
,in3_wire_ref
|
||||||
|
,in3_credit_val
|
||||||
|
,in3_credit_frac
|
||||||
|
,in3_exchange_account_name
|
||||||
|
,in3_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in4_reserve_pub
|
||||||
|
,in4_wire_ref
|
||||||
|
,in4_credit_val
|
||||||
|
,in4_credit_frac
|
||||||
|
,in4_exchange_account_name
|
||||||
|
,in4_wire_source_h_payto
|
||||||
|
,in_expiration_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_pub)
|
||||||
|
SELECT * FROM reserve_in_changes;
|
||||||
|
WHILE k < 4 LOOP
|
||||||
|
FETCH FROM curs_transaction_exist INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in3_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate3 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in4_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate4 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
k=k+1;
|
||||||
|
END LOOP;
|
||||||
|
/**ROLLBACK TRANSACTION IN SORTED PROCEDURE IS IT PROSSIBLE ?**/
|
||||||
|
/*IF transaction_duplicate
|
||||||
|
OR transaction_duplicate2
|
||||||
|
OR transaction_duplicate3
|
||||||
|
OR transaction_duplicate4
|
||||||
|
THEN
|
||||||
|
RAISE EXCEPTION 'Reserve did not exist, but INSERT into reserves_in gave conflict';
|
||||||
|
ROLLBACK;
|
||||||
|
CLOSE curs_transaction_exist;
|
||||||
|
RETURN;
|
||||||
|
END IF;*/
|
||||||
|
CLOSE curs_transaction_exist;
|
||||||
|
RETURN;
|
||||||
|
|
||||||
|
END $$;
|
506
src/exchangedb/exchange_do_batch8_reserves_in_insert.sql
Normal file
506
src/exchangedb/exchange_do_batch8_reserves_in_insert.sql
Normal file
@ -0,0 +1,506 @@
|
|||||||
|
--
|
||||||
|
-- This file is part of TALER
|
||||||
|
-- Copyright (C) 2014--2022 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
|
||||||
|
-- Foundation; either version 3, or (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License along with
|
||||||
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
--
|
||||||
|
CREATE OR REPLACE FUNCTION exchange_do_batch8_reserves_insert(
|
||||||
|
IN in_reserve_pub BYTEA,
|
||||||
|
IN in_expiration_date INT8,
|
||||||
|
IN in_gc_date INT8,
|
||||||
|
IN in_wire_ref INT8,
|
||||||
|
IN in_credit_val INT8,
|
||||||
|
IN in_credit_frac INT4,
|
||||||
|
IN in_exchange_account_name VARCHAR,
|
||||||
|
IN in_exectution_date INT8,
|
||||||
|
IN in_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in_payto_uri VARCHAR,
|
||||||
|
IN in_reserve_expiration INT8,
|
||||||
|
IN in_notify text,
|
||||||
|
IN in2_notify text,
|
||||||
|
IN in3_notify text,
|
||||||
|
IN in4_notify text,
|
||||||
|
IN in5_notify text,
|
||||||
|
IN in6_notify text,
|
||||||
|
IN in7_notify text,
|
||||||
|
IN in8_notify text,
|
||||||
|
IN in2_reserve_pub BYTEA,
|
||||||
|
IN in2_wire_ref INT8,
|
||||||
|
IN in2_credit_val INT8,
|
||||||
|
IN in2_credit_frac INT4,
|
||||||
|
IN in2_exchange_account_name VARCHAR,
|
||||||
|
IN in2_exectution_date INT8,
|
||||||
|
IN in2_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in2_payto_uri VARCHAR,
|
||||||
|
IN in2_reserve_expiration INT8,
|
||||||
|
IN in3_reserve_pub BYTEA,
|
||||||
|
IN in3_wire_ref INT8,
|
||||||
|
IN in3_credit_val INT8,
|
||||||
|
IN in3_credit_frac INT4,
|
||||||
|
IN in3_exchange_account_name VARCHAR,
|
||||||
|
IN in3_exectution_date INT8,
|
||||||
|
IN in3_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in3_payto_uri VARCHAR,
|
||||||
|
IN in3_reserve_expiration INT8,
|
||||||
|
IN in4_reserve_pub BYTEA,
|
||||||
|
IN in4_wire_ref INT8,
|
||||||
|
IN in4_credit_val INT8,
|
||||||
|
IN in4_credit_frac INT4,
|
||||||
|
IN in4_exchange_account_name VARCHAR,
|
||||||
|
IN in4_exectution_date INT8,
|
||||||
|
IN in4_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in4_payto_uri VARCHAR,
|
||||||
|
IN in4_reserve_expiration INT8,
|
||||||
|
IN in5_reserve_pub BYTEA,
|
||||||
|
IN in5_wire_ref INT8,
|
||||||
|
IN in5_credit_val INT8,
|
||||||
|
IN in5_credit_frac INT4,
|
||||||
|
IN in5_exchange_account_name VARCHAR,
|
||||||
|
IN in5_exectution_date INT8,
|
||||||
|
IN in5_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in5_payto_uri VARCHAR,
|
||||||
|
IN in5_reserve_expiration INT8,
|
||||||
|
IN in6_reserve_pub BYTEA,
|
||||||
|
IN in6_wire_ref INT8,
|
||||||
|
IN in6_credit_val INT8,
|
||||||
|
IN in6_credit_frac INT4,
|
||||||
|
IN in6_exchange_account_name VARCHAR,
|
||||||
|
IN in6_exectution_date INT8,
|
||||||
|
IN in6_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in6_payto_uri VARCHAR,
|
||||||
|
IN in6_reserve_expiration INT8,
|
||||||
|
IN in7_reserve_pub BYTEA,
|
||||||
|
IN in7_wire_ref INT8,
|
||||||
|
IN in7_credit_val INT8,
|
||||||
|
IN in7_credit_frac INT4,
|
||||||
|
IN in7_exchange_account_name VARCHAR,
|
||||||
|
IN in7_exectution_date INT8,
|
||||||
|
IN in7_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in7_payto_uri VARCHAR,
|
||||||
|
IN in7_reserve_expiration INT8,
|
||||||
|
IN in8_reserve_pub BYTEA,
|
||||||
|
IN in8_wire_ref INT8,
|
||||||
|
IN in8_credit_val INT8,
|
||||||
|
IN in8_credit_frac INT4,
|
||||||
|
IN in8_exchange_account_name VARCHAR,
|
||||||
|
IN in8_exectution_date INT8,
|
||||||
|
IN in8_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in8_payto_uri VARCHAR,
|
||||||
|
IN in8_reserve_expiration INT8,
|
||||||
|
OUT out_reserve_found BOOLEAN,
|
||||||
|
OUT out_reserve_found2 BOOLEAN,
|
||||||
|
OUT out_reserve_found3 BOOLEAN,
|
||||||
|
OUT out_reserve_found4 BOOLEAN,
|
||||||
|
OUT out_reserve_found5 BOOLEAN,
|
||||||
|
OUT out_reserve_found6 BOOLEAN,
|
||||||
|
OUT out_reserve_found7 BOOLEAN,
|
||||||
|
OUT out_reserve_found8 BOOLEAN,
|
||||||
|
OUT transaction_duplicate BOOLEAN,
|
||||||
|
OUT transaction_duplicate2 BOOLEAN,
|
||||||
|
OUT transaction_duplicate3 BOOLEAN,
|
||||||
|
OUT transaction_duplicate4 BOOLEAN,
|
||||||
|
OUT transaction_duplicate5 BOOLEAN,
|
||||||
|
OUT transaction_duplicate6 BOOLEAN,
|
||||||
|
OUT transaction_duplicate7 BOOLEAN,
|
||||||
|
OUT transaction_duplicate8 BOOLEAN,
|
||||||
|
OUT ruuid INT8,
|
||||||
|
OUT ruuid2 INT8,
|
||||||
|
OUT ruuid3 INT8,
|
||||||
|
OUT ruuid4 INT8,
|
||||||
|
OUT ruuid5 INT8,
|
||||||
|
OUT ruuid6 INT8,
|
||||||
|
OUT ruuid7 INT8,
|
||||||
|
OUT ruuid8 INT8)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
curs_reserve_existed refcursor;
|
||||||
|
DECLARE
|
||||||
|
k INT8;
|
||||||
|
DECLARE
|
||||||
|
curs_transaction_existed refcursor;
|
||||||
|
|
||||||
|
DECLARE
|
||||||
|
i RECORD;
|
||||||
|
DECLARE
|
||||||
|
r RECORD;
|
||||||
|
|
||||||
|
BEGIN
|
||||||
|
--INITIALIZATION
|
||||||
|
transaction_duplicate=TRUE;
|
||||||
|
transaction_duplicate2=TRUE;
|
||||||
|
transaction_duplicate3=TRUE;
|
||||||
|
transaction_duplicate4=TRUE;
|
||||||
|
transaction_duplicate5=TRUE;
|
||||||
|
transaction_duplicate6=TRUE;
|
||||||
|
transaction_duplicate7=TRUE;
|
||||||
|
transaction_duplicate8=TRUE;
|
||||||
|
out_reserve_found = TRUE;
|
||||||
|
out_reserve_found2 = TRUE;
|
||||||
|
out_reserve_found3 = TRUE;
|
||||||
|
out_reserve_found4 = TRUE;
|
||||||
|
out_reserve_found5 = TRUE;
|
||||||
|
out_reserve_found6 = TRUE;
|
||||||
|
out_reserve_found7 = TRUE;
|
||||||
|
out_reserve_found8 = TRUE;
|
||||||
|
ruuid=0;
|
||||||
|
ruuid2=0;
|
||||||
|
ruuid3=0;
|
||||||
|
ruuid4=0;
|
||||||
|
ruuid5=0;
|
||||||
|
ruuid6=0;
|
||||||
|
ruuid7=0;
|
||||||
|
ruuid8=0;
|
||||||
|
k=0;
|
||||||
|
|
||||||
|
--SIMPLE INSERT ON CONFLICT DO NOTHING
|
||||||
|
INSERT INTO wire_targets
|
||||||
|
(wire_target_h_payto
|
||||||
|
,payto_uri)
|
||||||
|
VALUES
|
||||||
|
(in_wire_source_h_payto
|
||||||
|
,in_payto_uri),
|
||||||
|
(in2_wire_source_h_payto
|
||||||
|
,in2_payto_uri),
|
||||||
|
(in3_wire_source_h_payto
|
||||||
|
,in3_payto_uri),
|
||||||
|
(in4_wire_source_h_payto
|
||||||
|
,in4_payto_uri),
|
||||||
|
(in5_wire_source_h_payto
|
||||||
|
,in5_payto_uri),
|
||||||
|
(in6_wire_source_h_payto
|
||||||
|
,in6_payto_uri),
|
||||||
|
(in7_wire_source_h_payto
|
||||||
|
,in7_payto_uri),
|
||||||
|
(in8_wire_source_h_payto
|
||||||
|
,in8_payto_uri)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
OPEN curs_reserve_existed FOR
|
||||||
|
WITH reserve_changes AS (
|
||||||
|
INSERT INTO reserves
|
||||||
|
(reserve_pub
|
||||||
|
,current_balance_val
|
||||||
|
,current_balance_frac
|
||||||
|
,expiration_date
|
||||||
|
,gc_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in3_reserve_pub
|
||||||
|
,in3_credit_val
|
||||||
|
,in3_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in4_reserve_pub
|
||||||
|
,in4_credit_val
|
||||||
|
,in4_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in5_reserve_pub
|
||||||
|
,in5_credit_val
|
||||||
|
,in5_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in6_reserve_pub
|
||||||
|
,in6_credit_val
|
||||||
|
,in6_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in7_reserve_pub
|
||||||
|
,in7_credit_val
|
||||||
|
,in7_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date),
|
||||||
|
(in8_reserve_pub
|
||||||
|
,in8_credit_val
|
||||||
|
,in8_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_uuid,reserve_pub)
|
||||||
|
SELECT * FROM reserve_changes;
|
||||||
|
|
||||||
|
WHILE k < 8 LOOP
|
||||||
|
|
||||||
|
FETCH FROM curs_reserve_existed INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid = i.reserve_uuid;
|
||||||
|
IF in_reserve_pub
|
||||||
|
NOT IN (in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid2 = i.reserve_uuid;
|
||||||
|
IF in2_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in3_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid3 = i.reserve_uuid;
|
||||||
|
IF in3_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found3 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in4_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid4 = i.reserve_uuid;
|
||||||
|
IF in4_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found4 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in5_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid5 = i.reserve_uuid;
|
||||||
|
IF in5_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found5 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in6_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid6 = i.reserve_uuid;
|
||||||
|
IF in6_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in7_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found6 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in7_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid7 = i.reserve_uuid;
|
||||||
|
IF in7_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in8_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found7 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
IF in8_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
ruuid8 = i.reserve_uuid;
|
||||||
|
IF in8_reserve_pub
|
||||||
|
NOT IN (in_reserve_pub
|
||||||
|
,in2_reserve_pub
|
||||||
|
,in3_reserve_pub
|
||||||
|
,in4_reserve_pub
|
||||||
|
,in5_reserve_pub
|
||||||
|
,in6_reserve_pub
|
||||||
|
,in7_reserve_pub)
|
||||||
|
THEN
|
||||||
|
out_reserve_found8 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
k=k+1;
|
||||||
|
END LOOP;
|
||||||
|
|
||||||
|
CLOSE curs_reserve_existed;
|
||||||
|
|
||||||
|
PERFORM pg_notify(in_notify, NULL);
|
||||||
|
PERFORM pg_notify(in2_notify, NULL);
|
||||||
|
PERFORM pg_notify(in3_notify, NULL);
|
||||||
|
PERFORM pg_notify(in4_notify, NULL);
|
||||||
|
PERFORM pg_notify(in5_notify, NULL);
|
||||||
|
PERFORM pg_notify(in6_notify, NULL);
|
||||||
|
PERFORM pg_notify(in7_notify, NULL);
|
||||||
|
PERFORM pg_notify(in8_notify, NULL);
|
||||||
|
k=0;
|
||||||
|
OPEN curs_transaction_existed FOR
|
||||||
|
WITH reserve_in_changes AS (
|
||||||
|
INSERT INTO reserves_in
|
||||||
|
(reserve_pub
|
||||||
|
,wire_reference
|
||||||
|
,credit_val
|
||||||
|
,credit_frac
|
||||||
|
,exchange_account_section
|
||||||
|
,wire_source_h_payto
|
||||||
|
,execution_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_wire_ref
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_exchange_account_name
|
||||||
|
,in_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in2_reserve_pub
|
||||||
|
,in2_wire_ref
|
||||||
|
,in2_credit_val
|
||||||
|
,in2_credit_frac
|
||||||
|
,in2_exchange_account_name
|
||||||
|
,in2_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in3_reserve_pub
|
||||||
|
,in3_wire_ref
|
||||||
|
,in3_credit_val
|
||||||
|
,in3_credit_frac
|
||||||
|
,in3_exchange_account_name
|
||||||
|
,in3_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in4_reserve_pub
|
||||||
|
,in4_wire_ref
|
||||||
|
,in4_credit_val
|
||||||
|
,in4_credit_frac
|
||||||
|
,in4_exchange_account_name
|
||||||
|
,in4_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in5_reserve_pub
|
||||||
|
,in5_wire_ref
|
||||||
|
,in5_credit_val
|
||||||
|
,in5_credit_frac
|
||||||
|
,in5_exchange_account_name
|
||||||
|
,in5_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in6_reserve_pub
|
||||||
|
,in6_wire_ref
|
||||||
|
,in6_credit_val
|
||||||
|
,in6_credit_frac
|
||||||
|
,in6_exchange_account_name
|
||||||
|
,in6_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in7_reserve_pub
|
||||||
|
,in7_wire_ref
|
||||||
|
,in7_credit_val
|
||||||
|
,in7_credit_frac
|
||||||
|
,in7_exchange_account_name
|
||||||
|
,in7_wire_source_h_payto
|
||||||
|
,in_expiration_date),
|
||||||
|
(in8_reserve_pub
|
||||||
|
,in8_wire_ref
|
||||||
|
,in8_credit_val
|
||||||
|
,in8_credit_frac
|
||||||
|
,in8_exchange_account_name
|
||||||
|
,in8_wire_source_h_payto
|
||||||
|
,in_expiration_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_pub)
|
||||||
|
SELECT * FROM reserve_in_changes;
|
||||||
|
|
||||||
|
WHILE k < 8 LOOP
|
||||||
|
FETCH FROM curs_transaction_existed INTO r;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF in_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in2_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate2 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in3_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate3 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in4_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate4 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in5_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate5 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in6_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate6 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in7_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate7 = FALSE;
|
||||||
|
END IF;
|
||||||
|
IF in8_reserve_pub = r.reserve_pub
|
||||||
|
THEN
|
||||||
|
transaction_duplicate8 = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
k=k+1;
|
||||||
|
END LOOP;
|
||||||
|
/* IF transaction_duplicate
|
||||||
|
OR transaction_duplicate2
|
||||||
|
OR transaction_duplicate3
|
||||||
|
OR transaction_duplicate4
|
||||||
|
OR transaction_duplicate5
|
||||||
|
OR transaction_duplicate6
|
||||||
|
OR transaction_duplicate7
|
||||||
|
OR transaction_duplicate8
|
||||||
|
THEN
|
||||||
|
CLOSE curs_transaction_existed;
|
||||||
|
ROLLBACK;
|
||||||
|
RETURN;
|
||||||
|
END IF;*/
|
||||||
|
CLOSE curs_transaction_existed;
|
||||||
|
RETURN;
|
||||||
|
END $$;
|
@ -1,477 +0,0 @@
|
|||||||
--
|
|
||||||
-- This file is part of TALER
|
|
||||||
-- Copyright (C) 2014--2022 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
|
|
||||||
-- Foundation; either version 3, or (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License along with
|
|
||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_batch4_known_coin(
|
|
||||||
IN in_coin_pub1 BYTEA,
|
|
||||||
IN in_denom_pub_hash1 BYTEA,
|
|
||||||
IN in_h_age_commitment1 BYTEA,
|
|
||||||
IN in_denom_sig1 BYTEA,
|
|
||||||
IN in_coin_pub2 BYTEA,
|
|
||||||
IN in_denom_pub_hash2 BYTEA,
|
|
||||||
IN in_h_age_commitment2 BYTEA,
|
|
||||||
IN in_denom_sig2 BYTEA,
|
|
||||||
IN in_coin_pub3 BYTEA,
|
|
||||||
IN in_denom_pub_hash3 BYTEA,
|
|
||||||
IN in_h_age_commitment3 BYTEA,
|
|
||||||
IN in_denom_sig3 BYTEA,
|
|
||||||
IN in_coin_pub4 BYTEA,
|
|
||||||
IN in_denom_pub_hash4 BYTEA,
|
|
||||||
IN in_h_age_commitment4 BYTEA,
|
|
||||||
IN in_denom_sig4 BYTEA,
|
|
||||||
OUT existed1 BOOLEAN,
|
|
||||||
OUT existed2 BOOLEAN,
|
|
||||||
OUT existed3 BOOLEAN,
|
|
||||||
OUT existed4 BOOLEAN,
|
|
||||||
OUT known_coin_id1 INT8,
|
|
||||||
OUT known_coin_id2 INT8,
|
|
||||||
OUT known_coin_id3 INT8,
|
|
||||||
OUT known_coin_id4 INT8,
|
|
||||||
OUT denom_pub_hash1 BYTEA,
|
|
||||||
OUT denom_pub_hash2 BYTEA,
|
|
||||||
OUT denom_pub_hash3 BYTEA,
|
|
||||||
OUT denom_pub_hash4 BYTEA,
|
|
||||||
OUT age_commitment_hash1 BYTEA,
|
|
||||||
OUT age_commitment_hash2 BYTEA,
|
|
||||||
OUT age_commitment_hash3 BYTEA,
|
|
||||||
OUT age_commitment_hash4 BYTEA)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
WITH dd AS (
|
|
||||||
SELECT
|
|
||||||
denominations_serial,
|
|
||||||
coin_val, coin_frac
|
|
||||||
FROM denominations
|
|
||||||
WHERE denom_pub_hash
|
|
||||||
IN
|
|
||||||
(in_denom_pub_hash1,
|
|
||||||
in_denom_pub_hash2,
|
|
||||||
in_denom_pub_hash3,
|
|
||||||
in_denom_pub_hash4)
|
|
||||||
),--dd
|
|
||||||
input_rows AS (
|
|
||||||
VALUES
|
|
||||||
(in_coin_pub1,
|
|
||||||
in_denom_pub_hash1,
|
|
||||||
in_h_age_commitment1,
|
|
||||||
in_denom_sig1),
|
|
||||||
(in_coin_pub2,
|
|
||||||
in_denom_pub_hash2,
|
|
||||||
in_h_age_commitment2,
|
|
||||||
in_denom_sig2),
|
|
||||||
(in_coin_pub3,
|
|
||||||
in_denom_pub_hash3,
|
|
||||||
in_h_age_commitment3,
|
|
||||||
in_denom_sig3),
|
|
||||||
(in_coin_pub4,
|
|
||||||
in_denom_pub_hash4,
|
|
||||||
in_h_age_commitment4,
|
|
||||||
in_denom_sig4)
|
|
||||||
),--ir
|
|
||||||
ins AS (
|
|
||||||
INSERT INTO known_coins (
|
|
||||||
coin_pub,
|
|
||||||
denominations_serial,
|
|
||||||
age_commitment_hash,
|
|
||||||
denom_sig,
|
|
||||||
remaining_val,
|
|
||||||
remaining_frac
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
ir.coin_pub,
|
|
||||||
dd.denominations_serial,
|
|
||||||
ir.age_commitment_hash,
|
|
||||||
ir.denom_sig,
|
|
||||||
dd.coin_val,
|
|
||||||
dd.coin_frac
|
|
||||||
FROM input_rows ir
|
|
||||||
JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
RETURNING known_coin_id
|
|
||||||
),--kc
|
|
||||||
exists AS (
|
|
||||||
SELECT
|
|
||||||
CASE
|
|
||||||
WHEN
|
|
||||||
ins.known_coin_id IS NOT NULL
|
|
||||||
THEN
|
|
||||||
FALSE
|
|
||||||
ELSE
|
|
||||||
TRUE
|
|
||||||
END AS existed,
|
|
||||||
ins.known_coin_id,
|
|
||||||
dd.denom_pub_hash,
|
|
||||||
kc.age_commitment_hash
|
|
||||||
FROM input_rows ir
|
|
||||||
LEFT JOIN ins
|
|
||||||
ON ins.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN known_coins kc
|
|
||||||
ON kc.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
)--exists
|
|
||||||
SELECT
|
|
||||||
exists.existed AS existed1,
|
|
||||||
exists.known_coin_id AS known_coin_id1,
|
|
||||||
exists.denom_pub_hash AS denom_pub_hash1,
|
|
||||||
exists.age_commitment_hash AS age_commitment_hash1,
|
|
||||||
(
|
|
||||||
SELECT exists.existed
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS existed2,
|
|
||||||
(
|
|
||||||
SELECT exists.known_coin_id
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS known_coin_id2,
|
|
||||||
(
|
|
||||||
SELECT exists.denom_pub_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS denom_pub_hash2,
|
|
||||||
(
|
|
||||||
SELECT exists.age_commitment_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
)AS age_commitment_hash2,
|
|
||||||
(
|
|
||||||
SELECT exists.existed
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash3
|
|
||||||
) AS existed3,
|
|
||||||
(
|
|
||||||
SELECT exists.known_coin_id
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash3
|
|
||||||
) AS known_coin_id3,
|
|
||||||
(
|
|
||||||
SELECT exists.denom_pub_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash3
|
|
||||||
) AS denom_pub_hash3,
|
|
||||||
(
|
|
||||||
SELECT exists.age_commitment_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash3
|
|
||||||
)AS age_commitment_hash3,
|
|
||||||
(
|
|
||||||
SELECT exists.existed
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash4
|
|
||||||
) AS existed4,
|
|
||||||
(
|
|
||||||
SELECT exists.known_coin_id
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash4
|
|
||||||
) AS known_coin_id4,
|
|
||||||
(
|
|
||||||
SELECT exists.denom_pub_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash4
|
|
||||||
) AS denom_pub_hash4,
|
|
||||||
(
|
|
||||||
SELECT exists.age_commitment_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash4
|
|
||||||
)AS age_commitment_hash4
|
|
||||||
FROM exists;
|
|
||||||
|
|
||||||
RETURN;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
|
|
||||||
IN in_coin_pub1 BYTEA,
|
|
||||||
IN in_denom_pub_hash1 BYTEA,
|
|
||||||
IN in_h_age_commitment1 BYTEA,
|
|
||||||
IN in_denom_sig1 BYTEA,
|
|
||||||
IN in_coin_pub2 BYTEA,
|
|
||||||
IN in_denom_pub_hash2 BYTEA,
|
|
||||||
IN in_h_age_commitment2 BYTEA,
|
|
||||||
IN in_denom_sig2 BYTEA,
|
|
||||||
OUT existed1 BOOLEAN,
|
|
||||||
OUT existed2 BOOLEAN,
|
|
||||||
OUT known_coin_id1 INT8,
|
|
||||||
OUT known_coin_id2 INT8,
|
|
||||||
OUT denom_pub_hash1 BYTEA,
|
|
||||||
OUT denom_pub_hash2 BYTEA,
|
|
||||||
OUT age_commitment_hash1 BYTEA,
|
|
||||||
OUT age_commitment_hash2 BYTEA)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
WITH dd AS (
|
|
||||||
SELECT
|
|
||||||
denominations_serial,
|
|
||||||
coin_val, coin_frac
|
|
||||||
FROM denominations
|
|
||||||
WHERE denom_pub_hash
|
|
||||||
IN
|
|
||||||
(in_denom_pub_hash1,
|
|
||||||
in_denom_pub_hash2)
|
|
||||||
),--dd
|
|
||||||
input_rows AS (
|
|
||||||
VALUES
|
|
||||||
(in_coin_pub1,
|
|
||||||
in_denom_pub_hash1,
|
|
||||||
in_h_age_commitment1,
|
|
||||||
in_denom_sig1),
|
|
||||||
(in_coin_pub2,
|
|
||||||
in_denom_pub_hash2,
|
|
||||||
in_h_age_commitment2,
|
|
||||||
in_denom_sig2)
|
|
||||||
),--ir
|
|
||||||
ins AS (
|
|
||||||
INSERT INTO known_coins (
|
|
||||||
coin_pub,
|
|
||||||
denominations_serial,
|
|
||||||
age_commitment_hash,
|
|
||||||
denom_sig,
|
|
||||||
remaining_val,
|
|
||||||
remaining_frac
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
ir.coin_pub,
|
|
||||||
dd.denominations_serial,
|
|
||||||
ir.age_commitment_hash,
|
|
||||||
ir.denom_sig,
|
|
||||||
dd.coin_val,
|
|
||||||
dd.coin_frac
|
|
||||||
FROM input_rows ir
|
|
||||||
JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
RETURNING known_coin_id
|
|
||||||
),--kc
|
|
||||||
exists AS (
|
|
||||||
SELECT
|
|
||||||
CASE
|
|
||||||
WHEN ins.known_coin_id IS NOT NULL
|
|
||||||
THEN
|
|
||||||
FALSE
|
|
||||||
ELSE
|
|
||||||
TRUE
|
|
||||||
END AS existed,
|
|
||||||
ins.known_coin_id,
|
|
||||||
dd.denom_pub_hash,
|
|
||||||
kc.age_commitment_hash
|
|
||||||
FROM input_rows ir
|
|
||||||
LEFT JOIN ins
|
|
||||||
ON ins.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN known_coins kc
|
|
||||||
ON kc.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
)--exists
|
|
||||||
SELECT
|
|
||||||
exists.existed AS existed1,
|
|
||||||
exists.known_coin_id AS known_coin_id1,
|
|
||||||
exists.denom_pub_hash AS denom_pub_hash1,
|
|
||||||
exists.age_commitment_hash AS age_commitment_hash1,
|
|
||||||
(
|
|
||||||
SELECT exists.existed
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS existed2,
|
|
||||||
(
|
|
||||||
SELECT exists.known_coin_id
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS known_coin_id2,
|
|
||||||
(
|
|
||||||
SELECT exists.denom_pub_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
) AS denom_pub_hash2,
|
|
||||||
(
|
|
||||||
SELECT exists.age_commitment_hash
|
|
||||||
FROM exists
|
|
||||||
WHERE exists.denom_pub_hash = in_denom_pub_hash2
|
|
||||||
)AS age_commitment_hash2
|
|
||||||
FROM exists;
|
|
||||||
|
|
||||||
RETURN;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_batch1_known_coin(
|
|
||||||
IN in_coin_pub1 BYTEA,
|
|
||||||
IN in_denom_pub_hash1 BYTEA,
|
|
||||||
IN in_h_age_commitment1 BYTEA,
|
|
||||||
IN in_denom_sig1 BYTEA,
|
|
||||||
OUT existed1 BOOLEAN,
|
|
||||||
OUT known_coin_id1 INT8,
|
|
||||||
OUT denom_pub_hash1 BYTEA,
|
|
||||||
OUT age_commitment_hash1 BYTEA)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
WITH dd AS (
|
|
||||||
SELECT
|
|
||||||
denominations_serial,
|
|
||||||
coin_val, coin_frac
|
|
||||||
FROM denominations
|
|
||||||
WHERE denom_pub_hash
|
|
||||||
IN
|
|
||||||
(in_denom_pub_hash1,
|
|
||||||
in_denom_pub_hash2)
|
|
||||||
),--dd
|
|
||||||
input_rows AS (
|
|
||||||
VALUES
|
|
||||||
(in_coin_pub1,
|
|
||||||
in_denom_pub_hash1,
|
|
||||||
in_h_age_commitment1,
|
|
||||||
in_denom_sig1)
|
|
||||||
),--ir
|
|
||||||
ins AS (
|
|
||||||
INSERT INTO known_coins (
|
|
||||||
coin_pub,
|
|
||||||
denominations_serial,
|
|
||||||
age_commitment_hash,
|
|
||||||
denom_sig,
|
|
||||||
remaining_val,
|
|
||||||
remaining_frac
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
ir.coin_pub,
|
|
||||||
dd.denominations_serial,
|
|
||||||
ir.age_commitment_hash,
|
|
||||||
ir.denom_sig,
|
|
||||||
dd.coin_val,
|
|
||||||
dd.coin_frac
|
|
||||||
FROM input_rows ir
|
|
||||||
JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
RETURNING known_coin_id
|
|
||||||
),--kc
|
|
||||||
exists AS (
|
|
||||||
SELECT
|
|
||||||
CASE
|
|
||||||
WHEN ins.known_coin_id IS NOT NULL
|
|
||||||
THEN
|
|
||||||
FALSE
|
|
||||||
ELSE
|
|
||||||
TRUE
|
|
||||||
END AS existed,
|
|
||||||
ins.known_coin_id,
|
|
||||||
dd.denom_pub_hash,
|
|
||||||
kc.age_commitment_hash
|
|
||||||
FROM input_rows ir
|
|
||||||
LEFT JOIN ins
|
|
||||||
ON ins.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN known_coins kc
|
|
||||||
ON kc.coin_pub = ir.coin_pub
|
|
||||||
LEFT JOIN dd
|
|
||||||
ON dd.denom_pub_hash = ir.denom_pub_hash
|
|
||||||
)--exists
|
|
||||||
SELECT
|
|
||||||
exists.existed AS existed1,
|
|
||||||
exists.known_coin_id AS known_coin_id1,
|
|
||||||
exists.denom_pub_hash AS denom_pub_hash1,
|
|
||||||
exists.age_commitment_hash AS age_commitment_hash1
|
|
||||||
FROM exists;
|
|
||||||
|
|
||||||
RETURN;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
/*** Experiment using a loop ***/
|
|
||||||
/*
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_batch2_known_coin(
|
|
||||||
IN in_coin_pub1 BYTEA,
|
|
||||||
IN in_denom_pub_hash1 TEXT,
|
|
||||||
IN in_h_age_commitment1 TEXT,
|
|
||||||
IN in_denom_sig1 TEXT,
|
|
||||||
IN in_coin_pub2 BYTEA,
|
|
||||||
IN in_denom_pub_hash2 TEXT,
|
|
||||||
IN in_h_age_commitment2 TEXT,
|
|
||||||
IN in_denom_sig2 TEXT,
|
|
||||||
OUT existed1 BOOLEAN,
|
|
||||||
OUT existed2 BOOLEAN,
|
|
||||||
OUT known_coin_id1 INT8,
|
|
||||||
OUT known_coin_id2 INT8,
|
|
||||||
OUT denom_pub_hash1 TEXT,
|
|
||||||
OUT denom_pub_hash2 TEXT,
|
|
||||||
OUT age_commitment_hash1 TEXT,
|
|
||||||
OUT age_commitment_hash2 TEXT)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
ins_values RECORD;
|
|
||||||
BEGIN
|
|
||||||
FOR i IN 1..2 LOOP
|
|
||||||
ins_values := (
|
|
||||||
SELECT
|
|
||||||
in_coin_pub1 AS coin_pub,
|
|
||||||
in_denom_pub_hash1 AS denom_pub_hash,
|
|
||||||
in_h_age_commitment1 AS age_commitment_hash,
|
|
||||||
in_denom_sig1 AS denom_sig
|
|
||||||
WHERE i = 1
|
|
||||||
UNION
|
|
||||||
SELECT
|
|
||||||
in_coin_pub2 AS coin_pub,
|
|
||||||
in_denom_pub_hash2 AS denom_pub_hash,
|
|
||||||
in_h_age_commitment2 AS age_commitment_hash,
|
|
||||||
in_denom_sig2 AS denom_sig
|
|
||||||
WHERE i = 2
|
|
||||||
);
|
|
||||||
WITH dd (denominations_serial, coin_val, coin_frac) AS (
|
|
||||||
SELECT denominations_serial, coin_val, coin_frac
|
|
||||||
FROM denominations
|
|
||||||
WHERE denom_pub_hash = ins_values.denom_pub_hash
|
|
||||||
),
|
|
||||||
input_rows(coin_pub) AS (
|
|
||||||
VALUES (ins_values.coin_pub)
|
|
||||||
),
|
|
||||||
ins AS (
|
|
||||||
INSERT INTO known_coins (
|
|
||||||
coin_pub,
|
|
||||||
denominations_serial,
|
|
||||||
age_commitment_hash,
|
|
||||||
denom_sig,
|
|
||||||
remaining_val,
|
|
||||||
remaining_frac
|
|
||||||
) SELECT
|
|
||||||
input_rows.coin_pub,
|
|
||||||
dd.denominations_serial,
|
|
||||||
ins_values.age_commitment_hash,
|
|
||||||
ins_values.denom_sig,
|
|
||||||
coin_val,
|
|
||||||
coin_frac
|
|
||||||
FROM dd
|
|
||||||
CROSS JOIN input_rows
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
RETURNING known_coin_id, denom_pub_hash
|
|
||||||
)
|
|
||||||
SELECT
|
|
||||||
CASE i
|
|
||||||
WHEN 1 THEN
|
|
||||||
COALESCE(ins.known_coin_id, 0) <> 0 AS existed1,
|
|
||||||
ins.known_coin_id AS known_coin_id1,
|
|
||||||
ins.denom_pub_hash AS denom_pub_hash1,
|
|
||||||
ins.age_commitment_hash AS age_commitment_hash1
|
|
||||||
WHEN 2 THEN
|
|
||||||
COALESCE(ins.known_coin_id, 0) <> 0 AS existed2,
|
|
||||||
ins.known_coin_id AS known_coin_id2,
|
|
||||||
ins.denom_pub_hash AS denom_pub_hash2,
|
|
||||||
ins.age_commitment_hash AS age_commitment_hash2
|
|
||||||
END
|
|
||||||
FROM ins;
|
|
||||||
END LOOP;
|
|
||||||
END;
|
|
||||||
$$;*/
|
|
116
src/exchangedb/exchange_do_batch_reserves_in_insert.sql
Normal file
116
src/exchangedb/exchange_do_batch_reserves_in_insert.sql
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
--
|
||||||
|
-- This file is part of TALER
|
||||||
|
-- Copyright (C) 2014--2022 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
|
||||||
|
-- Foundation; either version 3, or (at your option) any later version.
|
||||||
|
--
|
||||||
|
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
|
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
|
--
|
||||||
|
-- You should have received a copy of the GNU General Public License along with
|
||||||
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
--
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION exchange_do_batch_reserves_in_insert(
|
||||||
|
IN in_reserve_pub BYTEA,
|
||||||
|
IN in_expiration_date INT8,
|
||||||
|
IN in_gc_date INT8,
|
||||||
|
IN in_wire_ref INT8,
|
||||||
|
IN in_credit_val INT8,
|
||||||
|
IN in_credit_frac INT4,
|
||||||
|
IN in_exchange_account_name VARCHAR,
|
||||||
|
IN in_exectution_date INT8,
|
||||||
|
IN in_wire_source_h_payto BYTEA, ---h_payto
|
||||||
|
IN in_payto_uri VARCHAR,
|
||||||
|
IN in_reserve_expiration INT8,
|
||||||
|
IN in_notify text,
|
||||||
|
OUT out_reserve_found BOOLEAN,
|
||||||
|
OUT transaction_duplicate BOOLEAN,
|
||||||
|
OUT ruuid INT8)
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
curs refcursor;
|
||||||
|
DECLARE
|
||||||
|
i RECORD;
|
||||||
|
DECLARE
|
||||||
|
curs_trans refcursor;
|
||||||
|
BEGIN
|
||||||
|
ruuid= 0;
|
||||||
|
out_reserve_found = TRUE;
|
||||||
|
transaction_duplicate= TRUE;
|
||||||
|
--SIMPLE INSERT ON CONFLICT DO NOTHING
|
||||||
|
INSERT INTO wire_targets
|
||||||
|
(wire_target_h_payto
|
||||||
|
,payto_uri)
|
||||||
|
VALUES
|
||||||
|
(in_wire_source_h_payto
|
||||||
|
,in_payto_uri)
|
||||||
|
ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
|
OPEN curs FOR
|
||||||
|
WITH reserve_changes AS (
|
||||||
|
INSERT INTO reserves
|
||||||
|
(reserve_pub
|
||||||
|
,current_balance_val
|
||||||
|
,current_balance_frac
|
||||||
|
,expiration_date
|
||||||
|
,gc_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_expiration_date
|
||||||
|
,in_gc_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_uuid, reserve_pub)
|
||||||
|
SELECT * FROM reserve_changes;
|
||||||
|
FETCH FROM curs INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
-- We made a change, so the reserve did not previously exist.
|
||||||
|
IF in_reserve_pub = i.reserve_pub
|
||||||
|
THEN
|
||||||
|
out_reserve_found = FALSE;
|
||||||
|
ruuid = i.reserve_uuid;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
CLOSE curs;
|
||||||
|
|
||||||
|
PERFORM pg_notify(in_notify, NULL);
|
||||||
|
OPEN curs_trans FOR
|
||||||
|
WITH reserve_transaction AS(
|
||||||
|
INSERT INTO reserves_in
|
||||||
|
(reserve_pub
|
||||||
|
,wire_reference
|
||||||
|
,credit_val
|
||||||
|
,credit_frac
|
||||||
|
,exchange_account_section
|
||||||
|
,wire_source_h_payto
|
||||||
|
,execution_date)
|
||||||
|
VALUES
|
||||||
|
(in_reserve_pub
|
||||||
|
,in_wire_ref
|
||||||
|
,in_credit_val
|
||||||
|
,in_credit_frac
|
||||||
|
,in_exchange_account_name
|
||||||
|
,in_wire_source_h_payto
|
||||||
|
,in_expiration_date)
|
||||||
|
ON CONFLICT DO NOTHING
|
||||||
|
RETURNING reserve_pub)
|
||||||
|
SELECT * FROM reserve_transaction;
|
||||||
|
FETCH FROM curs_trans INTO i;
|
||||||
|
IF FOUND
|
||||||
|
THEN
|
||||||
|
IF i.reserve_pub = in_reserve_pub
|
||||||
|
THEN
|
||||||
|
-- HAPPY PATH THERE IS NO DUPLICATE TRANS
|
||||||
|
transaction_duplicate = FALSE;
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
CLOSE curs_trans;
|
||||||
|
RETURN;
|
||||||
|
END $$;
|
@ -123,7 +123,6 @@ THEN
|
|||||||
-- Deposit exists, but with differences. Not allowed.
|
-- Deposit exists, but with differences. Not allowed.
|
||||||
out_balance_ok=FALSE;
|
out_balance_ok=FALSE;
|
||||||
out_conflict=TRUE;
|
out_conflict=TRUE;
|
||||||
out_exchange_timestamp=0;
|
|
||||||
RETURN;
|
RETURN;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
--
|
|
||||||
-- This file is part of TALER
|
|
||||||
-- Copyright (C) 2014--2022 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
|
|
||||||
-- Foundation; either version 3, or (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License along with
|
|
||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_get_link_data(
|
|
||||||
IN in_coin_pub BYTEA
|
|
||||||
)
|
|
||||||
RETURNS SETOF record
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
curs CURSOR
|
|
||||||
FOR
|
|
||||||
SELECT
|
|
||||||
melt_serial_id
|
|
||||||
FROM refresh_commitments
|
|
||||||
WHERE old_coin_pub=in_coin_pub;
|
|
||||||
|
|
||||||
DECLARE
|
|
||||||
i RECORD;
|
|
||||||
BEGIN
|
|
||||||
OPEN curs;
|
|
||||||
LOOP
|
|
||||||
FETCH NEXT FROM curs INTO i;
|
|
||||||
EXIT WHEN NOT FOUND;
|
|
||||||
RETURN QUERY
|
|
||||||
SELECT
|
|
||||||
tp.transfer_pub
|
|
||||||
,denoms.denom_pub
|
|
||||||
,rrc.ev_sig
|
|
||||||
,rrc.ewv
|
|
||||||
,rrc.link_sig
|
|
||||||
,rrc.freshcoin_index
|
|
||||||
,rrc.coin_ev
|
|
||||||
FROM refresh_revealed_coins rrc
|
|
||||||
JOIN refresh_transfer_keys tp
|
|
||||||
ON (tp.melt_serial_id=rrc.melt_serial_id)
|
|
||||||
JOIN denominations denoms
|
|
||||||
ON (rrc.denominations_serial=denoms.denominations_serial)
|
|
||||||
WHERE rrc.melt_serial_id =i.melt_serial_id
|
|
||||||
/* GROUP BY tp.transfer_pub, denoms.denom_pub, rrc.ev_sig,rrc.ewv,rrc.link_sig,rrc.freshcoin_index, rrc.coin_ev*/
|
|
||||||
ORDER BY tp.transfer_pub,
|
|
||||||
rrc.freshcoin_index ASC
|
|
||||||
;
|
|
||||||
END LOOP;
|
|
||||||
CLOSE curs;
|
|
||||||
END $$;
|
|
@ -1,69 +0,0 @@
|
|||||||
--
|
|
||||||
-- This file is part of TALER
|
|
||||||
-- Copyright (C) 2014--2022 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
|
|
||||||
-- Foundation; either version 3, or (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License along with
|
|
||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
--
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_get_ready_deposit(
|
|
||||||
IN in_now INT8,
|
|
||||||
IN in_start_shard_now INT8,
|
|
||||||
IN in_end_shard_now INT8,
|
|
||||||
OUT out_payto_uri VARCHAR,
|
|
||||||
OUT out_merchant_pub BYTEA
|
|
||||||
)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
DECLARE
|
|
||||||
var_wire_target_h_payto BYTEA;
|
|
||||||
DECLARE
|
|
||||||
var_coin_pub BYTEA;
|
|
||||||
DECLARE
|
|
||||||
var_deposit_serial_id INT8;
|
|
||||||
DECLARE
|
|
||||||
curs CURSOR
|
|
||||||
FOR
|
|
||||||
SELECT
|
|
||||||
coin_pub
|
|
||||||
,deposit_serial_id
|
|
||||||
,wire_deadline
|
|
||||||
,shard
|
|
||||||
FROM deposits_by_ready
|
|
||||||
WHERE wire_deadline <= in_now
|
|
||||||
AND shard >=in_start_shard_now
|
|
||||||
AND shard <=in_end_shard_now
|
|
||||||
LIMIT 1;
|
|
||||||
DECLARE
|
|
||||||
i RECORD;
|
|
||||||
BEGIN
|
|
||||||
OPEN curs;
|
|
||||||
FETCH FROM curs INTO i;
|
|
||||||
IF NOT FOUND
|
|
||||||
THEN
|
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
SELECT
|
|
||||||
payto_uri
|
|
||||||
,merchant_pub
|
|
||||||
INTO
|
|
||||||
out_payto_uri
|
|
||||||
,out_merchant_pub
|
|
||||||
FROM deposits dep
|
|
||||||
JOIN wire_targets wt
|
|
||||||
ON (wt.wire_target_h_payto=dep.wire_target_h_payto)
|
|
||||||
WHERE dep.coin_pub=i.coin_pub
|
|
||||||
AND dep.deposit_serial_id=i.deposit_serial_id
|
|
||||||
ORDER BY
|
|
||||||
i.wire_deadline ASC
|
|
||||||
,i.shard ASC;
|
|
||||||
|
|
||||||
RETURN;
|
|
||||||
END $$;
|
|
@ -1,129 +0,0 @@
|
|||||||
--
|
|
||||||
-- This file is part of TALER
|
|
||||||
-- Copyright (C) 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
|
|
||||||
-- Foundation; either version 3, or (at your option) any later version.
|
|
||||||
--
|
|
||||||
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
||||||
-- A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU General Public License along with
|
|
||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION exchange_do_insert_aml_decision(
|
|
||||||
IN in_h_payto BYTEA,
|
|
||||||
IN in_new_threshold_val INT8,
|
|
||||||
IN in_new_threshold_frac INT4,
|
|
||||||
IN in_new_status INT4,
|
|
||||||
IN in_decision_time INT8,
|
|
||||||
IN in_justification VARCHAR,
|
|
||||||
IN in_decider_pub BYTEA,
|
|
||||||
IN in_decider_sig BYTEA,
|
|
||||||
IN in_notify_s VARCHAR,
|
|
||||||
IN in_kyc_requirements VARCHAR,
|
|
||||||
IN in_requirement_row INT8,
|
|
||||||
OUT out_invalid_officer BOOLEAN,
|
|
||||||
OUT out_last_date INT8)
|
|
||||||
LANGUAGE plpgsql
|
|
||||||
AS $$
|
|
||||||
BEGIN
|
|
||||||
-- Check officer is eligible to make decisions.
|
|
||||||
PERFORM
|
|
||||||
FROM exchange.aml_staff
|
|
||||||
WHERE decider_pub=in_decider_pub
|
|
||||||
AND is_active
|
|
||||||
AND NOT read_only;
|
|
||||||
IF NOT FOUND
|
|
||||||
THEN
|
|
||||||
out_invalid_officer=TRUE;
|
|
||||||
out_last_date=0;
|
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
out_invalid_officer=FALSE;
|
|
||||||
|
|
||||||
-- Check no more recent decision exists.
|
|
||||||
SELECT decision_time
|
|
||||||
INTO out_last_date
|
|
||||||
FROM exchange.aml_history
|
|
||||||
WHERE h_payto=in_h_payto
|
|
||||||
ORDER BY decision_time DESC;
|
|
||||||
IF FOUND
|
|
||||||
THEN
|
|
||||||
IF out_last_date >= in_decision_time
|
|
||||||
THEN
|
|
||||||
-- Refuse to insert older decision.
|
|
||||||
RETURN;
|
|
||||||
END IF;
|
|
||||||
UPDATE exchange.aml_status
|
|
||||||
SET threshold_val=in_new_threshold_val
|
|
||||||
,threshold_frac=in_new_threshold_frac
|
|
||||||
,status=in_new_status
|
|
||||||
,kyc_requirement=in_requirement_row
|
|
||||||
WHERE h_payto=in_h_payto;
|
|
||||||
ASSERT FOUND, 'cannot have AML decision history but no AML status';
|
|
||||||
ELSE
|
|
||||||
out_last_date = 0;
|
|
||||||
INSERT INTO exchange.aml_status
|
|
||||||
(h_payto
|
|
||||||
,threshold_val
|
|
||||||
,threshold_frac
|
|
||||||
,status
|
|
||||||
,kyc_requirement)
|
|
||||||
VALUES
|
|
||||||
(in_h_payto
|
|
||||||
,in_new_threshold_val
|
|
||||||
,in_new_threshold_frac
|
|
||||||
,in_new_status
|
|
||||||
,in_requirement_row);
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO exchange.aml_history
|
|
||||||
(h_payto
|
|
||||||
,new_threshold_val
|
|
||||||
,new_threshold_frac
|
|
||||||
,new_status
|
|
||||||
,decision_time
|
|
||||||
,justification
|
|
||||||
,kyc_requirements
|
|
||||||
,kyc_req_row
|
|
||||||
,decider_pub
|
|
||||||
,decider_sig
|
|
||||||
) VALUES
|
|
||||||
(in_h_payto
|
|
||||||
,in_new_threshold_val
|
|
||||||
,in_new_threshold_frac
|
|
||||||
,in_new_status
|
|
||||||
,in_decision_time
|
|
||||||
,in_justification
|
|
||||||
,in_kyc_requirements
|
|
||||||
,in_requirement_row
|
|
||||||
,in_decider_pub
|
|
||||||
,in_decider_sig);
|
|
||||||
|
|
||||||
|
|
||||||
-- wake up taler-exchange-aggregator
|
|
||||||
IF 0 = in_new_status
|
|
||||||
THEN
|
|
||||||
INSERT INTO kyc_alerts
|
|
||||||
(h_payto
|
|
||||||
,trigger_type)
|
|
||||||
VALUES
|
|
||||||
(in_h_payto,1);
|
|
||||||
|
|
||||||
EXECUTE FORMAT (
|
|
||||||
'NOTIFY %s'
|
|
||||||
,in_notify_s);
|
|
||||||
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
|
|
||||||
COMMENT ON FUNCTION exchange_do_insert_aml_decision(BYTEA, INT8, INT4, INT4, INT8, VARCHAR, BYTEA, BYTEA, VARCHAR, VARCHAR, INT8)
|
|
||||||
IS 'Checks whether the AML officer is eligible to make AML decisions and if so inserts the decision into the table';
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user