Merge branch 'master' into age-withdraw
This commit is contained in:
commit
4833234df6
@ -1 +1 @@
|
|||||||
Subproject commit bf43b20a0362ac19bcf1bab9c33215e55d8d9f36
|
Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504
|
@ -31,7 +31,13 @@
|
|||||||
<member kind="define">
|
<member kind="define">
|
||||||
<type>#define</type>
|
<type>#define</type>
|
||||||
<name>GNUNET_TIME_UNIT_FOREVER_ABS</name>
|
<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>
|
<arglist></arglist>
|
||||||
</member>
|
</member>
|
||||||
</compound>
|
</compound>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
DOXYFILE_ENCODING = UTF-8
|
DOXYFILE_ENCODING = UTF-8
|
||||||
PROJECT_NAME = "GNU Taler: Exchange"
|
PROJECT_NAME = "GNU Taler: Exchange"
|
||||||
PROJECT_NUMBER = 0.8.3
|
PROJECT_NUMBER = 0.9.3
|
||||||
PROJECT_LOGO = logo.svg
|
PROJECT_LOGO = logo.svg
|
||||||
OUTPUT_DIRECTORY = .
|
OUTPUT_DIRECTORY = .
|
||||||
CREATE_SUBDIRS = YES
|
CREATE_SUBDIRS = YES
|
||||||
@ -97,59 +97,11 @@ WARN_LOGFILE =
|
|||||||
INPUT = ../../src
|
INPUT = ../../src
|
||||||
INPUT_ENCODING = UTF-8
|
INPUT_ENCODING = UTF-8
|
||||||
FILE_PATTERNS = *.c \
|
FILE_PATTERNS = *.c \
|
||||||
*.cc \
|
*.h
|
||||||
*.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
|
|
||||||
RECURSIVE = YES
|
RECURSIVE = YES
|
||||||
EXCLUDE =
|
EXCLUDE =
|
||||||
EXCLUDE_SYMLINKS = NO
|
EXCLUDE_SYMLINKS = NO
|
||||||
EXCLUDE_PATTERNS = */test_* \
|
EXCLUDE_PATTERNS = */test_* \
|
||||||
*/.svn/* \
|
|
||||||
*/.git/* \
|
*/.git/* \
|
||||||
*/perf_* .* \
|
*/perf_* .* \
|
||||||
.* \
|
.* \
|
||||||
@ -191,7 +143,9 @@ HTML_STYLESHEET =
|
|||||||
GENERATE_HTMLHELP = NO
|
GENERATE_HTMLHELP = NO
|
||||||
GENERATE_DOCSET = NO
|
GENERATE_DOCSET = NO
|
||||||
DOCSET_FEEDNAME = "GNU Taler Source Documentation"
|
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
|
HTML_DYNAMIC_SECTIONS = NO
|
||||||
CHM_FILE =
|
CHM_FILE =
|
||||||
HHC_LOCATION =
|
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}
|
@ -193,7 +193,7 @@ echo " DONE"
|
|||||||
|
|
||||||
echo -n "Setting up merchant"
|
echo -n "Setting up merchant"
|
||||||
|
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://x-taler-bank/localhost/43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
||||||
|
|
||||||
|
|
||||||
echo " DONE"
|
echo " DONE"
|
||||||
|
@ -398,7 +398,7 @@ echo " DONE"
|
|||||||
|
|
||||||
echo -n "Setting up merchant"
|
echo -n "Setting up merchant"
|
||||||
|
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"payto_uris":["payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
curl -H "Content-Type: application/json" -X POST -d '{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
||||||
|
|
||||||
|
|
||||||
echo " DONE"
|
echo " DONE"
|
||||||
|
@ -400,7 +400,7 @@ echo " DONE"
|
|||||||
# Setup merchant
|
# Setup merchant
|
||||||
echo -n "Setting up merchant"
|
echo -n "Setting up merchant"
|
||||||
|
|
||||||
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "payto_uris":["payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"}, "accounts":[{"payto_uri":"payto://iban/SANDBOXX/DE474361?receiver-name=Merchant43"}],"id":"default","name":"default","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' http://localhost:9966/management/instances
|
||||||
|
|
||||||
|
|
||||||
# run wallet CLI
|
# run wallet CLI
|
||||||
|
@ -605,6 +605,9 @@ main (int argc,
|
|||||||
level,
|
level,
|
||||||
NULL));
|
NULL));
|
||||||
GNUNET_free (level);
|
GNUNET_free (level);
|
||||||
|
/* suppress compiler warnings... */
|
||||||
|
GNUNET_assert (NULL != src_cfgfile);
|
||||||
|
GNUNET_assert (NULL != dst_cfgfile);
|
||||||
if (0 == strcmp (src_cfgfile,
|
if (0 == strcmp (src_cfgfile,
|
||||||
dst_cfgfile))
|
dst_cfgfile))
|
||||||
{
|
{
|
||||||
|
@ -172,9 +172,9 @@ coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
|
|||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
|
|
||||||
memcpy (&i,
|
GNUNET_memcpy (&i,
|
||||||
coin_pub,
|
coin_pub,
|
||||||
sizeof (i));
|
sizeof (i));
|
||||||
return i % MAX_COIN_HISTORIES;
|
return i % MAX_COIN_HISTORIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,12 +674,12 @@ hash_rc (const char *receiver_account,
|
|||||||
size_t slen = strlen (receiver_account);
|
size_t slen = strlen (receiver_account);
|
||||||
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
|
char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
|
||||||
|
|
||||||
memcpy (buf,
|
GNUNET_memcpy (buf,
|
||||||
wtid,
|
wtid,
|
||||||
sizeof (*wtid));
|
sizeof (*wtid));
|
||||||
memcpy (&buf[sizeof (*wtid)],
|
GNUNET_memcpy (&buf[sizeof (*wtid)],
|
||||||
receiver_account,
|
receiver_account,
|
||||||
slen);
|
slen);
|
||||||
GNUNET_CRYPTO_hash (buf,
|
GNUNET_CRYPTO_hash (buf,
|
||||||
sizeof (buf),
|
sizeof (buf),
|
||||||
key);
|
key);
|
||||||
@ -1483,10 +1483,10 @@ history_debit_cb (void *cls,
|
|||||||
switch (dhr->http_status)
|
switch (dhr->http_status)
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
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
|
const struct TALER_BANK_DebitDetails *dd
|
||||||
= &dhr->details.success.details[i];
|
= &dhr->details.ok.details[i];
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||||
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
|
"Analyzing bank DEBIT at %s of %s with WTID %s\n",
|
||||||
GNUNET_TIME_timestamp2s (dd->execution_date),
|
GNUNET_TIME_timestamp2s (dd->execution_date),
|
||||||
@ -1504,9 +1504,9 @@ history_debit_cb (void *cls,
|
|||||||
roi->details.execution_date = dd->execution_date;
|
roi->details.execution_date = dd->execution_date;
|
||||||
roi->details.wtid = dd->wtid;
|
roi->details.wtid = dd->wtid;
|
||||||
roi->details.credit_account_uri = (const char *) &roi[1];
|
roi->details.credit_account_uri = (const char *) &roi[1];
|
||||||
memcpy (&roi[1],
|
GNUNET_memcpy (&roi[1],
|
||||||
dd->credit_account_uri,
|
dd->credit_account_uri,
|
||||||
slen);
|
slen);
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONTAINER_multihashmap_put (out_map,
|
GNUNET_CONTAINER_multihashmap_put (out_map,
|
||||||
&roi->subject_hash,
|
&roi->subject_hash,
|
||||||
@ -1678,9 +1678,9 @@ reserve_in_cb (void *cls,
|
|||||||
rii->details.execution_date = execution_date;
|
rii->details.execution_date = execution_date;
|
||||||
rii->details.reserve_pub = *reserve_pub;
|
rii->details.reserve_pub = *reserve_pub;
|
||||||
rii->details.debit_account_uri = (const char *) &rii[1];
|
rii->details.debit_account_uri = (const char *) &rii[1];
|
||||||
memcpy (&rii[1],
|
GNUNET_memcpy (&rii[1],
|
||||||
sender_account_details,
|
sender_account_details,
|
||||||
slen);
|
slen);
|
||||||
GNUNET_CRYPTO_hash (&wire_reference,
|
GNUNET_CRYPTO_hash (&wire_reference,
|
||||||
sizeof (uint64_t),
|
sizeof (uint64_t),
|
||||||
&rii->row_off_hash);
|
&rii->row_off_hash);
|
||||||
@ -1978,10 +1978,10 @@ history_credit_cb (void *cls,
|
|||||||
switch (chr->http_status)
|
switch (chr->http_status)
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
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
|
const struct TALER_BANK_CreditDetails *cd
|
||||||
= &chr->details.success.details[i];
|
= &chr->details.ok.details[i];
|
||||||
|
|
||||||
if (! analyze_credit (wa,
|
if (! analyze_credit (wa,
|
||||||
cd))
|
cd))
|
||||||
|
@ -131,8 +131,8 @@ parse_account_history (struct TALER_BANK_CreditHistoryHandle *hh,
|
|||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chr.details.success.details_length = len;
|
chr.details.ok.details_length = len;
|
||||||
chr.details.success.details = cd;
|
chr.details.ok.details = cd;
|
||||||
hh->hcb (hh->hcb_cls,
|
hh->hcb (hh->hcb_cls,
|
||||||
&chr);
|
&chr);
|
||||||
}
|
}
|
||||||
|
@ -133,8 +133,8 @@ parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
|
|||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dhr.details.success.details_length = len;
|
dhr.details.ok.details_length = len;
|
||||||
dhr.details.success.details = dd;
|
dhr.details.ok.details = dd;
|
||||||
hh->hcb (hh->hcb_cls,
|
hh->hcb (hh->hcb_cls,
|
||||||
&dhr);
|
&dhr);
|
||||||
}
|
}
|
||||||
|
@ -99,12 +99,12 @@ TALER_BANK_prepare_transfer (
|
|||||||
wp->account_len = htonl ((uint32_t) d_len);
|
wp->account_len = htonl ((uint32_t) d_len);
|
||||||
wp->exchange_url_len = htonl ((uint32_t) u_len);
|
wp->exchange_url_len = htonl ((uint32_t) u_len);
|
||||||
end = (char *) &wp[1];
|
end = (char *) &wp[1];
|
||||||
memcpy (end,
|
GNUNET_memcpy (end,
|
||||||
destination_account_payto_uri,
|
destination_account_payto_uri,
|
||||||
d_len);
|
d_len);
|
||||||
memcpy (end + d_len,
|
GNUNET_memcpy (end + d_len,
|
||||||
exchange_base_url,
|
exchange_base_url,
|
||||||
u_len);
|
u_len);
|
||||||
*buf = (char *) wp;
|
*buf = (char *) wp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1389,9 +1389,9 @@ make_transfer (
|
|||||||
if (NULL != timestamp)
|
if (NULL != timestamp)
|
||||||
*timestamp = t->date;
|
*timestamp = t->date;
|
||||||
t->type = T_DEBIT;
|
t->type = T_DEBIT;
|
||||||
memcpy (t->subject.debit.exchange_base_url,
|
GNUNET_memcpy (t->subject.debit.exchange_base_url,
|
||||||
exchange_base_url,
|
exchange_base_url,
|
||||||
url_len);
|
url_len);
|
||||||
t->subject.debit.wtid = *subject;
|
t->subject.debit.wtid = *subject;
|
||||||
if (NULL == request_uid)
|
if (NULL == request_uid)
|
||||||
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
|
GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
|
||||||
|
@ -179,10 +179,10 @@ credit_history_cb (void *cls,
|
|||||||
global_ret = 0;
|
global_ret = 0;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
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 =
|
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 credit/debit accounts were specified, use as a filter */
|
||||||
if ( (NULL != credit_account) &&
|
if ( (NULL != credit_account) &&
|
||||||
@ -279,10 +279,10 @@ debit_history_cb (void *cls,
|
|||||||
global_ret = 0;
|
global_ret = 0;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
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 =
|
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 credit/debit accounts were specified, use as a filter */
|
||||||
if ( (NULL != credit_account) &&
|
if ( (NULL != credit_account) &&
|
||||||
|
@ -644,26 +644,19 @@ do_upload (char *const *args)
|
|||||||
* a particular exchange and what keys the exchange is using.
|
* a particular exchange and what keys the exchange is using.
|
||||||
*
|
*
|
||||||
* @param cls closure with the `char **` remaining args
|
* @param cls closure with the `char **` remaining args
|
||||||
* @param hr HTTP response data
|
* @param kr response data
|
||||||
* @param keys information about the various keys used
|
|
||||||
* by the exchange, NULL if /keys failed
|
|
||||||
* @param compat protocol compatibility information
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
keys_cb (
|
keys_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_KeysResponse *kr)
|
||||||
const struct TALER_EXCHANGE_Keys *keys,
|
|
||||||
enum TALER_EXCHANGE_VersionCompatibility compat)
|
|
||||||
{
|
{
|
||||||
char *const *args = cls;
|
char *const *args = cls;
|
||||||
|
|
||||||
(void) keys;
|
switch (kr->hr.http_status)
|
||||||
(void) compat;
|
|
||||||
switch (hr->http_status)
|
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (! json_is_object (hr->reply))
|
if (! json_is_object (kr->hr.reply))
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
TALER_EXCHANGE_disconnect (exchange);
|
TALER_EXCHANGE_disconnect (exchange);
|
||||||
@ -676,9 +669,9 @@ keys_cb (
|
|||||||
default:
|
default:
|
||||||
fprintf (stderr,
|
fprintf (stderr,
|
||||||
"Failed to download keys: %s (HTTP status: %u/%u)\n",
|
"Failed to download keys: %s (HTTP status: %u/%u)\n",
|
||||||
hr->hint,
|
kr->hr.hint,
|
||||||
hr->http_status,
|
kr->hr.http_status,
|
||||||
(unsigned int) hr->ec);
|
(unsigned int) kr->hr.ec);
|
||||||
TALER_EXCHANGE_disconnect (exchange);
|
TALER_EXCHANGE_disconnect (exchange);
|
||||||
exchange = NULL;
|
exchange = NULL;
|
||||||
test_shutdown ();
|
test_shutdown ();
|
||||||
@ -689,7 +682,7 @@ keys_cb (
|
|||||||
GNUNET_JSON_pack_string ("operation",
|
GNUNET_JSON_pack_string ("operation",
|
||||||
OP_INPUT_KEYS),
|
OP_INPUT_KEYS),
|
||||||
GNUNET_JSON_pack_object_incref ("arguments",
|
GNUNET_JSON_pack_object_incref ("arguments",
|
||||||
(json_t *) hr->reply));
|
(json_t *) kr->hr.reply));
|
||||||
if (NULL == args[0])
|
if (NULL == args[0])
|
||||||
{
|
{
|
||||||
json_dumpf (in,
|
json_dumpf (in,
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_exchange_service.h"
|
#include "taler_exchange_service.h"
|
||||||
#include "taler_extensions.h"
|
#include "taler_extensions.h"
|
||||||
|
#include <regex.h>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the input for the 'sign' and 'show' operation.
|
* 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.
|
* Function called with information about the post revocation operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct DenomRevocationRequest`
|
* @param cls closure with a `struct DenomRevocationRequest`
|
||||||
* @param hr HTTP response data
|
* @param dr response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
denom_revocation_cb (
|
denom_revocation_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr)
|
||||||
{
|
{
|
||||||
struct DenomRevocationRequest *drr = cls;
|
struct DenomRevocationRequest *drr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the post revocation operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct SignkeyRevocationRequest`
|
* @param cls closure with a `struct SignkeyRevocationRequest`
|
||||||
* @param hr HTTP response data
|
* @param sr response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
signkey_revocation_cb (
|
signkey_revocation_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr)
|
||||||
{
|
{
|
||||||
struct SignkeyRevocationRequest *srr = cls;
|
struct SignkeyRevocationRequest *srr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the post wire add operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct WireAddRequest`
|
* @param cls closure with a `struct WireAddRequest`
|
||||||
* @param hr HTTP response data
|
* @param wer response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
wire_add_cb (void *cls,
|
wire_add_cb (void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer)
|
||||||
{
|
{
|
||||||
struct WireAddRequest *war = cls;
|
struct WireAddRequest *war = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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 GNUNET_TIME_Timestamp start_time;
|
||||||
struct WireAddRequest *war;
|
struct WireAddRequest *war;
|
||||||
const char *err_name;
|
const char *err_name;
|
||||||
|
const char *conversion_url = NULL;
|
||||||
|
json_t *debit_restrictions;
|
||||||
|
json_t *credit_restrictions;
|
||||||
unsigned int err_line;
|
unsigned int err_line;
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_string ("payto_uri",
|
GNUNET_JSON_spec_string ("payto_uri",
|
||||||
&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",
|
GNUNET_JSON_spec_timestamp ("validity_start",
|
||||||
&start_time),
|
&start_time),
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
|
GNUNET_JSON_spec_fixed_auto ("master_sig_add",
|
||||||
@ -1561,6 +1577,7 @@ upload_wire_add (const char *exchange_url,
|
|||||||
stderr,
|
stderr,
|
||||||
JSON_INDENT (2));
|
JSON_INDENT (2));
|
||||||
global_ret = EXIT_FAILURE;
|
global_ret = EXIT_FAILURE;
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
test_shutdown ();
|
test_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1574,6 +1591,7 @@ upload_wire_add (const char *exchange_url,
|
|||||||
"payto:// URI `%s' is malformed\n",
|
"payto:// URI `%s' is malformed\n",
|
||||||
payto_uri);
|
payto_uri);
|
||||||
global_ret = EXIT_FAILURE;
|
global_ret = EXIT_FAILURE;
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
test_shutdown ();
|
test_shutdown ();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1588,6 +1606,7 @@ upload_wire_add (const char *exchange_url,
|
|||||||
"payto URI is malformed: %s\n",
|
"payto URI is malformed: %s\n",
|
||||||
msg);
|
msg);
|
||||||
GNUNET_free (msg);
|
GNUNET_free (msg);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
test_shutdown ();
|
test_shutdown ();
|
||||||
global_ret = EXIT_INVALIDARGUMENT;
|
global_ret = EXIT_INVALIDARGUMENT;
|
||||||
return;
|
return;
|
||||||
@ -1599,6 +1618,9 @@ upload_wire_add (const char *exchange_url,
|
|||||||
TALER_EXCHANGE_management_enable_wire (ctx,
|
TALER_EXCHANGE_management_enable_wire (ctx,
|
||||||
exchange_url,
|
exchange_url,
|
||||||
payto_uri,
|
payto_uri,
|
||||||
|
conversion_url,
|
||||||
|
debit_restrictions,
|
||||||
|
credit_restrictions,
|
||||||
start_time,
|
start_time,
|
||||||
&master_sig_add,
|
&master_sig_add,
|
||||||
&master_sig_wire,
|
&master_sig_wire,
|
||||||
@ -1607,6 +1629,7 @@ upload_wire_add (const char *exchange_url,
|
|||||||
GNUNET_CONTAINER_DLL_insert (war_head,
|
GNUNET_CONTAINER_DLL_insert (war_head,
|
||||||
war_tail,
|
war_tail,
|
||||||
war);
|
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.
|
* Function called with information about the post wire del operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct WireDelRequest`
|
* @param cls closure with a `struct WireDelRequest`
|
||||||
* @param hr HTTP response data
|
* @param wdres response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
wire_del_cb (void *cls,
|
wire_del_cb (void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres)
|
||||||
{
|
{
|
||||||
struct WireDelRequest *wdr = cls;
|
struct WireDelRequest *wdr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the drain profits operation.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct DrainProfitsRequest`
|
* @param cls closure with a `struct DrainProfitsRequest`
|
||||||
* @param hr HTTP response data
|
* @param mdr response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
drain_profits_cb (
|
drain_profits_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementDrainResponse *mdr)
|
||||||
{
|
{
|
||||||
struct DrainProfitsRequest *dpr = cls;
|
struct DrainProfitsRequest *dpr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the post upload keys operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct UploadKeysRequest`
|
* @param cls closure with a `struct UploadKeysRequest`
|
||||||
* @param hr HTTP response data
|
* @param mr response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
keys_cb (
|
keys_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr)
|
||||||
{
|
{
|
||||||
struct UploadKeysRequest *ukr = cls;
|
struct UploadKeysRequest *ukr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the post upload extensions operation result.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct UploadExtensionsRequest`
|
* @param cls closure with a `struct UploadExtensionsRequest`
|
||||||
* @param hr HTTP response data
|
* @param er response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
extensions_cb (
|
extensions_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er)
|
||||||
{
|
{
|
||||||
struct UploadExtensionsRequest *uer = cls;
|
struct UploadExtensionsRequest *uer = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Function called with information about the AML officer update operation.
|
||||||
*
|
*
|
||||||
* @param cls closure with a `struct AmlStaffRequest`
|
* @param cls closure with a `struct AmlStaffRequest`
|
||||||
* @param hr HTTP response data
|
* @param ar response data
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
update_aml_officer_cb (
|
update_aml_officer_cb (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr)
|
const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar)
|
||||||
{
|
{
|
||||||
struct AmlStaffRequest *asr = cls;
|
struct AmlStaffRequest *asr = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr;
|
||||||
|
|
||||||
if (MHD_HTTP_NO_CONTENT != hr->http_status)
|
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.
|
* Add wire account.
|
||||||
*
|
*
|
||||||
@ -2961,6 +3079,10 @@ do_add_wire (char *const *args)
|
|||||||
struct TALER_MasterSignatureP master_sig_add;
|
struct TALER_MasterSignatureP master_sig_add;
|
||||||
struct TALER_MasterSignatureP master_sig_wire;
|
struct TALER_MasterSignatureP master_sig_wire;
|
||||||
struct GNUNET_TIME_Timestamp now;
|
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)
|
if (NULL != in)
|
||||||
{
|
{
|
||||||
@ -3011,24 +3133,101 @@ do_add_wire (char *const *args)
|
|||||||
}
|
}
|
||||||
GNUNET_free (wire_method);
|
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],
|
TALER_exchange_offline_wire_add_sign (args[0],
|
||||||
|
conversion_url,
|
||||||
|
debit_restrictions,
|
||||||
|
credit_restrictions,
|
||||||
now,
|
now,
|
||||||
&master_priv,
|
&master_priv,
|
||||||
&master_sig_add);
|
&master_sig_add);
|
||||||
TALER_exchange_wire_signature_make (args[0],
|
TALER_exchange_wire_signature_make (args[0],
|
||||||
|
conversion_url,
|
||||||
|
debit_restrictions,
|
||||||
|
credit_restrictions,
|
||||||
&master_priv,
|
&master_priv,
|
||||||
&master_sig_wire);
|
&master_sig_wire);
|
||||||
output_operation (OP_ENABLE_WIRE,
|
output_operation (OP_ENABLE_WIRE,
|
||||||
GNUNET_JSON_PACK (
|
GNUNET_JSON_PACK (
|
||||||
GNUNET_JSON_pack_string ("payto_uri",
|
GNUNET_JSON_pack_string ("payto_uri",
|
||||||
args[0]),
|
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",
|
GNUNET_JSON_pack_timestamp ("validity_start",
|
||||||
now),
|
now),
|
||||||
GNUNET_JSON_pack_data_auto ("master_sig_add",
|
GNUNET_JSON_pack_data_auto ("master_sig_add",
|
||||||
&master_sig_add),
|
&master_sig_add),
|
||||||
GNUNET_JSON_pack_data_auto ("master_sig_wire",
|
GNUNET_JSON_pack_data_auto ("master_sig_wire",
|
||||||
&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).
|
* whether there are subsequent commands).
|
||||||
*
|
*
|
||||||
* @param cls closure with the `char **` remaining args
|
* @param cls closure with the `char **` remaining args
|
||||||
* @param hr HTTP response data
|
* @param mgr response data
|
||||||
* @param keys information about the various keys used
|
|
||||||
* by the exchange, NULL if /management/keys failed
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
download_cb (void *cls,
|
download_cb (void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr)
|
||||||
const struct TALER_EXCHANGE_FutureKeys *keys)
|
|
||||||
{
|
{
|
||||||
char *const *args = cls;
|
char *const *args = cls;
|
||||||
|
const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr;
|
||||||
|
|
||||||
(void) keys;
|
|
||||||
mgkh = NULL;
|
mgkh = NULL;
|
||||||
switch (hr->http_status)
|
switch (hr->http_status)
|
||||||
{
|
{
|
||||||
@ -5045,7 +5241,7 @@ work (void *cls)
|
|||||||
{
|
{
|
||||||
.name = "enable-account",
|
.name = "enable-account",
|
||||||
.help =
|
.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
|
.cb = &do_add_wire
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -312,10 +312,10 @@ expired_reserve_cb (void *cls,
|
|||||||
memset (&wtid,
|
memset (&wtid,
|
||||||
0,
|
0,
|
||||||
sizeof (wtid));
|
sizeof (wtid));
|
||||||
memcpy (&wtid,
|
GNUNET_memcpy (&wtid,
|
||||||
reserve_pub,
|
reserve_pub,
|
||||||
GNUNET_MIN (sizeof (wtid),
|
GNUNET_MIN (sizeof (wtid),
|
||||||
sizeof (*reserve_pub)));
|
sizeof (*reserve_pub)));
|
||||||
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
|
qs = db_plugin->insert_reserve_closed (db_plugin->cls,
|
||||||
reserve_pub,
|
reserve_pub,
|
||||||
now,
|
now,
|
||||||
|
@ -542,7 +542,6 @@ handle_get_aml (struct TEH_RequestContext *rc,
|
|||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
NULL);
|
NULL);
|
||||||
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
|
||||||
GNUNET_break_op (0);
|
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_FORBIDDEN,
|
MHD_HTTP_FORBIDDEN,
|
||||||
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
|
TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
|
||||||
@ -932,9 +931,9 @@ proceed_with_handler (struct TEH_RequestContext *rc,
|
|||||||
|
|
||||||
/* Parse command-line arguments */
|
/* Parse command-line arguments */
|
||||||
/* make a copy of 'url' because 'strtok_r()' will modify */
|
/* make a copy of 'url' because 'strtok_r()' will modify */
|
||||||
memcpy (d,
|
GNUNET_memcpy (d,
|
||||||
url,
|
url,
|
||||||
ulen);
|
ulen);
|
||||||
i = 0;
|
i = 0;
|
||||||
args[i++] = strtok_r (d, "/", &sp);
|
args[i++] = strtok_r (d, "/", &sp);
|
||||||
while ( (NULL != args[i - 1]) &&
|
while ( (NULL != args[i - 1]) &&
|
||||||
@ -1617,33 +1616,8 @@ handle_mhd_request (void *cls,
|
|||||||
if (0 == strcasecmp (method,
|
if (0 == strcasecmp (method,
|
||||||
MHD_HTTP_METHOD_POST))
|
MHD_HTTP_METHOD_POST))
|
||||||
{
|
{
|
||||||
const char *cl;
|
TALER_MHD_check_content_length (connection,
|
||||||
|
TALER_MHD_REQUEST_BUFFER_MAX);
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2215,6 +2189,7 @@ do_shutdown (void *cls)
|
|||||||
|
|
||||||
mhd = TALER_MHD_daemon_stop ();
|
mhd = TALER_MHD_daemon_stop ();
|
||||||
TEH_resume_keys_requests (true);
|
TEH_resume_keys_requests (true);
|
||||||
|
TEH_deposits_get_cleanup ();
|
||||||
TEH_reserves_get_cleanup ();
|
TEH_reserves_get_cleanup ();
|
||||||
TEH_purses_get_cleanup ();
|
TEH_purses_get_cleanup ();
|
||||||
TEH_kyc_check_cleanup ();
|
TEH_kyc_check_cleanup ();
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
|
#include "taler-exchange-httpd_metrics.h"
|
||||||
|
#include "taler_exchangedb_plugin.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler-exchange-httpd_mhd.h"
|
#include "taler-exchange-httpd_mhd.h"
|
||||||
#include "taler-exchange-httpd_age-withdraw_reveal.h"
|
#include "taler-exchange-httpd_age-withdraw_reveal.h"
|
||||||
@ -387,12 +389,10 @@ denomination_is_valid (
|
|||||||
struct TEH_DenominationKey *dks,
|
struct TEH_DenominationKey *dks,
|
||||||
MHD_RESULT *result)
|
MHD_RESULT *result)
|
||||||
{
|
{
|
||||||
dks = TEH_keys_denomination_by_hash2 (
|
dks = TEH_keys_denomination_by_hash2 (ksh,
|
||||||
ksh,
|
denom_h,
|
||||||
denom_h,
|
connection,
|
||||||
connection,
|
result);
|
||||||
result);
|
|
||||||
|
|
||||||
if (NULL == dks)
|
if (NULL == dks)
|
||||||
{
|
{
|
||||||
/* The denomination doesn't exist */
|
/* The denomination doesn't exist */
|
||||||
@ -635,7 +635,7 @@ verify_commitment_and_max_age (
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* FIXME:oec: Refactor this block out into its own function */
|
/* FIXME[oec] Refactor this block out into its own function */
|
||||||
|
|
||||||
size_t j = (TALER_CNC_KAPPA - 1) * c + k; /* Index into disclosed_coin_secrets[] */
|
size_t j = (TALER_CNC_KAPPA - 1) * c + k; /* Index into disclosed_coin_secrets[] */
|
||||||
const struct TALER_PlanchetMasterSecretP *secret;
|
const struct TALER_PlanchetMasterSecretP *secret;
|
||||||
@ -784,6 +784,43 @@ verify_commitment_and_max_age (
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Send a response for "/age-withdraw/$RCH/reveal"
|
||||||
|
*
|
||||||
|
* @param connection The http connection to the client to send the response to
|
||||||
|
* @param num_coins Number of new coins with age restriction for which we reveal data
|
||||||
|
* @param awrcs array of @a num_coins signatures revealed
|
||||||
|
* @return a MHD result code
|
||||||
|
*/
|
||||||
|
static MHD_RESULT
|
||||||
|
reply_age_withdraw_reveal_success (
|
||||||
|
struct MHD_Connection *connection,
|
||||||
|
unsigned int num_coins,
|
||||||
|
const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs)
|
||||||
|
{
|
||||||
|
json_t *list = json_array ();
|
||||||
|
GNUNET_assert (NULL != list);
|
||||||
|
|
||||||
|
for (unsigned int index = 0;
|
||||||
|
index < num_coins;
|
||||||
|
index++)
|
||||||
|
{
|
||||||
|
json_t *obj = GNUNET_JSON_PACK (
|
||||||
|
TALER_JSON_pack_blinded_denom_sig ("ev_sig",
|
||||||
|
&awrcs[index].coin_sig));
|
||||||
|
GNUNET_assert (0 ==
|
||||||
|
json_array_append_new (list,
|
||||||
|
obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
|
connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
GNUNET_JSON_pack_array_steal ("ev_sigs",
|
||||||
|
list));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Signs and persists the undisclosed coins
|
* @brief Signs and persists the undisclosed coins
|
||||||
*
|
*
|
||||||
@ -796,7 +833,7 @@ verify_commitment_and_max_age (
|
|||||||
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
|
* @return GNUNET_OK on success, GNUNET_SYSERR otherwise
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_GenericReturnValue
|
static enum GNUNET_GenericReturnValue
|
||||||
finalize_age_withdraw_and_sign (
|
sign_and_finalize_age_withdraw (
|
||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
|
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
|
||||||
const uint32_t num_coins,
|
const uint32_t num_coins,
|
||||||
@ -806,7 +843,9 @@ finalize_age_withdraw_and_sign (
|
|||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
|
enum GNUNET_GenericReturnValue ret = GNUNET_SYSERR;
|
||||||
struct TEH_CoinSignData csds[num_coins];
|
struct TEH_CoinSignData csds[num_coins];
|
||||||
struct TALER_BlindedDenominationSignature bss[num_coins];
|
struct TALER_BlindedDenominationSignature bds[num_coins];
|
||||||
|
struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin awrcs[num_coins];
|
||||||
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
|
|
||||||
for (uint32_t i = 0; i<num_coins; i++)
|
for (uint32_t i = 0; i<num_coins; i++)
|
||||||
{
|
{
|
||||||
@ -814,13 +853,13 @@ finalize_age_withdraw_and_sign (
|
|||||||
csds[i].bp = &coin_evs[i];
|
csds[i].bp = &coin_evs[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* First, sign the the blinded coins */
|
/* Sign the the blinded coins first */
|
||||||
{
|
{
|
||||||
enum TALER_ErrorCode ec;
|
enum TALER_ErrorCode ec;
|
||||||
ec = TEH_keys_denomination_batch_sign (csds,
|
ec = TEH_keys_denomination_batch_sign (csds,
|
||||||
num_coins,
|
num_coins,
|
||||||
false,
|
false,
|
||||||
bss);
|
bds);
|
||||||
if (TALER_EC_NONE != ec)
|
if (TALER_EC_NONE != ec)
|
||||||
{
|
{
|
||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
@ -831,12 +870,107 @@ finalize_age_withdraw_and_sign (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO[oec]:
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
* - in a transaction: save the coins.
|
"Signatures ready, starting DB interaction\n");
|
||||||
* - add signature response
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma message "FIXME[oec]: implement finalize_age_withdraw_and_sign"
|
/* Prepare the data for insertion */
|
||||||
|
for (uint32_t i = 0; i<num_coins; i++)
|
||||||
|
{
|
||||||
|
TALER_coin_ev_hash (&coin_evs[i],
|
||||||
|
csds[i].h_denom_pub,
|
||||||
|
&awrcs[i].h_coin_ev);
|
||||||
|
awrcs[i].h_denom_pub = *csds[i].h_denom_pub;
|
||||||
|
awrcs[i].coin_sig = bds[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Persist operation result in DB, transactionally */
|
||||||
|
for (unsigned int r = 0; r < MAX_TRANSACTION_COMMIT_RETRIES; r++)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
/* Transaction start */
|
||||||
|
if (GNUNET_OK !=
|
||||||
|
TEH_plugin->start (TEH_plugin->cls,
|
||||||
|
"insert_age_withdraw_reveal batch"))
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
ret = TALER_MHD_reply_with_error (connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_START_FAILED,
|
||||||
|
NULL);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
qs = TEH_plugin->insert_age_withdraw_reveal (TEH_plugin->cls,
|
||||||
|
h_commitment,
|
||||||
|
num_coins,
|
||||||
|
awrcs);
|
||||||
|
|
||||||
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
|
{
|
||||||
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
ret = TALER_MHD_reply_with_error (connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_STORE_FAILED,
|
||||||
|
"insert_age_withdraw_reveal");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
changed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
|
||||||
|
|
||||||
|
/* Commit the transaction */
|
||||||
|
qs = TEH_plugin->commit (TEH_plugin->cls);
|
||||||
|
if (qs >= 0)
|
||||||
|
{
|
||||||
|
if (changed)
|
||||||
|
TEH_METRICS_num_success[TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL]++;
|
||||||
|
|
||||||
|
break; /* success */
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
ret = TALER_MHD_reply_with_error (connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_COMMIT_FAILED,
|
||||||
|
NULL);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
}
|
||||||
|
} /* end of retry */
|
||||||
|
|
||||||
|
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
|
TEH_plugin->rollback (TEH_plugin->cls);
|
||||||
|
ret = TALER_MHD_reply_with_error (connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_SOFT_FAILURE,
|
||||||
|
NULL);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate final (positive) response */
|
||||||
|
ret = reply_age_withdraw_reveal_success (connection,
|
||||||
|
num_coins,
|
||||||
|
awrcs);
|
||||||
|
cleanup:
|
||||||
|
GNUNET_break (GNUNET_OK != ret);
|
||||||
|
|
||||||
|
/* Free resources */
|
||||||
|
for (unsigned int i = 0; i<num_coins; i++)
|
||||||
|
TALER_blinded_denom_sig_free (&awrcs[i].coin_sig);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -922,7 +1056,7 @@ TEH_handler_age_withdraw_reveal (
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
/* Finally, sign and persist the coins */
|
/* Finally, sign and persist the coins */
|
||||||
if (GNUNET_OK != finalize_age_withdraw_and_sign (
|
if (GNUNET_OK != sign_and_finalize_age_withdraw (
|
||||||
rc->connection,
|
rc->connection,
|
||||||
&actx.commitment.h_commitment,
|
&actx.commitment.h_commitment,
|
||||||
actx.num_coins,
|
actx.num_coins,
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
*
|
*
|
||||||
* Returned via both /config and /keys endpoints.
|
* 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
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -23,6 +23,7 @@
|
|||||||
#include <jansson.h>
|
#include <jansson.h>
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include "taler_dbevents.h"
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_mhd_lib.h"
|
#include "taler_mhd_lib.h"
|
||||||
#include "taler_signatures.h"
|
#include "taler_signatures.h"
|
||||||
@ -37,6 +38,26 @@
|
|||||||
struct DepositWtidContext
|
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.
|
* Hash over the proposal data of the contract for which this deposit is made.
|
||||||
*/
|
*/
|
||||||
@ -64,6 +85,12 @@ struct DepositWtidContext
|
|||||||
*/
|
*/
|
||||||
struct TALER_WireTransferIdentifierRawP wtid;
|
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.
|
* 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;
|
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
|
* Set by #handle_wtid to the coin contribution to the transaction
|
||||||
* (that is, @e coin_contribution minus @e coin_fee).
|
* (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.
|
* Set to #GNUNET_SYSERR if there was a serious error.
|
||||||
*/
|
*/
|
||||||
enum GNUNET_GenericReturnValue pending;
|
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
|
* A merchant asked for details about a deposit. Provide
|
||||||
* them. Generates the 200 reply.
|
* 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.
|
* Lookup and return the wire transfer identifier.
|
||||||
*
|
*
|
||||||
* @param connection the MHD connection to handle
|
|
||||||
* @param ctx context of the signed request to execute
|
* @param ctx context of the signed request to execute
|
||||||
* @return MHD result code
|
* @return MHD result code
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
handle_track_transaction_request (
|
handle_track_transaction_request (
|
||||||
struct MHD_Connection *connection,
|
|
||||||
struct DepositWtidContext *ctx)
|
struct DepositWtidContext *ctx)
|
||||||
{
|
{
|
||||||
MHD_RESULT mhd_ret;
|
struct MHD_Connection *connection = ctx->rc->connection;
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if ( (GNUNET_TIME_absolute_is_future (ctx->timeout)) &&
|
||||||
TEH_DB_run_transaction (connection,
|
(NULL == ctx->eh) )
|
||||||
"handle deposits GET",
|
{
|
||||||
TEH_MT_REQUEST_OTHER,
|
struct TALER_CoinDepositEventP rep = {
|
||||||
&mhd_ret,
|
.header.size = htons (sizeof (rep)),
|
||||||
&deposits_get_transaction,
|
.header.type = htons (TALER_DBEVENT_EXCHANGE_DEPOSIT_STATUS_CHANGED),
|
||||||
ctx))
|
.merchant_pub = ctx->merchant
|
||||||
return mhd_ret;
|
};
|
||||||
|
|
||||||
|
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)
|
if (GNUNET_SYSERR == ctx->pending)
|
||||||
return TALER_MHD_reply_with_error (connection,
|
return TALER_MHD_reply_with_error (connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
|
TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
|
||||||
"wire fees exceed aggregate in database");
|
"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 (
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
connection,
|
connection,
|
||||||
MHD_HTTP_ACCEPTED,
|
MHD_HTTP_ACCEPTED,
|
||||||
@ -270,94 +401,118 @@ handle_track_transaction_request (
|
|||||||
ctx->kyc.ok),
|
ctx->kyc.ok),
|
||||||
GNUNET_JSON_pack_timestamp ("execution_time",
|
GNUNET_JSON_pack_timestamp ("execution_time",
|
||||||
ctx->execution_time));
|
ctx->execution_time));
|
||||||
|
}
|
||||||
return reply_deposit_details (connection,
|
return reply_deposit_details (connection,
|
||||||
ctx);
|
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
|
MHD_RESULT
|
||||||
TEH_handler_deposits_get (struct TEH_RequestContext *rc,
|
TEH_handler_deposits_get (struct TEH_RequestContext *rc,
|
||||||
const char *const args[4])
|
const char *const args[4])
|
||||||
{
|
{
|
||||||
enum GNUNET_GenericReturnValue res;
|
struct DepositWtidContext *ctx = rc->rh_ctx;
|
||||||
struct TALER_MerchantSignatureP merchant_sig;
|
|
||||||
struct DepositWtidContext ctx;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (NULL == ctx)
|
||||||
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]++;
|
|
||||||
{
|
{
|
||||||
|
ctx = GNUNET_new (struct DepositWtidContext);
|
||||||
|
ctx->rc = rc;
|
||||||
|
rc->rh_ctx = ctx;
|
||||||
|
rc->rh_cleaner = &dwc_cleaner;
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_merchant_deposit_verify (&ctx.merchant,
|
GNUNET_STRINGS_string_to_data (args[0],
|
||||||
&ctx.coin_pub,
|
strlen (args[0]),
|
||||||
&ctx.h_contract_terms,
|
&ctx->h_wire,
|
||||||
&ctx.h_wire,
|
sizeof (ctx->h_wire)))
|
||||||
&merchant_sig))
|
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_FORBIDDEN,
|
MHD_HTTP_BAD_REQUEST,
|
||||||
TALER_EC_EXCHANGE_DEPOSITS_GET_MERCHANT_SIGNATURE_INVALID,
|
TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_H_WIRE,
|
||||||
NULL);
|
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,
|
return handle_track_transaction_request (ctx);
|
||||||
&ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +26,13 @@
|
|||||||
#include "taler-exchange-httpd.h"
|
#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"
|
* Handle a "/deposits/$H_WIRE/$MERCHANT_PUB/$H_CONTRACT_TERMS/$COIN_PUB"
|
||||||
* request.
|
* request.
|
||||||
|
@ -2377,9 +2377,10 @@ finish_keys_response (struct TEH_KeyStateHandle *ksh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
|
GNUNET_CONTAINER_multihashmap_iterator_destroy (iter);
|
||||||
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GNUNET_CONTAINER_multihashmap_destroy (denominations_by_group);
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_CONTAINER_heap_destroy (heap);
|
GNUNET_CONTAINER_heap_destroy (heap);
|
||||||
|
@ -520,34 +520,8 @@ TEH_handler_kyc_check (
|
|||||||
"usertype");
|
"usertype");
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
TALER_MHD_parse_request_timeout (rc->connection,
|
||||||
const char *ts;
|
&kyp->timeout);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (NULL == kyp->eh) &&
|
if ( (NULL == kyp->eh) &&
|
||||||
|
@ -297,7 +297,6 @@ TEH_handler_kyc_proof (
|
|||||||
{
|
{
|
||||||
struct KycProofContext *kpc = rc->rh_ctx;
|
struct KycProofContext *kpc = rc->rh_ctx;
|
||||||
const char *provider_section_or_logic = args[0];
|
const char *provider_section_or_logic = args[0];
|
||||||
const char *h_payto;
|
|
||||||
|
|
||||||
if (NULL == kpc)
|
if (NULL == kpc)
|
||||||
{
|
{
|
||||||
@ -310,33 +309,13 @@ TEH_handler_kyc_proof (
|
|||||||
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
|
||||||
"'/kyc-proof/$PROVIDER_SECTION?state=$H_PAYTO' required");
|
"'/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 = GNUNET_new (struct KycProofContext);
|
||||||
kpc->rc = rc;
|
kpc->rc = rc;
|
||||||
rc->rh_ctx = kpc;
|
rc->rh_ctx = kpc;
|
||||||
rc->rh_cleaner = &clean_kpc;
|
rc->rh_cleaner = &clean_kpc;
|
||||||
if (GNUNET_OK !=
|
TALER_MHD_parse_request_arg_auto_t (rc->connection,
|
||||||
GNUNET_STRINGS_string_to_data (h_payto,
|
"state",
|
||||||
strlen (h_payto),
|
&kpc->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");
|
|
||||||
}
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_KYCLOGIC_lookup_logic (provider_section_or_logic,
|
TALER_KYCLOGIC_lookup_logic (provider_section_or_logic,
|
||||||
&kpc->logic,
|
&kpc->logic,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -114,6 +114,9 @@ del_wire (void *cls,
|
|||||||
}
|
}
|
||||||
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
||||||
awc->payto_uri,
|
awc->payto_uri,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
awc->validity_end,
|
awc->validity_end,
|
||||||
false);
|
false);
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -54,6 +54,21 @@ struct AddWireContext
|
|||||||
*/
|
*/
|
||||||
const char *payto_uri;
|
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.
|
* Timestamp for checking against replay attacks.
|
||||||
*/
|
*/
|
||||||
@ -114,11 +129,17 @@ add_wire (void *cls,
|
|||||||
if (0 == qs)
|
if (0 == qs)
|
||||||
qs = TEH_plugin->insert_wire (TEH_plugin->cls,
|
qs = TEH_plugin->insert_wire (TEH_plugin->cls,
|
||||||
awc->payto_uri,
|
awc->payto_uri,
|
||||||
|
awc->conversion_url,
|
||||||
|
awc->debit_restrictions,
|
||||||
|
awc->credit_restrictions,
|
||||||
awc->validity_start,
|
awc->validity_start,
|
||||||
&awc->master_sig_wire);
|
&awc->master_sig_wire);
|
||||||
else
|
else
|
||||||
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
qs = TEH_plugin->update_wire (TEH_plugin->cls,
|
||||||
awc->payto_uri,
|
awc->payto_uri,
|
||||||
|
awc->conversion_url,
|
||||||
|
awc->debit_restrictions,
|
||||||
|
awc->credit_restrictions,
|
||||||
awc->validity_start,
|
awc->validity_start,
|
||||||
true);
|
true);
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
@ -141,7 +162,9 @@ TEH_handler_management_post_wire (
|
|||||||
struct MHD_Connection *connection,
|
struct MHD_Connection *connection,
|
||||||
const json_t *root)
|
const json_t *root)
|
||||||
{
|
{
|
||||||
struct AddWireContext awc;
|
struct AddWireContext awc = {
|
||||||
|
.conversion_url = NULL
|
||||||
|
};
|
||||||
struct GNUNET_JSON_Specification spec[] = {
|
struct GNUNET_JSON_Specification spec[] = {
|
||||||
GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
|
GNUNET_JSON_spec_fixed_auto ("master_sig_wire",
|
||||||
&awc.master_sig_wire),
|
&awc.master_sig_wire),
|
||||||
@ -149,6 +172,14 @@ TEH_handler_management_post_wire (
|
|||||||
&awc.master_sig_add),
|
&awc.master_sig_add),
|
||||||
GNUNET_JSON_spec_string ("payto_uri",
|
GNUNET_JSON_spec_string ("payto_uri",
|
||||||
&awc.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",
|
GNUNET_JSON_spec_timestamp ("validity_start",
|
||||||
&awc.validity_start),
|
&awc.validity_start),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
@ -179,17 +210,22 @@ TEH_handler_management_post_wire (
|
|||||||
MHD_HTTP_BAD_REQUEST,
|
MHD_HTTP_BAD_REQUEST,
|
||||||
TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
|
TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
|
||||||
msg);
|
msg);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
GNUNET_free (msg);
|
GNUNET_free (msg);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_exchange_offline_wire_add_verify (awc.payto_uri,
|
TALER_exchange_offline_wire_add_verify (awc.payto_uri,
|
||||||
|
awc.conversion_url,
|
||||||
|
awc.debit_restrictions,
|
||||||
|
awc.credit_restrictions,
|
||||||
awc.validity_start,
|
awc.validity_start,
|
||||||
&TEH_master_public_key,
|
&TEH_master_public_key,
|
||||||
&awc.master_sig_add))
|
&awc.master_sig_add))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TALER_MHD_reply_with_error (
|
return TALER_MHD_reply_with_error (
|
||||||
connection,
|
connection,
|
||||||
MHD_HTTP_FORBIDDEN,
|
MHD_HTTP_FORBIDDEN,
|
||||||
@ -199,10 +235,14 @@ TEH_handler_management_post_wire (
|
|||||||
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_exchange_wire_signature_check (awc.payto_uri,
|
TALER_exchange_wire_signature_check (awc.payto_uri,
|
||||||
|
awc.conversion_url,
|
||||||
|
awc.debit_restrictions,
|
||||||
|
awc.credit_restrictions,
|
||||||
&TEH_master_public_key,
|
&TEH_master_public_key,
|
||||||
&awc.master_sig_wire))
|
&awc.master_sig_wire))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TALER_MHD_reply_with_error (
|
return TALER_MHD_reply_with_error (
|
||||||
connection,
|
connection,
|
||||||
MHD_HTTP_FORBIDDEN,
|
MHD_HTTP_FORBIDDEN,
|
||||||
@ -218,6 +258,7 @@ TEH_handler_management_post_wire (
|
|||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"payto:// URI `%s' is malformed\n",
|
"payto:// URI `%s' is malformed\n",
|
||||||
awc.payto_uri);
|
awc.payto_uri);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
return TALER_MHD_reply_with_error (
|
return TALER_MHD_reply_with_error (
|
||||||
connection,
|
connection,
|
||||||
MHD_HTTP_BAD_REQUEST,
|
MHD_HTTP_BAD_REQUEST,
|
||||||
@ -237,6 +278,7 @@ TEH_handler_management_post_wire (
|
|||||||
&ret,
|
&ret,
|
||||||
&add_wire,
|
&add_wire,
|
||||||
&awc);
|
&awc);
|
||||||
|
GNUNET_JSON_parse_free (spec);
|
||||||
if (GNUNET_SYSERR == res)
|
if (GNUNET_SYSERR == res)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,8 @@ enum TEH_MetricTypeSuccess
|
|||||||
TEH_MT_SUCCESS_BATCH_WITHDRAW = 3,
|
TEH_MT_SUCCESS_BATCH_WITHDRAW = 3,
|
||||||
TEH_MT_SUCCESS_MELT = 4,
|
TEH_MT_SUCCESS_MELT = 4,
|
||||||
TEH_MT_SUCCESS_REFRESH_REVEAL = 5,
|
TEH_MT_SUCCESS_REFRESH_REVEAL = 5,
|
||||||
TEH_MT_SUCCESS_COUNT = 6 /* MUST BE LAST! */
|
TEH_MT_SUCCESS_AGE_WITHDRAW_REVEAL = 6,
|
||||||
|
TEH_MT_SUCCESS_COUNT = 7 /* MUST BE LAST! */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,29 +57,9 @@ TEH_handler_purses_delete (
|
|||||||
TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
|
TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
|
||||||
args[0]);
|
args[0]);
|
||||||
}
|
}
|
||||||
{
|
TALER_MHD_parse_request_header_auto_t (connection,
|
||||||
const char *sig;
|
"Taler-Purse-Signature",
|
||||||
|
&purse_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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_wallet_purse_delete_verify (&purse_pub,
|
TALER_wallet_purse_delete_verify (&purse_pub,
|
||||||
&purse_sig))
|
&purse_sig))
|
||||||
|
@ -243,36 +243,8 @@ TEH_handler_purses_get (struct TEH_RequestContext *rc,
|
|||||||
args[1]);
|
args[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
TALER_MHD_parse_request_timeout (rc->connection,
|
||||||
const char *long_poll_timeout_ms;
|
&gc->timeout);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (GNUNET_TIME_absolute_is_future (gc->timeout)) &&
|
if ( (GNUNET_TIME_absolute_is_future (gc->timeout)) &&
|
||||||
(NULL == gc->eh) )
|
(NULL == gc->eh) )
|
||||||
{
|
{
|
||||||
|
@ -773,12 +773,17 @@ clean_age:
|
|||||||
NULL);
|
NULL);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
||||||
|
{
|
||||||
rrcs[i].coin_sig = bss[i];
|
rrcs[i].coin_sig = bss[i];
|
||||||
|
rrcs[i].blinded_planchet = rcds[i].blinded_planchet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
"Signatures ready, starting DB interaction\n");
|
"Signatures ready, starting DB interaction\n");
|
||||||
|
|
||||||
|
|
||||||
for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
|
for (unsigned int r = 0; r<MAX_TRANSACTION_COMMIT_RETRIES; r++)
|
||||||
{
|
{
|
||||||
bool changed;
|
bool changed;
|
||||||
@ -795,12 +800,7 @@ clean_age:
|
|||||||
NULL);
|
NULL);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
|
|
||||||
{
|
|
||||||
struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
|
|
||||||
|
|
||||||
rrc->blinded_planchet = rcds[i].blinded_planchet;
|
|
||||||
}
|
|
||||||
qs = TEH_plugin->insert_refresh_reveal (
|
qs = TEH_plugin->insert_refresh_reveal (
|
||||||
TEH_plugin->cls,
|
TEH_plugin->cls,
|
||||||
melt_serial_id,
|
melt_serial_id,
|
||||||
|
@ -52,8 +52,12 @@ struct ReservePoller
|
|||||||
struct MHD_Connection *connection;
|
struct MHD_Connection *connection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription for the database event we are
|
* Our request context.
|
||||||
* waiting for.
|
*/
|
||||||
|
struct TEH_RequestContext *rc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription for the database event we are waiting for.
|
||||||
*/
|
*/
|
||||||
struct GNUNET_DB_EventHandler *eh;
|
struct GNUNET_DB_EventHandler *eh;
|
||||||
|
|
||||||
@ -154,6 +158,8 @@ db_event_cb (void *cls,
|
|||||||
(void) extra_size;
|
(void) extra_size;
|
||||||
if (! rp->suspended)
|
if (! rp->suspended)
|
||||||
return; /* might get multiple wake-up events */
|
return; /* might get multiple wake-up events */
|
||||||
|
GNUNET_async_scope_enter (&rp->rc->async_scope_id,
|
||||||
|
&old_scope);
|
||||||
TEH_check_invariants ();
|
TEH_check_invariants ();
|
||||||
rp->suspended = false;
|
rp->suspended = false;
|
||||||
MHD_resume_connection (rp->connection);
|
MHD_resume_connection (rp->connection);
|
||||||
@ -171,11 +177,9 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
|||||||
|
|
||||||
if (NULL == rp)
|
if (NULL == rp)
|
||||||
{
|
{
|
||||||
struct GNUNET_TIME_Relative timeout
|
|
||||||
= GNUNET_TIME_UNIT_ZERO;
|
|
||||||
|
|
||||||
rp = GNUNET_new (struct ReservePoller);
|
rp = GNUNET_new (struct ReservePoller);
|
||||||
rp->connection = rc->connection;
|
rp->connection = rc->connection;
|
||||||
|
rp->rc = rc;
|
||||||
rc->rh_ctx = rp;
|
rc->rh_ctx = rp;
|
||||||
rc->rh_cleaner = &rp_cleanup;
|
rc->rh_cleaner = &rp_cleanup;
|
||||||
GNUNET_CONTAINER_DLL_insert (rp_head,
|
GNUNET_CONTAINER_DLL_insert (rp_head,
|
||||||
@ -193,34 +197,8 @@ TEH_handler_reserves_get (struct TEH_RequestContext *rc,
|
|||||||
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
|
TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
|
||||||
args[0]);
|
args[0]);
|
||||||
}
|
}
|
||||||
{
|
TALER_MHD_parse_request_timeout (rc->connection,
|
||||||
const char *long_poll_timeout_ms;
|
&rp->timeout);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
|
if ( (GNUNET_TIME_absolute_is_future (rp->timeout)) &&
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU Affero General Public License as published by the Free Software
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -224,12 +224,18 @@ TEH_wire_done ()
|
|||||||
*
|
*
|
||||||
* @param cls a `json_t *` object to expand with wire account details
|
* @param cls a `json_t *` object to expand with wire account details
|
||||||
* @param payto_uri the exchange bank account URI to add
|
* @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
|
* @param master_sig master key signature affirming that this is a bank
|
||||||
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
add_wire_account (void *cls,
|
add_wire_account (void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
const json_t *debit_restrictions,
|
||||||
|
const json_t *credit_restrictions,
|
||||||
const struct TALER_MasterSignatureP *master_sig)
|
const struct TALER_MasterSignatureP *master_sig)
|
||||||
{
|
{
|
||||||
json_t *a = cls;
|
json_t *a = cls;
|
||||||
@ -240,6 +246,13 @@ add_wire_account (void *cls,
|
|||||||
GNUNET_JSON_PACK (
|
GNUNET_JSON_PACK (
|
||||||
GNUNET_JSON_pack_string ("payto_uri",
|
GNUNET_JSON_pack_string ("payto_uri",
|
||||||
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",
|
GNUNET_JSON_pack_data_auto ("master_sig",
|
||||||
master_sig))))
|
master_sig))))
|
||||||
{
|
{
|
||||||
@ -462,6 +475,8 @@ build_wire_state (void)
|
|||||||
wsh->wire_reply = TALER_MHD_MAKE_JSON_PACK (
|
wsh->wire_reply = TALER_MHD_MAKE_JSON_PACK (
|
||||||
GNUNET_JSON_pack_array_steal ("accounts",
|
GNUNET_JSON_pack_array_steal ("accounts",
|
||||||
wire_accounts_array),
|
wire_accounts_array),
|
||||||
|
GNUNET_JSON_pack_array_steal ("wads", /* #7271 */
|
||||||
|
json_array ()),
|
||||||
GNUNET_JSON_pack_object_steal ("fees",
|
GNUNET_JSON_pack_object_steal ("fees",
|
||||||
wire_fee_object),
|
wire_fee_object),
|
||||||
GNUNET_JSON_pack_data_auto ("master_public_key",
|
GNUNET_JSON_pack_data_auto ("master_public_key",
|
||||||
|
@ -563,9 +563,9 @@ wire_prepare_cb (void *cls,
|
|||||||
}
|
}
|
||||||
wpd = GNUNET_malloc (sizeof (struct WirePrepareData)
|
wpd = GNUNET_malloc (sizeof (struct WirePrepareData)
|
||||||
+ buf_size);
|
+ buf_size);
|
||||||
memcpy (&wpd[1],
|
GNUNET_memcpy (&wpd[1],
|
||||||
buf,
|
buf,
|
||||||
buf_size);
|
buf_size);
|
||||||
wpd->buf_size = buf_size;
|
wpd->buf_size = buf_size;
|
||||||
wpd->row_id = rowid;
|
wpd->row_id = rowid;
|
||||||
GNUNET_CONTAINER_DLL_insert (wpd_head,
|
GNUNET_CONTAINER_DLL_insert (wpd_head,
|
||||||
|
@ -731,8 +731,8 @@ history_cb (void *cls,
|
|||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
process_reply (wrap_size,
|
process_reply (wrap_size,
|
||||||
reply->details.success.details,
|
reply->details.ok.details,
|
||||||
reply->details.success.details_length);
|
reply->details.ok.details_length);
|
||||||
return;
|
return;
|
||||||
case MHD_HTTP_NO_CONTENT:
|
case MHD_HTTP_NO_CONTENT:
|
||||||
transaction_completed ();
|
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-by-j-postgres
|
||||||
test-exchangedb-populate-link-data-postgres
|
test-exchangedb-populate-link-data-postgres
|
||||||
test-exchangedb-populate-ready-deposit-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
|
||||||
|
@ -14,24 +14,24 @@
|
|||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE FUNCTION create_table_withdraw_age_commitments(
|
CREATE FUNCTION create_table_age_withdraw_commitments(
|
||||||
IN partition_suffix VARCHAR DEFAULT NULL
|
IN partition_suffix VARCHAR DEFAULT NULL
|
||||||
)
|
)
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE %I'
|
'CREATE TABLE %I'
|
||||||
'(withdraw_age_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
'(age_withdraw_commitment_id BIGINT GENERATED BY DEFAULT AS IDENTITY'
|
||||||
',h_commitment BYTEA PRIMARY KEY CHECK (LENGTH(h_commitment)=64)'
|
',h_commitment BYTEA CHECK (LENGTH(h_commitment)=64)'
|
||||||
',amount_with_fee_val INT8 NOT NULL'
|
',amount_with_fee_val INT8 NOT NULL'
|
||||||
',amount_with_fee_frac INT4 NOT NULL'
|
',amount_with_fee_frac INT4 NOT NULL'
|
||||||
',max_age INT2 NOT NULL'
|
',max_age INT2 NOT NULL'
|
||||||
',reserve_pub BYTEA NOT NULL CHECK (LENGTH(reserve_pub)=32)'
|
',reserve_pub BYTEA CHECK (LENGTH(reserve_pub)=32)'
|
||||||
',reserve_sig BYTEA CHECK (LENGTH(reserve_sig)=64)'
|
',reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)'
|
||||||
',noreveal_index INT4 NOT NULL'
|
',noreveal_index INT4 NOT NULL'
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
@ -77,66 +77,58 @@ END
|
|||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
CREATE FUNCTION constrain_table_withdraw_age_commitments(
|
CREATE FUNCTION constrain_table_age_withdraw_commitments(
|
||||||
IN partition_suffix VARCHAR
|
IN partition_suffix VARCHAR
|
||||||
)
|
)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
table_name = concat_ws('_', table_name, partition_suffix);
|
table_name = concat_ws('_', table_name, partition_suffix);
|
||||||
|
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD PRIMARY KEY (h_commitment, reserve_pub);'
|
' ADD PRIMARY KEY (h_commitment);'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_withdraw_age_commitment_id_key'
|
' ADD CONSTRAINT ' || table_name || '_h_commitment_reserve_pub_key'
|
||||||
' UNIQUE (withdraw_age_commitment_id);'
|
' UNIQUE (h_commitment, reserve_pub);'
|
||||||
|
);
|
||||||
|
EXECUTE FORMAT (
|
||||||
|
'ALTER TABLE ' || table_name ||
|
||||||
|
' ADD CONSTRAINT ' || table_name || '_age_withdraw_commitment_id_key'
|
||||||
|
' UNIQUE (age_withdraw_commitment_id);'
|
||||||
);
|
);
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
CREATE FUNCTION foreign_table_withdraw_age_commitments()
|
CREATE FUNCTION foreign_table_age_withdraw_commitments()
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_commitments';
|
table_name VARCHAR DEFAULT 'age_withdraw_commitments';
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
|
' ADD CONSTRAINT ' || table_name || '_foreign_reserve_pub'
|
||||||
' FOREIGN KEY (reserve_pub)'
|
' FOREIGN KEY (reserve_pub)'
|
||||||
' REFERENCES reserves (reserve_pub) ON DELETE CASCADE;'
|
' REFERENCES reserves(reserve_pub) ON DELETE CASCADE;'
|
||||||
);
|
);
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
|
|
||||||
INSERT INTO exchange_tables
|
INSERT INTO exchange_tables
|
||||||
(name
|
(name
|
||||||
,version
|
,version
|
||||||
,action
|
,action
|
||||||
,partitioned
|
,partitioned
|
||||||
,by_range)
|
,by_range)
|
||||||
VALUES
|
VALUES
|
||||||
('withdraw_age_commitments'
|
('age_withdraw_commitments', 'exchange-0003', 'create', TRUE ,FALSE),
|
||||||
,'exchange-0003'
|
('age_withdraw_commitments', 'exchange-0003', 'constrain',TRUE ,FALSE),
|
||||||
,'create'
|
('age_withdraw_commitments', 'exchange-0003', 'foreign', TRUE ,FALSE);
|
||||||
,TRUE
|
|
||||||
,FALSE),
|
|
||||||
('withdraw_age_commitments'
|
|
||||||
,'exchange-0003'
|
|
||||||
,'constrain'
|
|
||||||
,TRUE
|
|
||||||
,FALSE),
|
|
||||||
('withdraw_age_commitments'
|
|
||||||
,'exchange-0003'
|
|
||||||
,'foreign'
|
|
||||||
,TRUE
|
|
||||||
,FALSE);
|
|
@ -14,25 +14,24 @@
|
|||||||
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
-- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
--
|
--
|
||||||
|
|
||||||
CREATE FUNCTION create_table_withdraw_age_revealed_coins(
|
CREATE FUNCTION create_table_age_withdraw_revealed_coins(
|
||||||
IN partition_suffix VARCHAR DEFAULT NULL
|
IN partition_suffix VARCHAR DEFAULT NULL
|
||||||
)
|
)
|
||||||
RETURNS VOID
|
RETURNS VOID
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
|
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
||||||
BEGIN
|
BEGIN
|
||||||
PERFORM create_partitioned_table(
|
PERFORM create_partitioned_table(
|
||||||
'CREATE TABLE %I'
|
'CREATE TABLE %I'
|
||||||
'(withdraw_age_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
|
'(age_withdraw_revealed_coins_id BIGINT GENERATED BY DEFAULT AS IDENTITY' -- UNIQUE
|
||||||
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=64)'
|
',h_commitment BYTEA NOT NULL CHECK (LENGTH(h_commitment)=64)'
|
||||||
',freshcoin_index INT4 NOT NULL'
|
',freshcoin_index INT4 NOT NULL'
|
||||||
',denominations_serial INT8 NOT NULL'
|
',denominations_serial INT8 NOT NULL'
|
||||||
',coin_ev BYTEA NOT NULL'
|
',coin_ev BYTEA NOT NULL'
|
||||||
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=64)'
|
',h_coin_ev BYTEA CHECK (LENGTH(h_coin_ev)=64)'
|
||||||
',ev_sig BYTEA NOT NULL'
|
',ev_sig BYTEA NOT NULL'
|
||||||
',ewv BYTEA NOT NULL'
|
|
||||||
') %s ;'
|
') %s ;'
|
||||||
,table_name
|
,table_name
|
||||||
,'PARTITION BY HASH (h_commitment)'
|
,'PARTITION BY HASH (h_commitment)'
|
||||||
@ -79,30 +78,24 @@ BEGIN
|
|||||||
,table_name
|
,table_name
|
||||||
,partition_suffix
|
,partition_suffix
|
||||||
);
|
);
|
||||||
PERFORM comment_partitioned_column(
|
|
||||||
'Exchange contributed values in the creation of the fresh coin (see /csr)'
|
|
||||||
,'ewv'
|
|
||||||
,table_name
|
|
||||||
,partition_suffix
|
|
||||||
);
|
|
||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE FUNCTION constrain_table_withdraw_age_revealed_coins(
|
CREATE FUNCTION constrain_table_age_withdraw_revealed_coins(
|
||||||
IN partition_suffix VARCHAR
|
IN partition_suffix VARCHAR
|
||||||
)
|
)
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
|
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
||||||
BEGIN
|
BEGIN
|
||||||
table_name = concat_ws('_', table_name, partition_suffix);
|
table_name = concat_ws('_', table_name, partition_suffix);
|
||||||
|
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_withdraw_age_revealed_coins_id_key'
|
' ADD CONSTRAINT ' || table_name || '_age_withdraw_revealed_coins_id_key'
|
||||||
' UNIQUE (withdraw_age_revealed_coins_id);'
|
' UNIQUE (age_withdraw_revealed_coins_id);'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
@ -112,18 +105,18 @@ BEGIN
|
|||||||
END
|
END
|
||||||
$$;
|
$$;
|
||||||
|
|
||||||
CREATE FUNCTION foreign_table_withdraw_age_revealed_coins()
|
CREATE FUNCTION foreign_table_age_withdraw_revealed_coins()
|
||||||
RETURNS void
|
RETURNS void
|
||||||
LANGUAGE plpgsql
|
LANGUAGE plpgsql
|
||||||
AS $$
|
AS $$
|
||||||
DECLARE
|
DECLARE
|
||||||
table_name VARCHAR DEFAULT 'withdraw_age_revealed_coins';
|
table_name VARCHAR DEFAULT 'age_withdraw_revealed_coins';
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
|
' ADD CONSTRAINT ' || table_name || '_foreign_h_commitment'
|
||||||
' FOREIGN KEY (h_commitment)'
|
' FOREIGN KEY (h_commitment)'
|
||||||
' REFERENCES withdraw_age_commitments (h_commitment) ON DELETE CASCADE;'
|
' REFERENCES age_withdraw_commitments (h_commitment) ON DELETE CASCADE;'
|
||||||
);
|
);
|
||||||
EXECUTE FORMAT (
|
EXECUTE FORMAT (
|
||||||
'ALTER TABLE ' || table_name ||
|
'ALTER TABLE ' || table_name ||
|
||||||
@ -142,17 +135,17 @@ INSERT INTO exchange_tables
|
|||||||
,partitioned
|
,partitioned
|
||||||
,by_range)
|
,by_range)
|
||||||
VALUES
|
VALUES
|
||||||
('withdraw_age_revealed_coins'
|
('age_withdraw_revealed_coins'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'create'
|
,'create'
|
||||||
,TRUE
|
,TRUE
|
||||||
,FALSE),
|
,FALSE),
|
||||||
('withdraw_age_revealed_coins'
|
('age_withdraw_revealed_coins'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'constrain'
|
,'constrain'
|
||||||
,TRUE
|
,TRUE
|
||||||
,FALSE),
|
,FALSE),
|
||||||
('withdraw_age_revealed_coins'
|
('age_withdraw_revealed_coins'
|
||||||
,'exchange-0003'
|
,'exchange-0003'
|
||||||
,'foreign'
|
,'foreign'
|
||||||
,TRUE
|
,TRUE
|
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 \
|
0002-*.sql \
|
||||||
exchange-0002.sql.in \
|
exchange-0002.sql.in \
|
||||||
0003-*.sql \
|
0003-*.sql \
|
||||||
exchange-0003.sql.in
|
exchange-0003.sql.in \
|
||||||
|
0004-*.sql \
|
||||||
|
exchange-0004.sql.in
|
||||||
|
|
||||||
sql_DATA = \
|
sql_DATA = \
|
||||||
benchmark-0001.sql \
|
benchmark-0001.sql \
|
||||||
@ -28,6 +30,7 @@ sql_DATA = \
|
|||||||
exchange-0001.sql \
|
exchange-0001.sql \
|
||||||
exchange-0002.sql \
|
exchange-0002.sql \
|
||||||
exchange-0003.sql \
|
exchange-0003.sql \
|
||||||
|
exchange-0004.sql \
|
||||||
drop.sql \
|
drop.sql \
|
||||||
procedures.sql
|
procedures.sql
|
||||||
|
|
||||||
@ -39,7 +42,8 @@ BUILT_SOURCES = \
|
|||||||
|
|
||||||
CLEANFILES = \
|
CLEANFILES = \
|
||||||
exchange-0002.sql \
|
exchange-0002.sql \
|
||||||
exchange-0003.sql
|
exchange-0003.sql \
|
||||||
|
exchange-0004.sql
|
||||||
|
|
||||||
procedures.sql: procedures.sql.in exchange_do_*.sql
|
procedures.sql: procedures.sql.in exchange_do_*.sql
|
||||||
chmod +w $@ || true
|
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' - >$@
|
gcc -E -P -undef - < exchange-0003.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
|
||||||
chmod ugo-w $@
|
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 = \
|
EXTRA_DIST = \
|
||||||
exchangedb.conf \
|
exchangedb.conf \
|
||||||
exchangedb-postgres.conf \
|
exchangedb-postgres.conf \
|
||||||
|
@ -169,9 +169,9 @@ bem_insert (struct GNUNET_PQ_Context *conn,
|
|||||||
GNUNET_CRYPTO_hash (&b,
|
GNUNET_CRYPTO_hash (&b,
|
||||||
sizeof (b),
|
sizeof (b),
|
||||||
&hc);
|
&hc);
|
||||||
memcpy (&ihc,
|
GNUNET_memcpy (&ihc,
|
||||||
&hc,
|
&hc,
|
||||||
sizeof (ihc));
|
sizeof (ihc));
|
||||||
{
|
{
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_auto_from_type (&hc),
|
GNUNET_PQ_query_param_auto_from_type (&hc),
|
||||||
@ -265,9 +265,9 @@ bem_select (struct GNUNET_PQ_Context *conn,
|
|||||||
GNUNET_CRYPTO_hash (&b,
|
GNUNET_CRYPTO_hash (&b,
|
||||||
sizeof (b),
|
sizeof (b),
|
||||||
&hc);
|
&hc);
|
||||||
memcpy (&ihc,
|
GNUNET_memcpy (&ihc,
|
||||||
&hc,
|
&hc,
|
||||||
sizeof (ihc));
|
sizeof (ihc));
|
||||||
{
|
{
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_uint32 (&ihc),
|
GNUNET_PQ_query_param_uint32 (&ihc),
|
||||||
|
@ -21,6 +21,7 @@ BEGIN;
|
|||||||
SELECT _v.unregister_patch('exchange-0001');
|
SELECT _v.unregister_patch('exchange-0001');
|
||||||
SELECT _v.unregister_patch('exchange-0002');
|
SELECT _v.unregister_patch('exchange-0002');
|
||||||
SELECT _v.unregister_patch('exchange-0003');
|
SELECT _v.unregister_patch('exchange-0003');
|
||||||
|
SELECT _v.unregister_patch('exchange-0004');
|
||||||
|
|
||||||
DROP SCHEMA exchange CASCADE;
|
DROP SCHEMA exchange CASCADE;
|
||||||
|
|
||||||
|
@ -25,6 +25,8 @@ SET search_path TO exchange;
|
|||||||
#include "0003-aml_status.sql"
|
#include "0003-aml_status.sql"
|
||||||
#include "0003-aml_staff.sql"
|
#include "0003-aml_staff.sql"
|
||||||
#include "0003-aml_history.sql"
|
#include "0003-aml_history.sql"
|
||||||
|
#include "0003-age_withdraw_commitments.sql"
|
||||||
|
#include "0003-age_withdraw_reveals.sql"
|
||||||
|
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
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_error_codes.h"
|
||||||
#include "taler_dbevents.h"
|
#include "taler_dbevents.h"
|
||||||
#include "taler_pq_lib.h"
|
#include "taler_pq_lib.h"
|
||||||
|
#include "pg_event_notify.h"
|
||||||
#include "pg_aggregate.h"
|
#include "pg_aggregate.h"
|
||||||
#include "pg_helper.h"
|
#include "pg_helper.h"
|
||||||
|
|
||||||
@ -35,34 +36,12 @@ TEH_PG_aggregate (
|
|||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
struct GNUNET_TIME_Absolute now = {0};
|
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_value;
|
||||||
uint64_t sum_deposit_frac;
|
uint64_t sum_deposit_frac;
|
||||||
uint64_t sum_refund_value;
|
uint64_t sum_refund_value;
|
||||||
uint64_t sum_refund_frac;
|
uint64_t sum_refund_frac;
|
||||||
uint64_t sum_fee_value;
|
uint64_t sum_fee_value;
|
||||||
uint64_t sum_fee_frac;
|
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;
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
struct TALER_Amount sum_deposit;
|
struct TALER_Amount sum_deposit;
|
||||||
struct TALER_Amount sum_refund;
|
struct TALER_Amount sum_refund;
|
||||||
@ -71,8 +50,6 @@ TEH_PG_aggregate (
|
|||||||
|
|
||||||
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
|
now = GNUNET_TIME_absolute_round_down (GNUNET_TIME_absolute_get (),
|
||||||
pg->aggregator_shift);
|
pg->aggregator_shift);
|
||||||
|
|
||||||
/* Used in #postgres_aggregate() */
|
|
||||||
PREPARE (pg,
|
PREPARE (pg,
|
||||||
"aggregate",
|
"aggregate",
|
||||||
"WITH dep AS (" /* restrict to our merchant and account and mark as done */
|
"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 ref ON (FALSE)" /* We just want all sums */
|
||||||
" FULL OUTER JOIN fees ON (FALSE);");
|
" 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,
|
qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"aggregate",
|
"aggregate",
|
||||||
params,
|
params,
|
||||||
rs);
|
rs);
|
||||||
|
}
|
||||||
if (qs < 0)
|
if (qs < 0)
|
||||||
{
|
{
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||||
@ -165,6 +166,18 @@ TEH_PG_aggregate (
|
|||||||
total));
|
total));
|
||||||
return qs;
|
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 ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_set_zero (pg->currency,
|
TALER_amount_set_zero (pg->currency,
|
||||||
&sum_deposit));
|
&sum_deposit));
|
||||||
|
@ -69,7 +69,7 @@ TEH_PG_get_age_withdraw_info (
|
|||||||
",amount_with_fee_val"
|
",amount_with_fee_val"
|
||||||
",amount_with_fee_frac"
|
",amount_with_fee_frac"
|
||||||
",noreveal_index"
|
",noreveal_index"
|
||||||
" FROM withdraw_age_commitments"
|
" FROM age_withdraw_commitments"
|
||||||
" WHERE reserve_pub=$1 and h_commitment=$2;");
|
" WHERE reserve_pub=$1 and h_commitment=$2;");
|
||||||
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
|
||||||
"get_age_withdraw_info",
|
"get_age_withdraw_info",
|
||||||
|
@ -66,10 +66,25 @@ get_wire_accounts_cb (void *cls,
|
|||||||
for (unsigned int i = 0; i < num_results; i++)
|
for (unsigned int i = 0; i < num_results; i++)
|
||||||
{
|
{
|
||||||
char *payto_uri;
|
char *payto_uri;
|
||||||
|
char *conversion_url = NULL;
|
||||||
|
json_t *debit_restrictions = NULL;
|
||||||
|
json_t *credit_restrictions = NULL;
|
||||||
struct TALER_MasterSignatureP master_sig;
|
struct TALER_MasterSignatureP master_sig;
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_string ("payto_uri",
|
GNUNET_PQ_result_spec_string ("payto_uri",
|
||||||
&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",
|
GNUNET_PQ_result_spec_auto_from_type ("master_sig",
|
||||||
&master_sig),
|
&master_sig),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
@ -84,8 +99,21 @@ get_wire_accounts_cb (void *cls,
|
|||||||
ctx->status = GNUNET_SYSERR;
|
ctx->status = GNUNET_SYSERR;
|
||||||
return;
|
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,
|
ctx->cb (ctx->cb_cls,
|
||||||
payto_uri,
|
payto_uri,
|
||||||
|
conversion_url,
|
||||||
|
debit_restrictions,
|
||||||
|
credit_restrictions,
|
||||||
&master_sig);
|
&master_sig);
|
||||||
GNUNET_PQ_cleanup_result (rs);
|
GNUNET_PQ_cleanup_result (rs);
|
||||||
}
|
}
|
||||||
@ -112,6 +140,9 @@ TEH_PG_get_wire_accounts (void *cls,
|
|||||||
"get_wire_accounts",
|
"get_wire_accounts",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" payto_uri"
|
" payto_uri"
|
||||||
|
",conversion_url"
|
||||||
|
",debit_restrictions"
|
||||||
|
",credit_restrictions"
|
||||||
",master_sig"
|
",master_sig"
|
||||||
" FROM wire_accounts"
|
" FROM wire_accounts"
|
||||||
" WHERE is_active");
|
" WHERE is_active");
|
||||||
@ -123,5 +154,4 @@ TEH_PG_get_wire_accounts (void *cls,
|
|||||||
if (GNUNET_OK != ctx.status)
|
if (GNUNET_OK != ctx.status)
|
||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
return qs;
|
return qs;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,8 @@ TEH_PG_insert_age_withdraw_reveal (
|
|||||||
/* TODO */
|
/* TODO */
|
||||||
#if 0
|
#if 0
|
||||||
PREPARE (pg,
|
PREPARE (pg,
|
||||||
"insert_withdraw_age_revealed_coin",
|
"insert_age_withdraw_revealed_coin",
|
||||||
"INSERT INTO withdraw_age_reveals "
|
"INSERT INTO age_withdraw_reveals "
|
||||||
"(h_commitment "
|
"(h_commitment "
|
||||||
",freshcoin_index "
|
",freshcoin_index "
|
||||||
",denominations_serial "
|
",denominations_serial "
|
||||||
|
@ -2062,39 +2062,39 @@ irbt_cb_table_purse_deletion (struct PostgresClosure *pg,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with withdraw_age_commitments records to insert into table.
|
* Function called with age_withdraw_commitments records to insert into table.
|
||||||
*
|
*
|
||||||
* @param pg plugin context
|
* @param pg plugin context
|
||||||
* @param td record to insert
|
* @param td record to insert
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
irbt_cb_table_withdraw_age_commitments (struct PostgresClosure *pg,
|
irbt_cb_table_age_withdraw_commitments (struct PostgresClosure *pg,
|
||||||
const struct
|
const struct
|
||||||
TALER_EXCHANGEDB_TableData *td)
|
TALER_EXCHANGEDB_TableData *td)
|
||||||
{
|
{
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_uint64 (&td->serial),
|
GNUNET_PQ_query_param_uint64 (&td->serial),
|
||||||
GNUNET_PQ_query_param_auto_from_type (
|
GNUNET_PQ_query_param_auto_from_type (
|
||||||
&td->details.withdraw_age_commitments.h_commitment),
|
&td->details.age_withdraw_commitments.h_commitment),
|
||||||
TALER_PQ_query_param_amount (
|
TALER_PQ_query_param_amount (
|
||||||
&td->details.withdraw_age_commitments.amount_with_fee),
|
&td->details.age_withdraw_commitments.amount_with_fee),
|
||||||
GNUNET_PQ_query_param_uint16 (
|
GNUNET_PQ_query_param_uint16 (
|
||||||
&td->details.withdraw_age_commitments.max_age),
|
&td->details.age_withdraw_commitments.max_age),
|
||||||
GNUNET_PQ_query_param_auto_from_type (
|
GNUNET_PQ_query_param_auto_from_type (
|
||||||
&td->details.withdraw_age_commitments.reserve_pub),
|
&td->details.age_withdraw_commitments.reserve_pub),
|
||||||
GNUNET_PQ_query_param_auto_from_type (
|
GNUNET_PQ_query_param_auto_from_type (
|
||||||
&td->details.withdraw_age_commitments.reserve_sig),
|
&td->details.age_withdraw_commitments.reserve_sig),
|
||||||
GNUNET_PQ_query_param_uint32 (
|
GNUNET_PQ_query_param_uint32 (
|
||||||
&td->details.withdraw_age_commitments.noreveal_index),
|
&td->details.age_withdraw_commitments.noreveal_index),
|
||||||
GNUNET_PQ_query_param_absolute_time (
|
GNUNET_PQ_query_param_absolute_time (
|
||||||
&td->details.withdraw_age_commitments.timestamp),
|
&td->details.age_withdraw_commitments.timestamp),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
|
|
||||||
PREPARE (pg,
|
PREPARE (pg,
|
||||||
"insert_into_table_withdraw_age_commitments",
|
"insert_into_table_age_withdraw_commitments",
|
||||||
"INSERT INTO withdraw_age_commitments"
|
"INSERT INTO age_withdraw_commitments"
|
||||||
"(withdraw_age_commitment_id"
|
"(age_withdraw_commitment_id"
|
||||||
",h_commitment"
|
",h_commitment"
|
||||||
",amount_with_fee_val"
|
",amount_with_fee_val"
|
||||||
",amount_with_fee_frac"
|
",amount_with_fee_frac"
|
||||||
@ -2106,19 +2106,19 @@ irbt_cb_table_withdraw_age_commitments (struct PostgresClosure *pg,
|
|||||||
") VALUES "
|
") VALUES "
|
||||||
"($1, $2, $3, $4, $5, $6, $7, $8, $9);");
|
"($1, $2, $3, $4, $5, $6, $7, $8, $9);");
|
||||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||||
"insert_into_table_withdraw_age_commitments",
|
"insert_into_table_age_withdraw_commitments",
|
||||||
params);
|
params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with withdraw_age_revealed_coins records to insert into table.
|
* Function called with age_withdraw_revealed_coins records to insert into table.
|
||||||
*
|
*
|
||||||
* @param pg plugin context
|
* @param pg plugin context
|
||||||
* @param td record to insert
|
* @param td record to insert
|
||||||
*/
|
*/
|
||||||
static enum GNUNET_DB_QueryStatus
|
static enum GNUNET_DB_QueryStatus
|
||||||
irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
|
irbt_cb_table_age_withdraw_revealed_coins (struct PostgresClosure *pg,
|
||||||
const struct
|
const struct
|
||||||
TALER_EXCHANGEDB_TableData *td)
|
TALER_EXCHANGEDB_TableData *td)
|
||||||
{
|
{
|
||||||
@ -2126,26 +2126,24 @@ irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
|
|||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_uint64 (&td->serial),
|
GNUNET_PQ_query_param_uint64 (&td->serial),
|
||||||
GNUNET_PQ_query_param_auto_from_type (
|
GNUNET_PQ_query_param_auto_from_type (
|
||||||
&td->details.withdraw_age_revealed_coins.h_commitment),
|
&td->details.age_withdraw_revealed_coins.h_commitment),
|
||||||
GNUNET_PQ_query_param_uint32 (
|
GNUNET_PQ_query_param_uint32 (
|
||||||
&td->details.withdraw_age_revealed_coins.freshcoin_index),
|
&td->details.age_withdraw_revealed_coins.freshcoin_index),
|
||||||
GNUNET_PQ_query_param_uint64 (
|
GNUNET_PQ_query_param_uint64 (
|
||||||
&td->details.withdraw_age_revealed_coins.denominations_serial),
|
&td->details.age_withdraw_revealed_coins.denominations_serial),
|
||||||
GNUNET_PQ_query_param_fixed_size (
|
GNUNET_PQ_query_param_fixed_size (
|
||||||
td->details.withdraw_age_revealed_coins.coin_ev,
|
td->details.age_withdraw_revealed_coins.coin_ev,
|
||||||
td->details.withdraw_age_revealed_coins.coin_ev_size),
|
td->details.age_withdraw_revealed_coins.coin_ev_size),
|
||||||
GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
|
GNUNET_PQ_query_param_auto_from_type (&h_coin_ev),
|
||||||
TALER_PQ_query_param_blinded_denom_sig (
|
TALER_PQ_query_param_blinded_denom_sig (
|
||||||
&td->details.withdraw_age_revealed_coins.ev_sig),
|
&td->details.age_withdraw_revealed_coins.ev_sig),
|
||||||
TALER_PQ_query_param_exchange_withdraw_values (
|
|
||||||
&td->details.withdraw_age_revealed_coins.ewv),
|
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
|
|
||||||
PREPARE (pg,
|
PREPARE (pg,
|
||||||
"insert_into_table_withdraw_age_revealed_coins",
|
"insert_into_table_age_withdraw_revealed_coins",
|
||||||
"INSERT INTO withdraw_age_revealed_coins"
|
"INSERT INTO age_withdraw_revealed_coins"
|
||||||
"(withdraw_age_revealed_coins_id"
|
"(age_withdraw_revealed_coins_id"
|
||||||
",h_commitment"
|
",h_commitment"
|
||||||
",freshcoin_index"
|
",freshcoin_index"
|
||||||
",denominations_serial"
|
",denominations_serial"
|
||||||
@ -2156,12 +2154,12 @@ irbt_cb_table_withdraw_age_revealed_coins (struct PostgresClosure *pg,
|
|||||||
") VALUES "
|
") VALUES "
|
||||||
"($1, $2, $3, $4, $5, $6, $7, $8);");
|
"($1, $2, $3, $4, $5, $6, $7, $8);");
|
||||||
|
|
||||||
GNUNET_CRYPTO_hash (td->details.withdraw_age_revealed_coins.coin_ev,
|
GNUNET_CRYPTO_hash (td->details.age_withdraw_revealed_coins.coin_ev,
|
||||||
td->details.withdraw_age_revealed_coins.coin_ev_size,
|
td->details.age_withdraw_revealed_coins.coin_ev_size,
|
||||||
&h_coin_ev);
|
&h_coin_ev);
|
||||||
|
|
||||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||||
"insert_into_table_withdraw_age_revealed_coins",
|
"insert_into_table_age_withdraw_revealed_coins",
|
||||||
params);
|
params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2314,10 +2312,10 @@ TEH_PG_insert_records_by_table (void *cls,
|
|||||||
rh = &irbt_cb_table_purse_deletion;
|
rh = &irbt_cb_table_purse_deletion;
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
||||||
rh = &irbt_cb_table_withdraw_age_commitments;
|
rh = &irbt_cb_table_age_withdraw_commitments;
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
||||||
rh = &irbt_cb_table_withdraw_age_revealed_coins;
|
rh = &irbt_cb_table_age_withdraw_revealed_coins;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL == rh)
|
if (NULL == rh)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -29,12 +29,20 @@
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
TEH_PG_insert_wire (void *cls,
|
TEH_PG_insert_wire (void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp start_date,
|
struct GNUNET_TIME_Timestamp start_date,
|
||||||
const struct TALER_MasterSignatureP *master_sig)
|
const struct TALER_MasterSignatureP *master_sig)
|
||||||
{
|
{
|
||||||
struct PostgresClosure *pg = cls;
|
struct PostgresClosure *pg = cls;
|
||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_string (payto_uri),
|
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_auto_from_type (master_sig),
|
||||||
GNUNET_PQ_query_param_timestamp (&start_date),
|
GNUNET_PQ_query_param_timestamp (&start_date),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
@ -44,11 +52,14 @@ TEH_PG_insert_wire (void *cls,
|
|||||||
"insert_wire",
|
"insert_wire",
|
||||||
"INSERT INTO wire_accounts "
|
"INSERT INTO wire_accounts "
|
||||||
"(payto_uri"
|
"(payto_uri"
|
||||||
|
",conversion_url"
|
||||||
|
",debit_restrictions"
|
||||||
|
",credit_restrictions"
|
||||||
",master_sig"
|
",master_sig"
|
||||||
",is_active"
|
",is_active"
|
||||||
",last_change"
|
",last_change"
|
||||||
") VALUES "
|
") VALUES "
|
||||||
"($1, $2, true, $3);");
|
"($1, $2, $3, $4, $5, true, $6);");
|
||||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||||
"insert_wire",
|
"insert_wire",
|
||||||
params);
|
params);
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param payto_uri wire account of the exchange
|
* @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
|
* @param start_date date when the account was added by the offline system
|
||||||
* (only to be used for replay detection)
|
* (only to be used for replay detection)
|
||||||
* @param master_sig public signature affirming the existence of the account,
|
* @param master_sig public signature affirming the existence of the account,
|
||||||
@ -38,6 +41,9 @@
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
TEH_PG_insert_wire (void *cls,
|
TEH_PG_insert_wire (void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp start_date,
|
struct GNUNET_TIME_Timestamp start_date,
|
||||||
const struct TALER_MasterSignatureP *master_sig);
|
const struct TALER_MasterSignatureP *master_sig);
|
||||||
|
|
||||||
|
@ -1118,9 +1118,9 @@ lrbt_cb_table_refresh_transfer_keys (void *cls,
|
|||||||
ctx->error = true;
|
ctx->error = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
memcpy (&td.details.refresh_transfer_keys.tprivs[0],
|
GNUNET_memcpy (&td.details.refresh_transfer_keys.tprivs[0],
|
||||||
tpriv,
|
tpriv,
|
||||||
tpriv_size);
|
tpriv_size);
|
||||||
ctx->cb (ctx->cb_cls,
|
ctx->cb (ctx->cb_cls,
|
||||||
&td);
|
&td);
|
||||||
GNUNET_PQ_cleanup_result (rs);
|
GNUNET_PQ_cleanup_result (rs);
|
||||||
@ -2767,14 +2767,14 @@ lrbt_cb_table_purse_deletion (void *cls,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with withdraw_age_commitments table entries.
|
* Function called with age_withdraw_commitments table entries.
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param result the postgres result
|
* @param result the postgres result
|
||||||
* @param num_results the number of results in @a result
|
* @param num_results the number of results in @a result
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
lrbt_cb_table_withdraw_age_commitments (void *cls,
|
lrbt_cb_table_age_withdraw_commitments (void *cls,
|
||||||
PGresult *result,
|
PGresult *result,
|
||||||
unsigned int num_results)
|
unsigned int num_results)
|
||||||
{
|
{
|
||||||
@ -2788,26 +2788,26 @@ lrbt_cb_table_withdraw_age_commitments (void *cls,
|
|||||||
{
|
{
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_uint64 (
|
GNUNET_PQ_result_spec_uint64 (
|
||||||
"withdraw_age_commitment_id",
|
"age_withdraw_commitment_id",
|
||||||
&td.serial),
|
&td.serial),
|
||||||
GNUNET_PQ_result_spec_auto_from_type (
|
GNUNET_PQ_result_spec_auto_from_type (
|
||||||
"h_commitment",
|
"h_commitment",
|
||||||
&td.details.withdraw_age_commitments.h_commitment),
|
&td.details.age_withdraw_commitments.h_commitment),
|
||||||
GNUNET_PQ_result_spec_uint16 (
|
GNUNET_PQ_result_spec_uint16 (
|
||||||
"max_age",
|
"max_age",
|
||||||
&td.details.withdraw_age_commitments.max_age),
|
&td.details.age_withdraw_commitments.max_age),
|
||||||
TALER_PQ_RESULT_SPEC_AMOUNT (
|
TALER_PQ_RESULT_SPEC_AMOUNT (
|
||||||
"amount_with_fee",
|
"amount_with_fee",
|
||||||
&td.details.withdraw_age_commitments.amount_with_fee),
|
&td.details.age_withdraw_commitments.amount_with_fee),
|
||||||
GNUNET_PQ_result_spec_auto_from_type (
|
GNUNET_PQ_result_spec_auto_from_type (
|
||||||
"reserve_pub",
|
"reserve_pub",
|
||||||
&td.details.withdraw_age_commitments.reserve_pub),
|
&td.details.age_withdraw_commitments.reserve_pub),
|
||||||
GNUNET_PQ_result_spec_auto_from_type (
|
GNUNET_PQ_result_spec_auto_from_type (
|
||||||
"reserve_sig",
|
"reserve_sig",
|
||||||
&td.details.withdraw_age_commitments.reserve_sig),
|
&td.details.age_withdraw_commitments.reserve_sig),
|
||||||
GNUNET_PQ_result_spec_uint32 (
|
GNUNET_PQ_result_spec_uint32 (
|
||||||
"noreveal_index",
|
"noreveal_index",
|
||||||
&td.details.withdraw_age_commitments.noreveal_index),
|
&td.details.age_withdraw_commitments.noreveal_index),
|
||||||
GNUNET_PQ_result_spec_end
|
GNUNET_PQ_result_spec_end
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2828,14 +2828,14 @@ lrbt_cb_table_withdraw_age_commitments (void *cls,
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function called with withdraw_age_revealed_coins table entries.
|
* Function called with age_withdraw_revealed_coins table entries.
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param result the postgres result
|
* @param result the postgres result
|
||||||
* @param num_results the number of results in @a result
|
* @param num_results the number of results in @a result
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
lrbt_cb_table_withdraw_age_revealed_coins (void *cls,
|
lrbt_cb_table_age_withdraw_revealed_coins (void *cls,
|
||||||
PGresult *result,
|
PGresult *result,
|
||||||
unsigned int num_results)
|
unsigned int num_results)
|
||||||
{
|
{
|
||||||
@ -2848,22 +2848,22 @@ lrbt_cb_table_withdraw_age_revealed_coins (void *cls,
|
|||||||
{
|
{
|
||||||
struct GNUNET_PQ_ResultSpec rs[] = {
|
struct GNUNET_PQ_ResultSpec rs[] = {
|
||||||
GNUNET_PQ_result_spec_uint64 (
|
GNUNET_PQ_result_spec_uint64 (
|
||||||
"withdraw_age_revealed_coins_id",
|
"age_withdraw_revealed_coins_id",
|
||||||
&td.serial),
|
&td.serial),
|
||||||
GNUNET_PQ_result_spec_auto_from_type (
|
GNUNET_PQ_result_spec_auto_from_type (
|
||||||
"h_commitment",
|
"h_commitment",
|
||||||
&td.details.withdraw_age_revealed_coins.h_commitment),
|
&td.details.age_withdraw_revealed_coins.h_commitment),
|
||||||
GNUNET_PQ_result_spec_uint32 (
|
GNUNET_PQ_result_spec_uint32 (
|
||||||
"freshcoin_index",
|
"freshcoin_index",
|
||||||
&td.details.withdraw_age_revealed_coins.freshcoin_index),
|
&td.details.age_withdraw_revealed_coins.freshcoin_index),
|
||||||
GNUNET_PQ_result_spec_uint64 (
|
GNUNET_PQ_result_spec_uint64 (
|
||||||
"denominations_serial",
|
"denominations_serial",
|
||||||
&td.details.withdraw_age_revealed_coins.denominations_serial),
|
&td.details.age_withdraw_revealed_coins.denominations_serial),
|
||||||
/* Note: h_coin_ev is recalculated */
|
/* Note: h_coin_ev is recalculated */
|
||||||
GNUNET_PQ_result_spec_variable_size (
|
GNUNET_PQ_result_spec_variable_size (
|
||||||
"coin_ev",
|
"coin_ev",
|
||||||
(void **) &td.details.withdraw_age_revealed_coins.coin_ev,
|
(void **) &td.details.age_withdraw_revealed_coins.coin_ev,
|
||||||
&td.details.withdraw_age_revealed_coins.coin_ev_size),
|
&td.details.age_withdraw_revealed_coins.coin_ev_size),
|
||||||
TALER_PQ_result_spec_blinded_denom_sig (
|
TALER_PQ_result_spec_blinded_denom_sig (
|
||||||
"ev_sig",
|
"ev_sig",
|
||||||
&td.details.refresh_revealed_coins.ev_sig),
|
&td.details.refresh_revealed_coins.ev_sig),
|
||||||
@ -3598,9 +3598,9 @@ TEH_PG_lookup_records_by_table (void *cls,
|
|||||||
rh = &lrbt_cb_table_purse_deletion;
|
rh = &lrbt_cb_table_purse_deletion;
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
||||||
XPREPARE ("select_above_serial_by_table_withdraw_age_commitments",
|
XPREPARE ("select_above_serial_by_table_age_withdraw_commitments",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" withdraw_age_commitment_id"
|
" age_withdraw_commitment_id"
|
||||||
",h_commitment"
|
",h_commitment"
|
||||||
",amount_with_fee_val"
|
",amount_with_fee_val"
|
||||||
",amount_with_fee_frac"
|
",amount_with_fee_frac"
|
||||||
@ -3608,15 +3608,15 @@ TEH_PG_lookup_records_by_table (void *cls,
|
|||||||
",reserve_pub"
|
",reserve_pub"
|
||||||
",reserve_sig"
|
",reserve_sig"
|
||||||
",noreveal_index"
|
",noreveal_index"
|
||||||
" FROM withdraw_age_commitments"
|
" FROM age_withdraw_commitments"
|
||||||
" WHERE withdraw_age_commitment_id > $1"
|
" WHERE age_withdraw_commitment_id > $1"
|
||||||
" ORDER BY withdraw_age_commitment_id ASC;");
|
" ORDER BY age_withdraw_commitment_id ASC;");
|
||||||
rh = &lrbt_cb_table_withdraw_age_commitments;
|
rh = &lrbt_cb_table_age_withdraw_commitments;
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
||||||
XPREPARE ("select_above_serial_by_table_withdraw_age_revealed_coins",
|
XPREPARE ("select_above_serial_by_table_age_withdraw_revealed_coins",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" withdraw_age_revealed_coins_serial_id"
|
" age_withdraw_revealed_coins_serial_id"
|
||||||
",h_commitment"
|
",h_commitment"
|
||||||
",freshcoin_index"
|
",freshcoin_index"
|
||||||
",denominations_serial"
|
",denominations_serial"
|
||||||
@ -3624,10 +3624,10 @@ TEH_PG_lookup_records_by_table (void *cls,
|
|||||||
",h_coin_ev"
|
",h_coin_ev"
|
||||||
",ev_sig"
|
",ev_sig"
|
||||||
",ewv"
|
",ewv"
|
||||||
" FROM withdraw_age_revealed_coins"
|
" FROM age_withdraw_revealed_coins"
|
||||||
" WHERE withdraw_age_revealed_coins_serial_id > $1"
|
" WHERE age_withdraw_revealed_coins_serial_id > $1"
|
||||||
" ORDER BY withdraw_age_revealed_coins_serial_id ASC;");
|
" ORDER BY age_withdraw_revealed_coins_serial_id ASC;");
|
||||||
rh = &lrbt_cb_table_withdraw_age_revealed_coins;
|
rh = &lrbt_cb_table_age_withdraw_revealed_coins;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL == rh)
|
if (NULL == rh)
|
||||||
|
@ -427,22 +427,22 @@ TEH_PG_lookup_serial_by_table (void *cls,
|
|||||||
statement = "select_serial_by_table_purse_deletion";
|
statement = "select_serial_by_table_purse_deletion";
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_COMMITMENTS:
|
||||||
XPREPARE ("select_serial_by_table_withdraw_age_commitments",
|
XPREPARE ("select_serial_by_table_age_withdraw_commitments",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" withdraw_age_commitment_id AS serial"
|
" age_withdraw_commitment_id AS serial"
|
||||||
" FROM withdraw_age_commitments"
|
" FROM age_withdraw_commitments"
|
||||||
" ORDER BY withdraw_age_commitment_id DESC"
|
" ORDER BY age_withdraw_commitment_id DESC"
|
||||||
" LIMIT 1;");
|
" LIMIT 1;");
|
||||||
statement = "select_serial_by_table_withdraw_age_commitments";
|
statement = "select_serial_by_table_age_withdraw_commitments";
|
||||||
break;
|
break;
|
||||||
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
case TALER_EXCHANGEDB_RT_WITHDRAW_AGE_REVEALED_COINS:
|
||||||
XPREPARE ("select_serial_by_table_withdraw_age_revealed_coins",
|
XPREPARE ("select_serial_by_table_age_withdraw_revealed_coins",
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" withdraw_age_revealed_coins_id AS serial"
|
" age_withdraw_revealed_coins_id AS serial"
|
||||||
" FROM withdraw_age_revealed_coins"
|
" FROM age_withdraw_revealed_coins"
|
||||||
" ORDER BY withdraw_age_revealed_coins_id DESC"
|
" ORDER BY age_withdraw_revealed_coins_id DESC"
|
||||||
" LIMIT 1;");
|
" LIMIT 1;");
|
||||||
statement = "select_serial_by_table_withdraw_age_revealed_coins";
|
statement = "select_serial_by_table_age_withdraw_revealed_coins";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL == statement)
|
if (NULL == statement)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -29,6 +29,9 @@
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
TEH_PG_update_wire (void *cls,
|
TEH_PG_update_wire (void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp change_date,
|
struct GNUNET_TIME_Timestamp change_date,
|
||||||
bool enabled)
|
bool enabled)
|
||||||
{
|
{
|
||||||
@ -36,17 +39,28 @@ TEH_PG_update_wire (void *cls,
|
|||||||
struct GNUNET_PQ_QueryParam params[] = {
|
struct GNUNET_PQ_QueryParam params[] = {
|
||||||
GNUNET_PQ_query_param_string (payto_uri),
|
GNUNET_PQ_query_param_string (payto_uri),
|
||||||
GNUNET_PQ_query_param_bool (enabled),
|
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_timestamp (&change_date),
|
||||||
GNUNET_PQ_query_param_end
|
GNUNET_PQ_query_param_end
|
||||||
};
|
};
|
||||||
|
|
||||||
/* used in #postgres_update_wire() */
|
|
||||||
PREPARE (pg,
|
PREPARE (pg,
|
||||||
"update_wire",
|
"update_wire",
|
||||||
"UPDATE wire_accounts"
|
"UPDATE wire_accounts"
|
||||||
" SET"
|
" SET"
|
||||||
" is_active=$2"
|
" is_active=$2"
|
||||||
" ,last_change=$3"
|
" ,conversion_url=$3"
|
||||||
|
" ,debit_restrictions=$4"
|
||||||
|
" ,credit_restrictions=$5"
|
||||||
|
" ,last_change=$6"
|
||||||
" WHERE payto_uri=$1");
|
" WHERE payto_uri=$1");
|
||||||
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
return GNUNET_PQ_eval_prepared_non_select (pg->conn,
|
||||||
"update_wire",
|
"update_wire",
|
||||||
|
@ -24,11 +24,16 @@
|
|||||||
#include "taler_util.h"
|
#include "taler_util.h"
|
||||||
#include "taler_json_lib.h"
|
#include "taler_json_lib.h"
|
||||||
#include "taler_exchangedb_plugin.h"
|
#include "taler_exchangedb_plugin.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update information about a wire account of the exchange.
|
* Update information about a wire account of the exchange.
|
||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param payto_uri account the update is about
|
* @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
|
* @param change_date date when the account status was last changed
|
||||||
* (only to be used for replay detection)
|
* (only to be used for replay detection)
|
||||||
* @param enabled true to enable, false to disable (the actual change)
|
* @param enabled true to enable, false to disable (the actual change)
|
||||||
@ -37,6 +42,9 @@
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
TEH_PG_update_wire (void *cls,
|
TEH_PG_update_wire (void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp change_date,
|
struct GNUNET_TIME_Timestamp change_date,
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
||||||
|
@ -207,10 +207,10 @@ libtaler_extension_age_restriction_init (void *arg)
|
|||||||
return NULL;
|
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) &&
|
if (GNUNET_OK != TALER_parse_age_group_string (groups, &mask))
|
||||||
(GNUNET_OK != TALER_parse_age_group_string (groups, &mask)))
|
|
||||||
{
|
{
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"[age restriction] couldn't parse age groups: '%s'\n",
|
"[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 timestamp time when the transaction was made.
|
||||||
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
|
* @param json detailed response from the HTTPD, or NULL if reply was not in JSON
|
||||||
*/
|
*/
|
||||||
|
// FIXME: bad API
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_BANK_AdminAddIncomingCallback) (
|
(*TALER_BANK_AdminAddIncomingCallback) (
|
||||||
void *cls,
|
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 row_id unique ID of the wire transfer in the bank's records
|
||||||
* @param timestamp when did the transaction go into effect
|
* @param timestamp when did the transaction go into effect
|
||||||
*/
|
*/
|
||||||
|
// FIXME: bad API
|
||||||
typedef void
|
typedef void
|
||||||
(*TALER_BANK_TransferCallback)(
|
(*TALER_BANK_TransferCallback)(
|
||||||
void *cls,
|
void *cls,
|
||||||
@ -337,7 +339,7 @@ struct TALER_BANK_CreditHistoryResponse
|
|||||||
*/
|
*/
|
||||||
unsigned int details_length;
|
unsigned int details_length;
|
||||||
|
|
||||||
} success;
|
} ok;
|
||||||
|
|
||||||
} details;
|
} details;
|
||||||
|
|
||||||
@ -493,7 +495,7 @@ struct TALER_BANK_DebitHistoryResponse
|
|||||||
*/
|
*/
|
||||||
unsigned int details_length;
|
unsigned int details_length;
|
||||||
|
|
||||||
} success;
|
} ok;
|
||||||
|
|
||||||
} details;
|
} details;
|
||||||
|
|
||||||
|
@ -5512,6 +5512,9 @@ TALER_exchange_offline_global_fee_verify (
|
|||||||
* Create wire account addition signature.
|
* Create wire account addition signature.
|
||||||
*
|
*
|
||||||
* @param payto_uri bank account
|
* @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 now timestamp to use for the signature (rounded)
|
||||||
* @param master_priv private key to sign with
|
* @param master_priv private key to sign with
|
||||||
* @param[out] master_sig where to write the signature
|
* @param[out] master_sig where to write the signature
|
||||||
@ -5519,6 +5522,9 @@ TALER_exchange_offline_global_fee_verify (
|
|||||||
void
|
void
|
||||||
TALER_exchange_offline_wire_add_sign (
|
TALER_exchange_offline_wire_add_sign (
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
const json_t *debit_restrictions,
|
||||||
|
const json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp now,
|
struct GNUNET_TIME_Timestamp now,
|
||||||
const struct TALER_MasterPrivateKeyP *master_priv,
|
const struct TALER_MasterPrivateKeyP *master_priv,
|
||||||
struct TALER_MasterSignatureP *master_sig);
|
struct TALER_MasterSignatureP *master_sig);
|
||||||
@ -5528,6 +5534,9 @@ TALER_exchange_offline_wire_add_sign (
|
|||||||
* Verify wire account addition signature.
|
* Verify wire account addition signature.
|
||||||
*
|
*
|
||||||
* @param payto_uri bank account
|
* @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 sign_time timestamp when signature was created
|
||||||
* @param master_pub public key to verify against
|
* @param master_pub public key to verify against
|
||||||
* @param master_sig the signature the signature
|
* @param master_sig the signature the signature
|
||||||
@ -5536,6 +5545,9 @@ TALER_exchange_offline_wire_add_sign (
|
|||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_exchange_offline_wire_add_verify (
|
TALER_exchange_offline_wire_add_verify (
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
const json_t *debit_restrictions,
|
||||||
|
const json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp sign_time,
|
struct GNUNET_TIME_Timestamp sign_time,
|
||||||
const struct TALER_MasterPublicKeyP *master_pub,
|
const struct TALER_MasterPublicKeyP *master_pub,
|
||||||
const struct TALER_MasterSignatureP *master_sig);
|
const struct TALER_MasterSignatureP *master_sig);
|
||||||
@ -5578,6 +5590,9 @@ TALER_exchange_offline_wire_del_verify (
|
|||||||
* Check the signature in @a master_sig.
|
* Check the signature in @a master_sig.
|
||||||
*
|
*
|
||||||
* @param payto_uri URI that is signed
|
* @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_pub master public key of the exchange
|
||||||
* @param master_sig signature of the exchange
|
* @param master_sig signature of the exchange
|
||||||
* @return #GNUNET_OK if signature is valid
|
* @return #GNUNET_OK if signature is valid
|
||||||
@ -5585,6 +5600,9 @@ TALER_exchange_offline_wire_del_verify (
|
|||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_exchange_wire_signature_check (
|
TALER_exchange_wire_signature_check (
|
||||||
const char *payto_uri,
|
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_MasterPublicKeyP *master_pub,
|
||||||
const struct TALER_MasterSignatureP *master_sig);
|
const struct TALER_MasterSignatureP *master_sig);
|
||||||
|
|
||||||
@ -5593,12 +5611,18 @@ TALER_exchange_wire_signature_check (
|
|||||||
* Create a signed wire statement for the given account.
|
* Create a signed wire statement for the given account.
|
||||||
*
|
*
|
||||||
* @param payto_uri account specification
|
* @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 master_priv private key to sign with
|
||||||
* @param[out] master_sig where to write the signature
|
* @param[out] master_sig where to write the signature
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_exchange_wire_signature_make (
|
TALER_exchange_wire_signature_make (
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
const json_t *debit_restrictions,
|
||||||
|
const json_t *credit_restrictions,
|
||||||
const struct TALER_MasterPrivateKeyP *master_priv,
|
const struct TALER_MasterPrivateKeyP *master_priv,
|
||||||
struct TALER_MasterSignatureP *master_sig);
|
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
|
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
|
struct TALER_ReserveEventP
|
||||||
{
|
{
|
||||||
@ -760,7 +778,7 @@ struct TALER_EXCHANGEDB_TableData
|
|||||||
struct TALER_ReserveSignatureP reserve_sig;
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
uint32_t noreveal_index;
|
uint32_t noreveal_index;
|
||||||
struct GNUNET_TIME_Absolute timestamp;
|
struct GNUNET_TIME_Absolute timestamp;
|
||||||
} withdraw_age_commitments;
|
} age_withdraw_commitments;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
@ -769,10 +787,9 @@ struct TALER_EXCHANGEDB_TableData
|
|||||||
uint64_t denominations_serial;
|
uint64_t denominations_serial;
|
||||||
void *coin_ev;
|
void *coin_ev;
|
||||||
size_t coin_ev_size;
|
size_t coin_ev_size;
|
||||||
struct TALER_ExchangeWithdrawValues ewv;
|
|
||||||
// h_coin_ev omitted, to be recomputed!
|
// h_coin_ev omitted, to be recomputed!
|
||||||
struct TALER_BlindedDenominationSignature ev_sig;
|
struct TALER_BlindedDenominationSignature ev_sig;
|
||||||
} withdraw_age_revealed_coins;
|
} age_withdraw_revealed_coins;
|
||||||
|
|
||||||
} details;
|
} details;
|
||||||
|
|
||||||
@ -1200,8 +1217,9 @@ struct TALER_EXCHANGEDB_AgeWithdrawCommitment
|
|||||||
struct TALER_ReservePublicKeyP reserve_pub;
|
struct TALER_ReservePublicKeyP reserve_pub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature confirming the age withdrawal, matching @e reserve_pub, @e
|
* Signature confirming the age withdrawal commitment, matching @e
|
||||||
* maximum_age_group and @e h_commitment and @e total_amount_with_fee.
|
* reserve_pub, @e maximum_age_group and @e h_commitment and @e
|
||||||
|
* total_amount_with_fee.
|
||||||
*/
|
*/
|
||||||
struct TALER_ReserveSignatureP reserve_sig;
|
struct TALER_ReserveSignatureP reserve_sig;
|
||||||
|
|
||||||
@ -2737,6 +2755,28 @@ struct TALER_EXCHANGEDB_CsRevealFreshCoinData
|
|||||||
uint32_t coin_off;
|
uint32_t coin_off;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a coin that was revealed to the exchange
|
||||||
|
* during reveal.
|
||||||
|
*/
|
||||||
|
struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Hash of the public denomination key of the coin.
|
||||||
|
*/
|
||||||
|
struct TALER_DenominationHashP h_denom_pub;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature generated by the exchange over the coin (in blinded format).
|
||||||
|
*/
|
||||||
|
struct TALER_BlindedDenominationSignature coin_sig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blinded hash of the new coin
|
||||||
|
*/
|
||||||
|
struct TALER_BlindedCoinHashP h_coin_ev;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic KYC status for some operation.
|
* Generic KYC status for some operation.
|
||||||
@ -2859,6 +2899,9 @@ typedef enum GNUNET_GenericReturnValue
|
|||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param payto_uri the exchange bank account URI
|
* @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
|
* @param master_sig master key signature affirming that this is a bank
|
||||||
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
* account of the exchange (of purpose #TALER_SIGNATURE_MASTER_WIRE_DETAILS)
|
||||||
*/
|
*/
|
||||||
@ -2866,6 +2909,9 @@ typedef void
|
|||||||
(*TALER_EXCHANGEDB_WireAccountCallback)(
|
(*TALER_EXCHANGEDB_WireAccountCallback)(
|
||||||
void *cls,
|
void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
const json_t *debit_restrictions,
|
||||||
|
const json_t *credit_restrictions,
|
||||||
const struct TALER_MasterSignatureP *master_sig);
|
const struct TALER_MasterSignatureP *master_sig);
|
||||||
|
|
||||||
|
|
||||||
@ -3810,19 +3856,18 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
* age restriction enabled in a given age-withdraw operation and the relevant
|
* age restriction enabled in a given age-withdraw operation and the relevant
|
||||||
* information we learned or created in the reveal steop
|
* information we learned or created in the reveal steop
|
||||||
*
|
*
|
||||||
* @param cls the `struct PostgresClosure` with the plugin-specific state
|
* @param cls The `struct PostgresClosure` with the plugin-specific state
|
||||||
* @param h_commitment The hash of the original age-withdraw commitment, which is a key into the withdraw_age_commitments table
|
* @param h_commitment The hash of the original age-withdraw commitment, which is a key into the age_withdraw_commitments table
|
||||||
* @param num_coins number of coins to generate, size of the @a coin_evs array
|
* @param num_awrcs Number of coins to generate, size of the @a coin_evs array
|
||||||
* TODO: oec
|
* @param awrcs Array of @a num_awrcs information about coins to be created
|
||||||
* @return query execution status
|
* @return query execution status
|
||||||
*/
|
*/
|
||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
(*insert_age_withdraw_reveal)(
|
(*insert_age_withdraw_reveal)(
|
||||||
void *cls,
|
void *cls,
|
||||||
uint64_t h_commitment,
|
const struct TALER_AgeWithdrawCommitmentHashP *h_commitment,
|
||||||
uint32_t num_coins
|
uint32_t num_awrcs,
|
||||||
/* TODO: oec */
|
const struct TALER_EXCHANGEDB_AgeWithdrawRevealedCoin *awrcs);
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup in the database for the fresh coins with age-restriction that
|
* Lookup in the database for the fresh coins with age-restriction that
|
||||||
@ -5523,6 +5568,9 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param payto_uri wire account of the exchange
|
* @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
|
* @param start_date date when the account was added by the offline system
|
||||||
* (only to be used for replay detection)
|
* (only to be used for replay detection)
|
||||||
* @param master_sig public signature affirming the existence of the account,
|
* @param master_sig public signature affirming the existence of the account,
|
||||||
@ -5532,6 +5580,9 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
(*insert_wire)(void *cls,
|
(*insert_wire)(void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp start_date,
|
struct GNUNET_TIME_Timestamp start_date,
|
||||||
const struct TALER_MasterSignatureP *master_sig);
|
const struct TALER_MasterSignatureP *master_sig);
|
||||||
|
|
||||||
@ -5541,6 +5592,9 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
*
|
*
|
||||||
* @param cls closure
|
* @param cls closure
|
||||||
* @param payto_uri account the update is about
|
* @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
|
* @param change_date date when the account status was last changed
|
||||||
* (only to be used for replay detection)
|
* (only to be used for replay detection)
|
||||||
* @param enabled true to enable, false to disable (the actual change)
|
* @param enabled true to enable, false to disable (the actual change)
|
||||||
@ -5549,6 +5603,9 @@ struct TALER_EXCHANGEDB_Plugin
|
|||||||
enum GNUNET_DB_QueryStatus
|
enum GNUNET_DB_QueryStatus
|
||||||
(*update_wire)(void *cls,
|
(*update_wire)(void *cls,
|
||||||
const char *payto_uri,
|
const char *payto_uri,
|
||||||
|
const char *conversion_url,
|
||||||
|
json_t *debit_restrictions,
|
||||||
|
json_t *credit_restrictions,
|
||||||
struct GNUNET_TIME_Timestamp change_date,
|
struct GNUNET_TIME_Timestamp change_date,
|
||||||
bool enabled);
|
bool enabled);
|
||||||
|
|
||||||
|
@ -344,10 +344,6 @@ TALER_extensions_verify_manifests_signature (
|
|||||||
* The default age mask represents the age groups
|
* The default age mask represents the age groups
|
||||||
* 0-7, 8-9, 10-11, 12-13, 14-15, 16-17, 18-20, 21-...
|
* 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"
|
#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);
|
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
|
* Extract a string from @a object under the field @a field, but respecting
|
||||||
* the Taler i18n rules and the language preferences expressed in @a
|
* 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
|
* Queues an error response to the connection if the parameter is missing or
|
||||||
* invalid.
|
* 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 param_name the name of the parameter with the key
|
||||||
* @param[out] out_data pointer to store the result
|
* @param[out] out_data pointer to store the result
|
||||||
* @param out_size expected size of @a out_data
|
* @param out_size expected size of @a out_data
|
||||||
|
* @param[out] present set to true if argument was found
|
||||||
* @return
|
* @return
|
||||||
* #GNUNET_YES if the the argument is present
|
* #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)
|
* #GNUNET_SYSERR on internal error (error response could not be sent)
|
||||||
*/
|
*/
|
||||||
enum GNUNET_GenericReturnValue
|
enum GNUNET_GenericReturnValue
|
||||||
TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
|
TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
|
||||||
const char *param_name,
|
const char *param_name,
|
||||||
void *out_data,
|
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
|
#define TALER_TEMPLATING_LIB_H
|
||||||
|
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
|
#include "taler_mhd_lib.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill in Mustach template @a tmpl using the data from @a root
|
* 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 char *taler_uri,
|
||||||
const json_t *root);
|
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.
|
* Preload templates.
|
||||||
*
|
*
|
||||||
|
@ -147,15 +147,11 @@ TALER_TESTING_prepare_exchange (const char *config_filename,
|
|||||||
*
|
*
|
||||||
* @param cls closure, typically, the "run" method containing
|
* @param cls closure, typically, the "run" method containing
|
||||||
* all the commands to be run, and a closure for it.
|
* all the commands to be run, and a closure for it.
|
||||||
* @param hr http response details
|
* @param kr response details
|
||||||
* @param keys the exchange's keys.
|
|
||||||
* @param compat protocol compatibility information.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
TALER_TESTING_cert_cb (void *cls,
|
TALER_TESTING_cert_cb (void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_KeysResponse *kr);
|
||||||
const struct TALER_EXCHANGE_Keys *keys,
|
|
||||||
enum TALER_EXCHANGE_VersionCompatibility compat);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1242,6 +1238,36 @@ TALER_TESTING_cmd_exec_wirewatch (const char *label,
|
|||||||
const char *config_filename);
|
const char *config_filename);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request URL via "wget".
|
||||||
|
*
|
||||||
|
* @param label command label.
|
||||||
|
* @param url URL to fetch
|
||||||
|
* @return the command.
|
||||||
|
*/
|
||||||
|
struct TALER_TESTING_Command
|
||||||
|
TALER_TESTING_cmd_exec_wget (const char *label,
|
||||||
|
const char *url);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request fetch-transactions via "wget".
|
||||||
|
*
|
||||||
|
* @param label command label.
|
||||||
|
* @param username username to use
|
||||||
|
* @param password password to use
|
||||||
|
* @param bank_base_url base URL of the nexus
|
||||||
|
* @param account_id account to fetch transactions for
|
||||||
|
* @return the command.
|
||||||
|
*/
|
||||||
|
struct TALER_TESTING_Command
|
||||||
|
TALER_TESTING_cmd_nexus_fetch_transactions (const char *label,
|
||||||
|
const char *username,
|
||||||
|
const char *password,
|
||||||
|
const char *bank_base_url,
|
||||||
|
const char *account_id);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make a "expire" CMD.
|
* Make a "expire" CMD.
|
||||||
*
|
*
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -229,6 +229,16 @@ void
|
|||||||
TALER_OS_init (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.
|
* URL-encode a string according to rfc3986.
|
||||||
*
|
*
|
||||||
@ -559,6 +569,58 @@ enum GNUNET_GenericReturnValue
|
|||||||
TALER_JSON_parse_age_groups (const json_t *root,
|
TALER_JSON_parse_age_groups (const json_t *root,
|
||||||
struct TALER_AgeMask *mask);
|
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__
|
#undef __TALER_UTIL_LIB_H_INSIDE__
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,12 +28,10 @@ libtalerjson_la_LIBADD = \
|
|||||||
$(XLIB)
|
$(XLIB)
|
||||||
|
|
||||||
TESTS = \
|
TESTS = \
|
||||||
test_json \
|
test_json
|
||||||
test_json_wire
|
|
||||||
|
|
||||||
check_PROGRAMS= \
|
check_PROGRAMS= \
|
||||||
test_json \
|
test_json
|
||||||
test_json_wire
|
|
||||||
|
|
||||||
test_json_SOURCES = \
|
test_json_SOURCES = \
|
||||||
test_json.c
|
test_json.c
|
||||||
@ -43,13 +41,3 @@ test_json_LDADD = \
|
|||||||
$(top_builddir)/src/util/libtalerutil.la \
|
$(top_builddir)/src/util/libtalerutil.la \
|
||||||
-lgnunetutil \
|
-lgnunetutil \
|
||||||
-ljansson
|
-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.
|
* Dump the @a json to a string and hash it.
|
||||||
*
|
*
|
||||||
@ -262,7 +98,7 @@ dump_and_hash (const json_t *json,
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
len = rfc8785encode (&wire_enc);
|
len = TALER_rfc8785encode (&wire_enc);
|
||||||
if (NULL == salt)
|
if (NULL == salt)
|
||||||
{
|
{
|
||||||
GNUNET_CRYPTO_hash (wire_enc,
|
GNUNET_CRYPTO_hash (wire_enc,
|
||||||
@ -819,6 +655,7 @@ parse_path (json_t *obj,
|
|||||||
json_t *next_obj = NULL;
|
json_t *next_obj = NULL;
|
||||||
char *next_dot;
|
char *next_dot;
|
||||||
|
|
||||||
|
GNUNET_assert (NULL != id); /* make stupid compiler happy */
|
||||||
if (NULL == next_id)
|
if (NULL == next_id)
|
||||||
{
|
{
|
||||||
cb (cb_cls,
|
cb (cb_cls,
|
||||||
@ -1031,7 +868,7 @@ TALER_JSON_canonicalize (const json_t *input)
|
|||||||
GNUNET_break (0);
|
GNUNET_break (0);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rfc8785encode (&wire_enc);
|
TALER_rfc8785encode (&wire_enc);
|
||||||
return 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 *
|
char *
|
||||||
TALER_JSON_wire_to_payto (const json_t *wire_s)
|
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 = \
|
EXTRA_DIST = \
|
||||||
$(pkgcfg_DATA) \
|
$(pkgcfg_DATA) \
|
||||||
sample.conf
|
sample.conf \
|
||||||
|
persona-sample-reply.json
|
||||||
|
|
||||||
|
bin_SCRIPTS = \
|
||||||
|
taler-exchange-kyc-persona-converter.sh
|
||||||
|
|
||||||
lib_LTLIBRARIES = \
|
lib_LTLIBRARIES = \
|
||||||
libtalerkyclogic.la
|
libtalerkyclogic.la
|
||||||
|
@ -29,6 +29,10 @@ KYC_PERSONA_SUBDOMAIN = taler
|
|||||||
# Authentication token to use.
|
# Authentication token to use.
|
||||||
KYC_PERSONA_AUTH_TOKEN = persona_sandbox_42
|
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.
|
# Form to use.
|
||||||
KYC_PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
|
KYC_PERSONA_TEMPLATE_ID = itempl_Uj6Xxxxx
|
||||||
|
|
||||||
|
@ -780,10 +780,11 @@ TALER_KYCLOGIC_kyc_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
|
|||||||
TALER_KYCLOGIC_kyc_done ();
|
TALER_KYCLOGIC_kyc_done ();
|
||||||
return GNUNET_SYSERR;
|
return GNUNET_SYSERR;
|
||||||
}
|
}
|
||||||
qsort (kyc_triggers,
|
if (0 != num_kyc_triggers)
|
||||||
num_kyc_triggers,
|
qsort (kyc_triggers,
|
||||||
sizeof (struct TALER_KYCLOGIC_KycTrigger *),
|
num_kyc_triggers,
|
||||||
&sort_by_timeframe);
|
sizeof (struct TALER_KYCLOGIC_KycTrigger *),
|
||||||
|
&sort_by_timeframe);
|
||||||
return GNUNET_OK;
|
return GNUNET_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,8 +490,6 @@ initiate_task (void *cls)
|
|||||||
struct PluginState *ps = pd->ps;
|
struct PluginState *ps = pd->ps;
|
||||||
char *hps;
|
char *hps;
|
||||||
char *url;
|
char *url;
|
||||||
char *redirect_uri;
|
|
||||||
char *redirect_uri_encoded;
|
|
||||||
char legi_s[42];
|
char legi_s[42];
|
||||||
|
|
||||||
ih->task = NULL;
|
ih->task = NULL;
|
||||||
@ -501,19 +499,27 @@ initiate_task (void *cls)
|
|||||||
(unsigned long long) ih->legitimization_uuid);
|
(unsigned long long) ih->legitimization_uuid);
|
||||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
hps = GNUNET_STRINGS_data_to_string_alloc (&ih->h_payto,
|
||||||
sizeof (ih->h_payto));
|
sizeof (ih->h_payto));
|
||||||
GNUNET_asprintf (&redirect_uri,
|
{
|
||||||
"%skyc-proof/%s?state=%s",
|
char *redirect_uri_encoded;
|
||||||
ps->exchange_base_url,
|
|
||||||
pd->section,
|
{
|
||||||
hps);
|
char *redirect_uri;
|
||||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
|
||||||
GNUNET_free (redirect_uri);
|
GNUNET_asprintf (&redirect_uri,
|
||||||
GNUNET_asprintf (&url,
|
"%skyc-proof/%s?state=%s",
|
||||||
"%s?response_type=code&client_id=%s&redirect_uri=%s",
|
ps->exchange_base_url,
|
||||||
pd->login_url,
|
pd->section,
|
||||||
pd->client_id,
|
hps);
|
||||||
redirect_uri_encoded);
|
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||||
GNUNET_free (redirect_uri_encoded);
|
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,
|
/* FIXME-API: why do we *redirect* the client here,
|
||||||
instead of making the HTTP request *ourselves*
|
instead of making the HTTP request *ourselves*
|
||||||
and forwarding the response? This prevents us
|
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
|
* Function called to asynchronously return the final
|
||||||
* result to the callback.
|
* result to the callback.
|
||||||
@ -602,10 +639,8 @@ return_proof_response (void *cls)
|
|||||||
ph->attributes,
|
ph->attributes,
|
||||||
ph->http_status,
|
ph->http_status,
|
||||||
ph->response);
|
ph->response);
|
||||||
GNUNET_free (ph->provider_user_id);
|
ph->response = NULL; /*Ownership passed to 'ph->cb'!*/
|
||||||
if (NULL != ph->attributes)
|
oauth2_proof_cancel (ph);
|
||||||
json_decref (ph->attributes);
|
|
||||||
GNUNET_free (ph);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1101,7 +1136,6 @@ oauth2_proof (void *cls,
|
|||||||
1));
|
1));
|
||||||
{
|
{
|
||||||
char *client_id;
|
char *client_id;
|
||||||
char *redirect_uri;
|
|
||||||
char *client_secret;
|
char *client_secret;
|
||||||
char *authorization_code;
|
char *authorization_code;
|
||||||
char *redirect_uri_encoded;
|
char *redirect_uri_encoded;
|
||||||
@ -1109,13 +1143,17 @@ oauth2_proof (void *cls,
|
|||||||
|
|
||||||
hps = GNUNET_STRINGS_data_to_string_alloc (&ph->h_payto,
|
hps = GNUNET_STRINGS_data_to_string_alloc (&ph->h_payto,
|
||||||
sizeof (ph->h_payto));
|
sizeof (ph->h_payto));
|
||||||
GNUNET_asprintf (&redirect_uri,
|
{
|
||||||
"%skyc-proof/%s?state=%s",
|
char *redirect_uri;
|
||||||
ps->exchange_base_url,
|
|
||||||
pd->section,
|
GNUNET_asprintf (&redirect_uri,
|
||||||
hps);
|
"%skyc-proof/%s?state=%s",
|
||||||
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
ps->exchange_base_url,
|
||||||
GNUNET_free (redirect_uri);
|
pd->section,
|
||||||
|
hps);
|
||||||
|
redirect_uri_encoded = TALER_urlencode (redirect_uri);
|
||||||
|
GNUNET_free (redirect_uri);
|
||||||
|
}
|
||||||
GNUNET_assert (NULL != redirect_uri_encoded);
|
GNUNET_assert (NULL != redirect_uri_encoded);
|
||||||
client_id = curl_easy_escape (ph->eh,
|
client_id = curl_easy_escape (ph->eh,
|
||||||
pd->client_id,
|
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
|
* Function to asynchronously return the 404 not found
|
||||||
* page for the webhook.
|
* page for the webhook.
|
||||||
|
@ -111,6 +111,12 @@ struct TALER_KYCLOGIC_ProviderDetails
|
|||||||
*/
|
*/
|
||||||
char *subdomain;
|
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.
|
* Where to redirect the client upon completion.
|
||||||
*/
|
*/
|
||||||
@ -230,6 +236,12 @@ struct TALER_KYCLOGIC_ProofHandle
|
|||||||
*/
|
*/
|
||||||
char *url;
|
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.
|
* Hash of the payto:// URI we are checking the KYC for.
|
||||||
*/
|
*/
|
||||||
@ -246,6 +258,11 @@ struct TALER_KYCLOGIC_ProofHandle
|
|||||||
*/
|
*/
|
||||||
char *provider_user_id;
|
char *provider_user_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account ID from the service.
|
||||||
|
*/
|
||||||
|
char *account_id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inquiry ID at the provider.
|
* Inquiry ID at the provider.
|
||||||
*/
|
*/
|
||||||
@ -294,6 +311,11 @@ struct TALER_KYCLOGIC_WebhookHandle
|
|||||||
*/
|
*/
|
||||||
char *inquiry_id;
|
char *inquiry_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account ID from the service.
|
||||||
|
*/
|
||||||
|
char *account_id;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL of the cURL request.
|
* URL of the cURL request.
|
||||||
*/
|
*/
|
||||||
@ -315,6 +337,12 @@ struct TALER_KYCLOGIC_WebhookHandle
|
|||||||
*/
|
*/
|
||||||
const char *template_id;
|
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.
|
* Our account ID.
|
||||||
*/
|
*/
|
||||||
@ -344,6 +372,7 @@ persona_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
|
|||||||
GNUNET_free (pd->auth_token);
|
GNUNET_free (pd->auth_token);
|
||||||
GNUNET_free (pd->template_id);
|
GNUNET_free (pd->template_id);
|
||||||
GNUNET_free (pd->subdomain);
|
GNUNET_free (pd->subdomain);
|
||||||
|
GNUNET_free (pd->conversion_binary);
|
||||||
GNUNET_free (pd->salt);
|
GNUNET_free (pd->salt);
|
||||||
GNUNET_free (pd->section);
|
GNUNET_free (pd->section);
|
||||||
GNUNET_free (pd->post_kyc_redirect_url);
|
GNUNET_free (pd->post_kyc_redirect_url);
|
||||||
@ -418,6 +447,18 @@ persona_load_configuration (void *cls,
|
|||||||
persona_unload_configuration (pd);
|
persona_unload_configuration (pd);
|
||||||
return NULL;
|
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 !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
GNUNET_CONFIGURATION_get_value_string (ps->cfg,
|
||||||
provider_section_name,
|
provider_section_name,
|
||||||
@ -838,8 +879,14 @@ persona_proof_cancel (struct TALER_KYCLOGIC_ProofHandle *ph)
|
|||||||
GNUNET_CURL_job_cancel (ph->job);
|
GNUNET_CURL_job_cancel (ph->job);
|
||||||
ph->job = NULL;
|
ph->job = NULL;
|
||||||
}
|
}
|
||||||
|
if (NULL != ph->ec)
|
||||||
|
{
|
||||||
|
TALER_JSON_external_conversion_stop (ph->ec);
|
||||||
|
ph->ec = NULL;
|
||||||
|
}
|
||||||
GNUNET_free (ph->url);
|
GNUNET_free (ph->url);
|
||||||
GNUNET_free (ph->provider_user_id);
|
GNUNET_free (ph->provider_user_id);
|
||||||
|
GNUNET_free (ph->account_id);
|
||||||
GNUNET_free (ph->inquiry_id);
|
GNUNET_free (ph->inquiry_id);
|
||||||
GNUNET_free (ph);
|
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
|
* Return a response for the @a ph request indicating a
|
||||||
* protocol violation by the Persona server.
|
* 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
|
* Function called when we're done processing the
|
||||||
* HTTP "/api/v1/inquiries/{inquiry-id}" request.
|
* HTTP "/api/v1/inquiries/{inquiry-id}" request.
|
||||||
@ -1283,46 +1255,15 @@ handle_proof_finished (void *cls,
|
|||||||
data);
|
data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
ph->account_id = GNUNET_strdup (account_id);
|
||||||
{
|
ph->ec = start_conversion (ph->pd,
|
||||||
struct MHD_Response *resp;
|
j,
|
||||||
struct GNUNET_TIME_Absolute expiration;
|
&proof_post_conversion_cb,
|
||||||
json_t *attr;
|
ph);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
GNUNET_JSON_parse_free (ispec);
|
GNUNET_JSON_parse_free (ispec);
|
||||||
}
|
}
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
break;
|
return; /* continued in proof_post_conversion_cb */
|
||||||
}
|
}
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
@ -1580,6 +1521,12 @@ persona_webhook_cancel (struct TALER_KYCLOGIC_WebhookHandle *wh)
|
|||||||
GNUNET_CURL_job_cancel (wh->job);
|
GNUNET_CURL_job_cancel (wh->job);
|
||||||
wh->job = NULL;
|
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->inquiry_id);
|
||||||
GNUNET_free (wh->url);
|
GNUNET_free (wh->url);
|
||||||
GNUNET_free (wh);
|
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
|
* Function called when we're done processing the
|
||||||
* HTTP "/api/v1/inquiries/{inquiry_id}" request.
|
* HTTP "/api/v1/inquiries/{inquiry_id}" request.
|
||||||
@ -1723,7 +1696,6 @@ handle_webhook_finished (void *cls,
|
|||||||
NULL),
|
NULL),
|
||||||
GNUNET_JSON_spec_end ()
|
GNUNET_JSON_spec_end ()
|
||||||
};
|
};
|
||||||
json_t *attr;
|
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_JSON_parse (attributes,
|
GNUNET_JSON_parse (attributes,
|
||||||
@ -1807,19 +1779,15 @@ handle_webhook_finished (void *cls,
|
|||||||
MHD_HTTP_BAD_GATEWAY);
|
MHD_HTTP_BAD_GATEWAY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
wh->account_id = GNUNET_strdup (account_id);
|
||||||
attr = convert_attributes (attributes);
|
wh->ec = start_conversion (wh->pd,
|
||||||
webhook_generic_reply (wh,
|
j,
|
||||||
TALER_KYCLOGIC_STATUS_SUCCESS,
|
&webhook_post_conversion_cb,
|
||||||
account_id,
|
wh);
|
||||||
inquiry_id,
|
|
||||||
attr,
|
|
||||||
MHD_HTTP_OK);
|
|
||||||
json_decref (attr);
|
|
||||||
GNUNET_JSON_parse_free (ispec);
|
GNUNET_JSON_parse_free (ispec);
|
||||||
}
|
}
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
break;
|
return; /* continued in webhook_post_conversion_cb */
|
||||||
}
|
}
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
case MHD_HTTP_NOT_FOUND:
|
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 */
|
/* Parse command-line arguments */
|
||||||
/* make a copy of 'url' because 'strtok_r()' will modify */
|
/* make a copy of 'url' because 'strtok_r()' will modify */
|
||||||
memcpy (d,
|
GNUNET_memcpy (d,
|
||||||
url,
|
url,
|
||||||
ulen);
|
ulen);
|
||||||
i = 0;
|
i = 0;
|
||||||
args[i++] = strtok_r (d, "/", &sp);
|
args[i++] = strtok_r (d, "/", &sp);
|
||||||
while ( (NULL != args[i - 1]) &&
|
while ( (NULL != args[i - 1]) &&
|
||||||
|
@ -247,7 +247,7 @@ handle_deposit_finished (void *cls,
|
|||||||
&dh->exchange_pub),
|
&dh->exchange_pub),
|
||||||
GNUNET_JSON_spec_mark_optional (
|
GNUNET_JSON_spec_mark_optional (
|
||||||
GNUNET_JSON_spec_string ("transaction_base_url",
|
GNUNET_JSON_spec_string ("transaction_base_url",
|
||||||
&dr.details.success.transaction_base_url),
|
&dr.details.ok.transaction_base_url),
|
||||||
NULL),
|
NULL),
|
||||||
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
GNUNET_JSON_spec_timestamp ("exchange_timestamp",
|
||||||
&dh->exchange_timestamp),
|
&dh->exchange_timestamp),
|
||||||
@ -332,7 +332,7 @@ handle_deposit_finished (void *cls,
|
|||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
dr.hr.http_status = 0;
|
dr.hr.http_status = 0;
|
||||||
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
|
dr.hr.ec = TALER_EC_EXCHANGE_DEPOSIT_INVALID_SIGNATURE_BY_EXCHANGE;
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,10 +341,10 @@ handle_deposit_finished (void *cls,
|
|||||||
dh);
|
dh);
|
||||||
GNUNET_JSON_parse_free (spec);
|
GNUNET_JSON_parse_free (spec);
|
||||||
}
|
}
|
||||||
dr.details.success.exchange_sigs = dh->exchange_sigs;
|
dr.details.ok.exchange_sigs = dh->exchange_sigs;
|
||||||
dr.details.success.exchange_pub = &dh->exchange_pub;
|
dr.details.ok.exchange_pub = &dh->exchange_pub;
|
||||||
dr.details.success.deposit_timestamp = dh->exchange_timestamp;
|
dr.details.ok.deposit_timestamp = dh->exchange_timestamp;
|
||||||
dr.details.success.num_signatures = dh->num_cdds;
|
dr.details.ok.num_signatures = dh->num_cdds;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* 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.
|
* HTTP /reserves/$RESERVE_PUB/batch-withdraw request.
|
||||||
*
|
*
|
||||||
* @param cls the `struct TALER_EXCHANGE_BatchWithdrawHandle`
|
* @param cls the `struct TALER_EXCHANGE_BatchWithdrawHandle`
|
||||||
* @param hr HTTP response data
|
* @param bw2r 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
|
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
handle_reserve_batch_withdraw_finished (
|
handle_reserve_batch_withdraw_finished (
|
||||||
void *cls,
|
void *cls,
|
||||||
const struct TALER_EXCHANGE_HttpResponse *hr,
|
const struct TALER_EXCHANGE_BatchWithdraw2Response *bw2r)
|
||||||
const struct TALER_BlindedDenominationSignature *blind_sigs,
|
|
||||||
unsigned int blind_sigs_length)
|
|
||||||
{
|
{
|
||||||
struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cls;
|
struct TALER_EXCHANGE_BatchWithdrawHandle *wh = cls;
|
||||||
struct TALER_EXCHANGE_BatchWithdrawResponse wr = {
|
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;
|
wh->wh2 = NULL;
|
||||||
memset (coins,
|
memset (coins,
|
||||||
0,
|
0,
|
||||||
sizeof (coins));
|
sizeof (coins));
|
||||||
if (blind_sigs_length != wh->num_coins)
|
switch (bw2r->hr.http_status)
|
||||||
{
|
|
||||||
GNUNET_break_op (0);
|
|
||||||
wr.hr.http_status = 0;
|
|
||||||
wr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
|
||||||
}
|
|
||||||
switch (hr->http_status)
|
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
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++)
|
for (unsigned int i = 0; i<wh->num_coins; i++)
|
||||||
{
|
{
|
||||||
struct CoinData *cd = &wh->coins[i];
|
struct CoinData *cd = &wh->coins[i];
|
||||||
@ -183,7 +180,7 @@ handle_reserve_batch_withdraw_finished (
|
|||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
TALER_planchet_to_coin (&cd->pk.key,
|
TALER_planchet_to_coin (&cd->pk.key,
|
||||||
&blind_sigs[i],
|
&bw2r->details.ok.blind_sigs[i],
|
||||||
&cd->bks,
|
&cd->bks,
|
||||||
&cd->priv,
|
&cd->priv,
|
||||||
cd->ach,
|
cd->ach,
|
||||||
@ -200,8 +197,8 @@ handle_reserve_batch_withdraw_finished (
|
|||||||
coin->sig = fc.sig;
|
coin->sig = fc.sig;
|
||||||
coin->exchange_vals = cd->alg_values;
|
coin->exchange_vals = cd->alg_values;
|
||||||
}
|
}
|
||||||
wr.details.success.coins = coins;
|
wr.details.ok.coins = coins;
|
||||||
wr.details.success.num_coins = wh->num_coins;
|
wr.details.ok.num_coins = wh->num_coins;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
||||||
@ -217,7 +214,7 @@ handle_reserve_batch_withdraw_finished (
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
GNUNET_JSON_parse (hr->reply,
|
GNUNET_JSON_parse (bw2r->hr.reply,
|
||||||
spec,
|
spec,
|
||||||
NULL, NULL))
|
NULL, NULL))
|
||||||
{
|
{
|
||||||
@ -289,7 +286,7 @@ withdraw_cs_stage_two_callback (
|
|||||||
switch (csrr->hr.http_status)
|
switch (csrr->hr.http_status)
|
||||||
{
|
{
|
||||||
case MHD_HTTP_OK:
|
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,
|
TALER_planchet_setup_coin_priv (&cd->ps,
|
||||||
&cd->alg_values,
|
&cd->alg_values,
|
||||||
&cd->priv);
|
&cd->priv);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU General Public License as published by the Free Software
|
||||||
@ -108,9 +108,9 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
|
|||||||
"ev_sigs");
|
"ev_sigs");
|
||||||
const json_t *j;
|
const json_t *j;
|
||||||
unsigned int index;
|
unsigned int index;
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
|
||||||
.reply = json,
|
.hr.reply = json,
|
||||||
.http_status = MHD_HTTP_OK
|
.hr.http_status = MHD_HTTP_OK
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( (NULL == ja) ||
|
if ( (NULL == ja) ||
|
||||||
@ -141,10 +141,10 @@ reserve_batch_withdraw_ok (struct TALER_EXCHANGE_BatchWithdraw2Handle *wh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* signature is valid, return it to the application */
|
/* 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,
|
wh->cb (wh->cb_cls,
|
||||||
&hr,
|
&bwr);
|
||||||
blind_sigs,
|
|
||||||
wh->num_coins);
|
|
||||||
/* make sure callback isn't called again after return */
|
/* make sure callback isn't called again after return */
|
||||||
wh->cb = NULL;
|
wh->cb = NULL;
|
||||||
for (unsigned int i = 0; i<wh->num_coins; i++)
|
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;
|
struct TALER_EXCHANGE_BatchWithdraw2Handle *wh = cls;
|
||||||
const json_t *j = response;
|
const json_t *j = response;
|
||||||
struct TALER_EXCHANGE_HttpResponse hr = {
|
struct TALER_EXCHANGE_BatchWithdraw2Response bwr = {
|
||||||
.reply = j,
|
.hr.reply = j,
|
||||||
.http_status = (unsigned int) response_code
|
.hr.http_status = (unsigned int) response_code
|
||||||
};
|
};
|
||||||
|
|
||||||
wh->job = NULL;
|
wh->job = NULL;
|
||||||
switch (response_code)
|
switch (response_code)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
bwr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_OK:
|
case MHD_HTTP_OK:
|
||||||
if (GNUNET_OK !=
|
if (GNUNET_OK !=
|
||||||
@ -281,8 +281,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
|||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.http_status = 0;
|
bwr.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
GNUNET_assert (NULL == wh->cb);
|
GNUNET_assert (NULL == wh->cb);
|
||||||
@ -304,8 +304,8 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
|||||||
NULL, NULL))
|
NULL, NULL))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.http_status = 0;
|
bwr.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,24 +313,24 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
|||||||
case MHD_HTTP_BAD_REQUEST:
|
case MHD_HTTP_BAD_REQUEST:
|
||||||
/* This should never happen, either us or the exchange is buggy
|
/* This should never happen, either us or the exchange is buggy
|
||||||
(or API version conflict); just pass JSON reply to the application */
|
(or API version conflict); just pass JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_FORBIDDEN:
|
case MHD_HTTP_FORBIDDEN:
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
/* Nothing really to verify, exchange says one of the signatures is
|
/* Nothing really to verify, exchange says one of the signatures is
|
||||||
invalid; as we checked them, this should never happen, we
|
invalid; as we checked them, this should never happen, we
|
||||||
should pass the JSON reply to the application */
|
should pass the JSON reply to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_NOT_FOUND:
|
case MHD_HTTP_NOT_FOUND:
|
||||||
/* Nothing really to verify, the exchange basically just says
|
/* Nothing really to verify, the exchange basically just says
|
||||||
that it doesn't know this reserve. Can happen if we
|
that it doesn't know this reserve. Can happen if we
|
||||||
query before the wire transfer went through.
|
query before the wire transfer went through.
|
||||||
We should simply pass the JSON reply to the application. */
|
We should simply pass the JSON reply to the application. */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_CONFLICT:
|
case MHD_HTTP_CONFLICT:
|
||||||
/* The exchange says that the reserve has insufficient funds;
|
/* The exchange says that the reserve has insufficient funds;
|
||||||
@ -340,13 +340,13 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
|||||||
j))
|
j))
|
||||||
{
|
{
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.http_status = 0;
|
bwr.hr.http_status = 0;
|
||||||
hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
bwr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_GONE:
|
case MHD_HTTP_GONE:
|
||||||
@ -354,32 +354,30 @@ handle_reserve_batch_withdraw_finished (void *cls,
|
|||||||
/* Note: one might want to check /keys for revocation
|
/* Note: one might want to check /keys for revocation
|
||||||
signature here, alas tricky in case our /keys
|
signature here, alas tricky in case our /keys
|
||||||
is outdated => left to clients */
|
is outdated => left to clients */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||||
/* Server had an internal issue; we should retry, but this API
|
/* Server had an internal issue; we should retry, but this API
|
||||||
leaves this to the application */
|
leaves this to the application */
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
/* unexpected response code */
|
/* unexpected response code */
|
||||||
GNUNET_break_op (0);
|
GNUNET_break_op (0);
|
||||||
hr.ec = TALER_JSON_get_error_code (j);
|
bwr.hr.ec = TALER_JSON_get_error_code (j);
|
||||||
hr.hint = TALER_JSON_get_error_hint (j);
|
bwr.hr.hint = TALER_JSON_get_error_hint (j);
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||||
"Unexpected response code %u/%d for exchange batch withdraw\n",
|
"Unexpected response code %u/%d for exchange batch withdraw\n",
|
||||||
(unsigned int) response_code,
|
(unsigned int) response_code,
|
||||||
(int) hr.ec);
|
(int) bwr.hr.ec);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (NULL != wh->cb)
|
if (NULL != wh->cb)
|
||||||
{
|
{
|
||||||
wh->cb (wh->cb_cls,
|
wh->cb (wh->cb_cls,
|
||||||
&hr,
|
&bwr);
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
wh->cb = NULL;
|
wh->cb = NULL;
|
||||||
}
|
}
|
||||||
TALER_EXCHANGE_batch_withdraw2_cancel (wh);
|
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 */
|
/* end of exchange_api_common.c */
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user