Compare commits
46 Commits
a04425df34
...
9f1f069cea
Author | SHA1 | Date | |
---|---|---|---|
9f1f069cea | |||
4833234df6 | |||
|
ff1a28319f | ||
|
bc03a27cba | ||
|
7899bc5621 | ||
|
ec8ad2e3b3 | ||
|
0dd0fff17d | ||
|
b15713f42e | ||
|
4954963405 | ||
|
5259ea0532 | ||
|
cc34502ac1 | ||
|
5f9c3021db | ||
|
09f09a2104 | ||
|
c014acf3c4 | ||
|
3ebd0a70b2 | ||
|
aedd13a778 | ||
|
1cf58e8ff8 | ||
|
4e79967f9b | ||
dc5b0fb0d3 | |||
|
d1379e492d | ||
|
f009e0bd12 | ||
|
85f6c8cdcc | ||
f40932196e | |||
|
fddd06c152 | ||
|
6d363488a1 | ||
|
1639cefa61 | ||
|
404b2b78f1 | ||
|
1f9427e1d9 | ||
|
737b3338ed | ||
|
1e88796045 | ||
|
7c0de44a2b | ||
|
2de2b6e3cf | ||
|
4c1a2c0307 | ||
|
0b8752bb1b | ||
|
82bb911720 | ||
|
8e0f9b40c0 | ||
|
4267f1d762 | ||
|
ffd4057c61 | ||
|
41cb79c685 | ||
|
00021d7e83 | ||
|
8ce9433736 | ||
|
647ae694cc | ||
|
faca037018 | ||
|
75f75c4a51 | ||
9130cda9e7 | |||
3ec14744f0 |
@ -1 +1 @@
|
||||
Subproject commit bd4e73b2ed06269fdee42eaad21acb5be8be9302
|
||||
Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504
|
@ -31,7 +31,13 @@
|
||||
<member kind="define">
|
||||
<type>#define</type>
|
||||
<name>GNUNET_TIME_UNIT_FOREVER_ABS</name>
|
||||
<anchorfile>gnunet_util_lib.h</anchorfile>
|
||||
<anchorfile>gnunet_time_lib.h</anchorfile>
|
||||
<arglist></arglist>
|
||||
</member>
|
||||
<member kind="define">
|
||||
<type>#define</type>
|
||||
<name>GNUNET_TIME_UNIT_ZERO_ABS</name>
|
||||
<anchorfile>gnunet_time_lib.h</anchorfile>
|
||||
<arglist></arglist>
|
||||
</member>
|
||||
</compound>
|
||||
|
@ -5,7 +5,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "GNU Taler: Exchange"
|
||||
PROJECT_NUMBER = 0.8.3
|
||||
PROJECT_NUMBER = 0.9.3
|
||||
PROJECT_LOGO = logo.svg
|
||||
OUTPUT_DIRECTORY = .
|
||||
CREATE_SUBDIRS = YES
|
||||
@ -97,59 +97,11 @@ WARN_LOGFILE =
|
||||
INPUT = ../../src
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
*.cxx \
|
||||
*.cpp \
|
||||
*.c++ \
|
||||
*.d \
|
||||
*.java \
|
||||
*.ii \
|
||||
*.ixx \
|
||||
*.ipp \
|
||||
*.i++ \
|
||||
*.inl \
|
||||
*.h \
|
||||
*.hh \
|
||||
*.hxx \
|
||||
*.hpp \
|
||||
*.h++ \
|
||||
*.idl \
|
||||
*.odl \
|
||||
*.cs \
|
||||
*.php \
|
||||
*.php3 \
|
||||
*.inc \
|
||||
*.m \
|
||||
*.mm \
|
||||
*.dox \
|
||||
*.py \
|
||||
*.f90 \
|
||||
*.f \
|
||||
*.vhd \
|
||||
*.vhdl \
|
||||
*.C \
|
||||
*.CC \
|
||||
*.C++ \
|
||||
*.II \
|
||||
*.I++ \
|
||||
*.H \
|
||||
*.HH \
|
||||
*.H++ \
|
||||
*.CS \
|
||||
*.PHP \
|
||||
*.PHP3 \
|
||||
*.M \
|
||||
*.MM \
|
||||
*.PY \
|
||||
*.F90 \
|
||||
*.F \
|
||||
*.VHD \
|
||||
*.VHDL
|
||||
*.h
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS = */test_* \
|
||||
*/.svn/* \
|
||||
*/.git/* \
|
||||
*/perf_* .* \
|
||||
.* \
|
||||
@ -191,7 +143,9 @@ HTML_STYLESHEET =
|
||||
GENERATE_HTMLHELP = NO
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "GNU Taler Source Documentation"
|
||||
DOCSET_BUNDLE_ID = net.taler
|
||||
DOCSET_BUNDLE_ID = net.taler.exchange
|
||||
DOCSET_PUBLISHER_ID = net.taler
|
||||
DOCSET_PUBLISHER_NAME = Taler Systems SA
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
|
3
doc/flows/Makefile
Normal file
3
doc/flows/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
all:
|
||||
pdflatex main.tex
|
||||
pdflatex main.tex
|
39
doc/flows/fees-coins.tex
Normal file
39
doc/flows/fees-coins.tex
Normal file
@ -0,0 +1,39 @@
|
||||
\section{Fees per coin} \label{sec:fees:coin}
|
||||
|
||||
Payments with Taler are always made using coins. Each coin has a specific
|
||||
denomination, and an exchange will issue coins in different denominations (in
|
||||
the same currency). The fees per coin depend on the operation and the
|
||||
denomination.
|
||||
|
||||
The primary fee to be paid is a {\bf deposit} fee that is
|
||||
charged whenever a coin is fully or partially deposited
|
||||
into a bank account or another wallet.
|
||||
|
||||
A secondary fee to be paid is a {\bf change} fee that is
|
||||
charged whenever a coin partially spent and change must
|
||||
be rendered.
|
||||
|
||||
Coins also have an {\bf expiration} date of approximately {\bf one year}.
|
||||
After the expiration date, coins become worthless. Wallets that are online
|
||||
{\bf three months} {\em before} a coin expires will automatically trade any
|
||||
such coins for one or more fresh coins with a later expiration date. This
|
||||
process is also subject to the {\bf change} fee.
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Fees per coin. Coin denomination values are given in units of CHF 0.01.}
|
||||
\label{table:fees:coins}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Denomination} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
|
||||
$2^{-4}-2^{ 0}$ & deposit & {\em CHF 0.00125} \\
|
||||
$2^{-4}-2^{ 0}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 0}-2^{ 3}$ & deposit & {\em CHF 0.00250} \\
|
||||
$2^{ 0}-2^{ 3}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 4}-2^{ 8}$ & deposit & {\em CHF 0.005} \\
|
||||
$2^{ 4}-2^{ 8}$ & change & {\em CHF 0.00125} \\
|
||||
$2^{ 8}-2^{12}$ & deposit & {\em CHF 0.01} \\
|
||||
$2^{ 8}-2^{12}$ & change & {\em CHF 0.00125} \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
30
doc/flows/fees-wire.tex
Normal file
30
doc/flows/fees-wire.tex
Normal file
@ -0,0 +1,30 @@
|
||||
\section{Fees per wire} \label{sec:fees:wire}
|
||||
|
||||
Wire fees apply whenever an exchange needs to initiate a wire transfer to
|
||||
another bank account. Wire fees do not apply to every individual payment to a
|
||||
merchant, as merchants can choose to {\em aggregate} multiple micropayments
|
||||
into one large payment on the wire. Wire fees also do not apply to
|
||||
wallet-to-wallet payments within the Taler system.
|
||||
|
||||
A {\bf wire} fee is applied when a merchant receives
|
||||
an aggregated payment into their bank account.
|
||||
|
||||
A {\bf closing} fee is applied when a wallet fails to
|
||||
withdraw coins and money has to be sent back to the
|
||||
originating bank account.
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Table with wire fees. Wire fees are set annually.}
|
||||
\label{table:fees:wire}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Year} & {\bf Fee type} & {\bf Amount} \\ \hline \hline
|
||||
2023 & wire & {\em CHF 0.05} \\
|
||||
2023 & closing & {\em CHF 0.10} \\
|
||||
2024 & wire & {\em CHF 0.05} \\
|
||||
2024 & closing & {\em CHF 0.10} \\
|
||||
2025 & wire & {\em CHF 0.05} \\
|
||||
2025 & closing & {\em CHF 0.10} \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
52
doc/flows/int-deposit.tex
Normal file
52
doc/flows/int-deposit.tex
Normal file
@ -0,0 +1,52 @@
|
||||
\section{Deposit}
|
||||
|
||||
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Customer bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{wallet}{Review deposit fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{Deposit {(Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{wallet}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
% \prelevel
|
||||
% \prelevel
|
||||
% \begin{sdblock}{User abort?}{}
|
||||
% \mess[0]{wallet}{{Request abort}}{exchange}
|
||||
% \mess[0]{exchange}{{Abort confirmation}}{wallet}
|
||||
% \end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and customer's bank.}
|
||||
\label{fig:int:deposit}
|
||||
\end{figure}
|
||||
|
||||
We do {\bf not} permit the customer to regain control over their funds {\em
|
||||
unless} they pass the KYC/AML checks. The technical reason is simply that
|
||||
the KYC/AML checks happen {\em after} the aggregation logic and at this point
|
||||
refunds are no longer permitted. From a compliance perspective, this also
|
||||
prevents malicious customers from risk-free probing of the system.
|
58
doc/flows/int-pay.tex
Normal file
58
doc/flows/int-pay.tex
Normal file
@ -0,0 +1,58 @@
|
||||
\section{Pay}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{merchant}{\shortstack{Merchant \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[1]{bank}{\shortstack{Merchant bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Commercial \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{Browse catalog}{merchant}
|
||||
\mess[0]{merchant}{Commercial offer}{wallet}
|
||||
\begin{callself}{wallet}{Review offer}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{Send payment {(Coins)}}{merchant}
|
||||
\mess[0]{merchant}{Deposit {(Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{merchant}
|
||||
\mess[0]{merchant}{{Refund purchase}}{wallet}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Confirm deposit}}{merchant}
|
||||
\mess[0]{merchant}{Fulfill order}{wallet}
|
||||
\begin{callself}{exchange}{Aggregate transactions}{}
|
||||
\end{callself}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, merchant,
|
||||
Taler exchange (payment service provider) and merchant bank.}
|
||||
\label{fig:int:pay}
|
||||
\end{figure}
|
||||
|
||||
{\bf Internal note:} The exchange refusing a deposit immediately based on
|
||||
unaccaptable merchant accounts may not be fully implemented (this is a very
|
||||
recent feature, after all); especially the merchant then automatically
|
||||
refunding the purchase to the customer is certainly missing. However,
|
||||
the entire situation only arises when a merchant is incorrectly configured
|
||||
and in violation of the terms of service.
|
55
doc/flows/int-pull.tex
Normal file
55
doc/flows/int-pull.tex
Normal file
@ -0,0 +1,55 @@
|
||||
\section{Pull payment (aka invoicing)}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{payer}{\shortstack{Payer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{payee}{\shortstack{Payee \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{payee}{Review pull payment fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{payee}{{Create invoice (Wallet ID)}}{exchange}
|
||||
|
||||
\mess[0]{exchange}{{Invoice ready}}{payee}
|
||||
\mess[0]{payee}{{Send invoice (e.g. via QR code)}}{payer}
|
||||
|
||||
\begin{callself}{payer}{Review invoice}{}
|
||||
\end{callself}
|
||||
\mess[0]{payer}{{Make payment (Coins)}}{exchange}
|
||||
|
||||
\begin{sdblock}{Domestic wallet?}{}
|
||||
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
|
||||
\mess[0]{exchange}{{Distribute digital cash}}{payee}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{Interactions between wallets and Taler exchange
|
||||
in a pull payment.}
|
||||
\label{fig:int:pull}
|
||||
\end{figure}
|
||||
|
||||
We do {\bf not} permit the payer to regain control over their funds, once the
|
||||
payment was made they are locked {\em until} the payee passes the KYC/AML
|
||||
checks. We only do the AML/KYC process once the funds are locked at the
|
||||
exchange. This ensures we know the actual transacted amounts (which may be
|
||||
lower than the total amounts requested) and prevents risk-free probing
|
||||
attacks.
|
47
doc/flows/int-push.tex
Normal file
47
doc/flows/int-push.tex
Normal file
@ -0,0 +1,47 @@
|
||||
\section{Push payment}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{payer}{\shortstack{Payer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Pre-funded \\ Wallet};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{payee}{\shortstack{Payee \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{payer}{Review push payment fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{payer}{{Push funds (Coins)}}{exchange}
|
||||
\mess[0]{payer}{{Offer payment (e.g. via QR code)}}{payee}
|
||||
\begin{callself}{payee}{Review payment offer}{}
|
||||
\end{callself}
|
||||
\mess[0]{payee}{{Request funds (Wallet ID)}}{exchange}
|
||||
\begin{sdblock}{Domestic wallet?}{}
|
||||
\begin{callself}{exchange}{Figure~\ref{fig:proc:domestic}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Distribute digital cash}}{payee}
|
||||
% \postlevel
|
||||
\begin{sdblock}{Payment offer expired?}{}
|
||||
\mess[0]{exchange}{{Return funds}}{payer}
|
||||
\end{sdblock}
|
||||
|
||||
\end{sequencediagram}
|
||||
\caption{Interactions between wallets and Taler exchange
|
||||
in a push payment.}
|
||||
\label{fig:int:push}
|
||||
\end{figure}
|
39
doc/flows/int-refund.tex
Normal file
39
doc/flows/int-refund.tex
Normal file
@ -0,0 +1,39 @@
|
||||
\section{Refund}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{merchant}{\shortstack{Merchant \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\begin{callself}{merchant}{Initiate refund}{}
|
||||
\end{callself}
|
||||
\mess[0]{merchant}{{Refund offer (QR code)}}{wallet}
|
||||
\mess[0]{wallet}{Download refund}{merchant}
|
||||
\mess[0]{merchant}{Approve refund}{exchange}
|
||||
\mess[0]{exchange}{Confirm refund}{merchant}
|
||||
\mess[0]{merchant}{Return refund confirmation}{wallet}
|
||||
\end{sequencediagram}
|
||||
\caption{Refund processing when a merchant is unable to fulfill
|
||||
a contract. Refunds must happen {\em before} the
|
||||
exchange has aggregated the original transaction for
|
||||
a bank transfer to the merchant. Furthermore, refunds
|
||||
can only go to the customer who made the original payment
|
||||
and the refund cannot exceed the amount of the original
|
||||
payment.}
|
||||
\label{fig:int:refund}
|
||||
\end{figure}
|
48
doc/flows/int-shutdown.tex
Normal file
48
doc/flows/int-shutdown.tex
Normal file
@ -0,0 +1,48 @@
|
||||
\section{Shutdown}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Customer bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
|
||||
\begin{callself}{exchange}{Operator initiates shutdown}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{{Shutdown alert}}{wallet}
|
||||
\begin{sdblock}{Bank account known?}{}
|
||||
\begin{callself}{wallet}{Designate bank account}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{wallet}{{Deposit (Coins)}}{exchange}
|
||||
\begin{sdblock}{Acceptable account?}{}
|
||||
\mess[0]{exchange}{{Refuse deposit}}{wallet}
|
||||
\end{sdblock}
|
||||
\begin{sdblock}{KYC/AML required?}{}
|
||||
\begin{callself}{exchange}{Figures~\ref{fig:proc:kyc}, \ref{fig:proc:aml}}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\mess[0]{exchange}{{Initiate transfer}}{bank}
|
||||
\end{sequencediagram}
|
||||
\caption{Shutdown interactions between customer, Taler exchange (payment
|
||||
service provider) and bank.}
|
||||
\label{fig:int:shutdown}
|
||||
\end{figure}
|
||||
|
||||
KYC/AML requirements are relaxed in cases where the customer is able to
|
||||
cryptographically demonstrate that they previously withdrew these coins from
|
||||
the designated checking account. Thus, KYC/AML checks here primarily still
|
||||
apply if the customer received the funds via P2P transfers from other wallets.
|
49
doc/flows/int-withdraw.tex
Normal file
49
doc/flows/int-withdraw.tex
Normal file
@ -0,0 +1,49 @@
|
||||
\section{Withdraw}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{bank}{\shortstack{Customer bank \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] {Checking \\ Accounts};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{Withdraw {(Amount)}}{exchange}
|
||||
\mess[0]{exchange}{{Configuration (ToS, Fees)}}{wallet}
|
||||
\begin{sdblock}{once}{}
|
||||
\begin{callself}{wallet}{Accept ToS}{}
|
||||
\end{callself}
|
||||
\end{sdblock}
|
||||
\begin{callself}{wallet}{Review withdraw fees}{}
|
||||
\end{callself}
|
||||
\mess[0]{wallet}{{Initiate transfer (Amount, Credit account, Wallet ID)}}{bank}
|
||||
\mess[0]{bank}{{Credit (Wallet ID)}}{exchange}
|
||||
|
||||
\begin{sdblock}{Acceptable transfer?}{}
|
||||
\mess[0]{exchange}{{Bounce funds}}{bank}
|
||||
\end{sdblock}
|
||||
\postlevel
|
||||
\mess[0]{exchange}{Confirm wire transfer}{wallet}
|
||||
\mess[0]{wallet}{Request digital cash}{exchange}
|
||||
\mess[0]{exchange}{Distribute digital cash}{wallet}
|
||||
\postlevel
|
||||
\begin{sdblock}{Withdraw period expired?}{}
|
||||
\mess[0]{exchange}{{Return remaining funds}}{bank}
|
||||
\end{sdblock}
|
||||
\end{sequencediagram}
|
||||
\caption{Withdraw interactions between customer, Taler exchange (payment
|
||||
service provider) and bank. The amount of digital cash distributed is
|
||||
subject to limits per origin account (see Figure~\ref{fig:kyc:withdraw}).}
|
||||
\label{fig:int:withdraw}
|
||||
\end{figure}
|
57
doc/flows/kyc-balance.tex
Normal file
57
doc/flows/kyc-balance.tex
Normal file
@ -0,0 +1,57 @@
|
||||
\section{KYC: Balance}
|
||||
|
||||
Note: this process is not implemented and would require non-trivial extra work
|
||||
if required.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (balance) [decision,below=of start,text width=3cm] {Transaction leaves wallet balance below AML threshold?};
|
||||
\node (registered) [decision,below=of balance,text width=3cm] {Wallet has been subject to KYC?};
|
||||
\node (kyc) [process, below=of registered] {KYC process};
|
||||
\node (aml) [process, left=of kyc] {AML process};
|
||||
\node (allow) [end, right=of balance] {Allow};
|
||||
\node (deny) [failed, right=of registered] {Deny};
|
||||
\draw[arr] (start) -> (balance) {};
|
||||
\draw[arr] (balance) -> (registered);
|
||||
\draw (balance) edge["No"] (registered);
|
||||
\draw[arr] (balance) -> (allow);
|
||||
\draw (balance) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (registered) -> (kyc);
|
||||
\draw (registered) edge["No"] (kyc);
|
||||
\draw[arr] (registered) -> (deny);
|
||||
\draw (registered) edge["Yes"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
\draw[arr] (kyc) -> (aml);
|
||||
\draw (kyc) edge["Ok"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (balance.west);
|
||||
\draw (aml) edge["New threshold"] (balance.west);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when a wallet exceeds its AML threshold.
|
||||
When the transfer is denied the transaction (withdraw, P2P transfer)
|
||||
is refused by the wallet.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the balance trigger}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Default AML threshold & Amount & {\em 1000 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
71
doc/flows/kyc-deposit.tex
Normal file
71
doc/flows/kyc-deposit.tex
Normal file
@ -0,0 +1,71 @@
|
||||
\section{KYC: Deposit}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (country) [decision,below=of start,text width=2.5cm] {Target account in allowed country?};
|
||||
\node (amount) [decision, below=of country,text width=2.5cm] {Target account received less than KYC threshold?};
|
||||
\node (kyc) [process, right=of amount] {KYC process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Target account received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (country) {};
|
||||
|
||||
\draw[arr] (country) -> (amount);
|
||||
\draw (country) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (country.east) -> (deny);
|
||||
\draw (country.east) edge["No"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when depositing digital cash into a bank
|
||||
account. When the transfer is denied, the money is returned to the
|
||||
originating wallet.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the deposit trigger}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
|
||||
KYC deposit threshold & Amount & {\em 1000 CHF} \\
|
||||
Default AML deposit threshold & Amount & {\em 2500 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
78
doc/flows/kyc-pull.tex
Normal file
78
doc/flows/kyc-pull.tex
Normal file
@ -0,0 +1,78 @@
|
||||
\section{KYC/AML: Pull Payment}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
|
||||
\node (domestic) [process, right=of wallet] {Validate phone number};
|
||||
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
|
||||
\node (kyc) [process, right=of amount] {KYC process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow invoicing};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (wallet) {};
|
||||
|
||||
\draw[arr] (wallet) -> (amount);
|
||||
\draw (wallet) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (wallet.east) -> (domestic);
|
||||
\draw (wallet.east) edge["No"] (domestic);
|
||||
|
||||
\draw[arr] (domestic) -> (amount);
|
||||
\draw (domestic) edge["Confirmed"] (amount);
|
||||
|
||||
\draw[arr] (domestic) -> (deny);
|
||||
\draw (domestic) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when receiving payments from another wallet.
|
||||
The threshold depends on the risk profile from the KYC process.
|
||||
When invoicing is denied the wallet cannot generate the invoice.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the pull payment trigger}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Permitted phone numbers & Dialing prefix & {\em +41} \\
|
||||
P2P KYC threshold & Amount & {\em 100 CHF} \\
|
||||
Default P2P AML threshold & Amount & {\em 1000 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
79
doc/flows/kyc-push.tex
Normal file
79
doc/flows/kyc-push.tex
Normal file
@ -0,0 +1,79 @@
|
||||
\section{KYC/AML: Push Payment}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=0.9cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (wallet) [decision,below=of start,text width=2.5cm] {Wallet linked to (domestic) phone number?};
|
||||
\node (domestic) [process, right=of wallet] {Validate phone number};
|
||||
\node (amount) [decision, below=of wallet,text width=2.5cm] {Wallet received less than KYC threshold from other wallets?};
|
||||
\node (kyc) [process, right=of amount] {KYC process};
|
||||
\node (high) [decision, below=of amount,text width=2.5cm] {Wallet received more than its AML threshold?};
|
||||
\node (aml) [process, right=of high] {AML process};
|
||||
\node (dummy) [below right=of aml] {};
|
||||
\node (allow) [end, below right=of dummy] {Allow};
|
||||
\node (deny) [failed, right=of kyc] {Deny};
|
||||
\draw[arr] (start) -> (wallet) {};
|
||||
|
||||
\draw[arr] (wallet) -> (amount);
|
||||
\draw (wallet) edge["Yes"] (amount);
|
||||
|
||||
\draw[arr] (wallet.east) -> (domestic);
|
||||
\draw (wallet.east) edge["No"] (domestic);
|
||||
|
||||
\draw[arr] (domestic) -> (amount);
|
||||
\draw (domestic) edge["Confirmed"] (amount);
|
||||
|
||||
\draw[arr] (domestic) -> (deny);
|
||||
\draw (domestic) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (amount) -> (high);
|
||||
\draw (amount) edge["Yes"] (high);
|
||||
|
||||
\draw[arr] (amount.east) -> (kyc);
|
||||
\draw (amount.east) edge["No"] (kyc);
|
||||
|
||||
\draw[arr] (kyc) -> (deny);
|
||||
\draw (kyc) edge["Failed"] (deny);
|
||||
|
||||
\draw[arr] (kyc) -> (high);
|
||||
\draw (kyc) edge["Succeeded"] (high);
|
||||
|
||||
\draw[arr] (high.south) -> (allow);
|
||||
\draw (high.south) edge["Yes"] (allow);
|
||||
|
||||
\draw[arr] (high.east) -> (aml);
|
||||
\draw (high.east) edge["No"] (aml);
|
||||
|
||||
\draw[arr] (aml) -> (deny);
|
||||
\draw (aml) edge["Violation"] (deny);
|
||||
|
||||
\draw[arr] (aml) -> (allow);
|
||||
\draw (aml) edge["Ok"] (allow);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when receiving payments from another wallet.
|
||||
The threshold depends on the risk profile from the KYC process.
|
||||
When the transfer is denied the money is (eventually) returned to
|
||||
the originating wallet.}
|
||||
\end{figure}
|
||||
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the push payment trigger}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Permitted phone numbers & Dialing prefix & {\em +41} \\
|
||||
P2P KYC threshold & Amount & {\em 100 CHF} \\
|
||||
Default P2P AML threshold & Amount & {\em 1000 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
45
doc/flows/kyc-withdraw.tex
Normal file
45
doc/flows/kyc-withdraw.tex
Normal file
@ -0,0 +1,45 @@
|
||||
\section{KYC: Withdraw}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{center}
|
||||
\begin{tikzpicture}[node distance=1cm,font=\sffamily,
|
||||
start/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=yellow!30},
|
||||
end/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm,text centered, draw=black, fill=red!30},
|
||||
process/.style={rectangle, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=orange!30},
|
||||
failed/.style={rectangle, rounded corners, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=red!30},
|
||||
io/.style={trapezium, trapezium left angle=70, trapezium right angle=110, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=blue!30},
|
||||
decision/.style={diamond, minimum width=3cm, minimum height=1cm, text centered, draw=black, fill=green!30},
|
||||
arr/.style={very thick,-latex},
|
||||
every edge quotes/.style = {auto, font=\footnotesize, sloped}
|
||||
]
|
||||
\node (start) [start] {Start};
|
||||
\node (country) [decision,below=of start,text width=3cm] {Wire transfer originates from allowed country?};
|
||||
\node (amount) [decision, below=of country,text width=3cm] {Transferred less than maximum amount from origin account over last month?};
|
||||
\node (allow) [end, below=of amount] {Allow};
|
||||
\node (deny) [failed, right=of allow] {Deny};
|
||||
\draw[arr] (start) -> (country) {};
|
||||
\draw[arr] (country) -> (amount);
|
||||
\draw (country) edge["Yes"] (amount);
|
||||
\draw[arr] (country.east) -> (deny);
|
||||
\draw (country.east) edge["No"] (deny);
|
||||
\draw[arr] (amount) -> (allow);
|
||||
\draw (amount) edge["Yes"] (allow);
|
||||
\draw[arr] (amount.east) -> (deny);
|
||||
\draw (amount.east) edge["No"] (deny);
|
||||
\end{tikzpicture}
|
||||
\end{center}
|
||||
\caption{Regulatory process when withdrawing digital cash from a
|
||||
bank account.
|
||||
When the transfer is denied the money is (eventually) returned to
|
||||
the originating bank account.}
|
||||
\label{fig:kyc:withdraw}
|
||||
\end{figure}
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Settings for the withdraw trigger}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Setting} & {\bf Type} & {\bf Value} \\ \hline \hline
|
||||
Allowed bank accounts & RFC 8905 RegEx & {\em CH*} \\ \hline
|
||||
Monthly withdraw maximum & Amount & {\em 1000 CHF} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
159
doc/flows/main.tex
Normal file
159
doc/flows/main.tex
Normal file
@ -0,0 +1,159 @@
|
||||
\documentclass[10pt,a4paper,oneside]{book}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{url}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{hyperref}
|
||||
\usepackage{qrcode}
|
||||
\usepackage{pgf-umlsd}
|
||||
\usepackage{tikz}
|
||||
\usetikzlibrary{shapes,arrows}
|
||||
\usetikzlibrary{positioning}
|
||||
\usetikzlibrary{calc}
|
||||
\usetikzlibrary{quotes}
|
||||
\author{Christian Grothoff}
|
||||
\title{Flows in the GNU Taler System}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\chapter{Interactions} \label{chap:interactions}
|
||||
|
||||
This chapter introduces the main payment interactions in the GNU Taler payment
|
||||
system. For each interaction, we introduce the parties involved and in which
|
||||
order they interact and how. In each interaction it is possible that the
|
||||
Taler exchange needs to trigger a compliance process. These regulatory
|
||||
riggers are described in more detail in Chapter~\ref{chap:triggers}.
|
||||
|
||||
The main interactions of the system are:
|
||||
|
||||
\begin{description}
|
||||
\item[withdraw] a customer withdraws digital cash to their wallet
|
||||
\item[deposit] a customer returns digital cash into their bank account
|
||||
\item[pay] a customer pays into bank account of a merchant
|
||||
\item[refund] a merchant decides to return funds to a customer
|
||||
\item[push] a customer sends a payment to another wallet
|
||||
\item[pull] a customer requests a payment from another wallet (effectively sending an invoice)
|
||||
\item[shutdown] the Taler payment system operator informs the customers that the system is being shut down for good
|
||||
\end{description}
|
||||
|
||||
Taler has no accounts (this is digital cash) and thus there is no ``opening''
|
||||
or ``closing'' of accounts. The equivalent of ``opening'' an account is thus
|
||||
to withdraw digital cash. The equivalent of ``closing'' an account is to
|
||||
either (1) deposit the funds explicitly into a bank account, or (2) the coins
|
||||
will expire if the wallet was lost (including long-term offline or
|
||||
uninstalled). Finally, if a wallet remains (occasionally) online but a user
|
||||
does simply not spend the coins will (3) diminish in value from the change
|
||||
fees (see Section~\ref{sec:fees:coin}) that apply to prevent the coins from
|
||||
expiring outright.
|
||||
|
||||
The following sections describe the respective processes for each of these
|
||||
interactions.
|
||||
|
||||
\include{int-withdraw}
|
||||
\include{int-deposit}
|
||||
\include{int-pay}
|
||||
\include{int-refund}
|
||||
\include{int-push}
|
||||
\include{int-pull}
|
||||
\include{int-shutdown}
|
||||
|
||||
|
||||
\chapter{Regulatory Triggers} \label{chap:triggers}
|
||||
|
||||
In this chapter we show decision diagrams for regulatory processes of the
|
||||
various core operations of the GNU Taler payment system. In each case, the
|
||||
{\bf start} state refers to one of the interactions described in the previous
|
||||
chapter. The payment system will then use the process to arrive at an {\bf
|
||||
allow} decision which permits the transaction to go through, or at a {\bf
|
||||
deny} decision which ensures that the funds are not moved.
|
||||
|
||||
The specific {\em decisions} (in green) depend on the risk profile and the
|
||||
regulatory environment. The tables in each section list the specific values
|
||||
that are to be configured.
|
||||
|
||||
There are five types if interactions that can trigger regulatory processes:
|
||||
|
||||
\begin{description}
|
||||
\item[withdraw] a customer withdraws digital cash from their {\bf bank account}
|
||||
\item[deposit] a merchant's {\bf bank account} is designated to receive a payment in digital cash
|
||||
\item[push] a {\bf wallet} accepts a payment from another wallet
|
||||
\item[pull] a {\bf wallet} requests a payment from another wallet
|
||||
\item[balance] a withdraw or P2P payment causes the balance of a {\bf wallet} to exceed a given threshold
|
||||
\end{description}
|
||||
|
||||
We note in bold the {\bf anchor} for the regulator process. The anchor is used
|
||||
to link the interaction to an identity. Once an identity has been established
|
||||
for a particular anchor, that link is considered established for all types of
|
||||
activities involving that anchor. A wallet is uniquely identified in the
|
||||
system by its unique cryptographic key. A bank account is uniquely identified
|
||||
in the system by its (RFC 8905) bank routing data (usually including BIC, IBAN
|
||||
and account owner name).
|
||||
|
||||
The KYC and AML processes themselves are described in
|
||||
Chapter~\ref{chap:regproc}.
|
||||
|
||||
\include{kyc-withdraw}
|
||||
\include{kyc-deposit}
|
||||
\include{kyc-push}
|
||||
\include{kyc-pull}
|
||||
\include{kyc-balance}
|
||||
|
||||
\chapter{Regulatory Processes} \label{chap:regproc}
|
||||
|
||||
This chapter describes the interactions between the customer, exchange and
|
||||
organizations or staff assisting with regulatory processes designed to ensure
|
||||
that customers are residents in the area of operation of the payment service
|
||||
provider, are properly identified, and do not engage in money laundering.
|
||||
|
||||
The three main regulatory processes are:
|
||||
|
||||
\begin{description}
|
||||
\item[domestic check] This process establishes that a user is generally
|
||||
eligible to use the payment system. The process checks that the user has an
|
||||
eligible address, but stops short of establishing the user's identity.
|
||||
\item[kyc] This process establishes a user's legal identity, possibly
|
||||
using external providers to review documents and check against blacklists.
|
||||
\item[aml] The AML process reviews suspicious payment activities for
|
||||
money laundering. Here AML staff reviews all collected information.
|
||||
\end{description}
|
||||
|
||||
\include{proc-domestic}
|
||||
\include{proc-kyc}
|
||||
\include{proc-aml}
|
||||
|
||||
\chapter{Fees} \label{chap:fees}
|
||||
|
||||
The business model for operating a Taler exchange is to charge transaction
|
||||
fees. Fees are charged on certain operations by the exchange. There are two
|
||||
types of fees, {\bf wire fees} and {\bf coin fees}. This chapter describes
|
||||
the fee structure.
|
||||
|
||||
Fixed, amount-independent {\bf wire fees} are charged on wire transfers using
|
||||
the core banking system. Details on wire fees are described in
|
||||
Section~\ref{sec:fees:wire}.
|
||||
|
||||
Coin fees are more complex, as they do not exactly follow neither the usual
|
||||
percentage of volume model of other payment systems. Instead, coin fees are
|
||||
applied per coin, resulting in a {\em logarithmic} fee structure. As a
|
||||
result, the effective fee {\em percentage} for tiny transactions is high (for
|
||||
example 50\% for transactions of 0.0025 CHF) while the effective fee
|
||||
percentage for large transactions is nominal (for example $\approx$ 0.05\% for
|
||||
transactions of $\approx$ 40 CHF). Details on coin fees are described in
|
||||
Section~\ref{sec:fees:coin}.
|
||||
|
||||
Fees are configurable (and that fee types beyond those described here are
|
||||
supported by the software). Thus, the specific fees may be adjusted in the
|
||||
future based on business decisions. However, changes to the fees are never
|
||||
retroactively applied to coins already in circulation. Wire fees that have
|
||||
been publicly announced for a particular time period also cannot be changed.
|
||||
Finally, any change to the terms of service must also be explicitly accepted
|
||||
by the users before they withdraw additional funds.
|
||||
|
||||
|
||||
\include{fees-wire}
|
||||
\include{fees-coins}
|
||||
%\include{fees-other}
|
||||
|
||||
|
||||
\end{document}
|
47
doc/flows/proc-aml.tex
Normal file
47
doc/flows/proc-aml.tex
Normal file
@ -0,0 +1,47 @@
|
||||
\section{AML process}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{staff}{\shortstack{AML staff \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Access \\ Token};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{Initial action}}{exchange}
|
||||
\begin{callself}{exchange}{Establish AML requirement}{}
|
||||
\end{callself}
|
||||
\begin{callself}{exchange}{Queue AML task}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Wait for AML}{wallet}
|
||||
\mess[0]{staff}{Request AML work}{exchange}
|
||||
\mess[0]{exchange}{{Open AML task(s)}}{staff}
|
||||
\mess[0]{staff}{Request details}{exchange}
|
||||
\mess[0]{exchange}{KYC/AML data}{staff}
|
||||
\begin{callself}{staff}{Review and decide}{}
|
||||
\end{callself}
|
||||
\mess[0]{staff}{{Decision documentation}}{exchange}
|
||||
\mess[0]{exchange}{AML decision}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and the AML staff. The process can be
|
||||
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.
|
||||
AML staff interactions are cryptographically secured and
|
||||
decisions and the provided reasoning are archived by the exchange.
|
||||
AML staff may interact with the customer (out-of-band)
|
||||
in its decision process.
|
||||
}
|
||||
\label{fig:proc:aml}
|
||||
\end{figure}
|
66
doc/flows/proc-domestic.tex
Normal file
66
doc/flows/proc-domestic.tex
Normal file
@ -0,0 +1,66 @@
|
||||
\section{Domestic wallet check}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer wallet \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Wallet ID};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{sms}{\shortstack{Address validator}}
|
||||
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{P2P payment (Wallet ID)}}{exchange}
|
||||
\begin{callself}{exchange}{New wallet?}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Request address validation}{sms}
|
||||
\mess[0]{sms}{Validation process ID}{exchange}
|
||||
\mess[0]{exchange}{Request address validation}{wallet}
|
||||
\mess[0]{wallet}{Send address}{sms}
|
||||
\mess[0]{sms}{{Send confirmation code (to address)}}{wallet}
|
||||
\mess[0]{wallet}{Supply confirmation code}{sms}
|
||||
\mess[0]{sms}{{Confirmed customer address}}{exchange}
|
||||
\mess[0]{exchange}{{Confirm completion}}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and external address validation service. The process can be
|
||||
triggered by wallet-to-wallet (P2P) payments described in Chapter~\ref{chap:triggers}.}
|
||||
\label{fig:proc:domestic}
|
||||
\end{figure}
|
||||
|
||||
Our users have to accept the terms of service which restrict the use of the
|
||||
service to domestic customers. For interactions with the core banking system,
|
||||
this simply means that we only accept payments from or to domestic bank
|
||||
accounts. For P2P payments between wallets, we require that the wallets are
|
||||
controlled by a domestic entity. We define domestic entities as those that
|
||||
are able to receive messages at a domestic address. Two types of addresses are
|
||||
supported:
|
||||
|
||||
\begin{itemize}
|
||||
\item Control over a domestic {\bf mobile phone number} is established
|
||||
by sending an SMS message with a confirmation code to the MSIN.
|
||||
\item Control over a domestic {\bf postal address} is established by
|
||||
sending a letter with a confirmation code to the address.
|
||||
\end{itemize}
|
||||
|
||||
Depending on the type of address, a validation has a limited validity period,
|
||||
as shown in Table~\ref{table:proc:domestic}. When the validity period is
|
||||
over, a wallet has to re-do the address validation before they can receive any
|
||||
further funds through the service.
|
||||
|
||||
\begin{table}[h!]
|
||||
\caption{Restrictions on address validations}
|
||||
\label{table:proc:domestic}
|
||||
\begin{tabular}{l|l|r}
|
||||
{\bf Type} & {\bf Validity period} & {\bf Restricted to} \\ \hline \hline
|
||||
Mobile phone number & 12 months & {\em +41} \\
|
||||
Postal address & 36 months & {\em Switzerland} \\
|
||||
\end{tabular}
|
||||
\end{table}
|
88
doc/flows/proc-kyc.tex
Normal file
88
doc/flows/proc-kyc.tex
Normal file
@ -0,0 +1,88 @@
|
||||
\section{KYC process}
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{sequencediagram}
|
||||
\newinst{wallet}{\shortstack{Customer \\
|
||||
\\ \begin{tikzpicture}
|
||||
\node [fill=gray!20,draw=black,thick,align=center] { Unique \\ Action};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{exchange}{\shortstack{Taler (exchange) \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
\newinst[2]{kyc}{\shortstack{KYC provider \\
|
||||
\\ \begin{tikzpicture}[shape aspect=.5]
|
||||
\tikzset{every node/.style={cylinder,shape border rotate=90, draw,fill=gray!25}}
|
||||
\node at (1.5,0) {\shortstack{{{\tiny Database}}}};
|
||||
\end{tikzpicture}
|
||||
}}
|
||||
|
||||
\postlevel
|
||||
\mess[0]{wallet}{{Initial action}}{exchange}
|
||||
\begin{callself}{exchange}{Establish KYC requirement}{}
|
||||
\end{callself}
|
||||
\mess[0]{exchange}{Request new KYC process}{kyc}
|
||||
\mess[0]{kyc}{{Process identifier (PI)}}{exchange}
|
||||
\mess[0]{exchange}{{KYC required (PI)}}{wallet}
|
||||
\mess[0]{wallet}{{KYC start (PI)}}{kyc}
|
||||
\mess[0]{kyc}{{Request identity documentation}}{wallet}
|
||||
\mess[0]{wallet}{{Upload identity documentation}}{kyc}
|
||||
\begin{callself}{kyc}{Validate documentation}{}
|
||||
\end{callself}
|
||||
\mess[0]{kyc}{{Share documentation (PI)}}{exchange}
|
||||
\mess[0]{kyc}{{Confirm completion}}{wallet}
|
||||
\mess[0]{wallet}{{Retry action}}{exchange}
|
||||
\end{sequencediagram}
|
||||
\caption{Deposit interactions between customer, Taler exchange (payment
|
||||
service provider) and external KYC provider. The process can be
|
||||
triggered by various {\em actions} described in Chapter~\ref{chap:triggers}.}
|
||||
\label{fig:proc:kyc}
|
||||
\end{figure}
|
||||
|
||||
At the beginning of the KYC process, the user needs to specify
|
||||
whether they are an {\bf individual} or a {\bf business}. This
|
||||
then determines which types of attributes are collected in the
|
||||
KYC process (Table~\ref{table:proc:kyc:individual} vs.
|
||||
Table~\ref{table:proc:kyc:business}).
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for individuals}
|
||||
\label{table:proc:kyc:individual}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Surname & yes & Mustermann \\
|
||||
First name(s) & yes & Max \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\
|
||||
Actual address of domicile & yes & Seestrasse 3, 8008 Zuerich \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
E-mail & no & me@example.com \\
|
||||
Identification document & yes & JPG image \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
||||
|
||||
|
||||
\begin{table}
|
||||
\caption{Information collected for businesses}
|
||||
\label{table:proc:kyc:business}
|
||||
\begin{center}
|
||||
\begin{tabular}{l|c|r}
|
||||
{\bf Type} & {\bf Required} & {\bf Example} \\ \hline \hline
|
||||
Company name & yes & Mega AG \\
|
||||
Registered office & yes & Seestrasse 4, 8008 Zuerich \\
|
||||
Company identification document & yes & PDF file \\ \hline
|
||||
Contact person name & yes & Max Mustermann \\
|
||||
Phone number & no & +41-123456789 \\
|
||||
E-mail & yes & me@example.com \\
|
||||
Identification document & yes & JPG image \\
|
||||
Date of birth & yes & 1.1.1980 \\
|
||||
Nationality & yes & Swiss \\ \hline
|
||||
Power of attorney arrangement & yes & PDF file \\
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\end{table}
|
@ -605,6 +605,9 @@ main (int argc,
|
||||
level,
|
||||
NULL));
|
||||
GNUNET_free (level);
|
||||
/* suppress compiler warnings... */
|
||||
GNUNET_assert (NULL != src_cfgfile);
|
||||
GNUNET_assert (NULL != dst_cfgfile);
|
||||
if (0 == strcmp (src_cfgfile,
|
||||
dst_cfgfile))
|
||||
{
|
||||
|
@ -172,9 +172,9 @@ coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
memcpy (&i,
|
||||
coin_pub,
|
||||
sizeof (i));
|
||||
GNUNET_memcpy (&i,
|
||||
coin_pub,
|
||||
sizeof (i));
|
||||
return i % MAX_COIN_HISTORIES;
|
||||
}
|
||||
|
||||
|
@ -674,12 +674,12 @@ hash_rc (const char *receiver_account,
|
||||
size_t slen = strlen (receiver_account);
|
||||
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
|
||||
|
||||
memcpy (buf,
|
||||
wtid,
|
||||
sizeof (*wtid));
|
||||
memcpy (&buf[sizeof (*wtid)],
|
||||
receiver_account,
|
||||
slen);
|
||||
GNUNET_memcpy (buf,
|
||||
wtid,
|
||||
sizeof (*wtid));
|
||||
GNUNET_memcpy (&buf[sizeof (*wtid)],
|
||||
receiver_account,
|
||||
slen);
|
||||
GNUNET_CRYPTO_hash (buf,
|
||||
sizeof (buf),
|
||||
key);
|
||||
@ -1483,10 +1483,10 @@ history_debit_cb (void *cls,
|
||||
switch (dhr->http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<dhr->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<dhr->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_DebitDetails *dd
|
||||
= &dhr->details.success.details[i];
|
||||
= &dhr->details.ok.details[i];
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
|
||||
GNUNET_TIME_timestamp2s (dd->execution_date),
|
||||
@ -1504,9 +1504,9 @@ history_debit_cb (void *cls,
|
||||
roi->details.execution_date = dd->execution_date;
|
||||
roi->details.wtid = dd->wtid;
|
||||
roi->details.credit_account_uri = (const char *) &roi[1];
|
||||
memcpy (&roi[1],
|
||||
dd->credit_account_uri,
|
||||
slen);
|
||||
GNUNET_memcpy (&roi[1],
|
||||
dd->credit_account_uri,
|
||||
slen);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONTAINER_multihashmap_put (out_map,
|
||||
&roi->subject_hash,
|
||||
@ -1678,9 +1678,9 @@ reserve_in_cb (void *cls,
|
||||
rii->details.execution_date = execution_date;
|
||||
rii->details.reserve_pub = *reserve_pub;
|
||||
rii->details.debit_account_uri = (const char *) &rii[1];
|
||||
memcpy (&rii[1],
|
||||
sender_account_details,
|
||||
slen);
|
||||
GNUNET_memcpy (&rii[1],
|
||||
sender_account_details,
|
||||
slen);
|
||||
GNUNET_CRYPTO_hash (&wire_reference,
|
||||
sizeof (uint64_t),
|
||||
&rii->row_off_hash);
|
||||
@ -1978,10 +1978,10 @@ history_credit_cb (void *cls,
|
||||
switch (chr->http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<chr->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<chr->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_CreditDetails *cd
|
||||
= &chr->details.success.details[i];
|
||||
= &chr->details.ok.details[i];
|
||||
|
||||
if (! analyze_credit (wa,
|
||||
cd))
|
||||
|
@ -131,8 +131,8 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
chr.details.success.details_length = len;
|
||||
chr.details.success.details = cd;
|
||||
chr.details.ok.details_length = len;
|
||||
chr.details.ok.details = cd;
|
||||
hh->hcb (hh->hcb_cls,
|
||||
&chr);
|
||||
}
|
||||
|
@ -133,8 +133,8 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
dhr.details.success.details_length = len;
|
||||
dhr.details.success.details = dd;
|
||||
dhr.details.ok.details_length = len;
|
||||
dhr.details.ok.details = dd;
|
||||
hh->hcb (hh->hcb_cls,
|
||||
&dhr);
|
||||
}
|
||||
|
@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
|
||||
wp->account_len = htonl ((uint32_t) d_len);
|
||||
wp->exchange_url_len = htonl ((uint32_t) u_len);
|
||||
end = (char *) &wp[1];
|
||||
memcpy (end,
|
||||
destination_account_payto_uri,
|
||||
d_len);
|
||||
memcpy (end + d_len,
|
||||
exchange_base_url,
|
||||
u_len);
|
||||
GNUNET_memcpy (end,
|
||||
destination_account_payto_uri,
|
||||
d_len);
|
||||
GNUNET_memcpy (end + d_len,
|
||||
exchange_base_url,
|
||||
u_len);
|
||||
*buf = (char *) wp;
|
||||
}
|
||||
|
||||
|
@ -1389,9 +1389,9 @@ make_transfer (
|
||||
if (NULL != timestamp)
|
||||
*timestamp = t->date;
|
||||
t->type = T_DEBIT;
|
||||
memcpy (t->subject.debit.exchange_base_url,
|
||||
exchange_base_url,
|
||||
url_len);
|
||||
GNUNET_memcpy (t->subject.debit.exchange_base_url,
|
||||
exchange_base_url,
|
||||
url_len);
|
||||
t->subject.debit.wtid = *subject;
|
||||
if (NULL == request_uid)
|
||||
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||
|
@ -179,10 +179,10 @@ credit_history_cb (void *cls,
|
||||
global_ret = 0;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_CreditDetails *cd =
|
||||
&reply->details.success.details[i];
|
||||
&reply->details.ok.details[i];
|
||||
|
||||
/* If credit/debit accounts were specified, use as a filter */
|
||||
if ( (NULL != credit_account) &&
|
||||
@ -279,10 +279,10 @@ debit_history_cb (void *cls,
|
||||
global_ret = 0;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
for (unsigned int i = 0; i<reply->details.success.details_length; i++)
|
||||
for (unsigned int i = 0; i<reply->details.ok.details_length; i++)
|
||||
{
|
||||
const struct TALER_BANK_DebitDetails *dd =
|
||||
&reply->details.success.details[i];
|
||||
&reply->details.ok.details[i];
|
||||
|
||||
/* If credit/debit accounts were specified, use as a filter */
|
||||
if ( (NULL != credit_account) &&
|
||||
|
@ -644,26 +644,19 @@ do_upload (char *const *args)
|
||||
* a particular exchange and what keys the exchange is using.
|
||||
*
|
||||
* @param cls closure with the `char **` remaining args
|
||||
* @param hr HTTP response data
|
||||
* @param keys information about the various keys used
|
||||
* by the exchange, NULL if /keys failed
|
||||
* @param compat protocol compatibility information
|
||||
* @param kr response data
|
||||
*/
|
||||
static void
|
||||
keys_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_EXCHANGE_Keys *keys,
|
||||
enum TALER_EXCHANGE_VersionCompatibility compat)
|
||||
const struct TALER_EXCHANGE_KeysResponse *kr)
|
||||
{
|
||||
char *const *args = cls;
|
||||
|
||||
(void) keys;
|
||||
(void) compat;
|
||||
switch (hr->http_status)
|
||||
switch (kr->hr.http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
if (! json_is_object (hr->reply))
|
||||
if (! json_is_object (kr->hr.reply))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
@ -676,9 +669,9 @@ keys_cb (
|
||||
default:
|
||||
fprintf (stderr,
|
||||
"Failed to download keys: %s (HTTP status: %u/%u)\n",
|
||||
hr->hint,
|
||||
hr->http_status,
|
||||
(unsigned int) hr->ec);
|
||||
kr->hr.hint,
|
||||
kr->hr.http_status,
|
||||
(unsigned int) kr->hr.ec);
|
||||
TALER_EXCHANGE_disconnect (exchange);
|
||||
exchange = NULL;
|
||||
test_shutdown ();
|
||||
@ -689,7 +682,7 @@ keys_cb (
|
||||
GNUNET_JSON_pack_string ("operation",
|
||||
OP_INPUT_KEYS),
|
||||
GNUNET_JSON_pack_object_incref ("arguments",
|
||||
(json_t *) hr->reply));
|
||||
(json_t *) kr->hr.reply));
|
||||
if (NULL == args[0])
|
||||
{
|
||||
json_dumpf (in,
|
||||
|
@ -24,6 +24,8 @@
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_exchange_service.h"
|
||||
#include "taler_extensions.h"
|
||||
#include <regex.h>
|
||||
|
||||
|
||||
/**
|
||||
* Name of the input for the 'sign' and 'show' operation.
|
||||
@ -1119,14 +1121,15 @@ load_offline_key (int do_create)
|
||||
* Function called with information about the post revocation operation result.
|
||||
*
|
||||
* @param cls closure with a `struct DenomRevocationRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param dr response data
|
||||
*/
|
||||
static void
|
||||
denom_revocation_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
|
||||
{
|
||||
struct DenomRevocationRequest *drr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1208,14 +1211,15 @@ upload_denom_revocation (const char *exchange_url,
|
||||
* Function called with information about the post revocation operation result.
|
||||
*
|
||||
* @param cls closure with a `struct SignkeyRevocationRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param sr response data
|
||||
*/
|
||||
static void
|
||||
signkey_revocation_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
|
||||
{
|
||||
struct SignkeyRevocationRequest *srr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1489,13 +1493,14 @@ upload_auditor_del (const char *exchange_url,
|
||||
* Function called with information about the post wire add operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireAddRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param wer response data
|
||||
*/
|
||||
static void
|
||||
wire_add_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
|
||||
{
|
||||
struct WireAddRequest *war = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1533,10 +1538,21 @@ upload_wire_add (const char *exchange_url,
|
||||
struct GNUNET_TIME_Timestamp start_time;
|
||||
struct WireAddRequest *war;
|
||||
const char *err_name;
|
||||
const char *conversion_url = NULL;
|
||||
json_t *debit_restrictions;
|
||||
json_t *credit_restrictions;
|
||||
unsigned int err_line;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("conversion_url",
|
||||
&conversion_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_json ("debit_restrictions",
|
||||
&debit_restrictions),
|
||||
GNUNET_JSON_spec_json ("credit_restrictions",
|
||||
&credit_restrictions),
|
||||
GNUNET_JSON_spec_timestamp ("validity_start",
|
||||
&start_time),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
|
||||
@ -1561,6 +1577,7 @@ upload_wire_add (const char *exchange_url,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
test_shutdown ();
|
||||
return;
|
||||
}
|
||||
@ -1574,6 +1591,7 @@ upload_wire_add (const char *exchange_url,
|
||||
"payto:// URI `%s' is malformed\n",
|
||||
payto_uri);
|
||||
global_ret = EXIT_FAILURE;
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
test_shutdown ();
|
||||
return;
|
||||
}
|
||||
@ -1588,6 +1606,7 @@ upload_wire_add (const char *exchange_url,
|
||||
"payto URI is malformed: %s\n",
|
||||
msg);
|
||||
GNUNET_free (msg);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
test_shutdown ();
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
return;
|
||||
@ -1599,6 +1618,9 @@ upload_wire_add (const char *exchange_url,
|
||||
TALER_EXCHANGE_management_enable_wire (ctx,
|
||||
exchange_url,
|
||||
payto_uri,
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
start_time,
|
||||
&master_sig_add,
|
||||
&master_sig_wire,
|
||||
@ -1607,6 +1629,7 @@ upload_wire_add (const char *exchange_url,
|
||||
GNUNET_CONTAINER_DLL_insert (war_head,
|
||||
war_tail,
|
||||
war);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
}
|
||||
|
||||
|
||||
@ -1614,13 +1637,14 @@ upload_wire_add (const char *exchange_url,
|
||||
* Function called with information about the post wire del operation result.
|
||||
*
|
||||
* @param cls closure with a `struct WireDelRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param wdres response data
|
||||
*/
|
||||
static void
|
||||
wire_del_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
|
||||
{
|
||||
struct WireDelRequest *wdr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -1927,14 +1951,15 @@ upload_global_fee (const char *exchange_url,
|
||||
* Function called with information about the drain profits operation.
|
||||
*
|
||||
* @param cls closure with a `struct DrainProfitsRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mdr response data
|
||||
*/
|
||||
static void
|
||||
drain_profits_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
|
||||
{
|
||||
struct DrainProfitsRequest *dpr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2033,14 +2058,15 @@ upload_drain (const char *exchange_url,
|
||||
* Function called with information about the post upload keys operation result.
|
||||
*
|
||||
* @param cls closure with a `struct UploadKeysRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param mr response data
|
||||
*/
|
||||
static void
|
||||
keys_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
|
||||
{
|
||||
struct UploadKeysRequest *ukr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2206,14 +2232,15 @@ upload_keys (const char *exchange_url,
|
||||
* Function called with information about the post upload extensions operation result.
|
||||
*
|
||||
* @param cls closure with a `struct UploadExtensionsRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param er response data
|
||||
*/
|
||||
static void
|
||||
extensions_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
|
||||
{
|
||||
struct UploadExtensionsRequest *uer = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2447,14 +2474,15 @@ add_partner (const char *exchange_url,
|
||||
* Function called with information about the AML officer update operation.
|
||||
*
|
||||
* @param cls closure with a `struct AmlStaffRequest`
|
||||
* @param hr HTTP response data
|
||||
* @param ar response data
|
||||
*/
|
||||
static void
|
||||
update_aml_officer_cb (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
||||
const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
|
||||
{
|
||||
struct AmlStaffRequest *asr = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
|
||||
|
||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
||||
{
|
||||
@ -2949,6 +2977,96 @@ do_del_auditor (char *const *args)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse account restriction.
|
||||
*
|
||||
* @param args the array of command-line arguments to process next
|
||||
* @param[in,out] restrictions JSON array to update
|
||||
* @return -1 on error, otherwise number of arguments from @a args that were used
|
||||
*/
|
||||
static int
|
||||
parse_restriction (char *const *args,
|
||||
json_t *restrictions)
|
||||
{
|
||||
if (NULL == args[0])
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Restriction TYPE argument missing\n");
|
||||
return -1;
|
||||
}
|
||||
if (0 == strcmp (args[0],
|
||||
"deny"))
|
||||
{
|
||||
GNUNET_assert (0 ==
|
||||
json_array_append_new (
|
||||
restrictions,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("type",
|
||||
"deny"))));
|
||||
return 1;
|
||||
}
|
||||
if (0 == strcmp (args[0],
|
||||
"regex"))
|
||||
{
|
||||
json_t *i18n;
|
||||
json_error_t err;
|
||||
|
||||
if ( (NULL == args[1]) ||
|
||||
(NULL == args[2]) ||
|
||||
(NULL == args[3]) )
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n");
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
regex_t ex;
|
||||
|
||||
if (0 != regcomp (&ex,
|
||||
args[1],
|
||||
REG_NOSUB | REG_EXTENDED))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid regular expression `%s'\n",
|
||||
args[1]);
|
||||
return -1;
|
||||
}
|
||||
regfree (&ex);
|
||||
}
|
||||
|
||||
i18n = json_loads (args[3],
|
||||
JSON_REJECT_DUPLICATES,
|
||||
&err);
|
||||
if (NULL == i18n)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid JSON for restriction of type `regex': `%s` at %d\n",
|
||||
args[3],
|
||||
err.position);
|
||||
return -1;
|
||||
}
|
||||
GNUNET_assert (0 ==
|
||||
json_array_append_new (
|
||||
restrictions,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("type",
|
||||
"regex"),
|
||||
GNUNET_JSON_pack_string ("regex",
|
||||
args[1]),
|
||||
GNUNET_JSON_pack_string ("human_hint",
|
||||
args[2]),
|
||||
GNUNET_JSON_pack_object_steal ("human_hint_i18n",
|
||||
i18n)
|
||||
)));
|
||||
return 4;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Restriction TYPE `%s' unsupported\n",
|
||||
args[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add wire account.
|
||||
*
|
||||
@ -2961,6 +3079,10 @@ do_add_wire (char *const *args)
|
||||
struct TALER_MasterSignatureP master_sig_add;
|
||||
struct TALER_MasterSignatureP master_sig_wire;
|
||||
struct GNUNET_TIME_Timestamp now;
|
||||
const char *conversion_url = NULL;
|
||||
json_t *debit_restrictions;
|
||||
json_t *credit_restrictions;
|
||||
unsigned int num_args = 1;
|
||||
|
||||
if (NULL != in)
|
||||
{
|
||||
@ -3011,24 +3133,101 @@ do_add_wire (char *const *args)
|
||||
}
|
||||
GNUNET_free (wire_method);
|
||||
}
|
||||
debit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != debit_restrictions);
|
||||
credit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != credit_restrictions);
|
||||
while (NULL != args[num_args])
|
||||
{
|
||||
if (0 == strcmp (args[num_args],
|
||||
"conversion-url"))
|
||||
{
|
||||
num_args++;
|
||||
conversion_url = args[num_args];
|
||||
if (NULL == conversion_url)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"'conversion-url' requires an argument\n");
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args++;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (args[num_args],
|
||||
"credit-restriction"))
|
||||
{
|
||||
int iret;
|
||||
|
||||
num_args++;
|
||||
iret = parse_restriction (&args[num_args],
|
||||
credit_restrictions);
|
||||
if (iret <= 0)
|
||||
{
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args += iret;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (args[num_args],
|
||||
"debit-restriction"))
|
||||
{
|
||||
int iret;
|
||||
|
||||
num_args++;
|
||||
iret = parse_restriction (&args[num_args],
|
||||
debit_restrictions);
|
||||
if (iret <= 0)
|
||||
{
|
||||
global_ret = EXIT_INVALIDARGUMENT;
|
||||
test_shutdown ();
|
||||
json_decref (debit_restrictions);
|
||||
json_decref (credit_restrictions);
|
||||
return;
|
||||
}
|
||||
num_args += iret;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
TALER_exchange_offline_wire_add_sign (args[0],
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
now,
|
||||
&master_priv,
|
||||
&master_sig_add);
|
||||
TALER_exchange_wire_signature_make (args[0],
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
&master_priv,
|
||||
&master_sig_wire);
|
||||
output_operation (OP_ENABLE_WIRE,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("payto_uri",
|
||||
args[0]),
|
||||
GNUNET_JSON_pack_array_steal ("debit_restrictions",
|
||||
debit_restrictions),
|
||||
GNUNET_JSON_pack_array_steal ("credit_restrictions",
|
||||
credit_restrictions),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string ("conversion_url",
|
||||
conversion_url)),
|
||||
GNUNET_JSON_pack_timestamp ("validity_start",
|
||||
now),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig_add",
|
||||
&master_sig_add),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig_wire",
|
||||
&master_sig_wire)));
|
||||
next (args + 1);
|
||||
next (args + num_args);
|
||||
}
|
||||
|
||||
|
||||
@ -3643,18 +3842,15 @@ enable_aml_staff (char *const *args)
|
||||
* whether there are subsequent commands).
|
||||
*
|
||||
* @param cls closure with the `char **` remaining args
|
||||
* @param hr HTTP response data
|
||||
* @param keys information about the various keys used
|
||||
* by the exchange, NULL if /management/keys failed
|
||||
* @param mgr response data
|
||||
*/
|
||||
static void
|
||||
download_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_EXCHANGE_FutureKeys *keys)
|
||||
const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
|
||||
{
|
||||
char *const *args = cls;
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
|
||||
|
||||
(void) keys;
|
||||
mgkh = NULL;
|
||||
switch (hr->http_status)
|
||||
{
|
||||
@ -5045,7 +5241,7 @@ work (void *cls)
|
||||
{
|
||||
.name = "enable-account",
|
||||
.help =
|
||||
"enable wire account of the exchange (payto-URI must be given as argument)",
|
||||
"enable wire account of the exchange (payto-URI must be given as argument; for optional argument see man page)",
|
||||
.cb = &do_add_wire
|
||||
},
|
||||
{
|
||||
|
@ -312,10 +312,10 @@ expired_reserve_cb (void *cls,
|
||||
memset (&wtid,
|
||||
0,
|
||||
sizeof (wtid));
|
||||
memcpy (&wtid,
|
||||
reserve_pub,
|
||||
GNUNET_MIN (sizeof (wtid),
|
||||
sizeof (*reserve_pub)));
|
||||
GNUNET_memcpy (&wtid,
|
||||
reserve_pub,
|
||||
GNUNET_MIN (sizeof (wtid),
|
||||
sizeof (*reserve_pub)));
|
||||
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
|
||||
reserve_pub,
|
||||
now,
|
||||
|
@ -542,7 +542,6 @@ handle_get_aml (struct TEH_RequestContext *rc,
|
||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||
NULL);
|
||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
|
||||
@ -932,9 +931,9 @@ proceed_with_handler (struct TEH_RequestContext *rc,
|
||||
|
||||
/* Parse command-line arguments */
|
||||
/* make a copy of 'url' because 'strtok_r()' will modify */
|
||||
memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
GNUNET_memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
i = 0;
|
||||
args[i++] = strtok_r (d, "/", &sp);
|
||||
while ( (NULL != args[i - 1]) &&
|
||||
@ -1617,33 +1616,8 @@ handle_mhd_request (void *cls,
|
||||
if (0 == strcasecmp (method,
|
||||
MHD_HTTP_METHOD_POST))
|
||||
{
|
||||
const char *cl;
|
||||
|
||||
/* Maybe check for maximum upload size
|
||||
and refuse requests if they are just too big. */
|
||||
cl = MHD_lookup_connection_value (connection,
|
||||
MHD_HEADER_KIND,
|
||||
MHD_HTTP_HEADER_CONTENT_LENGTH);
|
||||
if (NULL != cl)
|
||||
{
|
||||
unsigned long long cv;
|
||||
char dummy;
|
||||
|
||||
if (1 != sscanf (cl,
|
||||
"%llu%c",
|
||||
&cv,
|
||||
&dummy))
|
||||
{
|
||||
/* Not valid HTTP request, just close connection. */
|
||||
GNUNET_break_op (0);
|
||||
return MHD_NO;
|
||||
}
|
||||
if (cv > TALER_MHD_REQUEST_BUFFER_MAX)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_request_too_large (connection);
|
||||
}
|
||||
}
|
||||
TALER_MHD_check_content_length (connection,
|
||||
TALER_MHD_REQUEST_BUFFER_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2215,6 +2189,7 @@ do_shutdown (void *cls)
|
||||
|
||||
mhd = TALER_MHD_daemon_stop ();
|
||||
TEH_resume_keys_requests (true);
|
||||
TEH_deposits_get_cleanup ();
|
||||
TEH_reserves_get_cleanup ();
|
||||
TEH_purses_get_cleanup ();
|
||||
TEH_kyc_check_cleanup ();
|
||||
|
@ -41,7 +41,7 @@
|
||||
*
|
||||
* Returned via both /config and /keys endpoints.
|
||||
*/
|
||||
#define EXCHANGE_PROTOCOL_VERSION "14:0:2"
|
||||
#define EXCHANGE_PROTOCOL_VERSION "15:0:0"
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2017, 2021 Taler Systems SA
|
||||
Copyright (C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -23,6 +23,7 @@
|
||||
#include <jansson.h>
|
||||
#include <microhttpd.h>
|
||||
#include <pthread.h>
|
||||
#include "taler_dbevents.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_mhd_lib.h"
|
||||
#include "taler_signatures.h"
|
||||
@ -37,6 +38,26 @@
|
||||
struct DepositWtidContext
|
||||
{
|
||||
|
||||
/**
|
||||
* Kept in a DLL.
|
||||
*/
|
||||
struct DepositWtidContext *next;
|
||||
|
||||
/**
|
||||
* Kept in a DLL.
|
||||
*/
|
||||
struct DepositWtidContext *prev;
|
||||
|
||||
/**
|
||||
* Context for the request we are processing.
|
||||
*/
|
||||
struct TEH_RequestContext *rc;
|
||||
|
||||
/**
|
||||
* Subscription for the database event we are waiting for.
|
||||
*/
|
||||
struct GNUNET_DB_EventHandler *eh;
|
||||
|
||||
/**
|
||||
* Hash over the proposal data of the contract for which this deposit is made.
|
||||
*/
|
||||
@ -64,6 +85,12 @@ struct DepositWtidContext
|
||||
*/
|
||||
struct TALER_WireTransferIdentifierRawP wtid;
|
||||
|
||||
/**
|
||||
* Signature by the merchant.
|
||||
*/
|
||||
struct TALER_MerchantSignatureP merchant_sig;
|
||||
|
||||
|
||||
/**
|
||||
* Set by #handle_wtid data to the coin's contribution to the wire transfer.
|
||||
*/
|
||||
@ -79,6 +106,11 @@ struct DepositWtidContext
|
||||
*/
|
||||
struct GNUNET_TIME_Timestamp execution_time;
|
||||
|
||||
/**
|
||||
* Timeout of the request, for long-polling.
|
||||
*/
|
||||
struct GNUNET_TIME_Absolute timeout;
|
||||
|
||||
/**
|
||||
* Set by #handle_wtid to the coin contribution to the transaction
|
||||
* (that is, @e coin_contribution minus @e coin_fee).
|
||||
@ -101,9 +133,45 @@ struct DepositWtidContext
|
||||
* Set to #GNUNET_SYSERR if there was a serious error.
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue pending;
|
||||
|
||||
/**
|
||||
* #GNUNET_YES if we were suspended, #GNUNET_SYSERR
|
||||
* if we were woken up due to shutdown.
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue suspended;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Head of DLL of suspended requests.
|
||||
*/
|
||||
static struct DepositWtidContext *dwc_head;
|
||||
|
||||
/**
|
||||
* Tail of DLL of suspended requests.
|
||||
*/
|
||||
static struct DepositWtidContext *dwc_tail;
|
||||
|
||||
|
||||
void
|
||||
TEH_deposits_get_cleanup ()
|
||||
{
|
||||
struct DepositWtidContext *n;
|
||||
for (struct DepositWtidContext *ctx = dwc_head;
|
||||
NULL != ctx;
|
||||
ctx = n)
|
||||
{
|
||||
n = ctx->next;
|
||||
GNUNET_assert (GNUNET_YES == ctx->suspended);
|
||||
ctx->suspended = GNUNET_SYSERR;
|
||||
MHD_resume_connection (ctx->rc->connection);
|
||||
GNUNET_CONTAINER_DLL_remove (dwc_head,
|
||||
dwc_tail,
|
||||
ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A merchant asked for details about a deposit. Provide
|
||||
* them. Generates the 200 reply.
|
||||
@ -227,34 +295,97 @@ deposits_get_transaction (void *cls,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called on events received from Postgres.
|
||||
* Wakes up long pollers.
|
||||
*
|
||||
* @param cls the `struct DepositWtidContext *`
|
||||
* @param extra additional event data provided
|
||||
* @param extra_size number of bytes in @a extra
|
||||
*/
|
||||
static void
|
||||
db_event_cb (void *cls,
|
||||
const void *extra,
|
||||
size_t extra_size)
|
||||
{
|
||||
struct DepositWtidContext *ctx = cls;
|
||||
struct GNUNET_AsyncScopeSave old_scope;
|
||||
|
||||
(void) extra;
|
||||
(void) extra_size;
|
||||
if (GNUNET_NO != ctx->suspended)
|
||||
return; /* might get multiple wake-up events */
|
||||
GNUNET_CONTAINER_DLL_remove (dwc_head,
|
||||
dwc_tail,
|
||||
ctx);
|
||||
GNUNET_async_scope_enter (&ctx->rc->async_scope_id,
|
||||
&old_scope);
|
||||
TEH_check_invariants ();
|
||||
ctx->suspended = GNUNET_NO;
|
||||
MHD_resume_connection (ctx->rc->connection);
|
||||
TALER_MHD_daemon_trigger ();
|
||||
TEH_check_invariants ();
|
||||
GNUNET_async_scope_restore (&old_scope);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lookup and return the wire transfer identifier.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param ctx context of the signed request to execute
|
||||
* @return MHD result code
|
||||
*/
|
||||
static MHD_RESULT
|
||||
handle_track_transaction_request (
|
||||
struct MHD_Connection *connection,
|
||||
struct DepositWtidContext *ctx)
|
||||
{
|
||||
MHD_RESULT mhd_ret;
|
||||
struct MHD_Connection *connection = ctx->rc->connection;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_DB_run_transaction (connection,
|
||||
"handle deposits GET",
|
||||
TEH_MT_REQUEST_OTHER,
|
||||
&mhd_ret,
|
||||
&deposits_get_transaction,
|
||||
ctx))
|
||||
return mhd_ret;
|
||||
if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
|
||||
(NULL == ctx->eh) )
|
||||
{
|
||||
struct TALER_CoinDepositEventP rep = {
|
||||
.header.size = htons (sizeof (rep)),
|
||||
.header.type = htons (TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED),
|
||||
.merchant_pub = ctx->merchant
|
||||
};
|
||||
|
||||
ctx->eh = TEH_plugin->event_listen (
|
||||
TEH_plugin->cls,
|
||||
GNUNET_TIME_absolute_get_remaining (ctx->timeout),
|
||||
&rep.header,
|
||||
&db_event_cb,
|
||||
ctx);
|
||||
}
|
||||
{
|
||||
MHD_RESULT mhd_ret;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_DB_run_transaction (connection,
|
||||
"handle deposits GET",
|
||||
TEH_MT_REQUEST_OTHER,
|
||||
&mhd_ret,
|
||||
&deposits_get_transaction,
|
||||
ctx))
|
||||
return mhd_ret;
|
||||
}
|
||||
if (GNUNET_SYSERR == ctx->pending)
|
||||
return TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
|
||||
"wire fees exceed aggregate in database");
|
||||
if (ctx->pending)
|
||||
if (GNUNET_YES == ctx->pending)
|
||||
{
|
||||
if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
|
||||
(GNUNET_NO == ctx->suspended) )
|
||||
{
|
||||
GNUNET_CONTAINER_DLL_insert (dwc_head,
|
||||
dwc_tail,
|
||||
ctx);
|
||||
ctx->suspended = GNUNET_YES;
|
||||
MHD_suspend_connection (connection);
|
||||
return MHD_YES;
|
||||
}
|
||||
return TALER_MHD_REPLY_JSON_PACK (
|
||||
connection,
|
||||
MHD_HTTP_ACCEPTED,
|
||||
@ -270,94 +401,118 @@ handle_track_transaction_request (
|
||||
ctx->kyc.ok),
|
||||
GNUNET_JSON_pack_timestamp ("execution_time",
|
||||
ctx->execution_time));
|
||||
}
|
||||
return reply_deposit_details (connection,
|
||||
ctx);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called to clean up a context.
|
||||
*
|
||||
* @param rc request context with data to clean up
|
||||
*/
|
||||
static void
|
||||
dwc_cleaner (struct TEH_RequestContext *rc)
|
||||
{
|
||||
struct DepositWtidContext *ctx = rc->rh_ctx;
|
||||
|
||||
GNUNET_assert (GNUNET_NO == ctx->suspended);
|
||||
if (NULL != ctx->eh)
|
||||
{
|
||||
TEH_plugin->event_listen_cancel (TEH_plugin->cls,
|
||||
ctx->eh);
|
||||
ctx->eh = NULL;
|
||||
}
|
||||
GNUNET_free (ctx);
|
||||
}
|
||||
|
||||
|
||||
MHD_RESULT
|
||||
TEH_handler_deposits_get (struct TEH_RequestContext *rc,
|
||||
const char *const args[4])
|
||||
{
|
||||
enum GNUNET_GenericReturnValue res;
|
||||
struct TALER_MerchantSignatureP merchant_sig;
|
||||
struct DepositWtidContext ctx;
|
||||
struct DepositWtidContext *ctx = rc->rh_ctx;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[0],
|
||||
strlen (args[0]),
|
||||
&ctx.h_wire,
|
||||
sizeof (ctx.h_wire)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE,
|
||||
args[0]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[1],
|
||||
strlen (args[1]),
|
||||
&ctx.merchant,
|
||||
sizeof (ctx.merchant)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB,
|
||||
args[1]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[2],
|
||||
strlen (args[2]),
|
||||
&ctx.h_contract_terms,
|
||||
sizeof (ctx.h_contract_terms)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS,
|
||||
args[2]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[3],
|
||||
strlen (args[3]),
|
||||
&ctx.coin_pub,
|
||||
sizeof (ctx.coin_pub)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB,
|
||||
args[3]);
|
||||
}
|
||||
res = TALER_MHD_parse_request_arg_data (rc->connection,
|
||||
"merchant_sig",
|
||||
&merchant_sig,
|
||||
sizeof (merchant_sig));
|
||||
if (GNUNET_SYSERR == res)
|
||||
return MHD_NO; /* internal error */
|
||||
if (GNUNET_NO == res)
|
||||
return MHD_YES; /* parse error */
|
||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||
if (NULL == ctx)
|
||||
{
|
||||
ctx = GNUNET_new (struct DepositWtidContext);
|
||||
ctx->rc = rc;
|
||||
rc->rh_ctx = ctx;
|
||||
rc->rh_cleaner = &dwc_cleaner;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_merchant_deposit_verify (&ctx.merchant,
|
||||
&ctx.coin_pub,
|
||||
&ctx.h_contract_terms,
|
||||
&ctx.h_wire,
|
||||
&merchant_sig))
|
||||
GNUNET_STRINGS_string_to_data (args[0],
|
||||
strlen (args[0]),
|
||||
&ctx->h_wire,
|
||||
sizeof (ctx->h_wire)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID,
|
||||
NULL);
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE,
|
||||
args[0]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[1],
|
||||
strlen (args[1]),
|
||||
&ctx->merchant,
|
||||
sizeof (ctx->merchant)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_MERCHANT_PUB,
|
||||
args[1]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[2],
|
||||
strlen (args[2]),
|
||||
&ctx->h_contract_terms,
|
||||
sizeof (ctx->h_contract_terms)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_CONTRACT_TERMS,
|
||||
args[2]);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (args[3],
|
||||
strlen (args[3]),
|
||||
&ctx->coin_pub,
|
||||
sizeof (ctx->coin_pub)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_COIN_PUB,
|
||||
args[3]);
|
||||
}
|
||||
TALER_MHD_parse_request_arg_auto_t (rc->connection,
|
||||
"merchant_sig",
|
||||
&ctx->merchant_sig);
|
||||
TALER_MHD_parse_request_timeout (rc->connection,
|
||||
&ctx->timeout);
|
||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||
{
|
||||
if (GNUNET_OK !=
|
||||
TALER_merchant_deposit_verify (&ctx->merchant,
|
||||
&ctx->coin_pub,
|
||||
&ctx->h_contract_terms,
|
||||
&ctx->h_wire,
|
||||
&ctx->merchant_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
TALER_EC_EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handle_track_transaction_request (rc->connection,
|
||||
&ctx);
|
||||
return handle_track_transaction_request (ctx);
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,6 +26,13 @@
|
||||
#include "taler-exchange-httpd.h"
|
||||
|
||||
|
||||
/**
|
||||
* Resume long pollers on GET /deposits.
|
||||
*/
|
||||
void
|
||||
TEH_deposits_get_cleanup (void);
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/deposits/$H_WIRE/$MERCHANT_PUB/$H_CONTRACT_TERMS/$COIN_PUB"
|
||||
* request.
|
||||
|
@ -520,34 +520,8 @@ TEH_handler_kyc_check (
|
||||
"usertype");
|
||||
}
|
||||
|
||||
{
|
||||
const char *ts;
|
||||
|
||||
ts = MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"timeout_ms");
|
||||
if (NULL != ts)
|
||||
{
|
||||
char dummy;
|
||||
unsigned long long tms;
|
||||
|
||||
if (1 !=
|
||||
sscanf (ts,
|
||||
"%llu%c",
|
||||
&tms,
|
||||
&dummy))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"timeout_ms");
|
||||
}
|
||||
kyp->timeout = GNUNET_TIME_relative_to_absolute (
|
||||
GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
|
||||
tms));
|
||||
}
|
||||
}
|
||||
TALER_MHD_parse_request_timeout (rc->connection,
|
||||
&kyp->timeout);
|
||||
}
|
||||
|
||||
if ( (NULL == kyp->eh) &&
|
||||
|
@ -297,7 +297,6 @@ TEH_handler_kyc_proof (
|
||||
{
|
||||
struct KycProofContext *kpc = rc->rh_ctx;
|
||||
const char *provider_section_or_logic = args[0];
|
||||
const char *h_payto;
|
||||
|
||||
if (NULL == kpc)
|
||||
{
|
||||
@ -310,33 +309,13 @@ TEH_handler_kyc_proof (
|
||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
||||
"'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required");
|
||||
}
|
||||
h_payto = MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"state");
|
||||
if (NULL == h_payto)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MISSING,
|
||||
"h_payto");
|
||||
}
|
||||
kpc = GNUNET_new (struct KycProofContext);
|
||||
kpc->rc = rc;
|
||||
rc->rh_ctx = kpc;
|
||||
rc->rh_cleaner = &clean_kpc;
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (h_payto,
|
||||
strlen (h_payto),
|
||||
&kpc->h_payto,
|
||||
sizeof (kpc->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");
|
||||
}
|
||||
TALER_MHD_parse_request_arg_auto_t (rc->connection,
|
||||
"state",
|
||||
&kpc->h_payto);
|
||||
if (GNUNET_OK !=
|
||||
TALER_KYCLOGIC_lookup_logic (provider_section_or_logic,
|
||||
&kpc->logic,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2020 Taler Systems SA
|
||||
Copyright (C) 2020-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -114,6 +114,9 @@ del_wire (void *cls,
|
||||
}
|
||||
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
||||
awc->payto_uri,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
awc->validity_end,
|
||||
false);
|
||||
if (qs < 0)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2020 Taler Systems SA
|
||||
Copyright (C) 2020-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -54,6 +54,21 @@ struct AddWireContext
|
||||
*/
|
||||
const char *payto_uri;
|
||||
|
||||
/**
|
||||
* (optional) address of a conversion service for this account.
|
||||
*/
|
||||
const char *conversion_url;
|
||||
|
||||
/**
|
||||
* Restrictions imposed when crediting this account.
|
||||
*/
|
||||
json_t *credit_restrictions;
|
||||
|
||||
/**
|
||||
* Restrictions imposed when debiting this account.
|
||||
*/
|
||||
json_t *debit_restrictions;
|
||||
|
||||
/**
|
||||
* Timestamp for checking against replay attacks.
|
||||
*/
|
||||
@ -114,11 +129,17 @@ add_wire (void *cls,
|
||||
if (0 == qs)
|
||||
qs = TEH_plugin->insert_wire (TEH_plugin->cls,
|
||||
awc->payto_uri,
|
||||
awc->conversion_url,
|
||||
awc->debit_restrictions,
|
||||
awc->credit_restrictions,
|
||||
awc->validity_start,
|
||||
&awc->master_sig_wire);
|
||||
else
|
||||
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
||||
awc->payto_uri,
|
||||
awc->conversion_url,
|
||||
awc->debit_restrictions,
|
||||
awc->credit_restrictions,
|
||||
awc->validity_start,
|
||||
true);
|
||||
if (qs < 0)
|
||||
@ -141,7 +162,9 @@ TEH_handler_management_post_wire (
|
||||
struct MHD_Connection *connection,
|
||||
const json_t *root)
|
||||
{
|
||||
struct AddWireContext awc;
|
||||
struct AddWireContext awc = {
|
||||
.conversion_url = NULL
|
||||
};
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
|
||||
&awc.master_sig_wire),
|
||||
@ -149,6 +172,14 @@ TEH_handler_management_post_wire (
|
||||
&awc.master_sig_add),
|
||||
GNUNET_JSON_spec_string ("payto_uri",
|
||||
&awc.payto_uri),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("conversion_url",
|
||||
&awc.conversion_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_json ("credit_restrictions",
|
||||
&awc.credit_restrictions),
|
||||
GNUNET_JSON_spec_json ("debit_restrictions",
|
||||
&awc.debit_restrictions),
|
||||
GNUNET_JSON_spec_timestamp ("validity_start",
|
||||
&awc.validity_start),
|
||||
GNUNET_JSON_spec_end ()
|
||||
@ -179,17 +210,22 @@ TEH_handler_management_post_wire (
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
|
||||
msg);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
GNUNET_free (msg);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_exchange_offline_wire_add_verify (awc.payto_uri,
|
||||
awc.conversion_url,
|
||||
awc.debit_restrictions,
|
||||
awc.credit_restrictions,
|
||||
awc.validity_start,
|
||||
&TEH_master_public_key,
|
||||
&awc.master_sig_add))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TALER_MHD_reply_with_error (
|
||||
connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
@ -199,10 +235,14 @@ TEH_handler_management_post_wire (
|
||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||
if (GNUNET_OK !=
|
||||
TALER_exchange_wire_signature_check (awc.payto_uri,
|
||||
awc.conversion_url,
|
||||
awc.debit_restrictions,
|
||||
awc.credit_restrictions,
|
||||
&TEH_master_public_key,
|
||||
&awc.master_sig_wire))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TALER_MHD_reply_with_error (
|
||||
connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
@ -218,6 +258,7 @@ TEH_handler_management_post_wire (
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"payto:// URI `%s' is malformed\n",
|
||||
awc.payto_uri);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return TALER_MHD_reply_with_error (
|
||||
connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
@ -237,6 +278,7 @@ TEH_handler_management_post_wire (
|
||||
&ret,
|
||||
&add_wire,
|
||||
&awc);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
if (GNUNET_SYSERR == res)
|
||||
return ret;
|
||||
}
|
||||
|
@ -57,29 +57,9 @@ TEH_handler_purses_delete (
|
||||
TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
|
||||
args[0]);
|
||||
}
|
||||
{
|
||||
const char *sig;
|
||||
|
||||
sig = MHD_lookup_connection_value (connection,
|
||||
MHD_HEADER_KIND,
|
||||
"Taler-Purse-Signature");
|
||||
if ( (NULL == sig) ||
|
||||
(GNUNET_OK !=
|
||||
GNUNET_STRINGS_string_to_data (sig,
|
||||
strlen (sig),
|
||||
&purse_sig,
|
||||
sizeof (purse_sig))) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
(NULL == sig)
|
||||
? TALER_EC_GENERIC_PARAMETER_MISSING
|
||||
: TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"Taler-Purse-Signature");
|
||||
}
|
||||
}
|
||||
|
||||
TALER_MHD_parse_request_header_auto_t (connection,
|
||||
"Taler-Purse-Signature",
|
||||
&purse_sig);
|
||||
if (GNUNET_OK !=
|
||||
TALER_wallet_purse_delete_verify (&purse_pub,
|
||||
&purse_sig))
|
||||
|
@ -243,36 +243,8 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc,
|
||||
args[1]);
|
||||
}
|
||||
|
||||
{
|
||||
const char *long_poll_timeout_ms;
|
||||
|
||||
long_poll_timeout_ms
|
||||
= MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"timeout_ms");
|
||||
if (NULL != long_poll_timeout_ms)
|
||||
{
|
||||
unsigned int timeout_ms;
|
||||
char dummy;
|
||||
struct GNUNET_TIME_Relative timeout;
|
||||
|
||||
if (1 != sscanf (long_poll_timeout_ms,
|
||||
"%u%c",
|
||||
&timeout_ms,
|
||||
&dummy))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"timeout_ms (must be non-negative number)");
|
||||
}
|
||||
timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
|
||||
timeout_ms);
|
||||
gc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
|
||||
}
|
||||
}
|
||||
|
||||
TALER_MHD_parse_request_timeout (rc->connection,
|
||||
&gc->timeout);
|
||||
if ( (GNUNET_TIME_absolute_is_future (gc->timeout)) &&
|
||||
(NULL == gc->eh) )
|
||||
{
|
||||
|
@ -52,8 +52,12 @@ struct ReservePoller
|
||||
struct MHD_Connection *connection;
|
||||
|
||||
/**
|
||||
* Subscription for the database event we are
|
||||
* waiting for.
|
||||
* Our request context.
|
||||
*/
|
||||
struct TEH_RequestContext *rc;
|
||||
|
||||
/**
|
||||
* Subscription for the database event we are waiting for.
|
||||
*/
|
||||
struct GNUNET_DB_EventHandler *eh;
|
||||
|
||||
@ -154,6 +158,8 @@ db_event_cb (void *cls,
|
||||
(void) extra_size;
|
||||
if (! rp->suspended)
|
||||
return; /* might get multiple wake-up events */
|
||||
GNUNET_async_scope_enter (&rp->rc->async_scope_id,
|
||||
&old_scope);
|
||||
TEH_check_invariants ();
|
||||
rp->suspended = false;
|
||||
MHD_resume_connection (rp->connection);
|
||||
@ -171,11 +177,9 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
||||
|
||||
if (NULL == rp)
|
||||
{
|
||||
struct GNUNET_TIME_Relative timeout
|
||||
= GNUNET_TIME_UNIT_ZERO;
|
||||
|
||||
rp = GNUNET_new (struct ReservePoller);
|
||||
rp->connection = rc->connection;
|
||||
rp->rc = rc;
|
||||
rc->rh_ctx = rp;
|
||||
rc->rh_cleaner = &rp_cleanup;
|
||||
GNUNET_CONTAINER_DLL_insert (rp_head,
|
||||
@ -193,34 +197,8 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
||||
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
|
||||
args[0]);
|
||||
}
|
||||
{
|
||||
const char *long_poll_timeout_ms;
|
||||
|
||||
long_poll_timeout_ms
|
||||
= MHD_lookup_connection_value (rc->connection,
|
||||
MHD_GET_ARGUMENT_KIND,
|
||||
"timeout_ms");
|
||||
if (NULL != long_poll_timeout_ms)
|
||||
{
|
||||
unsigned int timeout_ms;
|
||||
char dummy;
|
||||
|
||||
if (1 != sscanf (long_poll_timeout_ms,
|
||||
"%u%c",
|
||||
&timeout_ms,
|
||||
&dummy))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return TALER_MHD_reply_with_error (rc->connection,
|
||||
MHD_HTTP_BAD_REQUEST,
|
||||
TALER_EC_GENERIC_PARAMETER_MALFORMED,
|
||||
"timeout_ms (must be non-negative number)");
|
||||
}
|
||||
timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
|
||||
timeout_ms);
|
||||
}
|
||||
}
|
||||
rp->timeout = GNUNET_TIME_relative_to_absolute (timeout);
|
||||
TALER_MHD_parse_request_timeout (rc->connection,
|
||||
&rp->timeout);
|
||||
}
|
||||
|
||||
if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2015-2022 Taler Systems SA
|
||||
Copyright (C) 2015-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -224,12 +224,18 @@ TEH_wire_done ()
|
||||
*
|
||||
* @param cls a `json_t *` object to expand with wire account details
|
||||
* @param payto_uri the exchange bank account URI to add
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account
|
||||
* @param master_sig master key signature affirming that this is a bank
|
||||
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
||||
*/
|
||||
static void
|
||||
add_wire_account (void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
const struct TALER_MasterSignatureP *master_sig)
|
||||
{
|
||||
json_t *a = cls;
|
||||
@ -240,6 +246,13 @@ add_wire_account (void *cls,
|
||||
GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("payto_uri",
|
||||
payto_uri),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string ("conversion_url",
|
||||
conversion_url)),
|
||||
GNUNET_JSON_pack_array_incref ("debit_restrictions",
|
||||
(json_t *) debit_restrictions),
|
||||
GNUNET_JSON_pack_array_incref ("credit_restrictions",
|
||||
(json_t *) credit_restrictions),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig",
|
||||
master_sig))))
|
||||
{
|
||||
@ -462,6 +475,8 @@ build_wire_state (void)
|
||||
wsh->wire_reply = TALER_MHD_MAKE_JSON_PACK (
|
||||
GNUNET_JSON_pack_array_steal ("accounts",
|
||||
wire_accounts_array),
|
||||
GNUNET_JSON_pack_array_steal ("wads", /* #7271 */
|
||||
json_array ()),
|
||||
GNUNET_JSON_pack_object_steal ("fees",
|
||||
wire_fee_object),
|
||||
GNUNET_JSON_pack_data_auto ("master_public_key",
|
||||
|
@ -563,9 +563,9 @@ wire_prepare_cb (void *cls,
|
||||
}
|
||||
wpd = GNUNET_malloc (sizeof (struct WirePrepareData)
|
||||
+ buf_size);
|
||||
memcpy (&wpd[1],
|
||||
buf,
|
||||
buf_size);
|
||||
GNUNET_memcpy (&wpd[1],
|
||||
buf,
|
||||
buf_size);
|
||||
wpd->buf_size = buf_size;
|
||||
wpd->row_id = rowid;
|
||||
GNUNET_CONTAINER_DLL_insert (wpd_head,
|
||||
|
@ -731,8 +731,8 @@ history_cb (void *cls,
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
process_reply (wrap_size,
|
||||
reply->details.success.details,
|
||||
reply->details.success.details_length);
|
||||
reply->details.ok.details,
|
||||
reply->details.ok.details_length);
|
||||
return;
|
||||
case MHD_HTTP_NO_CONTENT:
|
||||
transaction_completed ();
|
||||
|
3
src/exchangedb/.gitignore
vendored
3
src/exchangedb/.gitignore
vendored
@ -12,4 +12,5 @@ test-exchangedb-batch-reserves-in-insert-postgres
|
||||
test-exchangedb-by-j-postgres
|
||||
test-exchangedb-populate-link-data-postgres
|
||||
test-exchangedb-populate-ready-deposit-postgres
|
||||
test-exchangedb-populate-select-refunds-by-coin-postgres
|
||||
test-exchangedb-populate-select-refunds-by-coin-postgres
|
||||
exchange-0004.sql
|
||||
|
26
src/exchangedb/0004-wire_accounts.sql
Normal file
26
src/exchangedb/0004-wire_accounts.sql
Normal file
@ -0,0 +1,26 @@
|
||||
--
|
||||
-- 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/>
|
||||
--
|
||||
|
||||
ALTER TABLE wire_accounts
|
||||
ADD COLUMN conversion_url VARCHAR DEFAULT (NULL),
|
||||
ADD COLUMN debit_restrictions VARCHAR DEFAULT (NULL),
|
||||
ADD COLUMN credit_restrictions VARCHAR DEFAULT (NULL);
|
||||
COMMENT ON COLUMN wire_accounts.conversion_url
|
||||
IS 'URL of a currency conversion service if conversion is needed when this account is used; NULL if there is no conversion.';
|
||||
COMMENT ON COLUMN wire_accounts.debit_restrictions
|
||||
IS 'JSON array describing restrictions imposed when debiting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
|
||||
COMMENT ON COLUMN wire_accounts.credit_restrictions
|
||||
IS 'JSON array describing restrictions imposed when crediting this account. Empty for no restrictions, NULL if account was migrated from previous database revision or account is disabled.';
|
@ -20,7 +20,9 @@ sqlinputs = \
|
||||
0002-*.sql \
|
||||
exchange-0002.sql.in \
|
||||
0003-*.sql \
|
||||
exchange-0003.sql.in
|
||||
exchange-0003.sql.in \
|
||||
0004-*.sql \
|
||||
exchange-0004.sql.in
|
||||
|
||||
sql_DATA = \
|
||||
benchmark-0001.sql \
|
||||
@ -28,6 +30,7 @@ sql_DATA = \
|
||||
exchange-0001.sql \
|
||||
exchange-0002.sql \
|
||||
exchange-0003.sql \
|
||||
exchange-0004.sql \
|
||||
drop.sql \
|
||||
procedures.sql
|
||||
|
||||
@ -39,7 +42,8 @@ BUILT_SOURCES = \
|
||||
|
||||
CLEANFILES = \
|
||||
exchange-0002.sql \
|
||||
exchange-0003.sql
|
||||
exchange-0003.sql \
|
||||
exchange-0004.sql
|
||||
|
||||
procedures.sql: procedures.sql.in exchange_do_*.sql
|
||||
chmod +w $@ || true
|
||||
@ -56,6 +60,11 @@ exchange-0003.sql: exchange-0003.sql.in 0003-*.sql
|
||||
gcc -E -P -undef - < exchange-0003.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
|
||||
chmod ugo-w $@
|
||||
|
||||
exchange-0004.sql: exchange-0004.sql.in 0004-*.sql
|
||||
chmod +w $@ || true
|
||||
gcc -E -P -undef - < exchange-0004.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
|
||||
chmod ugo-w $@
|
||||
|
||||
EXTRA_DIST = \
|
||||
exchangedb.conf \
|
||||
exchangedb-postgres.conf \
|
||||
|
@ -169,9 +169,9 @@ bem_insert (struct GNUNET_PQ_Context *conn,
|
||||
GNUNET_CRYPTO_hash (&b,
|
||||
sizeof (b),
|
||||
&hc);
|
||||
memcpy (&ihc,
|
||||
&hc,
|
||||
sizeof (ihc));
|
||||
GNUNET_memcpy (&ihc,
|
||||
&hc,
|
||||
sizeof (ihc));
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_auto_from_type (&hc),
|
||||
@ -265,9 +265,9 @@ bem_select (struct GNUNET_PQ_Context *conn,
|
||||
GNUNET_CRYPTO_hash (&b,
|
||||
sizeof (b),
|
||||
&hc);
|
||||
memcpy (&ihc,
|
||||
&hc,
|
||||
sizeof (ihc));
|
||||
GNUNET_memcpy (&ihc,
|
||||
&hc,
|
||||
sizeof (ihc));
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_uint32 (&ihc),
|
||||
|
@ -21,6 +21,7 @@ BEGIN;
|
||||
SELECT _v.unregister_patch('exchange-0001');
|
||||
SELECT _v.unregister_patch('exchange-0002');
|
||||
SELECT _v.unregister_patch('exchange-0003');
|
||||
SELECT _v.unregister_patch('exchange-0004');
|
||||
|
||||
DROP SCHEMA exchange CASCADE;
|
||||
|
||||
|
24
src/exchangedb/exchange-0004.sql.in
Normal file
24
src/exchangedb/exchange-0004.sql.in
Normal file
@ -0,0 +1,24 @@
|
||||
--
|
||||
-- This file is part of TALER
|
||||
-- Copyright (C) 2014--2023 Taler Systems SA
|
||||
--
|
||||
-- TALER is free software; you can redistribute it and/or modify it under the
|
||||
-- terms of the GNU General Public License as published by the Free Software
|
||||
-- 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/>
|
||||
--
|
||||
|
||||
BEGIN;
|
||||
|
||||
SELECT _v.register_patch('exchange-0004', NULL, NULL);
|
||||
SET search_path TO exchange;
|
||||
|
||||
#include "0004-wire_accounts.sql"
|
||||
|
||||
COMMIT;
|
@ -22,6 +22,7 @@
|
||||
#include "taler_error_codes.h"
|
||||
#include "taler_dbevents.h"
|
||||
#include "taler_pq_lib.h"
|
||||
#include "pg_event_notify.h"
|
||||
#include "pg_aggregate.h"
|
||||
#include "pg_helper.h"
|
||||
|
||||
@ -35,34 +36,12 @@ TEH_PG_aggregate (
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_TIME_Absolute now = {0};
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_absolute_time (&now),
|
||||
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_auto_from_type (wtid),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
uint64_t sum_deposit_value;
|
||||
uint64_t sum_deposit_frac;
|
||||
uint64_t sum_refund_value;
|
||||
uint64_t sum_refund_frac;
|
||||
uint64_t sum_fee_value;
|
||||
uint64_t sum_fee_frac;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
|
||||
&sum_deposit_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
|
||||
&sum_deposit_frac),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
|
||||
&sum_refund_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
|
||||
&sum_refund_frac),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
|
||||
&sum_fee_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
|
||||
&sum_fee_frac),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
struct TALER_Amount sum_deposit;
|
||||
struct TALER_Amount sum_refund;
|
||||
@ -71,8 +50,6 @@ TEH_PG_aggregate (
|
||||
|
||||
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
|
||||
pg->aggregator_shift);
|
||||
|
||||
/* Used in #postgres_aggregate() */
|
||||
PREPARE (pg,
|
||||
"aggregate",
|
||||
"WITH dep AS (" /* restrict to our merchant and account and mark as done */
|
||||
@ -148,11 +125,35 @@ TEH_PG_aggregate (
|
||||
" FULL OUTER JOIN ref ON (FALSE)" /* We just want all sums */
|
||||
" FULL OUTER JOIN fees ON (FALSE);");
|
||||
|
||||
{
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_absolute_time (&now),
|
||||
GNUNET_PQ_query_param_auto_from_type (merchant_pub),
|
||||
GNUNET_PQ_query_param_auto_from_type (h_payto),
|
||||
GNUNET_PQ_query_param_auto_from_type (wtid),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_deposit_value",
|
||||
&sum_deposit_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_deposit_fraction",
|
||||
&sum_deposit_frac),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_refund_value",
|
||||
&sum_refund_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_refund_fraction",
|
||||
&sum_refund_frac),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_fee_value",
|
||||
&sum_fee_value),
|
||||
GNUNET_PQ_result_spec_uint64 ("sum_fee_fraction",
|
||||
&sum_fee_frac),
|
||||
GNUNET_PQ_result_spec_end
|
||||
};
|
||||
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"aggregate",
|
||||
params,
|
||||
rs);
|
||||
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||
"aggregate",
|
||||
params,
|
||||
rs);
|
||||
}
|
||||
if (qs < 0)
|
||||
{
|
||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||
@ -165,6 +166,18 @@ TEH_PG_aggregate (
|
||||
total));
|
||||
return qs;
|
||||
}
|
||||
{
|
||||
struct TALER_CoinDepositEventP rep = {
|
||||
.header.size = htons (sizeof (rep)),
|
||||
.header.type = htons (TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED),
|
||||
.merchant_pub = *merchant_pub
|
||||
};
|
||||
|
||||
TEH_PG_event_notify (pg,
|
||||
&rep.header,
|
||||
NULL,
|
||||
0);
|
||||
}
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_set_zero (pg->currency,
|
||||
&sum_deposit));
|
||||
|
@ -66,10 +66,25 @@ get_wire_accounts_cb (void *cls,
|
||||
for (unsigned int i = 0; i < num_results; i++)
|
||||
{
|
||||
char *payto_uri;
|
||||
char *conversion_url = NULL;
|
||||
json_t *debit_restrictions = NULL;
|
||||
json_t *credit_restrictions = NULL;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||
GNUNET_PQ_result_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
GNUNET_PQ_result_spec_string ("conversion_url",
|
||||
&conversion_url),
|
||||
NULL),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
TALER_PQ_result_spec_json ("debit_restrictions",
|
||||
&debit_restrictions),
|
||||
NULL),
|
||||
GNUNET_PQ_result_spec_allow_null (
|
||||
TALER_PQ_result_spec_json ("credit_restrictions",
|
||||
&credit_restrictions),
|
||||
NULL),
|
||||
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
||||
&master_sig),
|
||||
GNUNET_PQ_result_spec_end
|
||||
@ -84,8 +99,21 @@ get_wire_accounts_cb (void *cls,
|
||||
ctx->status = GNUNET_SYSERR;
|
||||
return;
|
||||
}
|
||||
if (NULL == debit_restrictions)
|
||||
{
|
||||
debit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != debit_restrictions);
|
||||
}
|
||||
if (NULL == credit_restrictions)
|
||||
{
|
||||
credit_restrictions = json_array ();
|
||||
GNUNET_assert (NULL != credit_restrictions);
|
||||
}
|
||||
ctx->cb (ctx->cb_cls,
|
||||
payto_uri,
|
||||
conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
&master_sig);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
}
|
||||
@ -112,6 +140,9 @@ TEH_PG_get_wire_accounts (void *cls,
|
||||
"get_wire_accounts",
|
||||
"SELECT"
|
||||
" payto_uri"
|
||||
",conversion_url"
|
||||
",debit_restrictions"
|
||||
",credit_restrictions"
|
||||
",master_sig"
|
||||
" FROM wire_accounts"
|
||||
" WHERE is_active");
|
||||
@ -123,5 +154,4 @@ TEH_PG_get_wire_accounts (void *cls,
|
||||
if (GNUNET_OK != ctx.status)
|
||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||
return qs;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022, 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
|
||||
@ -29,12 +29,20 @@
|
||||
enum GNUNET_DB_QueryStatus
|
||||
TEH_PG_insert_wire (void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp start_date,
|
||||
const struct TALER_MasterSignatureP *master_sig)
|
||||
{
|
||||
struct PostgresClosure *pg = cls;
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
NULL == conversion_url
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_string (conversion_url),
|
||||
TALER_PQ_query_param_json (debit_restrictions),
|
||||
TALER_PQ_query_param_json (credit_restrictions),
|
||||
GNUNET_PQ_query_param_auto_from_type (master_sig),
|
||||
GNUNET_PQ_query_param_timestamp (&start_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
@ -44,11 +52,14 @@ TEH_PG_insert_wire (void *cls,
|
||||
"insert_wire",
|
||||
"INSERT INTO wire_accounts "
|
||||
"(payto_uri"
|
||||
",conversion_url"
|
||||
",debit_restrictions"
|
||||
",credit_restrictions"
|
||||
",master_sig"
|
||||
",is_active"
|
||||
",last_change"
|
||||
") VALUES "
|
||||
"($1, $2, true, $3);");
|
||||
"($1, $2, $3, $4, $5, true, $6);");
|
||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"insert_wire",
|
||||
params);
|
||||
|
@ -29,6 +29,9 @@
|
||||
*
|
||||
* @param cls closure
|
||||
* @param payto_uri wire account of the exchange
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account
|
||||
* @param start_date date when the account was added by the offline system
|
||||
* (only to be used for replay detection)
|
||||
* @param master_sig public signature affirming the existence of the account,
|
||||
@ -38,6 +41,9 @@
|
||||
enum GNUNET_DB_QueryStatus
|
||||
TEH_PG_insert_wire (void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp start_date,
|
||||
const struct TALER_MasterSignatureP *master_sig);
|
||||
|
||||
|
@ -1118,9 +1118,9 @@ lrbt_cb_table_refresh_transfer_keys (void *cls,
|
||||
ctx->error = true;
|
||||
return;
|
||||
}
|
||||
memcpy (&td.details.refresh_transfer_keys.tprivs[0],
|
||||
tpriv,
|
||||
tpriv_size);
|
||||
GNUNET_memcpy (&td.details.refresh_transfer_keys.tprivs[0],
|
||||
tpriv,
|
||||
tpriv_size);
|
||||
ctx->cb (ctx->cb_cls,
|
||||
&td);
|
||||
GNUNET_PQ_cleanup_result (rs);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2022 Taler Systems SA
|
||||
Copyright (C) 2022, 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
|
||||
@ -29,6 +29,9 @@
|
||||
enum GNUNET_DB_QueryStatus
|
||||
TEH_PG_update_wire (void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp change_date,
|
||||
bool enabled)
|
||||
{
|
||||
@ -36,17 +39,28 @@ TEH_PG_update_wire (void *cls,
|
||||
struct GNUNET_PQ_QueryParam params[] = {
|
||||
GNUNET_PQ_query_param_string (payto_uri),
|
||||
GNUNET_PQ_query_param_bool (enabled),
|
||||
NULL == conversion_url
|
||||
? GNUNET_PQ_query_param_null ()
|
||||
: GNUNET_PQ_query_param_string (conversion_url),
|
||||
enabled
|
||||
? TALER_PQ_query_param_json (debit_restrictions)
|
||||
: GNUNET_PQ_query_param_null (),
|
||||
enabled
|
||||
? TALER_PQ_query_param_json (credit_restrictions)
|
||||
: GNUNET_PQ_query_param_null (),
|
||||
GNUNET_PQ_query_param_timestamp (&change_date),
|
||||
GNUNET_PQ_query_param_end
|
||||
};
|
||||
|
||||
/* used in #postgres_update_wire() */
|
||||
PREPARE (pg,
|
||||
"update_wire",
|
||||
"UPDATE wire_accounts"
|
||||
" SET"
|
||||
" is_active=$2"
|
||||
" ,last_change=$3"
|
||||
" ,conversion_url=$3"
|
||||
" ,debit_restrictions=$4"
|
||||
" ,credit_restrictions=$5"
|
||||
" ,last_change=$6"
|
||||
" WHERE payto_uri=$1");
|
||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||
"update_wire",
|
||||
|
@ -24,11 +24,16 @@
|
||||
#include "taler_util.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_exchangedb_plugin.h"
|
||||
|
||||
|
||||
/**
|
||||
* Update information about a wire account of the exchange.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param payto_uri account the update is about
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account; NULL allowed if not @a enabled
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account; NULL allowed if not @a enabled
|
||||
* @param change_date date when the account status was last changed
|
||||
* (only to be used for replay detection)
|
||||
* @param enabled true to enable, false to disable (the actual change)
|
||||
@ -37,6 +42,9 @@
|
||||
enum GNUNET_DB_QueryStatus
|
||||
TEH_PG_update_wire (void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp change_date,
|
||||
bool enabled);
|
||||
|
||||
|
@ -207,10 +207,10 @@ libtaler_extension_age_restriction_init (void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mask.bits = TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK;
|
||||
if (NULL == groups)
|
||||
groups = GNUNET_strdup (TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS);
|
||||
|
||||
if ((groups != NULL) &&
|
||||
(GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
|
||||
if (GNUNET_OK != TALER_parse_age_group_string (groups, &mask))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"[age restriction] couldn't parse age groups: '%s'\n",
|
||||
|
@ -111,6 +111,7 @@ struct TALER_BANK_AdminAddIncomingHandle;
|
||||
* @param timestamp time when the transaction was made.
|
||||
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
|
||||
*/
|
||||
// FIXME: bad API
|
||||
typedef void
|
||||
(*TALER_BANK_AdminAddIncomingCallback) (
|
||||
void *cls,
|
||||
@ -199,6 +200,7 @@ struct TALER_BANK_TransferHandle;
|
||||
* @param row_id unique ID of the wire transfer in the bank's records
|
||||
* @param timestamp when did the transaction go into effect
|
||||
*/
|
||||
// FIXME: bad API
|
||||
typedef void
|
||||
(*TALER_BANK_TransferCallback)(
|
||||
void *cls,
|
||||
@ -337,7 +339,7 @@ struct TALER_BANK_CreditHistoryResponse
|
||||
*/
|
||||
unsigned int details_length;
|
||||
|
||||
} success;
|
||||
} ok;
|
||||
|
||||
} details;
|
||||
|
||||
@ -493,7 +495,7 @@ struct TALER_BANK_DebitHistoryResponse
|
||||
*/
|
||||
unsigned int details_length;
|
||||
|
||||
} success;
|
||||
} ok;
|
||||
|
||||
} details;
|
||||
|
||||
|
@ -5512,6 +5512,9 @@ TALER_exchange_offline_global_fee_verify (
|
||||
* Create wire account addition signature.
|
||||
*
|
||||
* @param payto_uri bank account
|
||||
* @param conversion_url URL of the conversion service, or NULL if none
|
||||
* @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param now timestamp to use for the signature (rounded)
|
||||
* @param master_priv private key to sign with
|
||||
* @param[out] master_sig where to write the signature
|
||||
@ -5519,6 +5522,9 @@ TALER_exchange_offline_global_fee_verify (
|
||||
void
|
||||
TALER_exchange_offline_wire_add_sign (
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp now,
|
||||
const struct TALER_MasterPrivateKeyP *master_priv,
|
||||
struct TALER_MasterSignatureP *master_sig);
|
||||
@ -5528,6 +5534,9 @@ TALER_exchange_offline_wire_add_sign (
|
||||
* Verify wire account addition signature.
|
||||
*
|
||||
* @param payto_uri bank account
|
||||
* @param conversion_url URL of the conversion service, or NULL if none
|
||||
* @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param sign_time timestamp when signature was created
|
||||
* @param master_pub public key to verify against
|
||||
* @param master_sig the signature the signature
|
||||
@ -5536,6 +5545,9 @@ TALER_exchange_offline_wire_add_sign (
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_exchange_offline_wire_add_verify (
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp sign_time,
|
||||
const struct TALER_MasterPublicKeyP *master_pub,
|
||||
const struct TALER_MasterSignatureP *master_sig);
|
||||
@ -5578,6 +5590,9 @@ TALER_exchange_offline_wire_del_verify (
|
||||
* Check the signature in @a master_sig.
|
||||
*
|
||||
* @param payto_uri URI that is signed
|
||||
* @param conversion_url URL of the conversion service, or NULL if none
|
||||
* @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param master_pub master public key of the exchange
|
||||
* @param master_sig signature of the exchange
|
||||
* @return #GNUNET_OK if signature is valid
|
||||
@ -5585,6 +5600,9 @@ TALER_exchange_offline_wire_del_verify (
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_exchange_wire_signature_check (
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
const struct TALER_MasterPublicKeyP *master_pub,
|
||||
const struct TALER_MasterSignatureP *master_sig);
|
||||
|
||||
@ -5593,12 +5611,18 @@ TALER_exchange_wire_signature_check (
|
||||
* Create a signed wire statement for the given account.
|
||||
*
|
||||
* @param payto_uri account specification
|
||||
* @param conversion_url URL of the conversion service, or NULL if none
|
||||
* @param debit_restrictions JSON encoding of debit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param credit_restrictions JSON encoding of credit restrictions on the account; see AccountRestriction in the spec
|
||||
* @param master_priv private key to sign with
|
||||
* @param[out] master_sig where to write the signature
|
||||
*/
|
||||
void
|
||||
TALER_exchange_wire_signature_make (
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
const struct TALER_MasterPrivateKeyP *master_priv,
|
||||
struct TALER_MasterSignatureP *master_sig);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -149,7 +149,25 @@ struct TALER_EXCHANGEDB_DenominationKeyInformation
|
||||
GNUNET_NETWORK_STRUCT_BEGIN
|
||||
|
||||
/**
|
||||
* Signature of events signalling a reserve got funding.
|
||||
* Events signalling that a coin deposit status
|
||||
* changed.
|
||||
*/
|
||||
struct TALER_CoinDepositEventP
|
||||
{
|
||||
/**
|
||||
* Of type #TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED.
|
||||
*/
|
||||
struct GNUNET_DB_EventHeaderP header;
|
||||
|
||||
/**
|
||||
* Public key of the merchant.
|
||||
*/
|
||||
struct TALER_MerchantPublicKeyP merchant_pub;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Events signalling a reserve got funding.
|
||||
*/
|
||||
struct TALER_ReserveEventP
|
||||
{
|
||||
@ -2881,6 +2899,9 @@ typedef enum GNUNET_GenericReturnValue
|
||||
*
|
||||
* @param cls closure
|
||||
* @param payto_uri the exchange bank account URI
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account
|
||||
* @param master_sig master key signature affirming that this is a bank
|
||||
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
||||
*/
|
||||
@ -2888,6 +2909,9 @@ typedef void
|
||||
(*TALER_EXCHANGEDB_WireAccountCallback)(
|
||||
void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
const json_t *debit_restrictions,
|
||||
const json_t *credit_restrictions,
|
||||
const struct TALER_MasterSignatureP *master_sig);
|
||||
|
||||
|
||||
@ -5544,6 +5568,9 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
*
|
||||
* @param cls closure
|
||||
* @param payto_uri wire account of the exchange
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account
|
||||
* @param start_date date when the account was added by the offline system
|
||||
* (only to be used for replay detection)
|
||||
* @param master_sig public signature affirming the existence of the account,
|
||||
@ -5553,6 +5580,9 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*insert_wire)(void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp start_date,
|
||||
const struct TALER_MasterSignatureP *master_sig);
|
||||
|
||||
@ -5562,6 +5592,9 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
*
|
||||
* @param cls closure
|
||||
* @param payto_uri account the update is about
|
||||
* @param conversion_url URL of a conversion service, NULL if there is no conversion
|
||||
* @param debit_restrictions JSON array with debit restrictions on the account; NULL allowed if not @a enabled
|
||||
* @param credit_restrictions JSON array with credit restrictions on the account; NULL allowed if not @a enabled
|
||||
* @param change_date date when the account status was last changed
|
||||
* (only to be used for replay detection)
|
||||
* @param enabled true to enable, false to disable (the actual change)
|
||||
@ -5570,6 +5603,9 @@ struct TALER_EXCHANGEDB_Plugin
|
||||
enum GNUNET_DB_QueryStatus
|
||||
(*update_wire)(void *cls,
|
||||
const char *payto_uri,
|
||||
const char *conversion_url,
|
||||
json_t *debit_restrictions,
|
||||
json_t *credit_restrictions,
|
||||
struct GNUNET_TIME_Timestamp change_date,
|
||||
bool enabled);
|
||||
|
||||
|
@ -344,10 +344,6 @@ TALER_extensions_verify_manifests_signature (
|
||||
* The default age mask represents the age groups
|
||||
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
|
||||
*/
|
||||
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_MASK (1 | 1 << 8 | 1 << 10 \
|
||||
| 1 << 12 | 1 << 14 \
|
||||
| 1 << 16 | 1 << 18 \
|
||||
| 1 << 21)
|
||||
#define TALER_EXTENSION_AGE_RESTRICTION_DEFAULT_AGE_GROUPS "8:10:12:14:16:18:21"
|
||||
|
||||
|
||||
|
@ -674,33 +674,6 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s,
|
||||
struct TALER_MerchantWireHashP *hc);
|
||||
|
||||
|
||||
/**
|
||||
* Check the signature in @a wire_s. Also performs rudimentary
|
||||
* checks on the account data *if* supported.
|
||||
*
|
||||
* @param wire_s signed wire information of an exchange
|
||||
* @param master_pub master public key of the exchange
|
||||
* @return #GNUNET_OK if signature is valid
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_JSON_exchange_wire_signature_check (
|
||||
const json_t *wire_s,
|
||||
const struct TALER_MasterPublicKeyP *master_pub);
|
||||
|
||||
|
||||
/**
|
||||
* Create a signed wire statement for the given account.
|
||||
*
|
||||
* @param payto_uri account specification
|
||||
* @param master_priv private key to sign with
|
||||
* @return NULL if @a payto_uri is malformed
|
||||
*/
|
||||
json_t *
|
||||
TALER_JSON_exchange_wire_signature_make (
|
||||
const char *payto_uri,
|
||||
const struct TALER_MasterPrivateKeyP *master_priv);
|
||||
|
||||
|
||||
/**
|
||||
* Extract a string from @a object under the field @a field, but respecting
|
||||
* the Taler i18n rules and the language preferences expressed in @a
|
||||
|
@ -437,7 +437,47 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
|
||||
|
||||
|
||||
/**
|
||||
* Extract fixed-size base32crockford encoded data from request.
|
||||
* Extract optional "timeout_ms" argument from request.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout,
|
||||
* the current time plus the value given under "timeout_ms" otherwise
|
||||
* @return #GNUNET_OK on success, #GNUNET_NO if an
|
||||
* error was returned on @a connection (caller should return #MHD_YES) and
|
||||
* #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO)
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection,
|
||||
struct GNUNET_TIME_Absolute *expiration);
|
||||
|
||||
|
||||
/**
|
||||
* Extract optional "timeout_ms" argument from request.
|
||||
* Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms"
|
||||
* argument existed but failed to parse.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout,
|
||||
* the current time plus the value given under "timeout_ms" otherwise
|
||||
*/
|
||||
#define TALER_MHD_parse_request_timeout(connection,expiration) \
|
||||
do { \
|
||||
switch (TALER_MHD_parse_request_arg_timeout (connection, \
|
||||
expiration)) \
|
||||
{ \
|
||||
case GNUNET_SYSERR: \
|
||||
GNUNET_break (0); \
|
||||
return MHD_NO; \
|
||||
case GNUNET_NO: \
|
||||
GNUNET_break_op (0); \
|
||||
case GNUNET_OK: \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Extract fixed-size base32crockford encoded data from request argument.
|
||||
*
|
||||
* Queues an error response to the connection if the parameter is missing or
|
||||
* invalid.
|
||||
@ -446,16 +486,195 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
|
||||
* @param param_name the name of the parameter with the key
|
||||
* @param[out] out_data pointer to store the result
|
||||
* @param out_size expected size of @a out_data
|
||||
* @param[out] present set to true if argument was found
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_NO if the argument is malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
|
||||
const char *param_name,
|
||||
void *out_data,
|
||||
size_t out_size);
|
||||
size_t out_size,
|
||||
bool *present);
|
||||
|
||||
|
||||
/**
|
||||
* Extract fixed-size base32crockford encoded data from request header.
|
||||
*
|
||||
* Queues an error response to the connection if the parameter is missing or
|
||||
* invalid.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param header_name the name of the HTTP header with the value
|
||||
* @param[out] out_data pointer to store the result
|
||||
* @param out_size expected size of @a out_data
|
||||
* @param[out] present set to true if argument was found
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
|
||||
const char *header_name,
|
||||
void *out_data,
|
||||
size_t out_size,
|
||||
bool *present);
|
||||
|
||||
/**
|
||||
* Extract fixed-size base32crockford encoded data from request.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param name the name of the parameter with the key
|
||||
* @param[out] val pointer to store the result, type must determine size
|
||||
* @param[in,out] required pass true to require presence of this argument; if 'false'
|
||||
* set to true if the argument was found
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
#define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \
|
||||
do { \
|
||||
bool p; \
|
||||
switch (TALER_MHD_parse_request_arg_data (connection, name, \
|
||||
val, sizeof (*val), &p)) \
|
||||
{ \
|
||||
case GNUNET_SYSERR: \
|
||||
GNUNET_break (0); \
|
||||
return MHD_NO; \
|
||||
case GNUNET_NO: \
|
||||
GNUNET_break_op (0); \
|
||||
return MHD_YES; \
|
||||
case GNUNET_OK: \
|
||||
if (required & (! p)) \
|
||||
return TALER_MHD_reply_with_error ( \
|
||||
connection, \
|
||||
MHD_HTTP_BAD_REQUEST, \
|
||||
TALER_EC_GENERIC_PARAMETER_MISSING, \
|
||||
name); \
|
||||
required = p; \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Extract required fixed-size base32crockford encoded data from request.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param name the name of the parameter with the key
|
||||
* @param[out] val pointer to store the result, type must determine size
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
#define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \
|
||||
do { \
|
||||
bool b = true; \
|
||||
TALER_MHD_parse_request_arg_auto (connection,name,val,b); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Extract fixed-size base32crockford encoded data from request.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param name the name of the header with the key
|
||||
* @param[out] val pointer to store the result, type must determine size
|
||||
* @param[in,out] required pass true to require presence of this argument; if 'false'
|
||||
* set to true if the argument was found
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
#define TALER_MHD_parse_request_header_auto(connection,name,val,required) \
|
||||
do { \
|
||||
bool p; \
|
||||
switch (TALER_MHD_parse_request_header_data (connection, name, \
|
||||
val, sizeof (*val), &p)) \
|
||||
{ \
|
||||
case GNUNET_SYSERR: \
|
||||
GNUNET_break (0); \
|
||||
return MHD_NO; \
|
||||
case GNUNET_NO: \
|
||||
GNUNET_break_op (0); \
|
||||
return MHD_YES; \
|
||||
case GNUNET_OK: \
|
||||
if (required & (! p)) \
|
||||
return TALER_MHD_reply_with_error ( \
|
||||
connection, \
|
||||
MHD_HTTP_BAD_REQUEST, \
|
||||
TALER_EC_GENERIC_PARAMETER_MISSING, \
|
||||
name); \
|
||||
required = p; \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Extract required fixed-size base32crockford encoded data from request.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param name the name of the header with the key
|
||||
* @param[out] val pointer to store the result, type must determine size
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
#define TALER_MHD_parse_request_header_auto_t(connection,name,val) \
|
||||
do { \
|
||||
bool b = true; \
|
||||
TALER_MHD_parse_request_header_auto (connection,name,val,b); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Check that the 'Content-Length' header is giving
|
||||
* a length below @a max_len. If not, return an
|
||||
* appropriate error response and return the
|
||||
* correct #MHD_YES/#MHD_NO value from this function.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param max_len maximum allowed content length
|
||||
* @return
|
||||
* #GNUNET_YES if the the argument is present
|
||||
* #GNUNET_NO if the argument is absent or malformed
|
||||
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||
*/
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
|
||||
unsigned long long max_len);
|
||||
|
||||
|
||||
/**
|
||||
* Check that the 'Content-Length' header is giving
|
||||
* a length below @a max_len. If not, return an
|
||||
* appropriate error response and return the
|
||||
* correct #MHD_YES/#MHD_NO value from this function.
|
||||
*
|
||||
* @param connection the MHD connection
|
||||
* @param max_len maximum allowed content length
|
||||
*/
|
||||
#define TALER_MHD_check_content_length(connection,max_len) \
|
||||
do { \
|
||||
switch (TALER_MHD_check_content_length_ (connection, max_len)) \
|
||||
{ \
|
||||
case GNUNET_SYSERR: \
|
||||
GNUNET_break (0); \
|
||||
return MHD_NO; \
|
||||
case GNUNET_NO: \
|
||||
GNUNET_break_op (0); \
|
||||
return MHD_YES; \
|
||||
case GNUNET_OK: \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define TALER_TEMPLATING_LIB_H
|
||||
|
||||
#include <microhttpd.h>
|
||||
|
||||
#include "taler_mhd_lib.h"
|
||||
|
||||
/**
|
||||
* Fill in Mustach template @a tmpl using the data from @a root
|
||||
@ -91,6 +91,26 @@ TALER_TEMPLATING_reply (struct MHD_Connection *connection,
|
||||
const char *taler_uri,
|
||||
const json_t *root);
|
||||
|
||||
|
||||
/**
|
||||
* Load a @a template and substitute an error message based on @a ec and @a
|
||||
* detail, returning the result to the @a connection with the given @a
|
||||
* http_status code.
|
||||
*
|
||||
* @param connection the connection we act upon
|
||||
* @param template_basename basename of the template to load
|
||||
* @param http_status code to use on success
|
||||
* @param ec error code to return
|
||||
* @param detail optional text to add to the template
|
||||
* @return #MHD_YES on success, #MHD_NO to just close the connection
|
||||
*/
|
||||
MHD_RESULT
|
||||
TALER_TEMPLATING_reply_error (struct MHD_Connection *connection,
|
||||
const char *template_basename,
|
||||
unsigned int http_status,
|
||||
enum TALER_ErrorCode ec,
|
||||
const char *detail);
|
||||
|
||||
/**
|
||||
* Preload templates.
|
||||
*
|
||||
|
@ -147,15 +147,11 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
|
||||
*
|
||||
* @param cls closure, typically, the "run" method containing
|
||||
* all the commands to be run, and a closure for it.
|
||||
* @param hr http response details
|
||||
* @param keys the exchange's keys.
|
||||
* @param compat protocol compatibility information.
|
||||
* @param kr response details
|
||||
*/
|
||||
void
|
||||
TALER_TESTING_cert_cb (void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_EXCHANGE_Keys *keys,
|
||||
enum TALER_EXCHANGE_VersionCompatibility compat);
|
||||
const struct TALER_EXCHANGE_KeysResponse *kr);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2021 Taler Systems SA
|
||||
Copyright (C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -229,6 +229,16 @@ void
|
||||
TALER_OS_init (void);
|
||||
|
||||
|
||||
/**
|
||||
* Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2).
|
||||
*
|
||||
* @param[in,out] inp pointer to string to re-encode
|
||||
* @return number of bytes in resulting @a inp
|
||||
*/
|
||||
size_t
|
||||
TALER_rfc8785encode (char **inp);
|
||||
|
||||
|
||||
/**
|
||||
* URL-encode a string according to rfc3986.
|
||||
*
|
||||
@ -559,6 +569,58 @@ enum GNUNET_GenericReturnValue
|
||||
TALER_JSON_parse_age_groups (const json_t *root,
|
||||
struct TALER_AgeMask *mask);
|
||||
|
||||
|
||||
/**
|
||||
* Handle to an external process that will assist
|
||||
* with some JSON-to-JSON conversion.
|
||||
*/
|
||||
struct TALER_JSON_ExternalConversion;
|
||||
|
||||
/**
|
||||
* Type of a callback that receives a JSON @a result.
|
||||
*
|
||||
* @param cls closure
|
||||
* @param status_type how did the process die
|
||||
* @param code termination status code from the process
|
||||
* @param result some JSON result, NULL if we failed to get an JSON output
|
||||
*/
|
||||
typedef void
|
||||
(*TALER_JSON_JsonCallback) (void *cls,
|
||||
enum GNUNET_OS_ProcessStatusType status_type,
|
||||
unsigned long code,
|
||||
const json_t *result);
|
||||
|
||||
|
||||
/**
|
||||
* Launch some external helper @a binary to convert some @a input
|
||||
* and eventually call @a cb with the result.
|
||||
*
|
||||
* @param input JSON to serialize and pass to the helper process
|
||||
* @param cb function to call on the result
|
||||
* @param cb_cls closure for @a cb
|
||||
* @param binary name of the binary to execute
|
||||
* @param ... NULL-terminated list of arguments for the @a binary,
|
||||
* usually starting with again the name of the binary
|
||||
* @return handle to cancel the operation (and kill the helper)
|
||||
*/
|
||||
struct TALER_JSON_ExternalConversion *
|
||||
TALER_JSON_external_conversion_start (const json_t *input,
|
||||
TALER_JSON_JsonCallback cb,
|
||||
void *cb_cls,
|
||||
const char *binary,
|
||||
...);
|
||||
|
||||
/**
|
||||
* Abort external conversion, killing the process and preventing
|
||||
* the callback from being called. Must not be called after the
|
||||
* callback was invoked.
|
||||
*
|
||||
* @param[in] ec external conversion handle to cancel
|
||||
*/
|
||||
void
|
||||
TALER_JSON_external_conversion_stop (
|
||||
struct TALER_JSON_ExternalConversion *ec);
|
||||
|
||||
#undef __TALER_UTIL_LIB_H_INSIDE__
|
||||
|
||||
#endif
|
||||
|
@ -28,12 +28,10 @@ libtalerjson_la_LIBADD = \
|
||||
$(XLIB)
|
||||
|
||||
TESTS = \
|
||||
test_json \
|
||||
test_json_wire
|
||||
test_json
|
||||
|
||||
check_PROGRAMS= \
|
||||
test_json \
|
||||
test_json_wire
|
||||
test_json
|
||||
|
||||
test_json_SOURCES = \
|
||||
test_json.c
|
||||
@ -43,13 +41,3 @@ test_json_LDADD = \
|
||||
$(top_builddir)/src/util/libtalerutil.la \
|
||||
-lgnunetutil \
|
||||
-ljansson
|
||||
|
||||
|
||||
test_json_wire_SOURCES = \
|
||||
test_json_wire.c
|
||||
test_json_wire_LDADD = \
|
||||
$(top_builddir)/src/json/libtalerjson.la \
|
||||
-lgnunetjson \
|
||||
$(top_builddir)/src/util/libtalerutil.la \
|
||||
-lgnunetutil \
|
||||
-ljansson
|
||||
|
169
src/json/json.c
169
src/json/json.c
@ -61,170 +61,6 @@ contains_real (const json_t *json)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump character in the low range into @a buf
|
||||
* following RFC 8785.
|
||||
*
|
||||
* @param[in,out] buf buffer to modify
|
||||
* @param val value to dump
|
||||
*/
|
||||
static void
|
||||
lowdump (struct GNUNET_Buffer *buf,
|
||||
unsigned char val)
|
||||
{
|
||||
char scratch[7];
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case 0x8:
|
||||
GNUNET_buffer_write (buf,
|
||||
"\\b",
|
||||
2);
|
||||
break;
|
||||
case 0x9:
|
||||
GNUNET_buffer_write (buf,
|
||||
"\\t",
|
||||
2);
|
||||
break;
|
||||
case 0xA:
|
||||
GNUNET_buffer_write (buf,
|
||||
"\\n",
|
||||
2);
|
||||
break;
|
||||
case 0xC:
|
||||
GNUNET_buffer_write (buf,
|
||||
"\\f",
|
||||
2);
|
||||
break;
|
||||
case 0xD:
|
||||
GNUNET_buffer_write (buf,
|
||||
"\\r",
|
||||
2);
|
||||
break;
|
||||
default:
|
||||
GNUNET_snprintf (scratch,
|
||||
sizeof (scratch),
|
||||
"\\u%04x",
|
||||
(unsigned int) val);
|
||||
GNUNET_buffer_write (buf,
|
||||
scratch,
|
||||
6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Re-encode string at @a inp to match RFC 8785 (section 3.2.2.2).
|
||||
*
|
||||
* @param[in,out] inp pointer to string to re-encode
|
||||
* @return number of bytes in resulting @a inp
|
||||
*/
|
||||
static size_t
|
||||
rfc8785encode (char **inp)
|
||||
{
|
||||
struct GNUNET_Buffer buf = { 0 };
|
||||
size_t left = strlen (*inp) + 1;
|
||||
size_t olen;
|
||||
char *in = *inp;
|
||||
const char *pos = in;
|
||||
|
||||
GNUNET_buffer_prealloc (&buf,
|
||||
left + 40);
|
||||
buf.warn_grow = 0; /* disable, + 40 is just a wild guess */
|
||||
while (1)
|
||||
{
|
||||
int mbl = u8_mblen ((unsigned char *) pos,
|
||||
left);
|
||||
unsigned char val;
|
||||
|
||||
if (0 == mbl)
|
||||
break;
|
||||
val = (unsigned char) *pos;
|
||||
if ( (1 == mbl) &&
|
||||
(val <= 0x1F) )
|
||||
{
|
||||
/* Should not happen, as input is produced by
|
||||
* JSON stringification */
|
||||
GNUNET_break (0);
|
||||
lowdump (&buf,
|
||||
val);
|
||||
}
|
||||
else if ( (1 == mbl) && ('\\' == *pos) )
|
||||
{
|
||||
switch (*(pos + 1))
|
||||
{
|
||||
case '\\':
|
||||
mbl = 2;
|
||||
GNUNET_buffer_write (&buf,
|
||||
pos,
|
||||
mbl);
|
||||
break;
|
||||
case 'u':
|
||||
{
|
||||
unsigned int num;
|
||||
uint32_t n32;
|
||||
unsigned char res[8];
|
||||
size_t rlen;
|
||||
|
||||
GNUNET_assert ( (1 ==
|
||||
sscanf (pos + 2,
|
||||
"%4x",
|
||||
&num)) ||
|
||||
(1 ==
|
||||
sscanf (pos + 2,
|
||||
"%4X",
|
||||
&num)) );
|
||||
mbl = 6;
|
||||
n32 = (uint32_t) num;
|
||||
rlen = sizeof (res);
|
||||
u32_to_u8 (&n32,
|
||||
1,
|
||||
res,
|
||||
&rlen);
|
||||
if ( (1 == rlen) &&
|
||||
(res[0] <= 0x1F) )
|
||||
{
|
||||
lowdump (&buf,
|
||||
res[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
GNUNET_buffer_write (&buf,
|
||||
(const char *) res,
|
||||
rlen);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
mbl = 2;
|
||||
GNUNET_buffer_write (&buf,
|
||||
pos,
|
||||
mbl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GNUNET_buffer_write (&buf,
|
||||
pos,
|
||||
mbl);
|
||||
}
|
||||
left -= mbl;
|
||||
pos += mbl;
|
||||
}
|
||||
|
||||
/* 0-terminate buffer */
|
||||
GNUNET_buffer_write (&buf,
|
||||
"",
|
||||
1);
|
||||
GNUNET_free (in);
|
||||
*inp = GNUNET_buffer_reap (&buf,
|
||||
&olen);
|
||||
return olen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump the @a json to a string and hash it.
|
||||
*
|
||||
@ -262,7 +98,7 @@ dump_and_hash (const json_t *json,
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
len = rfc8785encode (&wire_enc);
|
||||
len = TALER_rfc8785encode (&wire_enc);
|
||||
if (NULL == salt)
|
||||
{
|
||||
GNUNET_CRYPTO_hash (wire_enc,
|
||||
@ -819,6 +655,7 @@ parse_path (json_t *obj,
|
||||
json_t *next_obj = NULL;
|
||||
char *next_dot;
|
||||
|
||||
GNUNET_assert (NULL != id); /* make stupid compiler happy */
|
||||
if (NULL == next_id)
|
||||
{
|
||||
cb (cb_cls,
|
||||
@ -1031,7 +868,7 @@ TALER_JSON_canonicalize (const json_t *input)
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
rfc8785encode (&wire_enc);
|
||||
TALER_rfc8785encode (&wire_enc);
|
||||
return wire_enc;
|
||||
}
|
||||
|
||||
|
@ -70,80 +70,6 @@ TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s,
|
||||
}
|
||||
|
||||
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_JSON_exchange_wire_signature_check (
|
||||
const json_t *wire_s,
|
||||
const struct TALER_MasterPublicKeyP *master_pub)
|
||||
{
|
||||
const char *payto_uri;
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("payto_uri",
|
||||
&payto_uri),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
||||
&master_sig),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (wire_s,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
{
|
||||
char *err;
|
||||
|
||||
err = TALER_payto_validate (payto_uri);
|
||||
if (NULL != err)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
||||
"URI `%s' ill-formed: %s\n",
|
||||
payto_uri,
|
||||
err);
|
||||
GNUNET_free (err);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
return TALER_exchange_wire_signature_check (payto_uri,
|
||||
master_pub,
|
||||
&master_sig);
|
||||
}
|
||||
|
||||
|
||||
json_t *
|
||||
TALER_JSON_exchange_wire_signature_make (
|
||||
const char *payto_uri,
|
||||
const struct TALER_MasterPrivateKeyP *master_priv)
|
||||
{
|
||||
struct TALER_MasterSignatureP master_sig;
|
||||
char *err;
|
||||
|
||||
if (NULL !=
|
||||
(err = TALER_payto_validate (payto_uri)))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Invalid payto URI `%s': %s\n",
|
||||
payto_uri,
|
||||
err);
|
||||
GNUNET_free (err);
|
||||
return NULL;
|
||||
}
|
||||
TALER_exchange_wire_signature_make (payto_uri,
|
||||
master_priv,
|
||||
&master_sig);
|
||||
return GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_string ("payto_uri",
|
||||
payto_uri),
|
||||
GNUNET_JSON_pack_data_auto ("master_sig",
|
||||
&master_sig));
|
||||
}
|
||||
|
||||
|
||||
char *
|
||||
TALER_JSON_wire_to_payto (const json_t *wire_s)
|
||||
{
|
||||
|
@ -1,80 +0,0 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015, 2016 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 json/test_json_wire.c
|
||||
* @brief Tests for Taler-specific crypto logic
|
||||
* @author Christian Grothoff <christian@grothoff.org>
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include "taler_util.h"
|
||||
#include "taler_json_lib.h"
|
||||
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
const char *const argv[])
|
||||
{
|
||||
struct TALER_MasterPublicKeyP master_pub;
|
||||
struct TALER_MasterPrivateKeyP master_priv;
|
||||
json_t *wire_xtalerbank;
|
||||
json_t *wire_iban;
|
||||
const char *payto_xtalerbank = "payto://x-taler-bank/42";
|
||||
const char *payto_iban =
|
||||
"payto://iban/BIC-TO-BE-SKIPPED/DE89370400440532013000?receiver-name=Test";
|
||||
char *p_xtalerbank;
|
||||
char *p_iban;
|
||||
|
||||
(void) argc;
|
||||
(void) argv;
|
||||
GNUNET_log_setup ("test-json-wire",
|
||||
"WARNING",
|
||||
NULL);
|
||||
GNUNET_CRYPTO_eddsa_key_create (&master_priv.eddsa_priv);
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv,
|
||||
&master_pub.eddsa_pub);
|
||||
wire_xtalerbank = TALER_JSON_exchange_wire_signature_make (payto_xtalerbank,
|
||||
&master_priv);
|
||||
wire_iban = TALER_JSON_exchange_wire_signature_make (payto_iban,
|
||||
&master_priv);
|
||||
if (NULL == wire_iban)
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Could not parse payto/IBAN (%s) into 'wire object'\n",
|
||||
payto_iban);
|
||||
return 1;
|
||||
}
|
||||
p_xtalerbank = TALER_JSON_wire_to_payto (wire_xtalerbank);
|
||||
p_iban = TALER_JSON_wire_to_payto (wire_iban);
|
||||
GNUNET_assert (0 == strcmp (p_xtalerbank, payto_xtalerbank));
|
||||
GNUNET_assert (0 == strcmp (p_iban, payto_iban));
|
||||
GNUNET_free (p_xtalerbank);
|
||||
GNUNET_free (p_iban);
|
||||
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_exchange_wire_signature_check (wire_xtalerbank,
|
||||
&master_pub));
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_JSON_exchange_wire_signature_check (wire_iban,
|
||||
&master_pub));
|
||||
json_decref (wire_xtalerbank);
|
||||
json_decref (wire_iban);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* end of test_json_wire.c */
|
@ -16,7 +16,11 @@ pkgcfg_DATA = \
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(pkgcfg_DATA) \
|
||||
sample.conf
|
||||
sample.conf \
|
||||
persona-sample-reply.json
|
||||
|
||||
bin_SCRIPTS = \
|
||||
taler-exchange-kyc-persona-converter.sh
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libtalerkyclogic.la
|
||||
|
@ -29,6 +29,10 @@ KYC_PERSONA_SUBDOMAIN = taler
|
||||
# Authentication token to use.
|
||||
KYC_PERSONA_AUTH_TOKEN = persona_sandbox_42
|
||||
|
||||
# Program that converts Persona KYC data into the
|
||||
# GNU Taler format.
|
||||
KYC_PERSONA_CONVERTER_HELPER = taler-exchange-kyc-persona-converter.sh
|
||||
|
||||
# Form to use.
|
||||
KYC_PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
|
||||
|
||||
|
@ -780,10 +780,11 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
|
||||
TALER_KYCLOGIC_kyc_done ();
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
qsort (kyc_triggers,
|
||||
num_kyc_triggers,
|
||||
sizeof (struct TALER_KYCLOGIC_KycTrigger *),
|
||||
&sort_by_timeframe);
|
||||
if (0 != num_kyc_triggers)
|
||||
qsort (kyc_triggers,
|
||||
num_kyc_triggers,
|
||||
sizeof (struct TALER_KYCLOGIC_KycTrigger *),
|
||||
&sort_by_timeframe);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
@ -490,8 +490,6 @@ initiate_task (void *cls)
|
||||
struct PluginState *ps = pd->ps;
|
||||
char *hps;
|
||||
char *url;
|
||||
char *redirect_uri;
|
||||
char *redirect_uri_encoded;
|
||||
char legi_s[42];
|
||||
|
||||
ih->task = NULL;
|
||||
@ -501,19 +499,27 @@ initiate_task (void *cls)
|
||||
(unsigned long long) ih->legitimization_uuid);
|
||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
||||
sizeof (ih->h_payto));
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%skyc-proof/%s?state=%s",
|
||||
ps->exchange_base_url,
|
||||
pd->section,
|
||||
hps);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
GNUNET_asprintf (&url,
|
||||
"%s?response_type=code&client_id=%s&redirect_uri=%s",
|
||||
pd->login_url,
|
||||
pd->client_id,
|
||||
redirect_uri_encoded);
|
||||
GNUNET_free (redirect_uri_encoded);
|
||||
{
|
||||
char *redirect_uri_encoded;
|
||||
|
||||
{
|
||||
char *redirect_uri;
|
||||
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%skyc-proof/%s?state=%s",
|
||||
ps->exchange_base_url,
|
||||
pd->section,
|
||||
hps);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
}
|
||||
GNUNET_asprintf (&url,
|
||||
"%s?response_type=code&client_id=%s&redirect_uri=%s",
|
||||
pd->login_url,
|
||||
pd->client_id,
|
||||
redirect_uri_encoded);
|
||||
GNUNET_free (redirect_uri_encoded);
|
||||
}
|
||||
/* FIXME-API: why do we *redirect* the client here,
|
||||
instead of making the HTTP request *ourselves*
|
||||
and forwarding the response? This prevents us
|
||||
@ -582,6 +588,37 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel KYC proof.
|
||||
*
|
||||
* @param[in] ph handle of operation to cancel
|
||||
*/
|
||||
static void
|
||||
oauth2_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
|
||||
{
|
||||
if (NULL != ph->task)
|
||||
{
|
||||
GNUNET_SCHEDULER_cancel (ph->task);
|
||||
ph->task = NULL;
|
||||
}
|
||||
if (NULL != ph->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (ph->job);
|
||||
ph->job = NULL;
|
||||
}
|
||||
if (NULL != ph->response)
|
||||
{
|
||||
MHD_destroy_response (ph->response);
|
||||
ph->response = NULL;
|
||||
}
|
||||
GNUNET_free (ph->provider_user_id);
|
||||
if (NULL != ph->attributes)
|
||||
json_decref (ph->attributes);
|
||||
GNUNET_free (ph->post_body);
|
||||
GNUNET_free (ph);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called to asynchronously return the final
|
||||
* result to the callback.
|
||||
@ -602,10 +639,8 @@ return_proof_response (void *cls)
|
||||
ph->attributes,
|
||||
ph->http_status,
|
||||
ph->response);
|
||||
GNUNET_free (ph->provider_user_id);
|
||||
if (NULL != ph->attributes)
|
||||
json_decref (ph->attributes);
|
||||
GNUNET_free (ph);
|
||||
ph->response = NULL; /*Ownership passed to 'ph->cb'!*/
|
||||
oauth2_proof_cancel (ph);
|
||||
}
|
||||
|
||||
|
||||
@ -1101,7 +1136,6 @@ oauth2_proof (void *cls,
|
||||
1));
|
||||
{
|
||||
char *client_id;
|
||||
char *redirect_uri;
|
||||
char *client_secret;
|
||||
char *authorization_code;
|
||||
char *redirect_uri_encoded;
|
||||
@ -1109,13 +1143,17 @@ oauth2_proof (void *cls,
|
||||
|
||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ph->h_payto,
|
||||
sizeof (ph->h_payto));
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%skyc-proof/%s?state=%s",
|
||||
ps->exchange_base_url,
|
||||
pd->section,
|
||||
hps);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
{
|
||||
char *redirect_uri;
|
||||
|
||||
GNUNET_asprintf (&redirect_uri,
|
||||
"%skyc-proof/%s?state=%s",
|
||||
ps->exchange_base_url,
|
||||
pd->section,
|
||||
hps);
|
||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||
GNUNET_free (redirect_uri);
|
||||
}
|
||||
GNUNET_assert (NULL != redirect_uri_encoded);
|
||||
client_id = curl_easy_escape (ph->eh,
|
||||
pd->client_id,
|
||||
@ -1164,34 +1202,6 @@ oauth2_proof (void *cls,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel KYC proof.
|
||||
*
|
||||
* @param[in] ph handle of operation to cancel
|
||||
*/
|
||||
static void
|
||||
oauth2_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
|
||||
{
|
||||
if (NULL != ph->task)
|
||||
{
|
||||
GNUNET_SCHEDULER_cancel (ph->task);
|
||||
ph->task = NULL;
|
||||
}
|
||||
if (NULL != ph->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (ph->job);
|
||||
ph->job = NULL;
|
||||
}
|
||||
if (NULL != ph->response)
|
||||
{
|
||||
MHD_destroy_response (ph->response);
|
||||
ph->response = NULL;
|
||||
}
|
||||
GNUNET_free (ph->post_body);
|
||||
GNUNET_free (ph);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function to asynchronously return the 404 not found
|
||||
* page for the webhook.
|
||||
|
@ -111,6 +111,12 @@ struct TALER_KYCLOGIC_ProviderDetails
|
||||
*/
|
||||
char *subdomain;
|
||||
|
||||
/**
|
||||
* Name of the program we use to convert outputs
|
||||
* from Persona into our JSON inputs.
|
||||
*/
|
||||
char *conversion_binary;
|
||||
|
||||
/**
|
||||
* Where to redirect the client upon completion.
|
||||
*/
|
||||
@ -230,6 +236,12 @@ struct TALER_KYCLOGIC_ProofHandle
|
||||
*/
|
||||
char *url;
|
||||
|
||||
/**
|
||||
* Handle to an external process that converts the
|
||||
* Persona response to our internal format.
|
||||
*/
|
||||
struct TALER_JSON_ExternalConversion *ec;
|
||||
|
||||
/**
|
||||
* Hash of the payto:// URI we are checking the KYC for.
|
||||
*/
|
||||
@ -246,6 +258,11 @@ struct TALER_KYCLOGIC_ProofHandle
|
||||
*/
|
||||
char *provider_user_id;
|
||||
|
||||
/**
|
||||
* Account ID from the service.
|
||||
*/
|
||||
char *account_id;
|
||||
|
||||
/**
|
||||
* Inquiry ID at the provider.
|
||||
*/
|
||||
@ -294,6 +311,11 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
*/
|
||||
char *inquiry_id;
|
||||
|
||||
/**
|
||||
* Account ID from the service.
|
||||
*/
|
||||
char *account_id;
|
||||
|
||||
/**
|
||||
* URL of the cURL request.
|
||||
*/
|
||||
@ -315,6 +337,12 @@ struct TALER_KYCLOGIC_WebhookHandle
|
||||
*/
|
||||
const char *template_id;
|
||||
|
||||
/**
|
||||
* Handle to an external process that converts the
|
||||
* Persona response to our internal format.
|
||||
*/
|
||||
struct TALER_JSON_ExternalConversion *ec;
|
||||
|
||||
/**
|
||||
* Our account ID.
|
||||
*/
|
||||
@ -344,6 +372,7 @@ persona_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
||||
GNUNET_free (pd->auth_token);
|
||||
GNUNET_free (pd->template_id);
|
||||
GNUNET_free (pd->subdomain);
|
||||
GNUNET_free (pd->conversion_binary);
|
||||
GNUNET_free (pd->salt);
|
||||
GNUNET_free (pd->section);
|
||||
GNUNET_free (pd->post_kyc_redirect_url);
|
||||
@ -418,6 +447,18 @@ persona_load_configuration (void *cls,
|
||||
persona_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||
provider_section_name,
|
||||
"KYC_PERSONA_CONVERTER_HELPER",
|
||||
&pd->conversion_binary))
|
||||
{
|
||||
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
|
||||
provider_section_name,
|
||||
"KYC_PERSONA_CONVERTER_HELPER");
|
||||
persona_unload_configuration (pd);
|
||||
return NULL;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||
provider_section_name,
|
||||
@ -838,8 +879,14 @@ persona_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
|
||||
GNUNET_CURL_job_cancel (ph->job);
|
||||
ph->job = NULL;
|
||||
}
|
||||
if (NULL != ph->ec)
|
||||
{
|
||||
TALER_JSON_external_conversion_stop (ph->ec);
|
||||
ph->ec = NULL;
|
||||
}
|
||||
GNUNET_free (ph->url);
|
||||
GNUNET_free (ph->provider_user_id);
|
||||
GNUNET_free (ph->account_id);
|
||||
GNUNET_free (ph->inquiry_id);
|
||||
GNUNET_free (ph);
|
||||
}
|
||||
@ -922,161 +969,6 @@ proof_reply_error (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert KYC attribute data from Persona response.
|
||||
*
|
||||
* @param attr json array with Persona attribute data
|
||||
* @return KYC attribute data
|
||||
*/
|
||||
static json_t *
|
||||
convert_attributes (const json_t *attr)
|
||||
{
|
||||
const char *country_code = NULL;
|
||||
const char *name_first = NULL;
|
||||
const char *name_middle = NULL;
|
||||
const char *name_last = NULL;
|
||||
const char *address_street_1 = NULL;
|
||||
const char *address_street_2 = NULL;
|
||||
const char *address_city = NULL;
|
||||
const char *address_postal_code = NULL;
|
||||
const char *birthdate = NULL;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("country-code",
|
||||
&country_code),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name-first",
|
||||
&name_first),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name-middle",
|
||||
&name_middle),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("name-last",
|
||||
&name_last),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address-street-1",
|
||||
&address_street_1),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address-street-2",
|
||||
&address_street_2),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address-city",
|
||||
&address_city),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("address-postal-code",
|
||||
&address_postal_code),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("birthdate",
|
||||
&birthdate),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
json_t *ret;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (attr,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
json_dumpf (attr,
|
||||
stderr,
|
||||
JSON_INDENT (2));
|
||||
return NULL;
|
||||
}
|
||||
{
|
||||
char *name = NULL;
|
||||
char *street = NULL;
|
||||
char *city = NULL;
|
||||
|
||||
if ( (NULL != name_last) ||
|
||||
(NULL != name_first) ||
|
||||
(NULL != name_middle) )
|
||||
{
|
||||
GNUNET_asprintf (&name,
|
||||
"%s, %s %s",
|
||||
(NULL != name_last)
|
||||
? name_last
|
||||
: "",
|
||||
(NULL != name_first)
|
||||
? name_first
|
||||
: "",
|
||||
(NULL != name_middle)
|
||||
? name_middle
|
||||
: "");
|
||||
}
|
||||
if ( (NULL != address_city) ||
|
||||
(NULL != address_postal_code) )
|
||||
{
|
||||
GNUNET_asprintf (&city,
|
||||
"%s%s%s %s",
|
||||
(NULL != country_code)
|
||||
? country_code
|
||||
: "",
|
||||
(NULL != country_code)
|
||||
? "-"
|
||||
: "",
|
||||
(NULL != address_postal_code)
|
||||
? address_postal_code
|
||||
: "",
|
||||
(NULL != address_city)
|
||||
? address_city
|
||||
: "");
|
||||
}
|
||||
if ( (NULL != address_street_1) ||
|
||||
(NULL != address_street_2) )
|
||||
{
|
||||
GNUNET_asprintf (&street,
|
||||
"%s%s%s",
|
||||
(NULL != address_street_1)
|
||||
? address_street_1
|
||||
: "",
|
||||
( (NULL != address_street_1) &&
|
||||
(NULL != address_street_2) )
|
||||
? "\n"
|
||||
: "",
|
||||
(NULL != address_street_2)
|
||||
? address_street_2
|
||||
: "");
|
||||
}
|
||||
ret = GNUNET_JSON_PACK (
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_BIRTHDATE,
|
||||
birthdate)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_FULL_NAME,
|
||||
name)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_ADDRESS_STREET,
|
||||
street)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_ADDRESS_CITY,
|
||||
city)),
|
||||
GNUNET_JSON_pack_allow_null (
|
||||
GNUNET_JSON_pack_string (
|
||||
TALER_ATTRIBUTE_RESIDENCES,
|
||||
country_code))
|
||||
);
|
||||
GNUNET_free (street);
|
||||
GNUNET_free (city);
|
||||
GNUNET_free (name);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a response for the @a ph request indicating a
|
||||
* protocol violation by the Persona server.
|
||||
@ -1115,6 +1007,86 @@ return_invalid_response (struct TALER_KYCLOGIC_ProofHandle *ph,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start the external conversion helper.
|
||||
*
|
||||
* @param pd configuration details
|
||||
* @param attr attributes to give to the helper
|
||||
* @param cb function to call with the result
|
||||
* @param cb_cls closure for @a cb
|
||||
* @return handle for the helper
|
||||
*/
|
||||
static struct TALER_JSON_ExternalConversion *
|
||||
start_conversion (const struct TALER_KYCLOGIC_ProviderDetails *pd,
|
||||
const json_t *attr,
|
||||
TALER_JSON_JsonCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
return TALER_JSON_external_conversion_start (
|
||||
attr,
|
||||
cb,
|
||||
cb_cls,
|
||||
pd->conversion_binary,
|
||||
pd->conversion_binary,
|
||||
"-a",
|
||||
pd->auth_token,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Type of a callback that receives a JSON @a result.
|
||||
*
|
||||
* @param cls closure with a `struct TALER_KYCLOGIC_ProofHandle *`
|
||||
* @param status_type how did the process die
|
||||
* @param code termination status code from the process
|
||||
* @param attr result some JSON result, NULL if we failed to get an JSON output
|
||||
*/
|
||||
static void
|
||||
proof_post_conversion_cb (void *cls,
|
||||
enum GNUNET_OS_ProcessStatusType status_type,
|
||||
unsigned long code,
|
||||
const json_t *attr)
|
||||
{
|
||||
struct TALER_KYCLOGIC_ProofHandle *ph = cls;
|
||||
struct MHD_Response *resp;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
|
||||
ph->ec = NULL;
|
||||
if ( (NULL == attr) ||
|
||||
(0 != code) )
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return_invalid_response (ph,
|
||||
MHD_HTTP_OK,
|
||||
ph->inquiry_id,
|
||||
"converter",
|
||||
NULL);
|
||||
persona_proof_cancel (ph);
|
||||
return;
|
||||
}
|
||||
expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
GNUNET_break (MHD_YES ==
|
||||
MHD_add_response_header (resp,
|
||||
MHD_HTTP_HEADER_LOCATION,
|
||||
ph->pd->post_kyc_redirect_url));
|
||||
TALER_MHD_add_global_headers (resp);
|
||||
ph->cb (ph->cb_cls,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
ph->account_id,
|
||||
ph->inquiry_id,
|
||||
expiration,
|
||||
attr,
|
||||
MHD_HTTP_SEE_OTHER,
|
||||
resp);
|
||||
persona_proof_cancel (ph);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called when we're done processing the
|
||||
* HTTP "/api/v1/inquiries/{inquiry-id}" request.
|
||||
@ -1283,46 +1255,15 @@ handle_proof_finished (void *cls,
|
||||
data);
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
struct MHD_Response *resp;
|
||||
struct GNUNET_TIME_Absolute expiration;
|
||||
json_t *attr;
|
||||
|
||||
attr = convert_attributes (attributes);
|
||||
if (NULL == attr)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return_invalid_response (ph,
|
||||
response_code,
|
||||
inquiry_id,
|
||||
"data-relationships-account-data-id",
|
||||
data);
|
||||
break;
|
||||
}
|
||||
expiration = GNUNET_TIME_relative_to_absolute (ph->pd->validity);
|
||||
resp = MHD_create_response_from_buffer (0,
|
||||
"",
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
GNUNET_break (MHD_YES ==
|
||||
MHD_add_response_header (resp,
|
||||
MHD_HTTP_HEADER_LOCATION,
|
||||
ph->pd->post_kyc_redirect_url));
|
||||
TALER_MHD_add_global_headers (resp);
|
||||
ph->cb (ph->cb_cls,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
account_id,
|
||||
inquiry_id,
|
||||
expiration,
|
||||
attr,
|
||||
MHD_HTTP_SEE_OTHER,
|
||||
resp);
|
||||
json_decref (attr);
|
||||
}
|
||||
ph->account_id = GNUNET_strdup (account_id);
|
||||
ph->ec = start_conversion (ph->pd,
|
||||
j,
|
||||
&proof_post_conversion_cb,
|
||||
ph);
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
return; /* continued in proof_post_conversion_cb */
|
||||
}
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
@ -1580,6 +1521,12 @@ persona_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
|
||||
GNUNET_CURL_job_cancel (wh->job);
|
||||
wh->job = NULL;
|
||||
}
|
||||
if (NULL != wh->ec)
|
||||
{
|
||||
TALER_JSON_external_conversion_stop (wh->ec);
|
||||
wh->ec = NULL;
|
||||
}
|
||||
GNUNET_free (wh->account_id);
|
||||
GNUNET_free (wh->inquiry_id);
|
||||
GNUNET_free (wh->url);
|
||||
GNUNET_free (wh);
|
||||
@ -1650,6 +1597,32 @@ webhook_reply_error (struct TALER_KYCLOGIC_WebhookHandle *wh,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Type of a callback that receives a JSON @a result.
|
||||
*
|
||||
* @param cls closure with a `struct TALER_KYCLOGIC_WebhookHandle *`
|
||||
* @param status_type how did the process die
|
||||
* @param code termination status code from the process
|
||||
* @param attr some JSON result, NULL if we failed to get an JSON output
|
||||
*/
|
||||
static void
|
||||
webhook_post_conversion_cb (void *cls,
|
||||
enum GNUNET_OS_ProcessStatusType status_type,
|
||||
unsigned long code,
|
||||
const json_t *attr)
|
||||
{
|
||||
struct TALER_KYCLOGIC_WebhookHandle *wh = cls;
|
||||
|
||||
wh->ec = NULL;
|
||||
webhook_generic_reply (wh,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
wh->account_id,
|
||||
wh->inquiry_id,
|
||||
attr,
|
||||
MHD_HTTP_OK);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function called when we're done processing the
|
||||
* HTTP "/api/v1/inquiries/{inquiry_id}" request.
|
||||
@ -1723,7 +1696,6 @@ handle_webhook_finished (void *cls,
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
json_t *attr;
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (attributes,
|
||||
@ -1807,19 +1779,15 @@ handle_webhook_finished (void *cls,
|
||||
MHD_HTTP_BAD_GATEWAY);
|
||||
break;
|
||||
}
|
||||
|
||||
attr = convert_attributes (attributes);
|
||||
webhook_generic_reply (wh,
|
||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
||||
account_id,
|
||||
inquiry_id,
|
||||
attr,
|
||||
MHD_HTTP_OK);
|
||||
json_decref (attr);
|
||||
wh->account_id = GNUNET_strdup (account_id);
|
||||
wh->ec = start_conversion (wh->pd,
|
||||
j,
|
||||
&webhook_post_conversion_cb,
|
||||
wh);
|
||||
GNUNET_JSON_parse_free (ispec);
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
return; /* continued in webhook_post_conversion_cb */
|
||||
}
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
|
54
src/kyclogic/taler-exchange-kyc-persona-converter.sh
Executable file
54
src/kyclogic/taler-exchange-kyc-persona-converter.sh
Executable file
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
# This file is in the public domain.
|
||||
#
|
||||
# This code converts (some of) the JSON output from Persona into the GNU Taler
|
||||
# specific KYC attribute data (again in JSON format). We may need to download
|
||||
# and inline file data in the process, for authorization pass "-a" with the
|
||||
# respective bearer token.
|
||||
#
|
||||
|
||||
# Die if anything goes wrong.
|
||||
set -eu
|
||||
|
||||
# Parse command-line options
|
||||
while getopts ':a:' OPTION; do
|
||||
case "$OPTION" in
|
||||
a)
|
||||
TOKEN="$OPTARG"
|
||||
;;
|
||||
?)
|
||||
echo "Unrecognized command line option"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
# First, extract everything from stdin.
|
||||
J=$(jq '{"first":.data.attributes."name-first","middle":.data.attributes."name-middle","last":.data.attributes."name-last","cc":.data.attributes.fields."address-country-code".value,"birthdate":.data.attributes.birthdate,"city":.data.attributes."address-city","postcode":.data.attributes."address-postal-code","street-1":.data.attributes."address-street-1","street-2":.data.attributes."address-street-2","address-subdivision":.data.attributes."address-subdivision","identification-number":.data.attributes."identification-number","photo":.included[]|select(.type=="verification/government-id")|.attributes|select(.status=="passed")|."front-photo-url"}')
|
||||
|
||||
|
||||
# Next, combine some fields into larger values.
|
||||
FULLNAME=$(echo "$J" | jq -r '[.first,.middle,.last]|join(" ")')
|
||||
STREET=$(echo $J | jq -r '[."street-1",."street-2"]|join(" ")')
|
||||
CITY=$(echo $J | jq -r '[.postcode,.city,."address-subdivision,.cc"]|join(" ")')
|
||||
|
||||
# Download and base32-encode the photo
|
||||
PHOTO_URL=$(echo "$J" | jq -r '.photo')
|
||||
PHOTO_FILE=$(mktemp -t tmp.XXXXXXXXXX)
|
||||
if [ -z "${TOKEN:-}" ]
|
||||
then
|
||||
wget -q --output-document=- "$PHOTO_URL" | gnunet-base32 > ${PHOTO_FILE}
|
||||
else
|
||||
wget -q --output-document=- --header "Authorization: Bearer $TOKEN" "$PHOTO_URL" | gnunet-base32 > ${PHOTO_FILE}
|
||||
fi
|
||||
|
||||
# Combine into final result.
|
||||
echo "$J" | jq \
|
||||
--arg full_name "${FULLNAME}" \
|
||||
--arg street "${STREET}" \
|
||||
--arg city "${CITY}" \
|
||||
--rawfile photo "${PHOTO_FILE}" \
|
||||
'{$full_name,$street,$city,"birthdate":.birthdate,"residences":.cc,"identification_number":."identification-number",$photo}'
|
||||
|
||||
exit 0
|
@ -990,9 +990,9 @@ proceed_with_handler (struct TEKT_RequestContext *rc,
|
||||
|
||||
/* Parse command-line arguments */
|
||||
/* make a copy of 'url' because 'strtok_r()' will modify */
|
||||
memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
GNUNET_memcpy (d,
|
||||
url,
|
||||
ulen);
|
||||
i = 0;
|
||||
args[i++] = strtok_r (d, "/", &sp);
|
||||
while ( (NULL != args[i - 1]) &&
|
||||
|
@ -247,7 +247,7 @@ handle_deposit_finished (void *cls,
|
||||
&dh->exchange_pub),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("transaction_base_url",
|
||||
&dr.details.success.transaction_base_url),
|
||||
&dr.details.ok.transaction_base_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
||||
&dh->exchange_timestamp),
|
||||
@ -332,7 +332,7 @@ handle_deposit_finished (void *cls,
|
||||
GNUNET_break_op (0);
|
||||
dr.hr.http_status = 0;
|
||||
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -341,10 +341,10 @@ handle_deposit_finished (void *cls,
|
||||
dh);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
}
|
||||
dr.details.success.exchange_sigs = dh->exchange_sigs;
|
||||
dr.details.success.exchange_pub = &dh->exchange_pub;
|
||||
dr.details.success.deposit_timestamp = dh->exchange_timestamp;
|
||||
dr.details.success.num_signatures = dh->num_cdds;
|
||||
dr.details.ok.exchange_sigs = dh->exchange_sigs;
|
||||
dr.details.ok.exchange_pub = &dh->exchange_pub;
|
||||
dr.details.ok.deposit_timestamp = dh->exchange_timestamp;
|
||||
dr.details.ok.num_signatures = dh->num_cdds;
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
|
@ -144,37 +144,34 @@ struct TALER_EXCHANGE_BatchWithdrawHandle
|
||||
* HTTP /reserves/$RESERVE_PUB/batch-withdraw request.
|
||||
*
|
||||
* @param cls the `struct TALER_EXCHANGE_BatchWithdrawHandle`
|
||||
* @param hr HTTP response data
|
||||
* @param blind_sigs array of blind signatures over the coins, NULL on error
|
||||
* @param blind_sigs_length length of the @a blind_sigs array
|
||||
* @param bw2r response data
|
||||
*/
|
||||
static void
|
||||
handle_reserve_batch_withdraw_finished (
|
||||
void *cls,
|
||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
||||
const struct TALER_BlindedDenominationSignature *blind_sigs,
|
||||
unsigned int blind_sigs_length)
|
||||
const struct TALER_EXCHANGE_BatchWithdraw2Response *bw2r)
|
||||
{
|
||||
struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cls;
|
||||
struct TALER_EXCHANGE_BatchWithdrawResponse wr = {
|
||||
.hr = *hr
|
||||
.hr = bw2r->hr
|
||||
};
|
||||
struct TALER_EXCHANGE_PrivateCoinDetails coins[wh->num_coins];
|
||||
struct TALER_EXCHANGE_PrivateCoinDetails coins[GNUNET_NZL (wh->num_coins)];
|
||||
|
||||
wh->wh2 = NULL;
|
||||
memset (coins,
|
||||
0,
|
||||
sizeof (coins));
|
||||
if (blind_sigs_length != wh->num_coins)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
wr.hr.http_status = 0;
|
||||
wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
switch (hr->http_status)
|
||||
switch (bw2r->hr.http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
{
|
||||
if (bw2r->details.ok.blind_sigs_length != wh->num_coins)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
wr.hr.http_status = 0;
|
||||
wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
for (unsigned int i = 0; i<wh->num_coins; i++)
|
||||
{
|
||||
struct CoinData *cd = &wh->coins[i];
|
||||
@ -183,7 +180,7 @@ handle_reserve_batch_withdraw_finished (
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_planchet_to_coin (&cd->pk.key,
|
||||
&blind_sigs[i],
|
||||
&bw2r->details.ok.blind_sigs[i],
|
||||
&cd->bks,
|
||||
&cd->priv,
|
||||
cd->ach,
|
||||
@ -200,8 +197,8 @@ handle_reserve_batch_withdraw_finished (
|
||||
coin->sig = fc.sig;
|
||||
coin->exchange_vals = cd->alg_values;
|
||||
}
|
||||
wr.details.success.coins = coins;
|
||||
wr.details.success.num_coins = wh->num_coins;
|
||||
wr.details.ok.coins = coins;
|
||||
wr.details.ok.num_coins = wh->num_coins;
|
||||
break;
|
||||
}
|
||||
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
||||
@ -217,7 +214,7 @@ handle_reserve_batch_withdraw_finished (
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (hr->reply,
|
||||
GNUNET_JSON_parse (bw2r->hr.reply,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
@ -289,7 +286,7 @@ withdraw_cs_stage_two_callback (
|
||||
switch (csrr->hr.http_status)
|
||||
{
|
||||
case MHD_HTTP_OK:
|
||||
cd->alg_values = csrr->details.success.alg_values;
|
||||
cd->alg_values = csrr->details.ok.alg_values;
|
||||
TALER_planchet_setup_coin_priv (&cd->ps,
|
||||
&cd->alg_values,
|
||||
&cd->priv);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2022 Taler Systems SA
|
||||
Copyright (C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -108,9 +108,9 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
|
||||
"ev_sigs");
|
||||
const json_t *j;
|
||||
unsigned int index;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = json,
|
||||
.http_status = MHD_HTTP_OK
|
||||
struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
|
||||
.hr.reply = json,
|
||||
.hr.http_status = MHD_HTTP_OK
|
||||
};
|
||||
|
||||
if ( (NULL == ja) ||
|
||||
@ -141,10 +141,10 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
|
||||
}
|
||||
|
||||
/* signature is valid, return it to the application */
|
||||
bwr.details.ok.blind_sigs = blind_sigs;
|
||||
bwr.details.ok.blind_sigs_length = wh->num_coins;
|
||||
wh->cb (wh->cb_cls,
|
||||
&hr,
|
||||
blind_sigs,
|
||||
wh->num_coins);
|
||||
&bwr);
|
||||
/* make sure callback isn't called again after return */
|
||||
wh->cb = NULL;
|
||||
for (unsigned int i = 0; i<wh->num_coins; i++)
|
||||
@ -264,16 +264,16 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_BatchWithdraw2Handle *wh = cls;
|
||||
const json_t *j = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = (unsigned int) response_code
|
||||
struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = (unsigned int) response_code
|
||||
};
|
||||
|
||||
wh->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
bwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
if (GNUNET_OK !=
|
||||
@ -281,8 +281,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
j))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
bwr.hr.http_status = 0;
|
||||
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
GNUNET_assert (NULL == wh->cb);
|
||||
@ -304,8 +304,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
bwr.hr.http_status = 0;
|
||||
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -313,24 +313,24 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
GNUNET_break_op (0);
|
||||
/* Nothing really to verify, exchange says one of the signatures is
|
||||
invalid; as we checked them, this should never happen, we
|
||||
should pass the JSON reply to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, the exchange basically just says
|
||||
that it doesn't know this reserve. Can happen if we
|
||||
query before the wire transfer went through.
|
||||
We should simply pass the JSON reply to the application. */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
/* The exchange says that the reserve has insufficient funds;
|
||||
@ -340,13 +340,13 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
j))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
bwr.hr.http_status = 0;
|
||||
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
}
|
||||
else
|
||||
{
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
}
|
||||
break;
|
||||
case MHD_HTTP_GONE:
|
||||
@ -354,32 +354,30 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
||||
/* Note: one might want to check /keys for revocation
|
||||
signature here, alas tricky in case our /keys
|
||||
is outdated => left to clients */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for exchange batch withdraw\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) bwr.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != wh->cb)
|
||||
{
|
||||
wh->cb (wh->cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
0);
|
||||
&bwr);
|
||||
wh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_batch_withdraw2_cancel (wh);
|
||||
|
@ -2194,4 +2194,196 @@ TALER_EXCHANGE_verify_deposit_signature_ (
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse account restriction in @a jrest into @a rest.
|
||||
*
|
||||
* @param jresta array of account restrictions in JSON
|
||||
* @param[out] resta_len set to length of @a resta
|
||||
* @param[out] resta account restriction array to set
|
||||
* @return #GNUNET_OK on success
|
||||
*/
|
||||
static enum GNUNET_GenericReturnValue
|
||||
parse_restrictions (const json_t *jresta,
|
||||
unsigned int *resta_len,
|
||||
struct TALER_EXCHANGE_AccountRestriction **resta)
|
||||
{
|
||||
if (! json_is_array (jresta))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
*resta_len = json_array_size (jresta);
|
||||
if (0 == *resta_len)
|
||||
{
|
||||
/* no restrictions, perfectly OK */
|
||||
*resta = NULL;
|
||||
return GNUNET_OK;
|
||||
}
|
||||
*resta = GNUNET_new_array (*resta_len,
|
||||
struct TALER_EXCHANGE_AccountRestriction);
|
||||
for (unsigned int i = 0; i<*resta_len; i++)
|
||||
{
|
||||
const json_t *jr = json_array_get (jresta,
|
||||
i);
|
||||
struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i];
|
||||
const char *type = json_string_value (json_object_get (jr,
|
||||
"type"));
|
||||
|
||||
if (NULL == type)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
goto fail;
|
||||
}
|
||||
if (0 == strcmp (type,
|
||||
"deny"))
|
||||
{
|
||||
ar->type = TALER_EXCHANGE_AR_DENY;
|
||||
continue;
|
||||
}
|
||||
if (0 == strcmp (type,
|
||||
"regex"))
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string (
|
||||
"payto_regex",
|
||||
&ar->details.regex.posix_egrep),
|
||||
GNUNET_JSON_spec_string (
|
||||
"human_hint",
|
||||
&ar->details.regex.human_hint),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_object_const (
|
||||
"human_hint_i18n",
|
||||
&ar->details.regex.human_hint_i18n),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (jr,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
/* bogus reply */
|
||||
GNUNET_break_op (0);
|
||||
goto fail;
|
||||
}
|
||||
ar->type = TALER_EXCHANGE_AR_REGEX;
|
||||
continue;
|
||||
}
|
||||
/* unsupported type */
|
||||
GNUNET_break (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
return GNUNET_OK;
|
||||
fail:
|
||||
GNUNET_free (*resta);
|
||||
*resta_len = 0;
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
|
||||
enum GNUNET_GenericReturnValue
|
||||
TALER_EXCHANGE_parse_accounts (const struct TALER_MasterPublicKeyP *master_pub,
|
||||
const json_t *accounts,
|
||||
struct TALER_EXCHANGE_WireAccount was[],
|
||||
unsigned int was_length)
|
||||
{
|
||||
memset (was,
|
||||
0,
|
||||
sizeof (struct TALER_EXCHANGE_WireAccount) * was_length);
|
||||
GNUNET_assert (was_length ==
|
||||
json_array_size (accounts));
|
||||
for (unsigned int i = 0;
|
||||
i<was_length;
|
||||
i++)
|
||||
{
|
||||
struct TALER_EXCHANGE_WireAccount *wa = &was[i];
|
||||
json_t *credit_restrictions;
|
||||
json_t *debit_restrictions;
|
||||
struct GNUNET_JSON_Specification spec_account[] = {
|
||||
GNUNET_JSON_spec_string ("payto_uri",
|
||||
&wa->payto_uri),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("conversion_url",
|
||||
&wa->conversion_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_json ("credit_restrictions",
|
||||
&credit_restrictions),
|
||||
GNUNET_JSON_spec_json ("debit_restrictions",
|
||||
&debit_restrictions),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_sig",
|
||||
&wa->master_sig),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
json_t *account;
|
||||
|
||||
account = json_array_get (accounts,
|
||||
i);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (account,
|
||||
spec_account,
|
||||
NULL, NULL))
|
||||
{
|
||||
/* bogus reply */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
{
|
||||
char *err;
|
||||
|
||||
err = TALER_payto_validate (wa->payto_uri);
|
||||
if (NULL != err)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
GNUNET_free (err);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (NULL != master_pub) &&
|
||||
(GNUNET_OK !=
|
||||
TALER_exchange_wire_signature_check (wa->payto_uri,
|
||||
wa->conversion_url,
|
||||
debit_restrictions,
|
||||
credit_restrictions,
|
||||
master_pub,
|
||||
&wa->master_sig)) )
|
||||
{
|
||||
/* bogus reply */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if ( (GNUNET_OK !=
|
||||
parse_restrictions (credit_restrictions,
|
||||
&wa->credit_restrictions_length,
|
||||
&wa->credit_restrictions)) ||
|
||||
(GNUNET_OK !=
|
||||
parse_restrictions (debit_restrictions,
|
||||
&wa->debit_restrictions_length,
|
||||
&wa->debit_restrictions)) )
|
||||
{
|
||||
/* bogus reply */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_JSON_parse_free (spec_account);
|
||||
} /* end 'for all accounts */
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TALER_EXCHANGE_free_accounts (struct TALER_EXCHANGE_WireAccount *was,
|
||||
unsigned int was_len)
|
||||
{
|
||||
for (unsigned int i = 0; i<was_len; i++)
|
||||
{
|
||||
struct TALER_EXCHANGE_WireAccount *wa = &was[i];
|
||||
|
||||
GNUNET_free (wa->credit_restrictions);
|
||||
GNUNET_free (wa->debit_restrictions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* end of exchange_api_common.c */
|
||||
|
@ -109,7 +109,7 @@ handle_contract_get_finished (void *cls,
|
||||
struct TALER_PurseContractSignatureP econtract_sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("purse_pub",
|
||||
&dr.details.success.purse_pub),
|
||||
&dr.details.ok.purse_pub),
|
||||
GNUNET_JSON_spec_fixed_auto ("econtract_sig",
|
||||
&econtract_sig),
|
||||
GNUNET_JSON_spec_varsize ("econtract",
|
||||
@ -133,7 +133,7 @@ handle_contract_get_finished (void *cls,
|
||||
econtract,
|
||||
econtract_size,
|
||||
&cgh->cpub,
|
||||
&dr.details.success.purse_pub,
|
||||
&dr.details.ok.purse_pub,
|
||||
&econtract_sig))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
@ -142,8 +142,8 @@ handle_contract_get_finished (void *cls,
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
break;
|
||||
}
|
||||
dr.details.success.econtract = econtract;
|
||||
dr.details.success.econtract_size = econtract_size;
|
||||
dr.details.ok.econtract = econtract;
|
||||
dr.details.ok.econtract_size = econtract_size;
|
||||
cgh->cb (cgh->cb_cls,
|
||||
&dr);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
|
@ -94,8 +94,8 @@ csr_ok (struct TALER_EXCHANGE_CsRMeltHandle *csrh,
|
||||
struct TALER_ExchangeWithdrawValues alg_values[GNUNET_NZL (alen)];
|
||||
struct TALER_EXCHANGE_CsRMeltResponse csrr = {
|
||||
.hr = *hr,
|
||||
.details.success.alg_values_len = alen,
|
||||
.details.success.alg_values = alg_values
|
||||
.details.ok.alg_values_len = alen,
|
||||
.details.ok.alg_values = alg_values
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i<alen; i++)
|
||||
|
@ -96,7 +96,7 @@ csr_ok (struct TALER_EXCHANGE_CsRWithdrawHandle *csrh,
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_exchange_withdraw_values (
|
||||
"ewv",
|
||||
&csrr.details.success.alg_values),
|
||||
&csrr.details.ok.alg_values),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
|
@ -236,7 +236,7 @@ handle_deposit_finished (void *cls,
|
||||
&dh->exchange_pub),
|
||||
GNUNET_JSON_spec_mark_optional (
|
||||
GNUNET_JSON_spec_string ("transaction_base_url",
|
||||
&dr.details.success.transaction_base_url),
|
||||
&dr.details.ok.transaction_base_url),
|
||||
NULL),
|
||||
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
||||
&dh->exchange_timestamp),
|
||||
@ -297,9 +297,9 @@ handle_deposit_finished (void *cls,
|
||||
&auditor_cb,
|
||||
dh);
|
||||
}
|
||||
dr.details.success.exchange_sig = &dh->exchange_sig;
|
||||
dr.details.success.exchange_pub = &dh->exchange_pub;
|
||||
dr.details.success.deposit_timestamp = dh->exchange_timestamp;
|
||||
dr.details.ok.exchange_sig = &dh->exchange_sig;
|
||||
dr.details.ok.exchange_pub = &dh->exchange_pub;
|
||||
dr.details.ok.deposit_timestamp = dh->exchange_timestamp;
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the exchange is buggy
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2021 Taler Systems SA
|
||||
Copyright (C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -118,15 +118,15 @@ handle_deposit_wtid_finished (void *cls,
|
||||
{
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("wtid",
|
||||
&dr.details.success.wtid),
|
||||
&dr.details.ok.wtid),
|
||||
GNUNET_JSON_spec_timestamp ("execution_time",
|
||||
&dr.details.success.execution_time),
|
||||
&dr.details.ok.execution_time),
|
||||
TALER_JSON_spec_amount_any ("coin_contribution",
|
||||
&dr.details.success.coin_contribution),
|
||||
&dr.details.ok.coin_contribution),
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
|
||||
&dr.details.success.exchange_sig),
|
||||
&dr.details.ok.exchange_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
|
||||
&dr.details.success.exchange_pub),
|
||||
&dr.details.ok.exchange_pub),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
const struct TALER_EXCHANGE_Keys *key_state;
|
||||
@ -145,7 +145,7 @@ handle_deposit_wtid_finished (void *cls,
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_test_signing_key (key_state,
|
||||
&dr.details.success.exchange_pub))
|
||||
&dr.details.ok.exchange_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
dr.hr.http_status = 0;
|
||||
@ -156,12 +156,12 @@ handle_deposit_wtid_finished (void *cls,
|
||||
TALER_exchange_online_confirm_wire_verify (
|
||||
&dwh->h_wire,
|
||||
&dwh->h_contract_terms,
|
||||
&dr.details.success.wtid,
|
||||
&dr.details.ok.wtid,
|
||||
&dwh->coin_pub,
|
||||
dr.details.success.execution_time,
|
||||
&dr.details.success.coin_contribution,
|
||||
&dr.details.success.exchange_pub,
|
||||
&dr.details.success.exchange_sig))
|
||||
dr.details.ok.execution_time,
|
||||
&dr.details.ok.coin_contribution,
|
||||
&dr.details.ok.exchange_pub,
|
||||
&dr.details.ok.exchange_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
dr.hr.http_status = 0;
|
||||
@ -260,6 +260,7 @@ TALER_EXCHANGE_deposits_get (
|
||||
const struct TALER_MerchantWireHashP *h_wire,
|
||||
const struct TALER_PrivateContractHashP *h_contract_terms,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
struct GNUNET_TIME_Relative timeout,
|
||||
TALER_EXCHANGE_DepositGetCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
@ -293,6 +294,7 @@ TALER_EXCHANGE_deposits_get (
|
||||
char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
|
||||
char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
|
||||
char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
|
||||
char timeout_str[24];
|
||||
char *end;
|
||||
|
||||
end = GNUNET_STRINGS_data_to_string (h_wire,
|
||||
@ -320,15 +322,33 @@ TALER_EXCHANGE_deposits_get (
|
||||
msig_str,
|
||||
sizeof (msig_str));
|
||||
*end = '\0';
|
||||
if (GNUNET_TIME_relative_is_zero (timeout))
|
||||
{
|
||||
timeout_str[0] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
GNUNET_snprintf (
|
||||
timeout_str,
|
||||
sizeof (timeout_str),
|
||||
"%llu",
|
||||
(unsigned long long) (
|
||||
timeout.rel_value_us
|
||||
/ GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us));
|
||||
}
|
||||
|
||||
GNUNET_snprintf (arg_str,
|
||||
sizeof (arg_str),
|
||||
"/deposits/%s/%s/%s/%s?merchant_sig=%s",
|
||||
"/deposits/%s/%s/%s/%s?merchant_sig=%s%s%s",
|
||||
whash_str,
|
||||
mpub_str,
|
||||
chash_str,
|
||||
cpub_str,
|
||||
msig_str);
|
||||
msig_str,
|
||||
GNUNET_TIME_relative_is_zero (timeout)
|
||||
? ""
|
||||
: "&timeout_ms=",
|
||||
timeout_str);
|
||||
}
|
||||
|
||||
dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2022 Taler Systems SA
|
||||
Copyright (C) 2014-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published
|
||||
@ -40,7 +40,7 @@
|
||||
* Which version of the Taler protocol is implemented
|
||||
* by this library? Used to determine compatibility.
|
||||
*/
|
||||
#define EXCHANGE_PROTOCOL_CURRENT 14
|
||||
#define EXCHANGE_PROTOCOL_CURRENT 15
|
||||
|
||||
/**
|
||||
* How many versions are we backwards compatible with?
|
||||
@ -1305,15 +1305,18 @@ keys_completed_cb (void *cls,
|
||||
{
|
||||
struct KeysRequest *kr = cls;
|
||||
struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
|
||||
struct TALER_EXCHANGE_Keys kd;
|
||||
struct TALER_EXCHANGE_Keys kd_old;
|
||||
enum TALER_EXCHANGE_VersionCompatibility vc;
|
||||
const json_t *j = resp_obj;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.reply = j,
|
||||
.http_status = (unsigned int) response_code
|
||||
struct TALER_EXCHANGE_Keys kd;
|
||||
struct TALER_EXCHANGE_KeysResponse kresp = {
|
||||
.hr.reply = j,
|
||||
.hr.http_status = (unsigned int) response_code,
|
||||
.details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR,
|
||||
};
|
||||
|
||||
memset (&kd,
|
||||
0,
|
||||
sizeof (kd));
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Received keys from URL `%s' with status %ld and expiration %s.\n",
|
||||
kr->url,
|
||||
@ -1330,10 +1333,6 @@ keys_completed_cb (void *cls,
|
||||
GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
|
||||
}
|
||||
kd_old = exchange->key_data;
|
||||
memset (&kd,
|
||||
0,
|
||||
sizeof (struct TALER_EXCHANGE_Keys));
|
||||
vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
@ -1358,17 +1357,19 @@ keys_completed_cb (void *cls,
|
||||
}
|
||||
/* We keep the denomination keys and auditor signatures from the
|
||||
previous iteration (/keys cherry picking) */
|
||||
kd.num_denom_keys = kd_old.num_denom_keys;
|
||||
kd.last_denom_issue_date = kd_old.last_denom_issue_date;
|
||||
kd.num_denom_keys
|
||||
= kd_old.num_denom_keys;
|
||||
kd.last_denom_issue_date
|
||||
= kd_old.last_denom_issue_date;
|
||||
GNUNET_array_grow (kd.denom_keys,
|
||||
kd.denom_keys_size,
|
||||
kd.num_denom_keys);
|
||||
|
||||
/* First make a shallow copy, we then need another pass for the RSA key... */
|
||||
memcpy (kd.denom_keys,
|
||||
kd_old.denom_keys,
|
||||
kd_old.num_denom_keys * sizeof (struct
|
||||
TALER_EXCHANGE_DenomPublicKey));
|
||||
GNUNET_memcpy (kd.denom_keys,
|
||||
kd_old.denom_keys,
|
||||
kd_old.num_denom_keys * sizeof (struct
|
||||
TALER_EXCHANGE_DenomPublicKey));
|
||||
|
||||
for (unsigned int i = 0; i<kd_old.num_denom_keys; i++)
|
||||
TALER_denom_pub_deep_copy (&kd.denom_keys[i].key,
|
||||
@ -1390,10 +1391,10 @@ keys_completed_cb (void *cls,
|
||||
GNUNET_array_grow (anew->denom_keys,
|
||||
anew->num_denom_keys,
|
||||
aold->num_denom_keys);
|
||||
memcpy (anew->denom_keys,
|
||||
aold->denom_keys,
|
||||
aold->num_denom_keys
|
||||
* sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
|
||||
GNUNET_memcpy (anew->denom_keys,
|
||||
aold->denom_keys,
|
||||
aold->num_denom_keys
|
||||
* sizeof (struct TALER_EXCHANGE_AuditorDenominationInfo));
|
||||
}
|
||||
|
||||
/* Old auditors got just copied into new ones. */
|
||||
@ -1401,11 +1402,11 @@ keys_completed_cb (void *cls,
|
||||
decode_keys_json (j,
|
||||
true,
|
||||
&kd,
|
||||
&vc))
|
||||
&kresp.details.ok.compat))
|
||||
{
|
||||
TALER_LOG_ERROR ("Could not decode /keys response\n");
|
||||
hr.http_status = 0;
|
||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
kresp.hr.http_status = 0;
|
||||
kresp.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||
for (unsigned int i = 0; i<kd.num_auditors; i++)
|
||||
{
|
||||
struct TALER_EXCHANGE_AuditorInformation *anew = &kd.auditors[i];
|
||||
@ -1436,13 +1437,13 @@ keys_completed_cb (void *cls,
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
if (NULL == j)
|
||||
{
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
hr.hint = TALER_ErrorCode_get_hint (hr.ec);
|
||||
kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
kresp.hr.ec = TALER_JSON_get_error_code (j);
|
||||
kresp.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -1450,18 +1451,18 @@ keys_completed_cb (void *cls,
|
||||
exchange->keys_error_count++;
|
||||
if (NULL == j)
|
||||
{
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
hr.hint = TALER_ErrorCode_get_hint (hr.ec);
|
||||
kresp.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
kresp.hr.hint = TALER_ErrorCode_get_hint (kresp.hr.ec);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr.ec = TALER_JSON_get_error_code (j);
|
||||
hr.hint = TALER_JSON_get_error_hint (j);
|
||||
kresp.hr.ec = TALER_JSON_get_error_code (j);
|
||||
kresp.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) kresp.hr.ec);
|
||||
break;
|
||||
}
|
||||
exchange->key_data = kd;
|
||||
@ -1491,9 +1492,7 @@ keys_completed_cb (void *cls,
|
||||
free_key_data (&kd_old);
|
||||
/* notify application that we failed */
|
||||
exchange->cert_cb (exchange->cert_cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
vc);
|
||||
&kresp);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1504,11 +1503,11 @@ keys_completed_cb (void *cls,
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Successfully downloaded exchange's keys\n");
|
||||
update_auditors (exchange);
|
||||
kresp.details.ok.keys = &exchange->key_data;
|
||||
|
||||
/* notify application about the key information */
|
||||
exchange->cert_cb (exchange->cert_cb_cls,
|
||||
&hr,
|
||||
&exchange->key_data,
|
||||
vc);
|
||||
&kresp);
|
||||
free_key_data (&kd_old);
|
||||
}
|
||||
|
||||
@ -1702,7 +1701,6 @@ static void
|
||||
deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
|
||||
const json_t *data)
|
||||
{
|
||||
enum TALER_EXCHANGE_VersionCompatibility vc;
|
||||
json_t *keys;
|
||||
const char *url;
|
||||
uint32_t version;
|
||||
@ -1719,10 +1717,11 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
struct TALER_EXCHANGE_Keys key_data;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.ec = TALER_EC_NONE,
|
||||
.http_status = MHD_HTTP_OK,
|
||||
.reply = data
|
||||
struct TALER_EXCHANGE_KeysResponse kresp = {
|
||||
.hr.ec = TALER_EC_NONE,
|
||||
.hr.http_status = MHD_HTTP_OK,
|
||||
.hr.reply = data,
|
||||
.details.ok.keys = &exchange->key_data
|
||||
};
|
||||
|
||||
if (NULL == data)
|
||||
@ -1754,7 +1753,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
|
||||
decode_keys_json (keys,
|
||||
false,
|
||||
&key_data,
|
||||
&vc))
|
||||
&kresp.details.ok.compat))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
@ -1771,9 +1770,7 @@ deserialize_data (struct TALER_EXCHANGE_Handle *exchange,
|
||||
update_auditors (exchange);
|
||||
/* notify application about the key information */
|
||||
exchange->cert_cb (exchange->cert_cb_cls,
|
||||
&hr,
|
||||
&exchange->key_data,
|
||||
vc);
|
||||
&kresp);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
}
|
||||
|
||||
@ -2056,17 +2053,18 @@ request_keys (void *cls)
|
||||
url);
|
||||
if (NULL == kr->url)
|
||||
{
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.ec = TALER_EC_GENERIC_CONFIGURATION_INVALID
|
||||
struct TALER_EXCHANGE_KeysResponse kresp = {
|
||||
.hr.ec = TALER_EC_GENERIC_CONFIGURATION_INVALID,
|
||||
/* Next line is technically unnecessary, as the
|
||||
http status we set is 0 */
|
||||
.details.ok.compat = TALER_EXCHANGE_VC_PROTOCOL_ERROR
|
||||
};
|
||||
|
||||
GNUNET_free (kr);
|
||||
exchange->keys_error_count++;
|
||||
exchange->state = MHS_FAILED;
|
||||
exchange->cert_cb (exchange->cert_cb_cls,
|
||||
&hr,
|
||||
NULL,
|
||||
TALER_EXCHANGE_VC_PROTOCOL_ERROR);
|
||||
&kresp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -100,11 +100,11 @@ handle_kyc_check_finished (void *cls,
|
||||
uint32_t status;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
|
||||
&ks.details.success.exchange_sig),
|
||||
&ks.details.ok.exchange_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
|
||||
&ks.details.success.exchange_pub),
|
||||
&ks.details.ok.exchange_pub),
|
||||
GNUNET_JSON_spec_timestamp ("now",
|
||||
&ks.details.success.timestamp),
|
||||
&ks.details.ok.timestamp),
|
||||
GNUNET_JSON_spec_json ("kyc_details",
|
||||
&kyc_details),
|
||||
GNUNET_JSON_spec_uint32 ("aml_status",
|
||||
@ -123,13 +123,13 @@ handle_kyc_check_finished (void *cls,
|
||||
ks.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
break;
|
||||
}
|
||||
ks.details.success.kyc_details = kyc_details;
|
||||
ks.details.success.aml_status
|
||||
ks.details.ok.kyc_details = kyc_details;
|
||||
ks.details.ok.aml_status
|
||||
= (enum TALER_AmlDecisionState) status;
|
||||
key_state = TALER_EXCHANGE_get_keys (kch->exchange);
|
||||
if (GNUNET_OK !=
|
||||
TALER_EXCHANGE_test_signing_key (key_state,
|
||||
&ks.details.success.exchange_pub))
|
||||
&ks.details.ok.exchange_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ks.http_status = 0;
|
||||
@ -141,10 +141,10 @@ handle_kyc_check_finished (void *cls,
|
||||
if (GNUNET_OK !=
|
||||
TALER_exchange_online_account_setup_success_verify (
|
||||
&kch->h_payto,
|
||||
ks.details.success.kyc_details,
|
||||
ks.details.success.timestamp,
|
||||
&ks.details.success.exchange_pub,
|
||||
&ks.details.success.exchange_sig))
|
||||
ks.details.ok.kyc_details,
|
||||
ks.details.ok.timestamp,
|
||||
&ks.details.ok.exchange_pub,
|
||||
&ks.details.ok.exchange_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ks.http_status = 0;
|
||||
|
@ -365,8 +365,8 @@ parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
|
||||
|
||||
if (off_coin == num_coins)
|
||||
{
|
||||
lr.details.success.num_coins = num_coins;
|
||||
lr.details.success.coins = lcis;
|
||||
lr.details.ok.num_coins = num_coins;
|
||||
lr.details.ok.coins = lcis;
|
||||
lh->link_cb (lh->link_cb_cls,
|
||||
&lr);
|
||||
lh->link_cb = NULL;
|
||||
|
@ -190,17 +190,17 @@ parse_decision_ok (struct TALER_EXCHANGE_LookupAmlDecision *lh,
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
lr.details.success.aml_history_length = json_array_size (aml_history);
|
||||
lr.details.success.kyc_attributes_length = json_array_size (kyc_attributes);
|
||||
lr.details.ok.aml_history_length = json_array_size (aml_history);
|
||||
lr.details.ok.kyc_attributes_length = json_array_size (kyc_attributes);
|
||||
{
|
||||
struct TALER_EXCHANGE_AmlDecisionDetail aml_history_ar[
|
||||
GNUNET_NZL (lr.details.success.aml_history_length)];
|
||||
GNUNET_NZL (lr.details.ok.aml_history_length)];
|
||||
struct TALER_EXCHANGE_KycHistoryDetail kyc_attributes_ar[
|
||||
lr.details.success.kyc_attributes_length];
|
||||
GNUNET_NZL (lr.details.ok.kyc_attributes_length)];
|
||||
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
|
||||
|
||||
lr.details.success.aml_history = aml_history_ar;
|
||||
lr.details.success.kyc_attributes = kyc_attributes_ar;
|
||||
lr.details.ok.aml_history = aml_history_ar;
|
||||
lr.details.ok.kyc_attributes = kyc_attributes_ar;
|
||||
ret = parse_aml_history (aml_history,
|
||||
aml_history_ar);
|
||||
if (GNUNET_OK == ret)
|
||||
@ -256,6 +256,8 @@ handle_lookup_finished (void *cls,
|
||||
GNUNET_assert (NULL == lh->decision_cb);
|
||||
TALER_EXCHANGE_lookup_aml_decision_cancel (lh);
|
||||
return;
|
||||
case MHD_HTTP_NO_CONTENT:
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
lr.hr.ec = TALER_JSON_get_error_code (j);
|
||||
lr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||
|
@ -139,13 +139,13 @@ parse_decisions_ok (struct TALER_EXCHANGE_LookupAmlDecisions *lh,
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
lr.details.success.decisions_length = json_array_size (records);
|
||||
lr.details.ok.decisions_length = json_array_size (records);
|
||||
{
|
||||
struct TALER_EXCHANGE_AmlDecisionSummary decisions[
|
||||
GNUNET_NZL (lr.details.success.decisions_length)];
|
||||
GNUNET_NZL (lr.details.ok.decisions_length)];
|
||||
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
|
||||
|
||||
lr.details.success.decisions = decisions;
|
||||
lr.details.ok.decisions = decisions;
|
||||
ret = parse_aml_decisions (records,
|
||||
decisions);
|
||||
if (GNUNET_OK == ret)
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2020-2022 Taler Systems SA
|
||||
Copyright (C) 2020-2023 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -79,9 +79,9 @@ handle_drain_profits_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_ManagementDrainProfitsHandle *dp = cls;
|
||||
const json_t *json = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.http_status = (unsigned int) response_code,
|
||||
.reply = json
|
||||
struct TALER_EXCHANGE_ManagementDrainResponse dr = {
|
||||
.hr.http_status = (unsigned int) response_code,
|
||||
.hr.reply = json
|
||||
};
|
||||
|
||||
dp->job = NULL;
|
||||
@ -90,32 +90,32 @@ handle_drain_profits_finished (void *cls,
|
||||
case MHD_HTTP_NO_CONTENT:
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
dr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
dr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
case MHD_HTTP_CONFLICT:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
dr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
dr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
case MHD_HTTP_PRECONDITION_FAILED:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
dr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
dr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
dr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
dr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for exchange management drain profits\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) dr.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != dp->cb)
|
||||
{
|
||||
dp->cb (dp->cb_cls,
|
||||
&hr);
|
||||
&dr);
|
||||
dp->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_management_drain_profits_cancel (dp);
|
||||
|
@ -75,11 +75,16 @@ struct TALER_EXCHANGE_ManagementGetKeysHandle
|
||||
* @param response the response
|
||||
* @return #GNUNET_OK if the response was well-formed
|
||||
*/
|
||||
static int
|
||||
static enum GNUNET_GenericReturnValue
|
||||
handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
const json_t *response)
|
||||
{
|
||||
struct TALER_EXCHANGE_FutureKeys fk;
|
||||
struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
|
||||
.hr.http_status = MHD_HTTP_OK,
|
||||
.hr.reply = response,
|
||||
};
|
||||
struct TALER_EXCHANGE_FutureKeys *fk
|
||||
= &gkr.details.ok.keys;
|
||||
json_t *sk;
|
||||
json_t *dk;
|
||||
bool ok;
|
||||
@ -89,13 +94,13 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
GNUNET_JSON_spec_json ("future_signkeys",
|
||||
&sk),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_pub",
|
||||
&fk.master_pub),
|
||||
&fk->master_pub),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key",
|
||||
&fk.denom_secmod_public_key),
|
||||
&fk->denom_secmod_public_key),
|
||||
GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key",
|
||||
&fk.denom_secmod_cs_public_key),
|
||||
&fk->denom_secmod_cs_public_key),
|
||||
GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key",
|
||||
&fk.signkey_secmod_public_key),
|
||||
&fk->signkey_secmod_public_key),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
@ -107,21 +112,21 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
fk.num_sign_keys = json_array_size (sk);
|
||||
fk.num_denom_keys = json_array_size (dk);
|
||||
fk.sign_keys = GNUNET_new_array (
|
||||
fk.num_sign_keys,
|
||||
fk->num_sign_keys = json_array_size (sk);
|
||||
fk->num_denom_keys = json_array_size (dk);
|
||||
fk->sign_keys = GNUNET_new_array (
|
||||
fk->num_sign_keys,
|
||||
struct TALER_EXCHANGE_FutureSigningPublicKey);
|
||||
fk.denom_keys = GNUNET_new_array (
|
||||
fk.num_denom_keys,
|
||||
fk->denom_keys = GNUNET_new_array (
|
||||
fk->num_denom_keys,
|
||||
struct TALER_EXCHANGE_FutureDenomPublicKey);
|
||||
ok = true;
|
||||
for (unsigned int i = 0; i<fk.num_sign_keys; i++)
|
||||
for (unsigned int i = 0; i<fk->num_sign_keys; i++)
|
||||
{
|
||||
json_t *j = json_array_get (sk,
|
||||
i);
|
||||
struct TALER_EXCHANGE_FutureSigningPublicKey *sign_key
|
||||
= &fk.sign_keys[i];
|
||||
= &fk->sign_keys[i];
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("key",
|
||||
&sign_key->key),
|
||||
@ -155,7 +160,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
&sign_key->key,
|
||||
sign_key->valid_from,
|
||||
duration,
|
||||
&fk.signkey_secmod_public_key,
|
||||
&fk->signkey_secmod_public_key,
|
||||
&sign_key->signkey_secmod_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
@ -164,12 +169,12 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned int i = 0; i<fk.num_denom_keys; i++)
|
||||
for (unsigned int i = 0; i<fk->num_denom_keys; i++)
|
||||
{
|
||||
json_t *j = json_array_get (dk,
|
||||
i);
|
||||
struct TALER_EXCHANGE_FutureDenomPublicKey *denom_key
|
||||
= &fk.denom_keys[i];
|
||||
= &fk->denom_keys[i];
|
||||
const char *section_name;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
TALER_JSON_spec_amount_any ("value",
|
||||
@ -236,7 +241,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
section_name,
|
||||
denom_key->valid_from,
|
||||
duration,
|
||||
&fk.denom_secmod_public_key,
|
||||
&fk->denom_secmod_public_key,
|
||||
&denom_key->denom_secmod_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
@ -256,7 +261,7 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
section_name,
|
||||
denom_key->valid_from,
|
||||
duration,
|
||||
&fk.denom_secmod_cs_public_key,
|
||||
&fk->denom_secmod_cs_public_key,
|
||||
&denom_key->denom_secmod_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
@ -277,19 +282,13 @@ handle_ok (struct TALER_EXCHANGE_ManagementGetKeysHandle *gh,
|
||||
}
|
||||
if (ok)
|
||||
{
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.http_status = MHD_HTTP_OK,
|
||||
.reply = response
|
||||
};
|
||||
|
||||
gh->cb (gh->cb_cls,
|
||||
&hr,
|
||||
&fk);
|
||||
&gkr);
|
||||
}
|
||||
for (unsigned int i = 0; i<fk.num_denom_keys; i++)
|
||||
TALER_denom_pub_free (&fk.denom_keys[i].key);
|
||||
GNUNET_free (fk.sign_keys);
|
||||
GNUNET_free (fk.denom_keys);
|
||||
for (unsigned int i = 0; i<fk->num_denom_keys; i++)
|
||||
TALER_denom_pub_free (&fk->denom_keys[i].key);
|
||||
GNUNET_free (fk->sign_keys);
|
||||
GNUNET_free (fk->denom_keys);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return (ok) ? GNUNET_OK : GNUNET_SYSERR;
|
||||
}
|
||||
@ -310,9 +309,9 @@ handle_get_keys_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_ManagementGetKeysHandle *gh = cls;
|
||||
const json_t *json = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.http_status = (unsigned int) response_code,
|
||||
.reply = json
|
||||
struct TALER_EXCHANGE_ManagementGetKeysResponse gkr = {
|
||||
.hr.http_status = (unsigned int) response_code,
|
||||
.hr.reply = json
|
||||
};
|
||||
|
||||
gh->job = NULL;
|
||||
@ -334,25 +333,24 @@ handle_get_keys_finished (void *cls,
|
||||
/* unexpected response code */
|
||||
if (NULL != json)
|
||||
{
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
gkr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
gkr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
hr.hint = TALER_ErrorCode_get_hint (hr.ec);
|
||||
gkr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||
gkr.hr.hint = TALER_ErrorCode_get_hint (gkr.hr.ec);
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for exchange management get keys\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) gkr.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != gh->cb)
|
||||
{
|
||||
gh->cb (gh->cb_cls,
|
||||
&hr,
|
||||
NULL);
|
||||
&gkr);
|
||||
gh->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_get_management_keys_cancel (gh);
|
||||
|
@ -83,9 +83,9 @@ handle_post_extensions_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_ManagementPostExtensionsHandle *ph = cls;
|
||||
const json_t *json = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.http_status = (unsigned int) response_code,
|
||||
.reply = json
|
||||
struct TALER_EXCHANGE_ManagementPostExtensionsResponse per = {
|
||||
.hr.http_status = (unsigned int) response_code,
|
||||
.hr.reply = json
|
||||
};
|
||||
|
||||
ph->job = NULL;
|
||||
@ -94,28 +94,28 @@ handle_post_extensions_finished (void *cls,
|
||||
case MHD_HTTP_NO_CONTENT:
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
per.hr.ec = TALER_JSON_get_error_code (json);
|
||||
per.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
per.hr.ec = TALER_JSON_get_error_code (json);
|
||||
per.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
per.hr.ec = TALER_JSON_get_error_code (json);
|
||||
per.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for exchange management post extensions\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) per.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != ph->cb)
|
||||
{
|
||||
ph->cb (ph->cb_cls,
|
||||
&hr);
|
||||
&per);
|
||||
ph->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_management_post_extensions_cancel (ph);
|
||||
|
@ -82,9 +82,9 @@ handle_post_keys_finished (void *cls,
|
||||
{
|
||||
struct TALER_EXCHANGE_ManagementPostKeysHandle *ph = cls;
|
||||
const json_t *json = response;
|
||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
||||
.http_status = (unsigned int) response_code,
|
||||
.reply = json
|
||||
struct TALER_EXCHANGE_ManagementPostKeysResponse pkr = {
|
||||
.hr.http_status = (unsigned int) response_code,
|
||||
.hr.reply = json
|
||||
};
|
||||
|
||||
ph->job = NULL;
|
||||
@ -93,32 +93,32 @@ handle_post_keys_finished (void *cls,
|
||||
case MHD_HTTP_NO_CONTENT:
|
||||
break;
|
||||
case MHD_HTTP_FORBIDDEN:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
pkr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
pkr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
pkr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
pkr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE:
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
pkr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
pkr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_break_op (0);
|
||||
hr.ec = TALER_JSON_get_error_code (json);
|
||||
hr.hint = TALER_JSON_get_error_hint (json);
|
||||
pkr.hr.ec = TALER_JSON_get_error_code (json);
|
||||
pkr.hr.hint = TALER_JSON_get_error_hint (json);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u/%d for exchange management post keys\n",
|
||||
(unsigned int) response_code,
|
||||
(int) hr.ec);
|
||||
(int) pkr.hr.ec);
|
||||
break;
|
||||
}
|
||||
if (NULL != ph->cb)
|
||||
{
|
||||
ph->cb (ph->cb_cls,
|
||||
&hr);
|
||||
&pkr);
|
||||
ph->cb = NULL;
|
||||
}
|
||||
TALER_EXCHANGE_post_management_keys_cancel (ph);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user