Merge branch 'protocolv8'

This commit is contained in:
Christian Grothoff 2020-07-05 20:43:28 +02:00
commit 6de49ea2c0
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
86 changed files with 3187 additions and 13095 deletions

3
.gitignore vendored
View File

@ -32,6 +32,7 @@ GPATH
GRTAGS
GTAGS
*.swp
src/include/taler_error_codes.h
src/lib/test_exchange_api
doc/doxygen/doxygen_sqlite3.db
src/auditor/taler-auditor-dbinit
@ -81,6 +82,7 @@ src/wire-plugins/test_ebics_wireformat
src/wire-plugins/test_wire_plugin
src/wire-plugins/test_wire_plugin_transactions_taler_bank
src/pq/test_pq
src/sq/test_sq
src/util/test_amount
src/util/test_crypto
src/util/test_json
@ -126,3 +128,4 @@ uncrustify.cfg
vgcore.*
tags
/.vscode
src/testing/test_bank_api_with_nexus

3
.gitmodules vendored
View File

@ -5,3 +5,6 @@
path = doc/prebuilt
url = https://git.taler.net/docs.git
branch = prebuilt
[submodule "contrib/gana"]
path = contrib/gana
url = https://gnunet.org/git/gana.git

2
README
View File

@ -33,7 +33,7 @@ Dependencies:
These are the direct dependencies for running a Taler exchange:
- GNUnet >= 0.10.2
- GNU libmicrohttpd >= 0.9.55
- GNU libmicrohttpd >= 0.9.71
- Postgres >= 9.5

View File

@ -1,5 +1,7 @@
#!/bin/sh
set -eu
if ! git --version >/dev/null; then
echo "git not installed"
exit 1
@ -7,6 +9,13 @@ fi
git submodule update --init
# Generate taler-error-codes.h in gana and copy it to
# src/include/taler_error_codes.h
cd contrib/gana/gnu-taler-error-codes
make
cd ../../..
cp contrib/gana/gnu-taler-error-codes/taler-error-codes.h src/include/taler_error_codes.h
# This is more portable than `which' but comes with
# the caveat of not(?) properly working on busybox's ash:
existence()

View File

@ -261,6 +261,16 @@ AS_IF([test $libgnunetpq != 1],
*** ]])])
# Check for GNUnet's libgnunetsq
libgnunetsq=0
AC_MSG_CHECKING([for libgnunetsq])
AC_CHECK_HEADERS([gnunet/gnunet_sq_lib.h],
[AC_CHECK_LIB([gnunetsq], [GNUNET_SQ_result_spec_string], libgnunetsq=1)],
[], [#ifdef HAVE_GNUNET_PLATFORM_H
#include <gnunet/platform.h>
#endif])
# check for libmicrohttpd
microhttpd=0
AC_MSG_CHECKING([for microhttpd])
@ -326,6 +336,35 @@ CFLAGS=$CFLAGS_SAVE
LDFLAGS=$LDFLAGS_SAVE
LIBS=$LIBS_SAVE
# test for sqlite
sqlite=false
AC_MSG_CHECKING(for SQLite)
AC_ARG_WITH(sqlite,
[ --with-sqlite=PFX base of SQLite installation],
[AC_MSG_RESULT("$with_sqlite")
AS_CASE([$with_sqlite],
[no],[],
[yes],[
AC_CHECK_HEADERS(sqlite3.h,
sqlite=true)],
[
LDFLAGS="-L$with_sqlite/lib $LDFLAGS"
CPPFLAGS="-I$with_sqlite/include $CPPFLAGS"
AC_CHECK_HEADERS(sqlite3.h,
EXT_LIB_PATH="-L$with_sqlite/lib $EXT_LIB_PATH"
SQLITE_LDFLAGS="-L$with_sqlite/lib"
SQLITE_CPPFLAGS="-I$with_sqlite/include"
sqlite=true)
LDFLAGS=$SAVE_LDFLAGS
CPPFLAGS=$SAVE_CPPFLAGS
])
],
[AC_MSG_RESULT([--with-sqlite not specified])
AC_CHECK_HEADERS(sqlite3.h, sqlite=true)])
AM_CONDITIONAL(HAVE_SQLITE, [test x$sqlite = xtrue] && [test $libgnunetsq = 1])
AC_SUBST(SQLITE_CPPFLAGS)
AC_SUBST(SQLITE_LDFLAGS)
# check for libtalertwistertesting
talertwister=0
AC_MSG_CHECKING([for talertwister])
@ -459,6 +498,7 @@ AM_CONDITIONAL([ENABLE_DOC], [test "x$enable_doc" = "xyes"])
AM_CONDITIONAL([HAVE_EXPENSIVE_TESTS], [false])
AM_CONDITIONAL([MHD_HAVE_EPOLL], [false])
AM_CONDITIONAL([HAVE_POSTGRESQL], [false])
AM_CONDITIONAL([HAVE_SQLITE], [false])
AM_CONDITIONAL([HAVE_LIBCURL], [false])
AM_CONDITIONAL([HAVE_LIBGNURL], [false])
AM_CONDITIONAL([HAVE_DEVELOPER], [false])
@ -488,6 +528,7 @@ AC_CONFIG_FILES([Makefile
src/json/Makefile
src/mhd/Makefile
src/pq/Makefile
src/sq/Makefile
src/util/Makefile
])
AC_OUTPUT

1
contrib/gana Submodule

@ -0,0 +1 @@
Subproject commit c0fedb8d45c41fb283fec714b48278e6661d51be

View File

@ -87,6 +87,16 @@ transfers that cannot be reversed. We will only refuse to execute transfers if
the transfers are prohibited by a competent legal authority and we are ordered
to do so.
When using our Services, you agree to not take any action that intentionally
imposes an unreasonable load on our infrastructure. If you find security
problems in our Services, you agree to first report them to
security@taler-systems.com and grant us the right to publish your report. We
warrant that we will ourselves publicly disclose any issues reported within 3
months, and that we will not prosecute anyone reporting security issues if
they did not exploit the issue beyond a proof-of-concept, and followed the
above responsible disclosure practice.
Fees
----
@ -104,17 +114,14 @@ We reserve the right to provide different types of rewards to users either in
the form of discount for our Services or in any other form at our discretion
and without prior notice to you.
Eligibility
-----------
Eligibility and Financial self-responsibility
---------------------------------------------
To be eligible to use our Services, you must be able to form legally binding
contracts or have the permission of your legal guardian. By using our
Services, you represent and warrant that you meet all eligibility requirements
that we outline in these Terms.
Financial self-responsibility
-----------------------------
You will be responsible for maintaining the availability, integrity and
confidentiality of the data stored in your wallet. When you setup a Taler
Wallet, you are strongly advised to follow the precautionary measures offered
@ -136,17 +143,6 @@ of Taler Systems SA. You are welcome to use the name in relation to processing
payments using the Taler protocol, assuming your use is compatible with an
official release from the GNU Project that is not older than two years.
Your use of our services
------------------------
When using our Services, you agree to not take any action that intentionally
imposes an unreasonable load on our infrastructure. If you find security
problems in our Services, you agree to first report them to
security@taler-systems.com and grant us the right to publish your report. We
warrant that we will ourselves publicly disclose any issues reported within 3
months, and that we will not prosecute anyone reporting security issues if
they did not exploit the issue beyond a proof-of-concept, and followed the
above responsible disclosure practice.
Limitation of liability & disclaimer of warranties
--------------------------------------------------
@ -175,9 +171,6 @@ Any other terms, conditions, warranties, or representations associated with
such content, are solely between you and such organizations and/or
individuals.
Limitation of liability
-----------------------
To the fullest extent permitted by applicable law, in no event will we or any
of our officers, directors, representatives, agents, servants, counsel,
employees, consultants, lawyers, and other personnel authorized to act,
@ -206,9 +199,6 @@ damages. Some jurisdictions do not allow the exclusion or limitation of
liability for consequential or incidental damages, so the above limitation may
not apply to you.
Warranty disclaimer
-------------------
Our services are provided "as is" and without warranty of any kind. To the
maximum extent permitted by law, we disclaim all representations and
warranties, express or implied, relating to the services and underlying
@ -225,8 +215,8 @@ implied warranties, so the foregoing disclaimers may not apply to you. This
paragraph gives you specific legal rights and you may also have other legal
rights that vary from state to state.
Indemnity
---------
Indemnity and Time limitation on claims and Termination
-------------------------------------------------------
To the extent permitted by applicable law, you agree to defend, indemnify, and
hold harmless the Taler Parties from and against any and all claims, damages,
@ -236,32 +226,16 @@ the Services; (b) any feedback or submissions you provide to us concerning the
Taler Wallet; (c) your violation of any term of this Agreement; or (d) your
violation of any law, rule, or regulation, or the rights of any third party.
Time limitation on claims
-------------------------
You agree that any claim you may have arising out of or related to your
relationship with us must be filed within one year after such claim arises,
otherwise, your claim in permanently barred.
Governing law
-------------
No matter where youre located, the laws of Switzerland will govern these
Terms. If any provisions of these Terms are inconsistent with any applicable
law, those provisions will be superseded or modified only to the extent such
provisions are inconsistent. The parties agree to submit to the ordinary
courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
arising out of or related to your use of the Services or your breach of these
Terms.
Termination
-----------
In the event of termination concerning your use of our Services, your
obligations under this Agreement will still continue.
Discontinuance of services
--------------------------
Discontinuance of services and Force majeure
--------------------------------------------
We may, in our sole discretion and without cost to you, with or without prior
notice, and at any time, modify or discontinue, temporarily or permanently,
@ -273,24 +247,6 @@ or liable for any loss of funds in the event that we discontinue or depreciate
the Services and your Taler Wallet fails to transfer out the coins within a
three months notification period.
No waiver
---------
Our failure to exercise or delay in exercising any right, power, or privilege
under this Agreement shall not operate as a waiver; nor shall any single or
partial exercise of any right, power, or privilege preclude any other or
further exercise thereof.
Severability
------------
If it turns out that any part of this Agreement is invalid, void, or for any
reason unenforceable, that term will be deemed severable and limited or
eliminated to the minimum extent necessary.
Force majeure
-------------
We shall not be held liable for any delays, failure in performance, or
interruptions of service which result directly or indirectly from any cause or
condition beyond our reasonable control, including but not limited to: any
@ -301,14 +257,29 @@ services, failure of equipment and/or software, other catastrophe, or any
other occurrence which is beyond our reasonable control and shall not affect
the validity and enforceability of any remaining provisions.
Assignment
----------
Governing law, Waivers, Severability and Assignment
------------------------------------------------
No matter where youre located, the laws of Switzerland will govern these
Terms. If any provisions of these Terms are inconsistent with any applicable
law, those provisions will be superseded or modified only to the extent such
provisions are inconsistent. The parties agree to submit to the ordinary
courts in Zurich, Switzerland for exclusive jurisdiction of any dispute
arising out of or related to your use of the Services or your breach of these
Terms.
Our failure to exercise or delay in exercising any right, power, or privilege
under this Agreement shall not operate as a waiver; nor shall any single or
partial exercise of any right, power, or privilege preclude any other or
further exercise thereof.
You agree that we may assign any of our rights and/or transfer, sub-contract,
or delegate any of our obligations under these Terms.
Entire agreement
----------------
If it turns out that any part of this Agreement is invalid, void, or for any
reason unenforceable, that term will be deemed severable and limited or
eliminated to the minimum extent necessary.
This Agreement sets forth the entire understanding and agreement as to the
subject matter hereof and supersedes any and all prior discussions,
@ -317,6 +288,7 @@ prior versions of this Agreement) and every nature between us. Except as
provided for above, any modification to this Agreement must be in writing and
must be signed by both parties.
Questions or comments
---------------------

BIN
doc/audit/report-202005.pdf Normal file

Binary file not shown.

View File

@ -0,0 +1,238 @@
\documentclass[11pt]{article}
\oddsidemargin=0in \evensidemargin=0in
\textwidth=6.2in \textheight=8.7in
%\topmargin=-0.2in
\usepackage[ansinew]{inputenc}
\usepackage{makeidx,amsmath,amssymb,exscale,multicol,epsfig,graphics}
\begin{document}
\pagestyle{headings}
\title{Preliminary response to the \\ GNU Taler security audit in Q2/Q3 2020}
\author{Christian Grothoff \and Florian Dold}
\maketitle
\section{Abstract}
This is the preliminary response to the source code audit report CodeBlau
created for GNU Taler in Q2/Q3 2020. A final response with more details is
expected later this year.
\section{Management Summary}
We thank CodeBlau for their detailed report and thorough analysis. We are
particularly impressed that they reported issues against components that were
not even in-scope, and also that they found an {\em interesting} new corner
case we had not previously considered. Finally, we also find several of their
architectural recommendations to strengthen security to be worthwhile, and
while some were already on our long-term roadmap, we will reprioritize our
roadmap given their recommendations.
Given our extensive discussions with CodeBlau, we also have the impression
that they really took the time to understand the system, and look forward
to working with CodeBlau as a competent auditor for GNU Taler in the future.
\section{Issues in the exchange}
We agree with the issues CodeBlau discovered and both parties believe that
they have all been addressed.
\section{Issues in the auditor}
We appreciate CodeBlau's extensive list of checks the Taler auditor performs,
which was previously not documented adequately by us. We agree that the
auditor still needs more comprehensive documentation.
As for issue \#6416, we agree with the analysis and the proposed fix, even if
the implications are not fully clear. It has not yet been implemented as we
want to carefully review all of the SQL statements implicated in the
resolution and ensure we fully understand the implications.
\section{Issues in GNUnet}
We agree with the issues CodeBlau discovered and both parties believe that
they have all been addressed.
\section{General remarks on the code}
We understand that writing the code in another programming language may make
certain checks for the auditor less work to implement. However, our choice of C
is based on the advantages that make it superior to contemporary languages for
our use case: relatively low complexity of the language (compared to C++);
availability of mature compilers, static and dynamic analysis tools;
predictable performance; access to stable and battle-tested libraries; and
future-proofness due to portability to older systems as well as new platforms.
We believe creating a parallel implementation in other languages would provide
advantages, especially with respect to avoiding ``the implementation is the
specification''-style issues. However, given limited resources will not make
this a priority.
We disagree that all modern software development has embraced the idea that
memory errors are to be handled in ways other than terminating or restarting
the process. Many programming languages (Erlang, Java) hardly offer any other
means of handling out-of-memory situations than to terminate the process. We
also insist that Taler {\em does} handle out-of-memory as it does have code
that terminates the process (we do {\em not} simply ignore the return value
from {\tt malloc()} or other allocation functions!). We simply consider that
terminating the process (which is run by a hypervisor that will restart the
service) is the correct way to handle out-of-memory situations. We also have
limits in place that should prevent attackers from causing large amounts of
memory to be consumed, and also have code to automatically preemptively
restart the process to guard against memory exhaustion from memory
fragmentation. Finally, a common problem with abrupt termination may be
corrupted files. However, the code mostly only reads from files and limits
writing to the Postgres database. Hence, there is no possibility of corrupt
files being left behind even in the case of abnormal termination.
\section{More specs and documentation code}
We agree with the recommendation that the documentation should be improved,
and will try to improve it along the lines recommended by CodeBlau.
\section{Protocol change: API for uniformly distributed seeds}
We agree with the suggestion, have made the necessary changes, and both
parties believe that the suggestion has been implemented.
\section{Reduce code complexity}
\subsection{Reduce global variables}
While we do not disagree with the general goal to have few global variables,
we also believe that there are cases where global variables make sense.
We have already tried to minimize the scope of variables. The remaining few
global variables are largely ``read-only'' configuration data. The report does
not point out specific instances that would be particularly beneficial to
eliminate. As we continue to work on the code, we will of course evaluate
whether the removal of a particular global variable would make the code
cleaner.
Also, we want to point out that all global variables we introduce
in the exchange are indicated with a prefix {\tt TEH\_} in the code, so they
are easy to identify as such.
\subsection{Callbacks, type p(r)unning}
We understand that higher order functions in C can be confusing, but this
is also a common pattern to enable code re-use and asynchronous execution
which is essential for network applications. We do not believe that we
use callbacks {\em excessively}. Rewriting the code in another language
may indeed make this part easier to understand, alas would have other
disadvantages as pointed out previously.
\subsection{Initializing structs with memset}
Using {\tt memset()} first prevents compiler (or valgrind) warnings about
using uninitialized memory, possibly hiding bugs. We also do use struct
initialization in many cases.
The GNUnet-wrappers are generally designed to be ``safer'' or ``stricter''
variants of the corresponding libc functions, and not merely ``the same''.
Hence we do not believe that renaming {\tt GNUNET\_malloc} is indicated.
The argument that {\tt memset()}ing first makes the code inherently more
obvious also seems fallacious, as it would commonly result in dead stores,
which can confuse developers and produce false-positive warnings from static
analysis tools.
\subsection{NULL pointer handling}
The problem with the ``goto fail'' style error handling is that it rarely
results in specific error handling where diagnostics are created that are
specific to the error. Using this style of programming encourages developers
to create simplistic error handling, which can result in inappropriate error
handling logic and also makes it harder to attribute errors to the specific
cause.
However, we have no prohibition on using this style of error handling either:
if it is appropriate, develpers should make a case-by-case decision as to how
to best handle a specific error.
We have made some first changes to how {\tt GNUNET\_free()} works in response
to the report, and will discuss further changes with the GNUnet development
team.
\subsection{Hidden security assumptions}
We disagree that the assumptions stated are ``hidden'', as (1) the Taler code
has its own checks to warrant that the requirements of the {\tt
GNUNET\_malloc()} API are satisfied (so enforcement is not limited to the
abstraction layer), and (2) the maximum allocation size limit is quite clearly
specified in the GNUnet documentation. Also, the GNUnet-functions are not
merely an abstraction layer for portability, but they provided extended
semantics that we rely upon. So it is not like it is possible to swap this
layer and expect anything to continue to work.
When we use the libjansson library, it is understood that it does not use
the GNUnet operations, and the code is careful about this distinction.
\subsection{Get rid of boolean function arguments}
We agree that this can make the code more readable, and have in some places
already changed the code in this way.
\section{Structural Recommendation}
\subsection{Least privilege}
It is wrong to say that GNU Taler has ``no work done'' on privilege separation.
For example, the {\tt taler-exchange-dbinit} tool is the only tool that requires
CREATE, ALTER and DROP rights on database tables, thus enusring that the ``main''
process does not need these rights.
We also already had the {\tt taler-exchange-keyup} tool responsible for
initializing keys. In response to the audit, we already changed the GNUnet API
to make sure that tools do not create keys as a side-effect of trying to read
non-existent key files.
We agree with the recommendation on further privilege separation for access
to cryptographic keys, and intend to implement this in the near future.
\subsection{File system access}
The auditor helpers actually only read from the file system, only the LaTeX
invocation to compile the final report to PDF inherently needs write
access. We do not predict that we will retool LaTeX. Also, the file system
access is completely uncritical, as the auditor by design runs on a system
that is separate from the production exchange system.
Because that system will not have {\em any} crypto keys (not even the one of
the auditor!), CodeBlau is wrong to assume that reading from or writing to the
file system represents a security threat.
We have started to better document the operational requirements on running the
auditor.
\subsection{Avoid dlopen}
Taler actually uses {\tt ltdlopen()} from GNU libtool, which provides
compiler flags to convert the dynamic linkage into static linkage. For
development, dynamic linkage has many advantages.
We plan to test and document how to build GNU Taler with only static
linkage, and will recommend this style of deployment for the Taler
exchange for production.
\subsection{Reduce reliance on PostgreSQL}
CodeBlau's suggestion to use an append-only transaction logging service in
addition to the PostgreSQL database is a reasonable suggestion for a
production-grade deployment of GNU Taler, as it would allow partial disaster
recovery even in the presence of an attacker that has gained write access to
the exchange's database.
We are currently still investigating whether the transaction logging should be
implemented directly by the exchange service, or via the database's extensible
replication mechanism. Any implementation of such an append-only logging
mechanism must be carefully designed to ensure it does not negatively impact
the exchange's availability and does not interfere with serializability of
database transactions. As such we believe that transaction logging can only be
provided on a best-effort basis. Fortunately, even a best-effort append-only
transaction log would serve to limit the financial damage incurred by the
exchange in an active database compromise scenario.
\end{document}

View File

@ -3,6 +3,9 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include
if HAVE_POSTGRESQL
PQ_DIR = pq
endif
if HAVE_SQLITE
SQ_DIR = sq
endif
pkgcfgdir = $(prefix)/share/taler/config.d/
pkgcfg_DATA = \
@ -17,6 +20,7 @@ SUBDIRS = \
json \
curl \
$(PQ_DIR) \
$(SQ_DIR) \
mhd \
bank-lib \
exchangedb \

View File

@ -1 +0,0 @@
1584124548

Binary file not shown.

View File

@ -1 +0,0 @@
BX3MKH0E1YPF03P1T2G4NKMYNHBGE1TC2N5P6RWHT1JHNXC32EN0

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,10 @@
set -eu
echo "Script disabled: taler-wallet-cli integration test known to fail right now!"
exit 1
trap "kill `jobs -p` &> /dev/null || true" ERR
# Exit, with status code "skip" (no 'real' failure)

View File

@ -1 +0,0 @@
1585247241

Binary file not shown.

View File

@ -1 +0,0 @@
NG08W20XYEN3M663JQ0THGSRH2QT7Y3390933FVSZE033Q9A9XE0

File diff suppressed because it is too large Load Diff

View File

@ -155,7 +155,7 @@ verify_and_execute_deposit_confirmation (
.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS)),
.h_contract_terms = dc->h_contract_terms,
.h_wire = dc->h_wire,
.timestamp = GNUNET_TIME_absolute_hton (dc->timestamp),
.exchange_timestamp = GNUNET_TIME_absolute_hton (dc->exchange_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (dc->refund_deadline),
.coin_pub = dc->coin_pub,
.merchant = dc->merchant
@ -224,7 +224,8 @@ TAH_DEPOSIT_CONFIRMATION_handler (struct TAH_RequestHandler *rh,
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &dc.h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire", &dc.h_wire),
GNUNET_JSON_spec_absolute_time ("timestamp", &dc.timestamp),
GNUNET_JSON_spec_absolute_time ("exchange_timestamp",
&dc.exchange_timestamp),
GNUNET_JSON_spec_absolute_time ("refund_deadline", &dc.refund_deadline),
TALER_JSON_spec_amount ("amount_without_fee", &dc.amount_without_fee),
GNUNET_JSON_spec_fixed_auto ("coin_pub", &dc.coin_pub),

View File

@ -1535,7 +1535,8 @@ refresh_session_cb (void *cls,
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param timestamp when did the deposit happen
* @param exchange_timestamp when did the exchange get the deposit
* @param wallet_timestamp when did the contract signing happen
* @param merchant_pub public key of the merchant
* @param denom_pub denomination public key of @a coin_pub
* @param coin_pub public key of the coin
@ -1553,7 +1554,8 @@ refresh_session_cb (void *cls,
static int
deposit_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -1611,7 +1613,7 @@ deposit_cb (void *cls,
.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
.purpose.size = htonl (sizeof (dr)),
.h_contract_terms = *h_contract_terms,
.timestamp = GNUNET_TIME_absolute_hton (timestamp),
.wallet_timestamp = GNUNET_TIME_absolute_hton (wallet_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
.deposit_fee = issue->fee_deposit,
.merchant = *merchant_pub,
@ -1802,7 +1804,6 @@ refund_cb (void *cls,
.coin_pub = *coin_pub,
.merchant = *merchant_pub,
.rtransaction_id = GNUNET_htonll (rtransaction_id),
.refund_fee = issue->fee_refund
};
TALER_amount_hton (&rr.refund_amount,

View File

@ -114,11 +114,15 @@ test_dc (void *cls,
.h_wire = dc->h_wire,
.refund_deadline = dc->refund_deadline
};
struct GNUNET_TIME_Absolute exchange_timestamp;
struct TALER_Amount deposit_fee;
qs = TALER_ARL_edb->have_deposit (TALER_ARL_edb->cls,
TALER_ARL_esession,
&dep,
GNUNET_NO /* do not check refund deadline */);
GNUNET_NO /* do not check refund deadline */,
&deposit_fee,
&exchange_timestamp);
if (qs > 0)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@ -137,7 +141,8 @@ test_dc (void *cls,
TALER_ARL_report (report_deposit_confirmation_inconsistencies,
json_pack ("{s:o, s:o, s:I, s:o}",
"timestamp",
TALER_ARL_json_from_time_abs (dc->timestamp),
TALER_ARL_json_from_time_abs (
dc->exchange_timestamp),
"amount",
TALER_JSON_from_amount (&dc->amount_without_fee),
"rowid",

View File

@ -550,7 +550,6 @@ handle_reserve_out (void *cls,
}
/* check reserve_sig (first: setup remaining members of wsrd) */
wsrd.withdraw_fee = issue->fee_withdraw;
TALER_amount_hton (&wsrd.amount_with_fee,
amount_with_fee);
if (GNUNET_OK !=

View File

@ -1872,6 +1872,12 @@ else
fi
fi
check_with_database "auditor-basedb"
# run tests with pre-build database, if one is available
if test -x auditor-basedb.mpub
then
check_with_database "auditor-basedb"
else
fail=77
fi
exit $fail

View File

@ -543,6 +543,12 @@ else
fi
fi
check_with_database "revoke-basedb"
# run tests with pre-build database, if one is available
if test -x revoke-basedb.mpub
then
check_with_database "revoke-basedb"
else
fail=77
fi
exit $fail

View File

@ -251,7 +251,7 @@ CREATE TABLE IF NOT EXISTS deposit_confirmations
,serial_id BIGSERIAL UNIQUE
,h_contract_terms BYTEA CHECK (LENGTH(h_contract_terms)=64)
,h_wire BYTEA CHECK (LENGTH(h_wire)=64)
,timestamp INT8 NOT NULL
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
,amount_without_fee_val INT8 NOT NULL
,amount_without_fee_frac INT4 NOT NULL

View File

@ -43,8 +43,8 @@ DROP TABLE IF EXISTS auditor_denominations CASCADE;
DROP TABLE IF EXISTS deposit_confirmations CASCADE;
DROP TABLE IF EXISTS auditor_exchanges CASCADE;
-- Drop versioning (0000.sql)
DROP SCHEMA IF EXISTS _v CASCADE;
-- Drop versioning (auditor-0001.sql)
SELECT _v.unregister_patch('auditor-0001');
-- And we're out of here...
COMMIT;

View File

@ -269,7 +269,7 @@ postgres_get_session (void *cls)
"(master_pub"
",h_contract_terms"
",h_wire"
",timestamp"
",exchange_timestamp"
",refund_deadline"
",amount_without_fee_val"
",amount_without_fee_frac"
@ -286,7 +286,7 @@ postgres_get_session (void *cls)
" serial_id"
",h_contract_terms"
",h_wire"
",timestamp"
",exchange_timestamp"
",refund_deadline"
",amount_without_fee_val"
",amount_without_fee_frac"
@ -1126,7 +1126,7 @@ postgres_insert_deposit_confirmation (
GNUNET_PQ_query_param_auto_from_type (&dc->master_public_key),
GNUNET_PQ_query_param_auto_from_type (&dc->h_contract_terms),
GNUNET_PQ_query_param_auto_from_type (&dc->h_wire),
TALER_PQ_query_param_absolute_time (&dc->timestamp),
TALER_PQ_query_param_absolute_time (&dc->exchange_timestamp),
TALER_PQ_query_param_absolute_time (&dc->refund_deadline),
TALER_PQ_query_param_amount (&dc->amount_without_fee),
GNUNET_PQ_query_param_auto_from_type (&dc->coin_pub),
@ -1207,8 +1207,8 @@ deposit_confirmation_cb (void *cls,
&dc.h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&dc.h_wire),
GNUNET_PQ_result_spec_absolute_time ("timestamp",
&dc.timestamp),
GNUNET_PQ_result_spec_absolute_time ("exchange_timestamp",
&dc.exchange_timestamp),
GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
&dc.refund_deadline),
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_without_fee",

View File

@ -1016,9 +1016,9 @@ create_wire_fee_for_method (void *cls,
af->wire_fee.currency)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid or missing amount in `%s' under `%s'\n",
section,
opt);
"Invalid or missing amount for option `%s' in section `%s'\n",
opt,
section);
*ret = GNUNET_SYSERR;
GNUNET_free (opt);
break;
@ -1038,9 +1038,9 @@ create_wire_fee_for_method (void *cls,
af->closing_fee.currency)) )
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid or missing amount in `%s' under `%s'\n",
section,
opt);
"Invalid or missing amount for option `%s' in section `%s'\n",
opt,
section);
*ret = GNUNET_SYSERR;
GNUNET_free (opt);
break;

View File

@ -330,6 +330,8 @@ refund_by_coin_cb (void *cls,
*
* @param cls a `struct AggregationUnit`
* @param row_id identifies database entry
* @param exchange_timestamp when did the deposit happen
* @param wallet_timestamp when did the contract happen
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
@ -343,6 +345,8 @@ refund_by_coin_cb (void *cls,
static enum GNUNET_DB_QueryStatus
deposit_cb (void *cls,
uint64_t row_id,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
@ -358,6 +362,8 @@ deposit_cb (void *cls,
/* NOTE: potential optimization: use custom SQL API to not
fetch this one: */
(void) wire_deadline; /* already checked by SQL query */
(void) exchange_timestamp;
(void) wallet_timestamp;
au->merchant_pub = *merchant_pub;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Aggregator processing payment %s with amount %s\n",
@ -501,6 +507,8 @@ deposit_cb (void *cls,
*
* @param cls a `struct AggregationUnit`
* @param row_id identifies database entry
* @param exchange_timestamp when did the exchange receive the deposit
* @param wallet_timestamp when did the wallet sign the contract
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
@ -514,6 +522,8 @@ deposit_cb (void *cls,
static enum GNUNET_DB_QueryStatus
aggregate_cb (void *cls,
uint64_t row_id,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
@ -529,6 +539,8 @@ aggregate_cb (void *cls,
/* NOTE: potential optimization: use custom SQL API to not
fetch these: */
(void) wire_deadline; /* checked by SQL */
(void) exchange_timestamp;
(void) wallet_timestamp;
(void) wire; /* must match */
GNUNET_break (0 == GNUNET_memcmp (&au->merchant_pub,
merchant_pub));

View File

@ -420,6 +420,45 @@ proceed_with_handler (const struct TEH_RequestHandler *rh,
}
/**
* Handle a "/seed" request.
*
* @param rh context of the handler
* @param connection the MHD connection to handle
* @param args array of additional options (must be empty for this function)
* @return MHD result code
*/
static MHD_RESULT
handler_seed (const struct TEH_RequestHandler *rh,
struct MHD_Connection *connection,
const char *const args[])
{
#define SEED_SIZE 32
char *body;
MHD_RESULT ret;
struct MHD_Response *resp;
(void) rh;
body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
if (NULL == body)
return MHD_NO;
GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
body,
SEED_SIZE);
resp = MHD_create_response_from_buffer (SEED_SIZE,
body,
MHD_RESPMEM_MUST_FREE);
TALER_MHD_add_global_headers (resp);
ret = MHD_queue_response (connection,
MHD_HTTP_OK,
resp);
GNUNET_break (MHD_YES == ret);
MHD_destroy_response (resp);
return ret;
#undef SEED_SIZE
}
/**
* Handle incoming HTTP request.
*
@ -472,6 +511,11 @@ handle_mhd_request (void *cls,
.method = MHD_HTTP_METHOD_GET,
.handler.get = &TEH_handler_agpl_redirect
},
{
.url = "seed",
.method = MHD_HTTP_METHOD_GET,
.handler.get = &handler_seed
},
/* Terms of service */
{
.url = "terms",
@ -654,9 +698,10 @@ handle_mhd_request (void *cls,
{
struct TEH_RequestHandler *rh = &handlers[i];
if (0 != strncmp (tok,
rh->url,
tok_size))
if ( (0 != strncmp (tok,
rh->url,
tok_size)) ||
(tok_size != strlen (rh->url) ) )
continue;
found = GNUNET_YES;
/* The URL is a match! What we now do depends on the method. */
@ -782,8 +827,8 @@ exchange_serve_process_config (void)
&TEH_master_public_key.
eddsa_pub))
{
fprintf (stderr,
"Invalid master public key given in exchange configuration.");
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Invalid master public key given in exchange configuration.");
GNUNET_free (master_public_key_str);
return GNUNET_SYSERR;
}
@ -795,14 +840,18 @@ exchange_serve_process_config (void)
if (GNUNET_OK !=
TEH_WIRE_init (TEH_cfg))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to setup wire subsystem\n");
return GNUNET_SYSERR;
}
if (NULL ==
(TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg)))
{
fprintf (stderr,
"Failed to initialize DB subsystem\n");
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to initialize DB subsystem\n");
TEH_WIRE_done ();
return GNUNET_SYSERR;
}
@ -814,6 +863,8 @@ exchange_serve_process_config (void)
&serve_unixpath,
&unixpath_mode))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to setup HTTPd subsystem\n");
TEH_WIRE_done ();
return GNUNET_SYSERR;
}

View File

@ -47,7 +47,7 @@
* @param coin_pub public key of the coin
* @param h_wire hash of wire details
* @param h_contract_terms hash of contract details
* @param timestamp client's timestamp
* @param exchange_timestamp exchange's timestamp
* @param refund_deadline until when this deposit be refunded
* @param merchant merchant public key
* @param amount_without_fee fraction of coin value to deposit, without the fee
@ -58,7 +58,7 @@ reply_deposit_success (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_MerchantPublicKeyP *merchant,
const struct TALER_Amount *amount_without_fee)
@ -70,7 +70,7 @@ reply_deposit_success (struct MHD_Connection *connection,
.purpose.size = htonl (sizeof (dc)),
.h_contract_terms = *h_contract_terms,
.h_wire = *h_wire,
.timestamp = GNUNET_TIME_absolute_hton (timestamp),
.exchange_timestamp = GNUNET_TIME_absolute_hton (exchange_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
.coin_pub = *coin_pub,
.merchant = *merchant
@ -88,12 +88,16 @@ reply_deposit_success (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
"no keys");
}
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:o, s:o}",
"status", "DEPOSIT_OK",
"sig", GNUNET_JSON_from_data_auto (&sig),
"pub", GNUNET_JSON_from_data_auto (&pub));
return TALER_MHD_reply_json_pack (
connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o}",
"exchange_timestamp",
GNUNET_JSON_from_time_abs (exchange_timestamp),
"exchange_sig",
GNUNET_JSON_from_data_auto (&sig),
"exchange_pub",
GNUNET_JSON_from_data_auto (&pub));
}
@ -107,6 +111,11 @@ struct DepositContext
*/
const struct TALER_EXCHANGEDB_Deposit *deposit;
/**
* Our timestamp (when we received the request).
*/
struct GNUNET_TIME_Absolute exchange_timestamp;
/**
* Value of the coin.
*/
@ -115,6 +124,74 @@ struct DepositContext
};
/**
* Check if /deposit is already in the database. IF it returns a non-error
* code, the transaction logic MUST NOT queue a MHD response. IF it returns
* an hard error, the transaction logic MUST queue a MHD response and set @a
* mhd_ret. We do return a "hard" error also if we found the deposit in the
* database and generated a regular response.
*
* @param cls a `struct DepositContext`
* @param connection MHD request context
* @param session database session and transaction to use
* @param[out] mhd_ret set to MHD status on error
* @return transaction status
*/
static enum GNUNET_DB_QueryStatus
deposit_precheck (void *cls,
struct MHD_Connection *connection,
struct TALER_EXCHANGEDB_Session *session,
MHD_RESULT *mhd_ret)
{
struct DepositContext *dc = cls;
const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
struct TALER_Amount deposit_fee;
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
session,
deposit,
GNUNET_YES /* check refund deadline */,
&deposit_fee,
&dc->exchange_timestamp);
if (qs < 0)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_DEPOSIT_HISTORY_DB_ERROR,
"Could not check for existing identical deposit");
return GNUNET_DB_STATUS_HARD_ERROR;
}
return qs;
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
struct TALER_Amount amount_without_fee;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/deposit replay, accepting again!\n");
GNUNET_assert (0 <=
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit_fee));
*mhd_ret = reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
dc->exchange_timestamp,
deposit->refund_deadline,
&deposit->merchant_pub,
&amount_without_fee);
/* Treat as 'hard' DB error as we want to rollback and
never try again. */
return GNUNET_DB_STATUS_HARD_ERROR;
}
return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
}
/**
* Execute database transaction for /deposit. Runs the transaction
* logic; IF it returns a non-error code, the transaction logic MUST
@ -140,44 +217,15 @@ deposit_transaction (void *cls,
struct TALER_Amount spent;
enum GNUNET_DB_QueryStatus qs;
qs = TEH_plugin->have_deposit (TEH_plugin->cls,
session,
deposit,
GNUNET_YES /* check refund deadline */);
/* Theoretically, someone other threat may have received
and committed the deposit in the meantime. Check now
that we are in the transaction scope. */
qs = deposit_precheck (cls,
connection,
session,
mhd_ret);
if (qs < 0)
{
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_DEPOSIT_HISTORY_DB_ERROR,
"Could not check for existing identical deposit");
return GNUNET_DB_STATUS_HARD_ERROR;
}
return qs;
}
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
struct TALER_Amount amount_without_fee;
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"/deposit replay, accepting again!\n");
GNUNET_assert (0 <=
TALER_amount_subtract (&amount_without_fee,
&deposit->amount_with_fee,
&deposit->deposit_fee));
*mhd_ret = reply_deposit_success (connection,
&deposit->coin.coin_pub,
&deposit->h_wire,
&deposit->h_contract_terms,
deposit->timestamp,
deposit->refund_deadline,
&deposit->merchant_pub,
&amount_without_fee);
/* Treat as 'hard' DB error as we want to rollback and
never try again. */
return GNUNET_DB_STATUS_HARD_ERROR;
}
/* Start with fee for THIS transaction */
spent = deposit->amount_with_fee;
@ -237,6 +285,7 @@ deposit_transaction (void *cls,
}
qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
session,
dc->exchange_timestamp,
deposit);
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
@ -250,45 +299,6 @@ deposit_transaction (void *cls,
}
/**
* Check that @a ts is reasonably close to our own RTC.
*
* @param ts timestamp to check
* @return #GNUNET_OK if @a ts is reasonable
*/
static int
check_timestamp_current (struct GNUNET_TIME_Absolute ts)
{
struct GNUNET_TIME_Relative r;
struct GNUNET_TIME_Relative tolerance;
/* Let's be VERY generous (after all, this is basically about
which year the deposit counts for in terms of tax purposes) */
tolerance = GNUNET_TIME_UNIT_MONTHS;
r = GNUNET_TIME_absolute_get_duration (ts);
if (r.rel_value_us > tolerance.rel_value_us)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposit timestamp too old: %llu vs %llu > %llu\n",
(unsigned long long) ts.abs_value_us,
(unsigned long long) GNUNET_TIME_absolute_get ().abs_value_us,
(unsigned long long) tolerance.rel_value_us);
return GNUNET_SYSERR;
}
r = GNUNET_TIME_absolute_get_remaining (ts);
if (r.rel_value_us > tolerance.rel_value_us)
{
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Deposit timestamp too new: %llu vs %llu < - %llu\n",
(unsigned long long) ts.abs_value_us,
(unsigned long long) GNUNET_TIME_absolute_get ().abs_value_us,
(unsigned long long) tolerance.rel_value_us);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Handle a "/coins/$COIN_PUB/deposit" request. Parses the JSON, and, if
* successful, passes the JSON data to #deposit_transaction() to
@ -312,15 +322,22 @@ TEH_handler_deposit (struct MHD_Connection *connection,
struct GNUNET_HashCode my_h_wire;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_json ("wire", &wire),
TALER_JSON_spec_amount ("contribution", &deposit.amount_with_fee),
TALER_JSON_spec_amount ("contribution",
&deposit.amount_with_fee),
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
&deposit.coin.denom_pub_hash),
TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("merchant_pub", &deposit.merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &deposit.h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire", &deposit.h_wire),
GNUNET_JSON_spec_fixed_auto ("coin_sig", &deposit.csig),
GNUNET_JSON_spec_absolute_time ("timestamp", &deposit.timestamp),
TALER_JSON_spec_denomination_signature ("ub_sig",
&deposit.coin.denom_sig),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&deposit.merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&deposit.h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("h_wire",
&deposit.h_wire),
GNUNET_JSON_spec_fixed_auto ("coin_sig",
&deposit.csig),
GNUNET_JSON_spec_absolute_time ("timestamp",
&deposit.timestamp),
GNUNET_JSON_spec_absolute_time ("refund_deadline",
&deposit.refund_deadline),
GNUNET_JSON_spec_absolute_time ("wire_transfer_deadline",
@ -359,17 +376,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
TALER_EC_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
"refund_deadline");
}
if (GNUNET_OK !=
check_timestamp_current (deposit.timestamp))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_DEPOSIT_INVALID_TIMESTAMP,
"timestamp");
}
if (GNUNET_OK !=
TALER_JSON_merchant_wire_signature_hash (wire,
&my_h_wire))
@ -393,6 +399,26 @@ TEH_handler_deposit (struct MHD_Connection *connection,
"h_wire");
}
/* Check for idempotency: did we get this request before? */
dc.deposit = &deposit;
{
MHD_RESULT mhd_ret;
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
"precheck deposit",
&mhd_ret,
&deposit_precheck,
&dc))
{
GNUNET_JSON_parse_free (spec);
return mhd_ret;
}
}
/* new deposit */
dc.exchange_timestamp = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&dc.exchange_timestamp);
/* check denomination exists and is valid */
{
struct TEH_KS_StateHandle *key_state;
@ -400,7 +426,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
enum TALER_ErrorCode ec;
unsigned int hc;
key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
key_state = TEH_KS_acquire (dc.exchange_timestamp);
if (NULL == key_state)
{
TALER_LOG_ERROR ("Lacking keys to operate\n");
@ -494,7 +520,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
.purpose.size = htonl (sizeof (dr)),
.h_contract_terms = deposit.h_contract_terms,
.h_wire = deposit.h_wire,
.timestamp = GNUNET_TIME_absolute_hton (deposit.timestamp),
.wallet_timestamp = GNUNET_TIME_absolute_hton (deposit.timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (deposit.refund_deadline),
.merchant = deposit.merchant_pub,
.coin_pub = deposit.coin.coin_pub
@ -520,7 +546,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
}
/* execute transaction */
dc.deposit = &deposit;
{
MHD_RESULT mhd_ret;
@ -549,7 +574,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
&deposit.coin.coin_pub,
&deposit.h_wire,
&deposit.h_contract_terms,
deposit.timestamp,
dc.exchange_timestamp,
deposit.refund_deadline,
&deposit.merchant_pub,
&amount_without_fee);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014--2019 Taler Systems SA
Copyright (C) 2014--2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -44,7 +44,7 @@
* #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
* exchange_api_handle.c!
*/
#define EXCHANGE_PROTOCOL_VERSION "7:0:0"
#define EXCHANGE_PROTOCOL_VERSION "8:0:0"
/**
@ -801,7 +801,7 @@ reload_keys_denom_iter (void *cls,
handle_signal (SIGTERM);
return GNUNET_SYSERR;
}
GNUNET_assert (NULL != dki->denom_priv.rsa_private_key);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Adding denomination key `%s' (%s) to active set\n",
alias,
@ -1660,6 +1660,9 @@ reload_public_denoms_cb (
GNUNET_h2s (&issue->properties.denom_hash));
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Loaded denomination key %s from DB, no private key (hopefully revoked!)\n",
GNUNET_h2s (&issue->properties.denom_hash));
/* we can assert here as we checked for duplicates just above */
GNUNET_assert (GNUNET_OK == ret);
}

View File

@ -73,12 +73,13 @@ reply_refund_success (struct MHD_Connection *connection,
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
"no online signing key");
}
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:s, s:o, s:o}",
"status", "REFUND_OK",
"sig", GNUNET_JSON_from_data_auto (&sig),
"pub", GNUNET_JSON_from_data_auto (&pub));
return TALER_MHD_reply_json_pack (
connection,
MHD_HTTP_OK,
"{s:o, s:o, s:o}",
"refund_fee", TALER_JSON_from_amount (&refund->refund_fee),
"exchange_sig", GNUNET_JSON_from_data_auto (&sig),
"exchange_pub", GNUNET_JSON_from_data_auto (&pub));
}
@ -246,18 +247,15 @@ refund_transaction (void *cls,
}
/* check currency is compatible */
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&refund->details.refund_amount,
&dep->amount_with_fee)) ||
(GNUNET_YES !=
TALER_amount_cmp_currency (&refund->details.refund_fee,
&dep->deposit_fee)) )
if (GNUNET_YES !=
TALER_amount_cmp_currency (&refund->details.refund_amount,
&dep->amount_with_fee))
{
GNUNET_break_op (0); /* currency mismatch */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
*mhd_ret = TALER_MHD_reply_with_error (connection,
MHD_HTTP_PRECONDITION_FAILED,
MHD_HTTP_BAD_REQUEST,
TALER_EC_REFUND_CURRENCY_MISMATCH,
"currencies involved do not match");
return GNUNET_DB_STATUS_HARD_ERROR;
@ -339,15 +337,14 @@ refund_transaction (void *cls,
* the fee structure, so this is not done here.
*
* @param connection the MHD connection to handle
* @param refund information about the refund
* @param[in,out] refund information about the refund
* @return MHD result code
*/
static MHD_RESULT
verify_and_execute_refund (struct MHD_Connection *connection,
const struct TALER_EXCHANGEDB_Refund *refund)
struct TALER_EXCHANGEDB_Refund *refund)
{
struct GNUNET_HashCode denom_hash;
struct TALER_Amount expect_fee;
{
struct TALER_RefundRequestPS rr = {
@ -361,8 +358,6 @@ verify_and_execute_refund (struct MHD_Connection *connection,
TALER_amount_hton (&rr.refund_amount,
&refund->details.refund_amount);
TALER_amount_hton (&rr.refund_fee,
&refund->details.refund_fee);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
&rr,
@ -429,43 +424,12 @@ verify_and_execute_refund (struct MHD_Connection *connection,
ec,
"denomination not found, but coin known");
}
TALER_amount_ntoh (&expect_fee,
TALER_amount_ntoh (&refund->details.refund_fee,
&dki->issue.properties.fee_refund);
}
TEH_KS_release (key_state);
}
/* Check refund fee matches fee of denomination key! */
if (GNUNET_YES !=
TALER_amount_cmp_currency (&expect_fee,
&refund->details.refund_fee) )
{
GNUNET_break_op (0);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_REFUND_FEE_CURRENCY_MISMATCH,
"refund_fee");
}
{
int fee_cmp;
fee_cmp = TALER_amount_cmp (&refund->details.refund_fee,
&expect_fee);
if (-1 == fee_cmp)
{
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_REFUND_FEE_TOO_LOW,
"refund_fee");
}
if (1 == fee_cmp)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Refund fee proposed by merchant is higher than necessary.\n");
}
}
/* Finally run the actual transaction logic */
{
MHD_RESULT mhd_ret;
@ -502,16 +466,20 @@ TEH_handler_refund (struct MHD_Connection *connection,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const json_t *root)
{
struct TALER_EXCHANGEDB_Refund refund;
struct TALER_EXCHANGEDB_Refund refund = {
.details.refund_fee.currency = {0} /* set to invalid, just to be sure */
};
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("refund_amount", &refund.details.refund_amount),
TALER_JSON_spec_amount ("refund_fee", &refund.details.refund_fee),
TALER_JSON_spec_amount ("refund_amount",
&refund.details.refund_amount),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&refund.details.h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("merchant_pub", &refund.details.merchant_pub),
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
&refund.details.merchant_pub),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&refund.details.rtransaction_id),
GNUNET_JSON_spec_fixed_auto ("merchant_sig", &refund.details.merchant_sig),
GNUNET_JSON_spec_fixed_auto ("merchant_sig",
&refund.details.merchant_sig),
GNUNET_JSON_spec_end ()
};
@ -527,27 +495,6 @@ TEH_handler_refund (struct MHD_Connection *connection,
if (GNUNET_NO == res)
return MHD_YES; /* failure */
}
if (GNUNET_YES !=
TALER_amount_cmp_currency (&refund.details.refund_amount,
&refund.details.refund_fee) )
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_REFUND_FEE_CURRENCY_MISMATCH,
"refund_amount or refund_fee");
}
if (-1 == TALER_amount_cmp (&refund.details.refund_amount,
&refund.details.refund_fee) )
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
TALER_EC_REFUND_FEE_ABOVE_AMOUNT,
"refund_amount");
}
{
MHD_RESULT res;

View File

@ -66,7 +66,7 @@ TEH_RESPONSE_compile_transaction_history (
.purpose.size = htonl (sizeof (dr)),
.h_contract_terms = deposit->h_contract_terms,
.h_wire = deposit->h_wire,
.timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp),
.wallet_timestamp = GNUNET_TIME_absolute_hton (deposit->timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (
deposit->refund_deadline),
.merchant = deposit->merchant_pub,
@ -185,8 +185,6 @@ TEH_RESPONSE_compile_transaction_history (
TALER_amount_hton (&rr.refund_amount,
&refund->refund_amount);
TALER_amount_hton (&rr.refund_fee,
&refund->refund_fee);
#if ENABLE_SANITY_CHECKS
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=

View File

@ -341,6 +341,8 @@ TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
&ret);
if (GNUNET_OK != ret)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Error setting up bank accounts\n");
TEH_WIRE_done ();
return GNUNET_SYSERR;
}
@ -349,6 +351,8 @@ TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
(0 == json_object_size (wire_fee_object)) )
{
TEH_WIRE_done ();
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"No bank accounts configured\n");
return GNUNET_SYSERR;
}
wire_methods = json_pack ("{s:O, s:O, s:o}",

View File

@ -306,8 +306,6 @@ withdraw_transaction (void *cls,
#endif
wc->collectable.denom_pub_hash = wc->denom_pub_hash;
wc->collectable.amount_with_fee = wc->amount_required;
TALER_amount_ntoh (&wc->collectable.withdraw_fee,
&wc->dki->issue.properties.fee_withdraw);
wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
wc->collectable.h_coin_envelope = wc->wsrd.h_coin_envelope;
wc->collectable.reserve_sig = wc->signature;
@ -436,8 +434,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
}
TALER_amount_hton (&wc.wsrd.amount_with_fee,
&wc.amount_required);
TALER_amount_hton (&wc.wsrd.withdraw_fee,
&fee_withdraw);
}
/* verify signature! */

View File

@ -43,8 +43,8 @@ DROP TABLE IF EXISTS reserves CASCADE;
DROP TABLE IF EXISTS denomination_revocations CASCADE;
DROP TABLE IF EXISTS denominations CASCADE;
-- Drop versioning (0000.sql)
DROP SCHEMA IF EXISTS _v CASCADE;
-- Unregister patch (0001.sql)
SELECT _v.unregister_patch('exchange-0001');
-- And we're out of here...
COMMIT;

View File

@ -254,7 +254,8 @@ CREATE TABLE IF NOT EXISTS deposits
,coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE CASCADE
,amount_with_fee_val INT8 NOT NULL
,amount_with_fee_frac INT4 NOT NULL
,timestamp INT8 NOT NULL
,wallet_timestamp INT8 NOT NULL
,exchange_timestamp INT8 NOT NULL
,refund_deadline INT8 NOT NULL
,wire_deadline INT8 NOT NULL
,merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)

View File

@ -796,7 +796,7 @@ postgres_get_session (void *cls)
"(coin_pub"
",amount_with_fee_val"
",amount_with_fee_frac"
",timestamp"
",wallet_timestamp"
",refund_deadline"
",wire_deadline"
",merchant_pub"
@ -804,22 +804,28 @@ postgres_get_session (void *cls)
",h_wire"
",coin_sig"
",wire"
",exchange_timestamp"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
" $11);",
11),
" $11, $12);",
12),
/* Fetch an existing deposit request, used to ensure idempotency
during /deposit processing. Used in #postgres_have_deposit(). */
GNUNET_PQ_make_prepare ("get_deposit",
"SELECT"
" amount_with_fee_val"
",amount_with_fee_frac"
",timestamp"
",denominations.fee_deposit_val"
",denominations.fee_deposit_frac"
",wallet_timestamp"
",exchange_timestamp"
",refund_deadline"
",wire_deadline"
",h_contract_terms"
",h_wire"
" FROM deposits"
" JOIN known_coins USING (coin_pub)"
" JOIN denominations USING (denom_pub_hash)"
" WHERE ((coin_pub=$1)"
" AND (merchant_pub=$3)"
" AND (h_contract_terms=$2))"
@ -830,7 +836,8 @@ postgres_get_session (void *cls)
"SELECT"
" amount_with_fee_val"
",amount_with_fee_frac"
",timestamp"
",wallet_timestamp"
",exchange_timestamp"
",merchant_pub"
",denom.denom_pub"
",coin_pub"
@ -881,6 +888,8 @@ postgres_get_session (void *cls)
",wire"
",merchant_pub"
",coin_pub"
",exchange_timestamp"
",wallet_timestamp"
" FROM deposits"
" JOIN known_coins USING (coin_pub)"
" JOIN denominations denom USING (denom_pub_hash)"
@ -900,6 +909,8 @@ postgres_get_session (void *cls)
",denom.fee_deposit_val"
",denom.fee_deposit_frac"
",wire_deadline"
",exchange_timestamp"
",wallet_timestamp"
",h_contract_terms"
",coin_pub"
" FROM deposits"
@ -945,7 +956,7 @@ postgres_get_session (void *cls)
",amount_with_fee_frac"
",denom.fee_deposit_val"
",denom.fee_deposit_frac"
",timestamp"
",wallet_timestamp"
",refund_deadline"
",wire_deadline"
",merchant_pub"
@ -1801,8 +1812,12 @@ postgres_iterate_denomination_info (void *cls,
.cb_cls = cb_cls,
.pg = pc
};
struct TALER_EXCHANGEDB_Session *session;
return GNUNET_PQ_eval_prepared_multi_select (postgres_get_session (pc)->conn,
session = postgres_get_session (pc);
if (NULL == session)
return GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_PQ_eval_prepared_multi_select (session->conn,
"denomination_iterate",
params,
&domination_cb_helper,
@ -2571,6 +2586,8 @@ postgres_get_reserve_history (void *cls,
* @param session database connection
* @param deposit deposit to search for
* @param check_extras whether to check extra fields match or not
* @param[out] deposit_fee set to the deposit fee the exchange charged
* @param[out] exchange_timestamp set to the time when the exchange received the deposit
* @return 1 if we know this operation,
* 0 if this exact deposit is unknown to us,
* otherwise transaction error status
@ -2579,7 +2596,9 @@ static enum GNUNET_DB_QueryStatus
postgres_have_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit,
int check_extras)
int check_extras,
struct TALER_Amount *deposit_fee,
struct GNUNET_TIME_Absolute *exchange_timestamp)
{
struct PostgresClosure *pg = cls;
struct GNUNET_PQ_QueryParam params[] = {
@ -2592,12 +2611,16 @@ postgres_have_deposit (void *cls,
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&deposit2.amount_with_fee),
TALER_PQ_result_spec_absolute_time ("timestamp",
TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
&deposit2.timestamp),
TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
exchange_timestamp),
TALER_PQ_result_spec_absolute_time ("refund_deadline",
&deposit2.refund_deadline),
TALER_PQ_result_spec_absolute_time ("wire_deadline",
&deposit2.wire_deadline),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
deposit_fee),
GNUNET_PQ_result_spec_auto_from_type ("h_wire",
&deposit2.h_wire),
GNUNET_PQ_result_spec_end
@ -2776,6 +2799,8 @@ postgres_get_ready_deposit (void *cls,
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_TIME_Absolute wallet_timestamp;
struct GNUNET_TIME_Absolute exchange_timestamp;
struct GNUNET_HashCode h_contract_terms;
struct TALER_MerchantPublicKeyP merchant_pub;
struct TALER_CoinSpendPublicKeyP coin_pub;
@ -2788,6 +2813,10 @@ postgres_get_ready_deposit (void *cls,
&amount_with_fee),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
&deposit_fee),
TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
&exchange_timestamp),
TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
&wallet_timestamp),
TALER_PQ_result_spec_absolute_time ("wire_deadline",
&wire_deadline),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
@ -2817,6 +2846,8 @@ postgres_get_ready_deposit (void *cls,
qs = deposit_cb (deposit_cb_cls,
serial_id,
exchange_timestamp,
wallet_timestamp,
&merchant_pub,
&coin_pub,
&amount_with_fee,
@ -2898,6 +2929,8 @@ match_deposit_cb (void *cls,
{
struct TALER_Amount amount_with_fee;
struct TALER_Amount deposit_fee;
struct GNUNET_TIME_Absolute exchange_timestamp;
struct GNUNET_TIME_Absolute wallet_timestamp;
struct GNUNET_TIME_Absolute wire_deadline;
struct GNUNET_HashCode h_contract_terms;
struct TALER_CoinSpendPublicKeyP coin_pub;
@ -2912,6 +2945,10 @@ match_deposit_cb (void *cls,
&deposit_fee),
TALER_PQ_result_spec_absolute_time ("wire_deadline",
&wire_deadline),
TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
&exchange_timestamp),
TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
&wallet_timestamp),
GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
&h_contract_terms),
GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
@ -2930,6 +2967,8 @@ match_deposit_cb (void *cls,
}
qs = mdc->deposit_cb (mdc->deposit_cb_cls,
serial_id,
exchange_timestamp,
wallet_timestamp,
mdc->merchant_pub,
&coin_pub,
&amount_with_fee,
@ -3033,6 +3072,8 @@ postgres_get_known_coin (void *cls,
coin_info->coin_pub = *coin_pub;
if (NULL == session)
session = postgres_get_session (pc);
if (NULL == session)
return GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_known_coin",
params,
@ -3072,6 +3113,8 @@ postgres_get_coin_denomination (
TALER_B2S (coin_pub));
if (NULL == session)
session = postgres_get_session (pc);
if (NULL == session)
return GNUNET_DB_STATUS_HARD_ERROR;
return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_coin_denomination",
params,
@ -3210,12 +3253,14 @@ postgres_ensure_coin_known (void *cls,
*
* @param cls the `struct PostgresClosure` with the plugin-specific state
* @param session connection to the database
* @param exchange_timestamp time the exchange received the deposit request
* @param deposit deposit information to store
* @return query result status
*/
static enum GNUNET_DB_QueryStatus
postgres_insert_deposit (void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_TIME_Absolute exchange_timestamp,
const struct TALER_EXCHANGEDB_Deposit *deposit)
{
struct GNUNET_PQ_QueryParam params[] = {
@ -3229,6 +3274,7 @@ postgres_insert_deposit (void *cls,
GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
GNUNET_PQ_query_param_auto_from_type (&deposit->csig),
TALER_PQ_query_param_json (deposit->receiver_wire_account),
TALER_PQ_query_param_absolute_time (&exchange_timestamp),
GNUNET_PQ_query_param_end
};
@ -3437,6 +3483,8 @@ postgres_get_melt (void *cls,
melt->session.coin.denom_sig.rsa_signature = NULL;
if (NULL == session)
session = postgres_get_session (pg);
if (NULL == session)
return GNUNET_DB_STATUS_HARD_ERROR;
qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
"get_melt",
params,
@ -4042,7 +4090,7 @@ add_coin_deposit (void *cls,
&deposit->amount_with_fee),
TALER_PQ_RESULT_SPEC_AMOUNT ("fee_deposit",
&deposit->deposit_fee),
TALER_PQ_result_spec_absolute_time ("timestamp",
TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
&deposit->timestamp),
TALER_PQ_result_spec_absolute_time ("refund_deadline",
&deposit->refund_deadline),
@ -5462,14 +5510,17 @@ deposit_serial_helper_cb (void *cls,
for (unsigned int i = 0; i<num_results; i++)
{
struct TALER_EXCHANGEDB_Deposit deposit;
struct GNUNET_TIME_Absolute exchange_timestamp;
struct TALER_DenominationPublicKey denom_pub;
uint8_t done = 0;
uint64_t rowid;
struct GNUNET_PQ_ResultSpec rs[] = {
TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
&deposit.amount_with_fee),
TALER_PQ_result_spec_absolute_time ("timestamp",
TALER_PQ_result_spec_absolute_time ("wallet_timestamp",
&deposit.timestamp),
TALER_PQ_result_spec_absolute_time ("exchange_timestamp",
&exchange_timestamp),
GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
&deposit.merchant_pub),
GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
@ -5505,6 +5556,7 @@ deposit_serial_helper_cb (void *cls,
}
ret = dsc->cb (dsc->cb_cls,
rowid,
exchange_timestamp,
deposit.timestamp,
&deposit.merchant_pub,
&denom_pub,

View File

@ -833,6 +833,8 @@ static uint64_t deposit_rowid;
* @param cls closure a `struct TALER_EXCHANGEDB_Deposit *`
* @param rowid unique ID for the deposit in our DB, used for marking
* it as 'tiny' or 'done'
* @param exchange_timestamp when did the deposit happen
* @param wallet_timestamp when did the wallet sign the contract
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
@ -846,6 +848,8 @@ static uint64_t deposit_rowid;
static enum GNUNET_DB_QueryStatus
deposit_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
@ -890,7 +894,8 @@ deposit_cb (void *cls,
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param timestamp when did the deposit happen
* @param exchange_timestamp when did the deposit happen
* @param wallet_timestamp when did the wallet sign the contract
* @param merchant_pub public key of the merchant
* @param denom_pub denomination of the @a coin_pub
* @param coin_pub public key of the coin
@ -908,7 +913,8 @@ deposit_cb (void *cls,
static int
audit_deposit_cb (void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -1878,15 +1884,27 @@ run (void *cls)
plugin->ensure_coin_known (plugin->cls,
session,
&deposit.coin));
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls,
{
struct GNUNET_TIME_Absolute now;
struct GNUNET_TIME_Absolute r;
struct TALER_Amount deposit_fee;
now = GNUNET_TIME_absolute_get ();
GNUNET_TIME_round_abs (&now);
FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
plugin->insert_deposit (plugin->cls,
session,
now,
&deposit));
FAILIF (1 !=
plugin->have_deposit (plugin->cls,
session,
&deposit));
FAILIF (1 !=
plugin->have_deposit (plugin->cls,
session,
&deposit,
GNUNET_YES));
&deposit,
GNUNET_YES,
&deposit_fee,
&r));
FAILIF (now.abs_value_us != r.abs_value_us);
}
{
struct GNUNET_TIME_Absolute start_range;
struct GNUNET_TIME_Absolute end_range;
@ -1983,18 +2001,27 @@ run (void *cls)
session,
"test-2"));
RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2,
GNUNET_YES));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2,
GNUNET_YES));
{
struct GNUNET_TIME_Absolute r;
struct TALER_Amount deposit_fee;
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2,
GNUNET_YES,
&deposit_fee,
&r));
deposit2.merchant_pub = deposit.merchant_pub;
RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
plugin->have_deposit (plugin->cls,
session,
&deposit2,
GNUNET_YES,
&deposit_fee,
&r));
}
FAILIF (GNUNET_OK !=
test_melting (session));
FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=

View File

@ -300,9 +300,9 @@ struct TALER_AUDITORDB_DepositConfirmation
struct GNUNET_HashCode h_wire;
/**
* Time when this confirmation was generated.
* Time when this deposit confirmation was generated by the exchange.
*/
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_TIME_Absolute exchange_timestamp;
/**
* How much time does the @e merchant have to issue a refund

View File

@ -384,16 +384,16 @@ struct TALER_BANK_DebitDetails
const char *exchange_base_url;
/**
* payto://-URL of the source account that
* payto://-URI of the source account that
* send the funds.
*/
const char *debit_account_url;
const char *debit_account_url; // FIXME: rename: url->uri
/**
* payto://-URL of the target account that
* payto://-URI of the target account that
* received the funds.
*/
const char *credit_account_url;
const char *credit_account_url; // FIXME: rename: url->uri
};

File diff suppressed because it is too large Load Diff

View File

@ -744,6 +744,32 @@ TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
/* ********************* /coins/$COIN_PUB/deposit *********************** */
/**
* Sign a deposit permission. Function for wallets.
*
* @param amount the amount to be deposited
* @param deposit_fee the deposit fee we expect to pay
* @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param coin_priv coins private key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
* @param[out] coin_sig set to the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT
*/
void
TALER_EXCHANGE_deposit_permission_sign (
const struct TALER_Amount *amount,
const struct TALER_Amount *deposit_fee,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
struct GNUNET_TIME_Absolute refund_deadline,
struct TALER_CoinSpendSignatureP *coin_sig);
/**
* @brief A Deposit Handle
*/
@ -756,6 +782,7 @@ struct TALER_EXCHANGE_DepositHandle;
*
* @param cls closure
* @param hr HTTP response data
* @param deposit_timestamp time when the exchange generated the deposit confirmation
* @param exchange_sig signature provided by the exchange
* @param exchange_pub exchange key used to sign @a obj, or NULL
*/
@ -763,6 +790,7 @@ typedef void
(*TALER_EXCHANGE_DepositResultCallback) (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
struct GNUNET_TIME_Absolute deposit_timestamp,
const struct TALER_ExchangeSignatureP *exchange_sig,
const struct TALER_ExchangePublicKeyP *exchange_pub);
@ -851,6 +879,7 @@ struct TALER_EXCHANGE_RefundHandle;
*
* @param cls closure
* @param hr HTTP response data
* @param refund_fee the refund fee the exchange charged us
* @param sign_key exchange key used to sign @a obj, or NULL
* @param signature the actual signature, or NULL on error
*/
@ -858,6 +887,7 @@ typedef void
(*TALER_EXCHANGE_RefundCallback) (
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_Amount *refund_fee,
const struct TALER_ExchangePublicKeyP *sign_key,
const struct TALER_ExchangeSignatureP *signature);
@ -878,7 +908,6 @@ typedef void
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
@ -894,7 +923,6 @@ typedef void
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
@ -922,7 +950,6 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
@ -939,7 +966,6 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
@ -1584,32 +1610,68 @@ TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh);
struct TALER_EXCHANGE_TransfersGetHandle;
/**
* Information the exchange returns per wire transfer.
*/
struct TALER_EXCHANGE_TransferData
{
/**
* exchange key used to sign
*/
struct TALER_ExchangePublicKeyP exchange_pub;
/**
* exchange signature over the transfer data
*/
struct TALER_ExchangeSignatureP exchange_sig;
/**
* hash of the wire transfer address the transfer went to
*/
struct GNUNET_HashCode h_wire;
/**
* time when the exchange claims to have performed the wire transfer
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* Actual amount of the wire transfer, excluding the wire fee.
*/
struct TALER_Amount total_amount;
/**
* wire fee that was charged by the exchange
*/
struct TALER_Amount wire_fee;
/**
* length of the @e details array
*/
unsigned int details_length;
/**
* array with details about the combined transactions
*/
const struct TALER_TrackTransferDetails *details;
};
/**
* Function called with detailed wire transfer data, including all
* of the coin transactions that were combined into the wire transfer.
*
* @param cls closure
* @param hr HTTP response data
* @param sign_key exchange key used to sign @a json, or NULL
* @param h_wire hash of the wire transfer address the transfer went to, or NULL on error
* @param execution_time time when the exchange claims to have performed the wire transfer
* @param total_amount total amount of the wire transfer, or NULL if the exchange could
* not provide any @a wtid (set only if @a http_status is #MHD_HTTP_OK)
* @param wire_fee wire fee that was charged by the exchange
* @param details_length length of the @a details array
* @param details array with details about the combined transactions
* @param ta transfer data, (set only if @a http_status is #MHD_HTTP_OK, otherwise NULL)
*/
typedef void
(*TALER_EXCHANGE_TransfersGetCallback)(
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_ExchangePublicKeyP *sign_key,
const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *total_amount,
const struct TALER_Amount *wire_fee,
unsigned int details_length,
const struct TALER_TrackTransferDetails *details);
const struct TALER_EXCHANGE_TransferData *ta);
/**
@ -1650,25 +1712,57 @@ TALER_EXCHANGE_transfers_get_cancel (
struct TALER_EXCHANGE_DepositGetHandle;
/**
* Data returned for a successful GET /deposits/ request. Note that
* most fields are only set if the status is #MHD_HTTP_OK. Only
* the @e execution_time is available if the status is #MHD_HTTP_ACCEPTED.
*/
struct TALER_EXCHANGE_DepositData
{
/**
* exchange key used to sign, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_ExchangePublicKeyP exchange_pub;
/**
* signature from the exchange over the deposit data, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_ExchangeSignatureP exchange_sig;
/**
* wire transfer identifier used by the exchange, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_WireTransferIdentifierRawP wtid;
/**
* actual or planned execution time for the wire transfer
*/
struct GNUNET_TIME_Absolute execution_time;
/**
* contribution to the total amount by this coin, all zeros if exchange did not
* yet execute the transaction
*/
struct TALER_Amount coin_contribution;
};
/**
* Function called with detailed wire transfer data.
*
* @param cls closure
* @param hr HTTP response data
* @param sign_key exchange key used to sign @a json, or NULL
* @param wtid wire transfer identifier used by the exchange, NULL if exchange did not
* yet execute the transaction
* @param execution_time actual or planned execution time for the wire transfer
* @param coin_contribution contribution to the total amount by this coin (can be NULL)
* @param dd details about the deposit (NULL on errors)
*/
typedef void
(*TALER_EXCHANGE_DepositGetCallback)(
void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_ExchangePublicKeyP *sign_key,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution);
const struct TALER_EXCHANGE_DepositData *dd);
/**

View File

@ -975,6 +975,8 @@ struct TALER_EXCHANGEDB_Session;
* @param cls closure
* @param rowid unique ID for the deposit in our DB, used for marking
* it as 'tiny' or 'done'
* @param exchange_timestamp when did the exchange receive the deposit
* @param wallet_timestamp when did the wallet sign the contract
* @param merchant_pub public key of the merchant
* @param coin_pub public key of the coin
* @param amount_with_fee amount that was deposited including fee
@ -990,6 +992,8 @@ typedef enum GNUNET_DB_QueryStatus
(*TALER_EXCHANGEDB_DepositIterator)(
void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
const struct TALER_Amount *amount_with_fee,
@ -1022,7 +1026,8 @@ typedef void
*
* @param cls closure
* @param rowid unique serial ID for the deposit in our DB
* @param timestamp when did the deposit happen
* @param exchange_timestamp when did the deposit happen
* @param wallet_timestamp when did the contract happen
* @param merchant_pub public key of the merchant
* @param denom_pub denomination public key of @a coin_pub
* @param coin_pub public key of the coin
@ -1042,7 +1047,8 @@ typedef int
(*TALER_EXCHANGEDB_DepositCallback)(
void *cls,
uint64_t rowid,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
const struct TALER_DenominationPublicKey *denom_pub,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -1841,6 +1847,8 @@ struct TALER_EXCHANGEDB_Plugin
* @param session database connection
* @param deposit deposit to search for
* @param check_extras whether to check extra fields or not
* @param[out] deposit_fee set to the deposit fee the exchange charged
* @param[out] exchange_timestamp set to the time when the exchange received the deposit
* @return 1 if we know this operation,
* 0 if this exact deposit is unknown to us,
* otherwise transaction error status
@ -1849,7 +1857,9 @@ struct TALER_EXCHANGEDB_Plugin
(*have_deposit)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
const struct TALER_EXCHANGEDB_Deposit *deposit,
int check_extras);
int check_extras,
struct TALER_Amount *deposit_fee,
struct GNUNET_TIME_Absolute *exchange_timestamp);
/**
@ -1857,12 +1867,14 @@ struct TALER_EXCHANGEDB_Plugin
*
* @param cls the @e cls of this struct with the plugin-specific state
* @param session connection to the database
* @param exchange_timestamp time the exchange received the deposit request
* @param deposit deposit information to store
* @return query result status
*/
enum GNUNET_DB_QueryStatus
(*insert_deposit)(void *cls,
struct TALER_EXCHANGEDB_Session *session,
struct GNUNET_TIME_Absolute exchange_timestamp,
const struct TALER_EXCHANGEDB_Deposit *deposit);

View File

@ -264,7 +264,8 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls);
/**
* Parse JSON object into components based on the given field
* specification.
* specification. If parsing fails, we return an HTTP
* status code of 400 (#MHD_HTTP_BAD_REQUEST).
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
@ -282,6 +283,30 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
struct GNUNET_JSON_Specification *spec);
/**
* Parse JSON object that we (the server!) generated into components based on
* the given field specification. The difference to
* #TALER_MHD_parse_json_data() is that this function will fail
* with an HTTP failure of 500 (internal server error) in case
* parsing fails, instead of blaming it on the client with a
* 400 (#MHD_HTTP_BAD_REQUEST).
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* GNUNET_JSON_parse_free() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_JSON_Specification *spec);
/**
* Parse JSON array into components based on the given field
* specification. Generates error response on parse errors.

View File

@ -316,19 +316,6 @@ struct TALER_WithdrawRequestPS
*/
struct TALER_AmountNBO amount_with_fee;
/**
* Withdrawal fee charged by the exchange. This must match the Exchange's
* denomination key's withdrawal fee. If the client puts in an
* invalid withdrawal fee (too high or too low) that does not match
* the Exchange's denomination key, the withdraw operation is invalid
* and will be rejected by the exchange. The @e amount_with_fee minus
* the @e withdraw_fee is must match the value of the generated
* coin. We include this in what is being signed so that we can
* verify a exchange's accounting without needing to access the
* respective denomination key information each time.
*/
struct TALER_AmountNBO withdraw_fee;
/**
* Hash of the denomination public key for the coin that is withdrawn.
*/
@ -376,7 +363,7 @@ struct TALER_DepositRequestPS
* deposit request in a timely fashion (so back-dating is not
* prevented).
*/
struct GNUNET_TIME_AbsoluteNBO timestamp;
struct GNUNET_TIME_AbsoluteNBO wallet_timestamp;
/**
* How much time does the merchant have to issue a refund request?
@ -442,9 +429,10 @@ struct TALER_DepositConfirmationPS
struct GNUNET_HashCode h_wire GNUNET_PACKED;
/**
* Time when this confirmation was generated.
* Time when this confirmation was generated / when the exchange received
* the deposit request.
*/
struct GNUNET_TIME_AbsoluteNBO timestamp;
struct GNUNET_TIME_AbsoluteNBO exchange_timestamp;
/**
* How much time does the @e merchant have to issue a refund
@ -517,17 +505,6 @@ struct TALER_RefundRequestPS
*/
struct TALER_AmountNBO refund_amount;
/**
* Refund fee charged by the exchange. This must match the
* Exchange's denomination key's refund fee. If the client puts in
* an invalid refund fee (too high or too low) that does not match
* the Exchange's denomination key, the refund operation is invalid
* and will be rejected by the exchange. The @e amount_with_fee
* minus the @e refund_fee is the amount that will be credited to
* the original coin.
*/
struct TALER_AmountNBO refund_fee;
};
@ -1153,10 +1130,8 @@ struct TALER_ProposalDataPS
struct PaymentResponsePS
{
/**
* Set to TALER_SIGNATURE_MERCHANT_PAYMENT_OK so far. Note that
* unsuccessful payments are usually proven by some exchange's signature,
* thus it is unlikely that a merchant needs to set a purpose other than
* the above mentioned
* Set to #TALER_SIGNATURE_MERCHANT_PAYMENT_OK. Note that
* unsuccessful payments are usually proven by some exchange's signature.
*/
struct GNUNET_CRYPTO_EccSignaturePurpose purpose;

152
src/include/taler_sq_lib.h Normal file
View File

@ -0,0 +1,152 @@
/*
This file is part of TALER
Copyright (C) 2020 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 include/taler_sq_lib.h
* @brief helper functions for DB interactions with SQLite
* @author Jonathan Buchanan
*/
#ifndef TALER_SQ_LIB_H_
#define TALER_SQ_LIB_H_
#include <sqlite3.h>
#include <jansson.h>
#include <gnunet/gnunet_sq_lib.h>
#include "taler_util.h"
/**
* Generate query parameter for a currency, consisting of the
* components "value", "fraction" in this order. The
* types must be a 64-bit integer and a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_amount_nbo (const struct TALER_AmountNBO *x);
/**
* Generate query parameter for a currency, consisting of the
* components "value", "fraction" in this order. The
* types must be a 64-bit integer and a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_amount (const struct TALER_Amount *x);
/**
* Generate query parameter for a JSON object (stored as a string
* in the DB). Note that @a x must really be a JSON object or array,
* passing just a value (string, integer) is not supported and will
* result in an abort.
*
* @param x pointer to the json object to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_json (const json_t *x);
/**
* Generate query parameter for an absolute time value.
* In contrast to
* #GNUNET_SQ_query_param_absolute_time(), this function
* will abort (!) if the time given is not rounded!
* The database must store a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x);
/**
* Generate query parameter for an absolute time value.
* In contrast to
* #GNUNET_SQ_query_param_absolute_time(), this function
* will abort (!) if the time given is not rounded!
* The database must store a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_absolute_time_nbo (const struct
GNUNET_TIME_AbsoluteNBO *x);
/**
* Currency amount expected.
*
* @param currency currency to use for @a amount
* @param[out] amount where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_amount_nbo (const char *currency,
struct TALER_AmountNBO *amount);
/**
* Currency amount expected.
*
* @param currency currency to use for @a amount
* @param[out] amount where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_amount (const char *currency,
struct TALER_Amount *amount);
/**
* json_t expected.
*
* @param[out] jp where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_json (json_t **jp);
/**
* Rounded absolute time expected.
* In contrast to #GNUNET_SQ_query_param_absolute_time_nbo(),
* this function ensures that the result is rounded and can
* be converted to JSON.
*
* @param[out] at where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_absolute_time (struct GNUNET_TIME_Absolute *at);
/**
* Rounded absolute time expected.
* In contrast to #GNUNET_SQ_result_spec_absolute_time_nbo(),
* this function ensures that the result is rounded and can
* be converted to JSON.
*
* @param[out] at where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_absolute_time_nbo (struct GNUNET_TIME_AbsoluteNBO *at);
#endif /* TALER_SQ_LIB_H_ */
/* end of include/taler_sq_lib.h */

View File

@ -388,10 +388,9 @@ struct TALER_TESTING_Interpreter
void *final_cleanup_cb_cls;
/**
* Instruction pointer. Tells #interpreter_run() which
* instruction to run next. Need (signed) int because
* it gets -1 when rewinding the interpreter to the first
* CMD.
* Instruction pointer. Tells #interpreter_run() which instruction to run
* next. Need (signed) int because it gets -1 when rewinding the
* interpreter to the first CMD.
*/
int ip;
@ -599,7 +598,22 @@ TALER_TESTING_interpreter_fail (struct TALER_TESTING_Interpreter *is);
* @return a end-command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_end ();
TALER_TESTING_cmd_end (void);
/**
* Make the instruction pointer point to @a target_label
* only if @a counter is greater than zero.
*
* @param label command label
* @param target_label label of the new instruction pointer's destination after the jump;
* must be before the current instruction
* @param counter counts how many times the rewinding is to happen.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_rewind_ip (const char *label,
const char *target_label,
unsigned int counter);
/**
@ -816,7 +830,6 @@ TALER_TESTING_setup_with_auditor_and_exchange (TALER_TESTING_Main main_cb,
* @param config_filename configuration filename.
* @param bank_url base URL of the bank, used by `wget' to check
* that the bank was started right.
*
* @return the process, or NULL if the process could not
* be started.
*/
@ -839,6 +852,7 @@ TALER_TESTING_run_bank (const char *config_filename,
struct TALER_TESTING_LibeufinServices
TALER_TESTING_run_libeufin (const struct TALER_TESTING_BankConfiguration *bc);
/**
* Runs the Fakebank by guessing / extracting the portnumber
* from the base URL.
@ -1089,7 +1103,7 @@ TALER_TESTING_cmd_admin_add_incoming (
* @param payto_debit_account which account sends money.
* @param auth authentication data
* @param ref reference to a command that can offer a reserve
* private key.
* private key or public key.
* @return the command.
*/
struct TALER_TESTING_Command
@ -1345,6 +1359,16 @@ TALER_TESTING_cmd_status (const char *label,
const char *expected_balance,
unsigned int expected_response_code);
/**
* Index of the deposit value trait of a deposit command.
*/
#define TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE 0
/**
* Index of the deposit fee trait of a deposit command.
*/
#define TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_FEE 1
/**
* Create a "deposit" command.
*
@ -1670,7 +1694,6 @@ TALER_TESTING_cmd_check_bank_empty (const char *label);
* @param label command label.
* @param expected_response_code expected HTTP status code.
* @param refund_amount the amount to ask a refund for.
* @param refund_fee expected refund fee.
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
* @param refund_transaction_id transaction id to use
@ -1682,7 +1705,6 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_refund_with_id (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *refund_fee,
const char *deposit_reference,
uint64_t refund_transaction_id);
@ -1693,7 +1715,6 @@ TALER_TESTING_cmd_refund_with_id (const char *label,
* @param label command label.
* @param expected_response_code expected HTTP status code.
* @param refund_amount the amount to ask a refund for.
* @param refund_fee expected refund fee.
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
*
@ -1703,7 +1724,6 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_refund (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *refund_fee,
const char *deposit_reference);
@ -1890,6 +1910,18 @@ TALER_TESTING_cmd_batch_next (struct TALER_TESTING_Interpreter *is);
struct TALER_TESTING_Command *
TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd);
/**
* Set what command the batch should be at.
*
* @param cmd current batch command
* @param new_ip where to move the IP
*/
void
TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd,
unsigned int new_ip);
/**
* Make a serialize-keys CMD.
*
@ -1921,6 +1953,7 @@ TALER_TESTING_cmd_connect_with_state (const char *label,
* @param dbc collects plugin and session handles
* @param merchant_name Human-readable name of the merchant.
* @param merchant_account merchant's account name (NOT a payto:// URI)
* @param exchange_timestamp when did the exchange receive the deposit
* @param wire_deadline point in time where the aggregator should have
* wired money to the merchant.
* @param amount_with_fee amount to deposit (inclusive of deposit fee)
@ -1928,14 +1961,15 @@ TALER_TESTING_cmd_connect_with_state (const char *label,
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_insert_deposit (const char *label,
const struct
TALER_TESTING_DatabaseConnection *dbc,
const char *merchant_name,
const char *merchant_account,
struct GNUNET_TIME_Relative wire_deadline,
const char *amount_with_fee,
const char *deposit_fee);
TALER_TESTING_cmd_insert_deposit (
const char *label,
const struct TALER_TESTING_DatabaseConnection *dbc,
const char *merchant_name,
const char *merchant_account,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Relative wire_deadline,
const char *amount_with_fee,
const char *deposit_fee);
/**
@ -2339,6 +2373,31 @@ TALER_TESTING_make_trait_denom_sig (
const struct TALER_DenominationSignature *sig);
/**
* Offer number trait, 32-bit version.
*
* @param index the number's index number.
* @param n number to offer.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_uint32 (unsigned int index,
const uint32_t *n);
/**
* Obtain a "number" value from @a cmd, 32-bit version.
*
* @param cmd command to extract the number from.
* @param index the number's index number.
* @param[out] n set to the number coming from @a cmd.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_uint32 (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const uint32_t **n);
/**
* Offer number trait, 64-bit version.
*
@ -2364,6 +2423,31 @@ TALER_TESTING_get_trait_uint64 (const struct TALER_TESTING_Command *cmd,
const uint64_t **n);
/**
* Offer number trait, 64-bit signed version.
*
* @param index the number's index number.
* @param n number to offer.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_int64 (unsigned int index,
const int64_t *n);
/**
* Obtain a "number" value from @a cmd, 64-bit signed version.
*
* @param cmd command to extract the number from.
* @param index the number's index number.
* @param[out] n set to the number coming from @a cmd.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_int64 (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const int64_t **n);
/**
* Offer a number.
*
@ -2513,6 +2597,33 @@ TALER_TESTING_make_trait_exchange_keys (unsigned int index,
const json_t *keys);
/**
* Obtain json from @a cmd.
*
* @param cmd command to extract the json from.
* @param index index number associate with the json on offer.
* @param[out] json where to write the json.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_json (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const json_t **json);
/**
* Offer json in a trait.
*
* @param index index number associate with the json
* on offer.
* @param json json to offer.
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_json (unsigned int index,
const json_t *json);
/**
* Obtain a private key from a "merchant". Used e.g. to obtain
* a merchant's priv to sign a /track request.
@ -2854,4 +2965,33 @@ TALER_TESTING_make_trait_absolute_time (
unsigned int index,
const struct GNUNET_TIME_Absolute *time);
/**
* Obtain a relative time from @a cmd.
*
* @param cmd command to extract trait from
* @param index which time to pick if
* @a cmd has multiple on offer.
* @param[out] time set to the wanted WTID.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_relative_time (
const struct TALER_TESTING_Command *cmd,
unsigned int index,
const struct GNUNET_TIME_Relative **time);
/**
* Offer a relative time.
*
* @param index associate the object with this index
* @param time which object should be returned
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_relative_time (
unsigned int index,
const struct GNUNET_TIME_Relative *time);
#endif

View File

@ -298,4 +298,52 @@ char *
TALER_xtalerbank_account_from_payto (const char *payto);
/**
* Possible values for a binary filter.
*/
enum TALER_EXCHANGE_YesNoAll
{
/**
* If condition is yes.
*/
TALER_EXCHANGE_YNA_YES = 1,
/**
* If condition is no.
*/
TALER_EXCHANGE_YNA_NO = 2,
/**
* Condition disabled.
*/
TALER_EXCHANGE_YNA_ALL = 3
};
/**
* Convert query argument to @a yna value.
*
* @param connection connection to take query argument from
* @param arg argument to try for
* @param default_val value to assign if the argument is not present
* @param[out] value to set
* @return true on success, false if the parameter was malformed
*/
bool
TALER_arg_to_yna (struct MHD_Connection *connection,
const char *arg,
enum TALER_EXCHANGE_YesNoAll default_val,
enum TALER_EXCHANGE_YesNoAll *yna);
/**
* Convert YNA value to a string.
*
* @param yna value to convert
* @return string representation ("yes"/"no"/"all").
*/
const char *
TALER_yna_to_string (enum TALER_EXCHANGE_YesNoAll yna);
#endif

View File

@ -148,7 +148,7 @@ handle_deposit_confirmation_finished (void *cls,
*
* @param h_wire hash of merchant wire details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param timestamp timestamp when the contract was finalized, must not be too far in the future
* @param exchange_timestamp timestamp when the deposit was received by the wallet
* @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline
* @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant
* @param coin_pub coins public key
@ -165,7 +165,7 @@ handle_deposit_confirmation_finished (void *cls,
static int
verify_signatures (const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_Amount *amount_without_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -184,7 +184,7 @@ verify_signatures (const struct GNUNET_HashCode *h_wire,
.purpose.size = htonl (sizeof (dc)),
.h_contract_terms = *h_contract_terms,
.h_wire = *h_wire,
.timestamp = GNUNET_TIME_absolute_hton (timestamp),
.exchange_timestamp = GNUNET_TIME_absolute_hton (exchange_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
.coin_pub = *coin_pub,
.merchant = *merchant_pub
@ -256,7 +256,7 @@ verify_signatures (const struct GNUNET_HashCode *h_wire,
* @param auditor the auditor handle; the auditor must be ready to operate
* @param h_wire hash of merchant wire details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
* @param timestamp timestamp when the contract was finalized, must not be too far in the future
* @param exchange_timestamp timestamp when deposit was received by the exchange
* @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline
* @param amount_without_fee the amount confirmed to be wired by the exchange to the merchant
* @param coin_pub coins public key
@ -278,7 +278,7 @@ TALER_AUDITOR_deposit_confirmation (
struct TALER_AUDITOR_Handle *auditor,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
struct GNUNET_TIME_Absolute timestamp,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Absolute refund_deadline,
const struct TALER_Amount *amount_without_fee,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
@ -298,7 +298,7 @@ TALER_AUDITOR_deposit_confirmation (
json_t *deposit_confirmation_obj;
CURL *eh;
(void) GNUNET_TIME_round_abs (&timestamp);
(void) GNUNET_TIME_round_abs (&exchange_timestamp);
(void) GNUNET_TIME_round_abs (&refund_deadline);
(void) GNUNET_TIME_round_abs (&ep_start);
(void) GNUNET_TIME_round_abs (&ep_expire);
@ -308,7 +308,7 @@ TALER_AUDITOR_deposit_confirmation (
if (GNUNET_OK !=
verify_signatures (h_wire,
h_contract_terms,
timestamp,
exchange_timestamp,
refund_deadline,
amount_without_fee,
coin_pub,
@ -336,7 +336,8 @@ TALER_AUDITOR_deposit_confirmation (
"h_wire", GNUNET_JSON_from_data_auto (h_wire),
"h_contract_terms", GNUNET_JSON_from_data_auto (
h_contract_terms),
"timestamp", GNUNET_JSON_from_time_abs (timestamp),
"exchange_timestamp", GNUNET_JSON_from_time_abs (
exchange_timestamp),
"refund_deadline", GNUNET_JSON_from_time_abs (refund_deadline),
"amount_without_fee", TALER_JSON_from_amount (
amount_without_fee),

View File

@ -146,11 +146,12 @@ TALER_EXCHANGE_parse_reserve_history (
{
struct TALER_ReserveSignatureP sig;
struct TALER_WithdrawRequestPS withdraw_purpose;
struct TALER_Amount withdraw_fee;
struct GNUNET_JSON_Specification withdraw_spec[] = {
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
&sig),
TALER_JSON_spec_amount_nbo ("withdraw_fee",
&withdraw_purpose.withdraw_fee),
TALER_JSON_spec_amount ("withdraw_fee",
&withdraw_fee),
GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
&withdraw_purpose.h_denomination_pub),
GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
@ -189,26 +190,23 @@ TALER_EXCHANGE_parse_reserve_history (
{
const struct TALER_EXCHANGE_Keys *key_state;
const struct TALER_EXCHANGE_DenomPublicKey *dki;
struct TALER_Amount fee;
key_state = TALER_EXCHANGE_get_keys (exchange);
dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
&withdraw_purpose.
h_denomination_pub);
TALER_amount_ntoh (&fee,
&withdraw_purpose.withdraw_fee);
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&fee,
TALER_amount_cmp_currency (&withdraw_fee,
&dki->fee_withdraw)) ||
(0 !=
TALER_amount_cmp (&fee,
TALER_amount_cmp (&withdraw_fee,
&dki->fee_withdraw)) )
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (withdraw_spec);
return GNUNET_SYSERR;
}
rh->details.withdraw.fee = fee;
rh->details.withdraw.fee = withdraw_fee;
}
rh->details.withdraw.out_authorization_sig
= json_object_get (transaction,
@ -526,7 +524,7 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_JSON_spec_fixed_auto ("h_wire",
&dr.h_wire),
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
&dr.timestamp),
&dr.wallet_timestamp),
GNUNET_JSON_spec_absolute_time_nbo ("refund_deadline",
&dr.refund_deadline),
TALER_JSON_spec_amount_nbo ("deposit_fee",
@ -634,9 +632,16 @@ TALER_EXCHANGE_verify_coin_history (
else if (0 == strcasecmp (type,
"REFUND"))
{
struct TALER_RefundRequestPS rr;
struct TALER_MerchantSignatureP sig;
struct TALER_Amount refund_fee;
struct TALER_RefundRequestPS rr = {
.purpose.size = htonl (sizeof (rr)),
.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND),
.coin_pub = *coin_pub
};
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("refund_fee",
&refund_fee),
GNUNET_JSON_spec_fixed_auto ("merchant_sig",
&sig),
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
@ -645,8 +650,6 @@ TALER_EXCHANGE_verify_coin_history (
&rr.merchant),
GNUNET_JSON_spec_uint64 ("rtransaction_id",
&rr.rtransaction_id),
TALER_JSON_spec_amount_nbo ("refund_fee",
&rr.refund_fee),
GNUNET_JSON_spec_end ()
};
@ -658,9 +661,6 @@ TALER_EXCHANGE_verify_coin_history (
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
rr.purpose.size = htonl (sizeof (rr));
rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
rr.coin_pub = *coin_pub;
TALER_amount_hton (&rr.refund_amount,
&amount);
if (GNUNET_OK !=
@ -683,13 +683,11 @@ TALER_EXCHANGE_verify_coin_history (
/* check that refund fee matches our expectations from /keys! */
if (NULL != dk)
{
TALER_amount_ntoh (&fee,
&rr.refund_fee);
if ( (GNUNET_YES !=
TALER_amount_cmp_currency (&fee,
TALER_amount_cmp_currency (&refund_fee,
&dk->fee_refund)) ||
(0 !=
TALER_amount_cmp (&fee,
TALER_amount_cmp (&refund_fee,
&dk->fee_refund)) )
{
GNUNET_break_op (0);

View File

@ -160,7 +160,7 @@ auditor_cb (void *cls,
ah,
&dh->depconf.h_wire,
&dh->depconf.h_contract_terms,
GNUNET_TIME_absolute_ntoh (dh->depconf.timestamp),
GNUNET_TIME_absolute_ntoh (dh->depconf.exchange_timestamp),
GNUNET_TIME_absolute_ntoh (dh->depconf.refund_deadline),
&amount_without_fee,
&dh->depconf.coin_pub,
@ -196,8 +196,10 @@ verify_deposit_signature_ok (struct TALER_EXCHANGE_DepositHandle *dh,
{
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig", exchange_sig),
GNUNET_JSON_spec_fixed_auto ("pub", exchange_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
GNUNET_JSON_spec_absolute_time_nbo ("exchange_timestamp",
&dh->depconf.exchange_timestamp),
GNUNET_JSON_spec_end ()
};
@ -386,6 +388,7 @@ handle_deposit_finished (void *cls,
}
dh->cb (dh->cb_cls,
&hr,
GNUNET_TIME_absolute_ntoh (dh->depconf.exchange_timestamp),
es,
ep);
TALER_EXCHANGE_deposit_cancel (dh);
@ -429,7 +432,7 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki,
.purpose.size = htonl (sizeof (dr)),
.h_contract_terms = *h_contract_terms,
.h_wire = *h_wire,
.timestamp = GNUNET_TIME_absolute_hton (timestamp),
.wallet_timestamp = GNUNET_TIME_absolute_hton (timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
.merchant = *merchant_pub,
.coin_pub = *coin_pub
@ -487,6 +490,59 @@ verify_signatures (const struct TALER_EXCHANGE_DenomPublicKey *dki,
}
/**
* Sign a deposit permission. Function for wallets.
*
* @param amount the amount to be deposited
* @param deposit_fee the deposit fee we expect to pay
* @param h_wire hash of the merchants account details
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the exchange)
* @param coin_priv coins private key
* @param wallet_timestamp timestamp when the contract was finalized, must not be too far in the future
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
* @param refund_deadline date until which the merchant can issue a refund to the customer via the exchange (can be zero if refunds are not allowed); must not be after the @a wire_deadline
* @param[out] coin_sig set to the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT
*/
void
TALER_EXCHANGE_deposit_permission_sign (
const struct TALER_Amount *amount,
const struct TALER_Amount *deposit_fee,
const struct GNUNET_HashCode *h_wire,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPrivateKeyP *coin_priv,
struct GNUNET_TIME_Absolute wallet_timestamp,
const struct TALER_MerchantPublicKeyP *merchant_pub,
struct GNUNET_TIME_Absolute refund_deadline,
struct TALER_CoinSpendSignatureP *coin_sig)
{
struct TALER_DepositRequestPS dr = {
.purpose.size = htonl
(sizeof (dr)),
.purpose.purpose = htonl
(TALER_SIGNATURE_WALLET_COIN_DEPOSIT),
.h_contract_terms = *h_contract_terms,
.h_wire = *h_wire,
.wallet_timestamp = GNUNET_TIME_absolute_hton (wallet_timestamp),
.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline),
.merchant = *merchant_pub
};
GNUNET_assert (GNUNET_OK ==
GNUNET_TIME_round_abs (&wallet_timestamp));
GNUNET_assert (GNUNET_OK ==
GNUNET_TIME_round_abs (&refund_deadline));
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&dr.coin_pub.eddsa_pub);
TALER_amount_hton (&dr.amount_with_fee,
amount);
TALER_amount_hton (&dr.deposit_fee,
deposit_fee);
GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv,
&dr,
&coin_sig->eddsa_signature);
}
/**
* Submit a deposit permission to the exchange and get the exchange's response.
* Note that while we return the response verbatim to the caller for
@ -658,7 +714,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle *exchange,
TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
dh->depconf.h_contract_terms = *h_contract_terms;
dh->depconf.h_wire = h_wire;
dh->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp);
/* dh->depconf.exchange_timestamp; -- initialized later from exchange reply! */
dh->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
TALER_amount_hton (&dh->depconf.amount_without_fee,
&amount_without_fee);

View File

@ -33,7 +33,7 @@
/**
* @brief A Deposit Wtid Handle
* @brief A Deposit Get Handle
*/
struct TALER_EXCHANGE_DepositGetHandle
{
@ -84,31 +84,19 @@ struct TALER_EXCHANGE_DepositGetHandle
*
* @param dwh deposit wtid handle
* @param json json reply with the signature
* @param[out] exchange_pub set to the exchange's public key
* @param exchange_pub the exchange's public key
* @param exchange_sig the exchange's signature
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static int
verify_deposit_wtid_signature_ok (
const struct TALER_EXCHANGE_DepositGetHandle *dwh,
const json_t *json,
struct TALER_ExchangePublicKeyP *exchange_pub)
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_ExchangeSignatureP *exchange_sig)
{
struct TALER_ExchangeSignatureP exchange_sig;
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (json,
spec,
NULL, NULL))
{
GNUNET_break_op (0);
return GNUNET_SYSERR;
}
key_state = TALER_EXCHANGE_get_keys (dwh->exchange);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (key_state,
@ -120,7 +108,7 @@ verify_deposit_wtid_signature_ok (
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE,
&dwh->depconf,
&exchange_sig.eddsa_signature,
&exchange_sig->eddsa_signature,
&exchange_pub->eddsa_pub))
{
GNUNET_break_op (0);
@ -144,12 +132,6 @@ handle_deposit_wtid_finished (void *cls,
const void *response)
{
struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
const struct TALER_Amount *coin_contribution = NULL;
struct TALER_Amount coin_contribution_s;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangePublicKeyP *ep = NULL;
const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = j,
@ -164,10 +146,13 @@ handle_deposit_wtid_finished (void *cls,
break;
case MHD_HTTP_OK:
{
struct TALER_EXCHANGE_DepositData dd;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("wtid", &dwh->depconf.wtid),
GNUNET_JSON_spec_absolute_time ("execution_time", &execution_time),
TALER_JSON_spec_amount ("coin_contribution", &coin_contribution_s),
GNUNET_JSON_spec_absolute_time ("execution_time", &dd.execution_time),
TALER_JSON_spec_amount ("coin_contribution", &dd.coin_contribution),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &dd.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &dd.exchange_pub),
GNUNET_JSON_spec_end ()
};
@ -181,15 +166,15 @@ handle_deposit_wtid_finished (void *cls,
hr.ec = TALER_EC_DEPOSITS_INVALID_BODY_BY_EXCHANGE;
break;
}
wtid = &dwh->depconf.wtid;
dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time);
dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (
dd.execution_time);
TALER_amount_hton (&dwh->depconf.coin_contribution,
&coin_contribution_s);
coin_contribution = &coin_contribution_s;
&dd.coin_contribution);
if (GNUNET_OK !=
verify_deposit_wtid_signature_ok (dwh,
j,
&exchange_pub))
&dd.exchange_pub,
&dd.exchange_sig))
{
GNUNET_break_op (0);
hr.http_status = 0;
@ -197,13 +182,19 @@ handle_deposit_wtid_finished (void *cls,
}
else
{
ep = &exchange_pub;
dd.wtid = dwh->depconf.wtid;
dwh->cb (dwh->cb_cls,
&hr,
&dd);
TALER_EXCHANGE_deposits_get_cancel (dwh);
return;
}
}
break;
case MHD_HTTP_ACCEPTED:
{
/* Transaction known, but not executed yet */
struct GNUNET_TIME_Absolute execution_time;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_absolute_time ("execution_time", &execution_time),
GNUNET_JSON_spec_end ()
@ -219,6 +210,18 @@ handle_deposit_wtid_finished (void *cls,
hr.ec = TALER_EC_DEPOSITS_INVALID_BODY_BY_EXCHANGE;
break;
}
else
{
struct TALER_EXCHANGE_DepositData dd = {
.execution_time = execution_time
};
dwh->cb (dwh->cb_cls,
&hr,
&dd);
TALER_EXCHANGE_deposits_get_cancel (dwh);
return;
}
}
break;
case MHD_HTTP_BAD_REQUEST:
@ -259,10 +262,7 @@ handle_deposit_wtid_finished (void *cls,
}
dwh->cb (dwh->cb_cls,
&hr,
ep,
wtid,
execution_time,
coin_contribution);
NULL);
TALER_EXCHANGE_deposits_get_cancel (dwh);
}

View File

@ -39,7 +39,7 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
#define EXCHANGE_PROTOCOL_CURRENT 7
#define EXCHANGE_PROTOCOL_CURRENT 8
/**
* How many versions are we backwards compatible with?

View File

@ -445,11 +445,11 @@ TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
char *end;
end = GNUNET_STRINGS_data_to_string (&melt.coin_pub,
sizeof (struct
TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
end = GNUNET_STRINGS_data_to_string (
&melt.coin_pub,
sizeof (struct TALER_CoinSpendPublicKeyP),
pub_str,
sizeof (pub_str));
*end = '\0';
GNUNET_snprintf (arg_str,
sizeof (arg_str),

View File

@ -81,23 +81,23 @@ struct TALER_EXCHANGE_RefundHandle
* Verify that the signature on the "200 OK" response
* from the exchange is valid.
*
* @param rh refund handle
* @param[in,out] rh refund handle (refund fee added)
* @param json json reply with the signature
* @param[out] exchange_pub set to the exchange's public key
* @param[out] exchange_sig set to the exchange's signature
* @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
*/
static int
verify_refund_signature_ok (const struct TALER_EXCHANGE_RefundHandle *rh,
verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh,
const json_t *json,
struct TALER_ExchangePublicKeyP *exchange_pub,
struct TALER_ExchangeSignatureP *exchange_sig)
{
const struct TALER_EXCHANGE_Keys *key_state;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_fixed_auto ("sig", exchange_sig),
GNUNET_JSON_spec_fixed_auto ("pub", exchange_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
TALER_JSON_spec_amount_nbo ("refund_fee", &rh->depconf.refund_fee),
GNUNET_JSON_spec_end ()
};
@ -148,12 +148,15 @@ handle_refund_finished (void *cls,
struct TALER_ExchangeSignatureP exchange_sig;
struct TALER_ExchangePublicKeyP *ep = NULL;
struct TALER_ExchangeSignatureP *es = NULL;
struct TALER_Amount ra;
const struct TALER_Amount *rf = NULL;
const json_t *j = response;
struct TALER_EXCHANGE_HttpResponse hr = {
.reply = j,
.http_status = (unsigned int) response_code
};
rh->job = NULL;
switch (response_code)
{
@ -175,6 +178,9 @@ handle_refund_finished (void *cls,
{
ep = &exchange_pub;
es = &exchange_sig;
TALER_amount_ntoh (&ra,
&rh->depconf.refund_fee);
rf = &ra;
}
break;
case MHD_HTTP_BAD_REQUEST:
@ -233,6 +239,7 @@ handle_refund_finished (void *cls,
}
rh->cb (rh->cb_cls,
&hr,
rf,
ep,
es);
TALER_EXCHANGE_refund_cancel (rh);
@ -256,7 +263,6 @@ handle_refund_finished (void *cls,
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
@ -272,7 +278,6 @@ handle_refund_finished (void *cls,
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
@ -294,14 +299,11 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
rr.rtransaction_id = GNUNET_htonll (rtransaction_id);
TALER_amount_hton (&rr.refund_amount,
amount);
TALER_amount_hton (&rr.refund_fee,
refund_fee);
GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
&rr,
&merchant_sig.eddsa_sig);
return TALER_EXCHANGE_refund2 (exchange,
amount,
refund_fee,
h_contract_terms,
coin_pub,
rtransaction_id,
@ -329,7 +331,6 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
* @param amount the amount to be refunded; must be larger than the refund fee
* (as that fee is still being subtracted), and smaller than the amount
* (with deposit fee) of the original deposit contribution of this coin
* @param refund_fee fee applicable to this coin for the refund
* @param h_contract_terms hash of the contact of the merchant with the customer that is being refunded
* @param coin_pub coins public key of the coin from the original deposit operation
* @param rtransaction_id transaction id for the transaction between merchant and customer (of refunding operation);
@ -346,7 +347,6 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle *exchange,
struct TALER_EXCHANGE_RefundHandle *
TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
const struct TALER_Amount *amount,
const struct TALER_Amount *refund_fee,
const struct GNUNET_HashCode *h_contract_terms,
const struct TALER_CoinSpendPublicKeyP *coin_pub,
uint64_t rtransaction_id,
@ -376,12 +376,11 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
"/coins/%s/refund",
pub_str);
}
refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
refund_obj = json_pack ("{s:o," /* amount */
" s:o," /* h_contract_terms */
" s:I," /* rtransaction id */
" s:o, s:o}", /* merchant_pub, merchant_sig */
"refund_amount", TALER_JSON_from_amount (amount),
"refund_fee", TALER_JSON_from_amount (refund_fee),
"h_contract_terms", GNUNET_JSON_from_data_auto (
h_contract_terms),
"rtransaction_id", (json_int_t) rtransaction_id,
@ -410,8 +409,6 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle *exchange,
rh->depconf.rtransaction_id = GNUNET_htonll (rtransaction_id);
TALER_amount_hton (&rh->depconf.refund_amount,
amount);
TALER_amount_hton (&rh->depconf.refund_fee,
refund_fee);
eh = TALER_EXCHANGE_curl_easy_get_ (rh->url);
if ( (NULL == eh) ||

View File

@ -85,24 +85,18 @@ check_transfers_get_response_ok (
const json_t *json)
{
json_t *details_j;
struct GNUNET_HashCode h_wire;
struct GNUNET_TIME_Absolute exec_time;
struct TALER_Amount total_amount;
struct TALER_EXCHANGE_TransferData td;
struct TALER_Amount total_expected;
struct TALER_Amount wire_fee;
struct TALER_MerchantPublicKeyP merchant_pub;
unsigned int num_details;
struct TALER_ExchangePublicKeyP exchange_pub;
struct TALER_ExchangeSignatureP exchange_sig;
struct GNUNET_JSON_Specification spec[] = {
TALER_JSON_spec_amount ("total", &total_amount),
TALER_JSON_spec_amount ("wire_fee", &wire_fee),
TALER_JSON_spec_amount ("total", &td.total_amount),
TALER_JSON_spec_amount ("wire_fee", &td.wire_fee),
GNUNET_JSON_spec_fixed_auto ("merchant_pub", &merchant_pub),
GNUNET_JSON_spec_fixed_auto ("h_wire", &h_wire),
GNUNET_JSON_spec_absolute_time ("execution_time", &exec_time),
GNUNET_JSON_spec_fixed_auto ("h_wire", &td.h_wire),
GNUNET_JSON_spec_absolute_time ("execution_time", &td.execution_time),
GNUNET_JSON_spec_json ("deposits", &details_j),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &exchange_pub),
GNUNET_JSON_spec_fixed_auto ("exchange_sig", &td.exchange_sig),
GNUNET_JSON_spec_fixed_auto ("exchange_pub", &td.exchange_pub),
GNUNET_JSON_spec_end ()
};
struct TALER_EXCHANGE_HttpResponse hr = {
@ -119,22 +113,32 @@ check_transfers_get_response_ok (
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
TALER_amount_get_zero (total_amount.currency,
TALER_amount_get_zero (td.total_amount.currency,
&total_expected))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
num_details = json_array_size (details_j);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (
TALER_EXCHANGE_get_keys (wdh->exchange),
&td.exchange_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
td.details_length = json_array_size (details_j);
{
struct TALER_TrackTransferDetails details[num_details];
unsigned int i;
struct GNUNET_HashContext *hash_context;
struct TALER_WireDepositDetailP dd;
struct TALER_WireDepositDataPS wdp;
struct TALER_TrackTransferDetails *details;
details = GNUNET_new_array (td.details_length,
struct TALER_TrackTransferDetails);
td.details = details;
hash_context = GNUNET_CRYPTO_hash_context_start ();
for (i = 0; i<num_details; i++)
for (unsigned int i = 0; i<td.details_length; i++)
{
struct TALER_TrackTransferDetails *detail = &details[i];
struct json_t *detail_j = json_array_get (details_j, i);
@ -147,25 +151,17 @@ check_transfers_get_response_ok (
GNUNET_JSON_spec_end ()
};
if (GNUNET_OK !=
GNUNET_JSON_parse (detail_j,
spec_detail,
NULL, NULL))
{
GNUNET_break_op (0);
GNUNET_CRYPTO_hash_context_abort (hash_context);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
/* build up big hash for signature checking later */
dd.h_contract_terms = detail->h_contract_terms;
dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
dd.coin_pub = detail->coin_pub;
TALER_amount_hton (&dd.deposit_value,
&detail->coin_value);
TALER_amount_hton (&dd.deposit_fee,
&detail->coin_fee);
if ( (0 >
if ( (GNUNET_OK !=
GNUNET_JSON_parse (detail_j,
spec_detail,
NULL, NULL)) ||
(GNUNET_OK !=
TALER_amount_cmp_currency (&total_expected,
&detail->coin_value)) ||
(GNUNET_OK !=
TALER_amount_cmp_currency (&total_expected,
&detail->coin_fee)) ||
(0 >
TALER_amount_add (&total_expected,
&total_expected,
&detail->coin_value)) ||
@ -177,71 +173,78 @@ check_transfers_get_response_ok (
GNUNET_break_op (0);
GNUNET_CRYPTO_hash_context_abort (hash_context);
GNUNET_JSON_parse_free (spec);
GNUNET_free (details);
return GNUNET_SYSERR;
}
GNUNET_CRYPTO_hash_context_read (
hash_context,
&dd,
sizeof (struct TALER_WireDepositDetailP));
/* build up big hash for signature checking later */
{
struct TALER_WireDepositDetailP dd;
dd.h_contract_terms = detail->h_contract_terms;
dd.execution_time = GNUNET_TIME_absolute_hton (td.execution_time);
dd.coin_pub = detail->coin_pub;
TALER_amount_hton (&dd.deposit_value,
&detail->coin_value);
TALER_amount_hton (&dd.deposit_fee,
&detail->coin_fee);
GNUNET_CRYPTO_hash_context_read (hash_context,
&dd,
sizeof (dd));
}
}
/* Check signature */
wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
TALER_amount_hton (&wdp.total,
&total_amount);
TALER_amount_hton (&wdp.wire_fee,
&wire_fee);
wdp.merchant_pub = merchant_pub;
wdp.h_wire = h_wire;
GNUNET_CRYPTO_hash_context_finish (hash_context,
&wdp.h_details);
if (GNUNET_OK !=
TALER_EXCHANGE_test_signing_key (TALER_EXCHANGE_get_keys (
wdh->exchange),
&exchange_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
}
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT,
&wdp,
&exchange_sig.eddsa_signature,
&exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
return GNUNET_SYSERR;
struct TALER_WireDepositDataPS wdp = {
.purpose.purpose = htonl (
TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT),
.purpose.size = htonl (sizeof (wdp)),
.merchant_pub = merchant_pub,
.h_wire = td.h_wire
};
TALER_amount_hton (&wdp.total,
&td.total_amount);
TALER_amount_hton (&wdp.wire_fee,
&td.wire_fee);
GNUNET_CRYPTO_hash_context_finish (hash_context,
&wdp.h_details);
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (
TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT,
&wdp,
&td.exchange_sig.eddsa_signature,
&td.exchange_pub.eddsa_pub))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
GNUNET_free (details);
return GNUNET_SYSERR;
}
}
if (0 >
TALER_amount_subtract (&total_expected,
&total_expected,
&wire_fee))
&td.wire_fee))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
GNUNET_free (details);
return GNUNET_SYSERR;
}
if (0 !=
TALER_amount_cmp (&total_expected,
&total_amount))
&td.total_amount))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
GNUNET_free (details);
return GNUNET_SYSERR;
}
wdh->cb (wdh->cb_cls,
&hr,
&exchange_pub,
&h_wire,
exec_time,
&total_amount,
&wire_fee,
num_details,
details);
&td);
GNUNET_free (details);
}
GNUNET_JSON_parse_free (spec);
TALER_EXCHANGE_transfers_get_cancel (wdh);
@ -322,12 +325,7 @@ handle_transfers_get_finished (void *cls,
}
wdh->cb (wdh->cb_cls,
&hr,
NULL,
NULL,
GNUNET_TIME_UNIT_ZERO_ABS,
NULL,
NULL,
0, NULL);
NULL);
TALER_EXCHANGE_transfers_get_cancel (wdh);
}

View File

@ -429,8 +429,6 @@ TALER_EXCHANGE_withdraw2 (
TALER_amount_hton (&req.amount_with_fee,
&wh->requested_amount);
TALER_amount_hton (&req.withdraw_fee,
&dk->fee_withdraw);
GNUNET_CRYPTO_hash (pd->coin_ev,
pd->coin_ev_size,
&req.h_coin_envelope);

View File

@ -1,6 +1,6 @@
/*
This file is part of TALER
Copyright (C) 2014--2019 Taler Systems SA
Copyright (C) 2014--2020 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@ -56,7 +56,7 @@
* (we could not even queue an error message,
* close HTTP session with MHD_NO)
*/
int
enum GNUNET_GenericReturnValue
TALER_MHD_parse_post_json (struct MHD_Connection *connection,
void **con_cls,
const char *upload_data,
@ -186,12 +186,12 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
int
enum GNUNET_GenericReturnValue
TALER_MHD_parse_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_JSON_Specification *spec)
{
int ret;
enum GNUNET_GenericReturnValue ret;
const char *error_json_name;
unsigned int error_line;
@ -220,6 +220,58 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
}
/**
* Parse JSON object that we (the server!) generated into components based on
* the given field specification. The difference to
* #TALER_MHD_parse_json_data() is that this function will fail
* with an HTTP failure of 500 (internal server error) in case
* parsing fails, instead of blaming it on the client with a
* 400 (#MHD_HTTP_BAD_REQUEST).
*
* @param connection the connection to send an error response to
* @param root the JSON node to start the navigation at.
* @param spec field specification for the parser
* @return
* #GNUNET_YES if navigation was successful (caller is responsible
* for freeing allocated variable-size data using
* GNUNET_JSON_parse_free() when done)
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
enum GNUNET_GenericReturnValue
TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_JSON_Specification *spec)
{
enum GNUNET_GenericReturnValue ret;
const char *error_json_name;
unsigned int error_line;
ret = GNUNET_JSON_parse (root,
spec,
&error_json_name,
&error_line);
if (GNUNET_SYSERR == ret)
{
if (NULL == error_json_name)
error_json_name = "<no field>";
ret = (MHD_YES ==
TALER_MHD_reply_json_pack (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
"{s:s, s:I, s:s, s:I}",
"hint", "JSON parse error",
"code",
(json_int_t)
TALER_EC_INTERNAL_INVARIANT_FAILURE,
"field", error_json_name,
"line", (json_int_t) error_line))
? GNUNET_NO : GNUNET_SYSERR;
return ret;
}
return GNUNET_YES;
}
/**
* Parse JSON array into components based on the given field
* specification. Generates error response on parse errors.
@ -235,13 +287,13 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
* #GNUNET_NO if json is malformed, error response was generated
* #GNUNET_SYSERR on internal error
*/
int
enum GNUNET_GenericReturnValue
TALER_MHD_parse_json_array (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_JSON_Specification *spec,
...)
{
int ret;
enum GNUNET_GenericReturnValue ret;
const char *error_json_name;
unsigned int error_line;
va_list ap;

40
src/sq/Makefile.am Normal file
View File

@ -0,0 +1,40 @@
# This Makefile.am is in the public domain
AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(SQLITE_CPPFLAGS)
if USE_COVERAGE
AM_CFLAGS = --coverage -O0
XLIB = -lgcov
endif
lib_LTLIBRARIES = \
libtalersq.la
libtalersq_la_SOURCES = \
sq_query_helper.c \
sq_result_helper.c
libtalersq_la_LIBADD = \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil -ljansson \
-lsqlite3 $(XLIB)
libtalersq_la_LDFLAGS = \
$(SQLITE_LDFLAGS) \
-version-info 0:0:0 \
-export-dynamic -no-undefined
TESTS = \
test_sq
check_PROGRAMS= \
test_sq
test_sq_SOURCES = \
test_sq.c
test_sq_LDADD = \
libtalersq.la \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetsq \
-lgnunetutil \
-ljansson \
-lsqlite3 $(XLIB)

288
src/sq/sq_query_helper.c Normal file
View File

@ -0,0 +1,288 @@
/*
This file is part of TALER
Copyright (C) 2020 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 sq/sq_query_helper.c
* @brief helper functions for Taler-specific SQLite3 interactions
* @author Jonathan Buchanan
*/
#include "platform.h"
#include <sqlite3.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_sq_lib.h>
#include "taler_sq_lib.h"
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument, here a `struct TALER_Amount`
* @param data_len number of bytes in @a data (if applicable)
* @param stmt sqlite statement to parameters for
* @param off offset of the argument to bind in @a stmt, numbered from 1,
* so immediately suitable for passing to `sqlite3_bind`-functions.
* @return #GNUNET_SYSERR on error, #GNUNET_OK on success
*/
static int
qconv_amount (void *cls,
const void *data,
size_t data_len,
sqlite3_stmt *stmt,
unsigned int off)
{
const struct TALER_Amount *amount = data;
(void) cls;
GNUNET_assert (sizeof (struct TALER_Amount) == data_len);
if (SQLITE_OK != sqlite3_bind_int64 (stmt,
(int) off,
(sqlite3_int64) amount->value))
return GNUNET_SYSERR;
if (SQLITE_OK != sqlite3_bind_int64 (stmt,
(int) off + 1,
(sqlite3_int64) amount->fraction))
return GNUNET_SYSERR;
return GNUNET_OK;
}
/**
* Generate query parameter for a currency, consisting of the
* components "value", "fraction" in this order. The
* types must be a 64-bit integer and a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_amount (const struct TALER_Amount *x)
{
struct GNUNET_SQ_QueryParam res =
{ &qconv_amount, NULL, x, sizeof (*x), 2 };
return res;
}
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument, here a `struct TALER_AmountNBO`
* @param data_len number of bytes in @a data (if applicable)
* @param stmt sqlite statement to parameters for
* @param off offset of the argument to bind in @a stmt, numbered from 1,
* so immediately suitable for passing to `sqlite3_bind`-functions.
* @return #GNUNET_SYSERR on error, #GNUNET_OK on success
*/
static int
qconv_amount_nbo (void *cls,
const void *data,
size_t data_len,
sqlite3_stmt *stmt,
unsigned int off)
{
const struct TALER_AmountNBO *amount = data;
struct TALER_Amount amount_hbo;
(void) cls;
TALER_amount_ntoh (&amount_hbo,
amount);
return qconv_amount (cls,
&amount_hbo,
sizeof (struct TALER_Amount),
stmt,
off);
}
/**
* Generate query parameter for a currency, consisting of the
* components "value", "fraction" in this order. The
* types must be a 64-bit integer and a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_amount_nbo (const struct TALER_AmountNBO *x)
{
struct GNUNET_SQ_QueryParam res =
{ &qconv_amount_nbo, NULL, x, sizeof (*x), 2 };
return res;
}
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument, here a `struct TALER_Amount`
* @param data_len number of bytes in @a data (if applicable)
* @param stmt sqlite statement to parameters for
* @param off offset of the argument to bind in @a stmt, numbered from 1,
* so immediately suitable for passing to `sqlite3_bind`-functions.
* @return #GNUNET_SYSERR on error, #GNUNET_OK on success
*/
static int
qconv_json (void *cls,
const void *data,
size_t data_len,
sqlite3_stmt *stmt,
unsigned int off)
{
const json_t *json = data;
char *str;
(void) cls;
(void) data_len;
str = json_dumps (json, JSON_COMPACT);
if (NULL == str)
return GNUNET_SYSERR;
if (SQLITE_OK != sqlite3_bind_text (stmt,
(int) off,
str,
strlen (str) + 1,
SQLITE_TRANSIENT))
return GNUNET_SYSERR;
GNUNET_free (str);
return GNUNET_OK;
}
/**
* Generate query parameter for a JSON object (stored as a string
* in the DB). Note that @a x must really be a JSON object or array,
* passing just a value (string, integer) is not supported and will
* result in an abort.
*
* @param x pointer to the json object to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_json (const json_t *x)
{
struct GNUNET_SQ_QueryParam res =
{ &qconv_json, NULL, x, sizeof (*x), 1 };
return res;
}
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument, here a `struct TALER_Amount`
* @param data_len number of bytes in @a data (if applicable)
* @param stmt sqlite statement to parameters for
* @param off offset of the argument to bind in @a stmt, numbered from 1,
* so immediately suitable for passing to `sqlite3_bind`-functions.
* @return #GNUNET_SYSERR on error, #GNUNET_OK on success
*/
static int
qconv_round_time (void *cls,
const void *data,
size_t data_len,
sqlite3_stmt *stmt,
unsigned int off)
{
const struct GNUNET_TIME_Absolute *at = data;
struct GNUNET_TIME_Absolute tmp;
(void) cls;
GNUNET_assert (sizeof (struct GNUNET_TIME_AbsoluteNBO) == data_len);
GNUNET_break (NULL == cls);
tmp = *at;
GNUNET_assert (GNUNET_OK ==
GNUNET_TIME_round_abs (&tmp));
if (SQLITE_OK != sqlite3_bind_int64 (stmt,
(int) off,
(sqlite3_int64) at->abs_value_us))
return GNUNET_SYSERR;
return GNUNET_OK;
}
/**
* Generate query parameter for an absolute time value.
* In contrast to
* #GNUNET_SQ_query_param_absolute_time(), this function
* will abort (!) if the time given is not rounded!
* The database must store a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
{
struct GNUNET_SQ_QueryParam res =
{ &qconv_round_time, NULL, x, sizeof (*x), 1 };
return res;
}
/**
* Function called to convert input argument into SQL parameters.
*
* @param cls closure
* @param data pointer to input argument, here a `struct TALER_Amount`
* @param data_len number of bytes in @a data (if applicable)
* @param stmt sqlite statement to parameters for
* @param off offset of the argument to bind in @a stmt, numbered from 1,
* so immediately suitable for passing to `sqlite3_bind`-functions.
* @return #GNUNET_SYSERR on error, #GNUNET_OK on success
*/
static int
qconv_round_time_abs (void *cls,
const void *data,
size_t data_len,
sqlite3_stmt *stmt,
unsigned int off)
{
const struct GNUNET_TIME_AbsoluteNBO *at = data;
struct GNUNET_TIME_Absolute tmp;
(void) cls;
GNUNET_assert (sizeof (struct GNUNET_TIME_AbsoluteNBO) == data_len);
GNUNET_break (NULL == cls);
tmp = GNUNET_TIME_absolute_ntoh (*at);
GNUNET_assert (GNUNET_OK ==
GNUNET_TIME_round_abs (&tmp));
if (SQLITE_OK != sqlite3_bind_int64 (stmt,
(int) off,
(sqlite3_int64) tmp.abs_value_us))
return GNUNET_SYSERR;
return GNUNET_OK;
}
/**
* Generate query parameter for an absolute time value.
* In contrast to
* #GNUNET_SQ_query_param_absolute_time(), this function
* will abort (!) if the time given is not rounded!
* The database must store a 64-bit integer.
*
* @param x pointer to the query parameter to pass
*/
struct GNUNET_SQ_QueryParam
TALER_SQ_query_param_absolute_time_nbo (const struct
GNUNET_TIME_AbsoluteNBO *x)
{
struct GNUNET_SQ_QueryParam res =
{ &qconv_round_time_abs, NULL, x, sizeof (*x), 1 };
return res;
}
/* end of sq/sq_query_helper.c */

381
src/sq/sq_result_helper.c Normal file
View File

@ -0,0 +1,381 @@
/*
This file is part of TALER
Copyright (C) 2020 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 sq/sq_result_helper.c
* @brief functions to initialize parameter arrays
* @author Jonathan Buchanan
*/
#include "platform.h"
#include <sqlite3.h>
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_sq_lib.h>
#include "taler_sq_lib.h"
#include "taler_amount_lib.h"
/**
* Extract amount data from a SQLite database
*
* @param cls closure, a `const char *` giving the currency
* @param result where to extract data from
* @param column column to extract data from
* @param[in,out] dst_size where to store size of result, may be NULL
* @param[out] dst where to store the result
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
*/
static int
extract_amount (void *cls,
sqlite3_stmt *result,
unsigned int column,
size_t *dst_size,
void *dst)
{
struct TALER_Amount *amount = dst;
const char *currency = cls;
if ((sizeof (struct TALER_Amount) != *dst_size) ||
(SQLITE_INTEGER != sqlite3_column_type (result,
(int) column)) ||
(SQLITE_INTEGER != sqlite3_column_type (result,
(int) column + 1)))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_strlcpy (amount->currency,
currency,
TALER_CURRENCY_LEN);
amount->value = (uint64_t) sqlite3_column_int64 (result,
(int) column);
uint64_t frac = (uint64_t) sqlite3_column_int64 (result,
(int) column + 1);
amount->fraction = (uint32_t) frac;
return GNUNET_YES;
}
/**
* Currency amount expected.
*
* @param currency the currency to use for @a amount
* @param[out] amount where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_amount (const char *currency,
struct TALER_Amount *amount)
{
struct GNUNET_SQ_ResultSpec res = {
.conv = &extract_amount,
.cls = (void *) currency,
.dst = (void *) amount,
.dst_size = sizeof (struct TALER_Amount),
.num_params = 2
};
return res;
}
/**
* Extract amount data from a SQLite database
*
* @param cls closure, a `const char *` giving the currency
* @param result where to extract data from
* @param column column to extract data from
* @param[in,out] dst_size where to store size of result, may be NULL
* @param[out] dst where to store the result
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
*/
static int
extract_amount_nbo (void *cls,
sqlite3_stmt *result,
unsigned int column,
size_t *dst_size,
void *dst)
{
struct TALER_AmountNBO *amount = dst;
struct TALER_Amount amount_hbo;
size_t amount_hbo_size = sizeof (struct TALER_Amount);
if (GNUNET_YES != extract_amount (cls,
result,
column,
&amount_hbo_size,
&amount_hbo))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
TALER_amount_hton (amount,
&amount_hbo);
return GNUNET_YES;
}
/**
* Currency amount expected.
*
* @param currency the currency to use for @a amount
* @param[out] amount where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_amount_nbo (const char *currency,
struct TALER_AmountNBO *amount)
{
struct GNUNET_SQ_ResultSpec res = {
.conv = &extract_amount_nbo,
.cls = (void *) currency,
.dst = (void *) amount,
.dst_size = sizeof (struct TALER_AmountNBO),
.num_params = 2
};
return res;
}
/**
* Extract amount data from a SQLite database
*
* @param cls closure
* @param result where to extract data from
* @param column column to extract data from
* @param[in,out] dst_size where to store size of result, may be NULL
* @param[out] dst where to store the result
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
*/
static int
extract_json (void *cls,
sqlite3_stmt *result,
unsigned int column,
size_t *dst_size,
void *dst)
{
json_t **j_dst = dst;
const char *res;
json_error_t json_error;
size_t slen;
(void) cls;
(void) dst_size;
if (SQLITE_TEXT != sqlite3_column_type (result,
column))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
res = (const char *) sqlite3_column_text (result,
column);
slen = strlen (res);
*j_dst = json_loadb (res,
slen,
JSON_REJECT_DUPLICATES,
&json_error);
if (NULL == *j_dst)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse JSON result for column %d: %s (%s)\n",
column,
json_error.text,
json_error.source);
return GNUNET_SYSERR;
}
return GNUNET_OK;
}
/**
* Function called to clean up memory allocated
* by a #GNUNET_SQ_ResultConverter.
*
* @param cls closure
*/
static void
clean_json (void *cls)
{
json_t **dst = cls;
(void) cls;
if (NULL != *dst)
{
json_decref (*dst);
*dst = NULL;
}
}
/**
* json_t expected.
*
* @param[out] jp where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_json (json_t **jp)
{
struct GNUNET_SQ_ResultSpec res = {
.conv = &extract_json,
.cleaner = &clean_json,
.dst = (void *) jp,
.cls = (void *) jp,
.num_params = 1
};
return res;
}
/**
* Extract amount data from a SQLite database
*
* @param cls closure
* @param result where to extract data from
* @param column column to extract data from
* @param[in,out] dst_size where to store size of result, may be NULL
* @param[out] dst where to store the result
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
*/
static int
extract_round_time (void *cls,
sqlite3_stmt *result,
unsigned int column,
size_t *dst_size,
void *dst)
{
struct GNUNET_TIME_Absolute *udst = dst;
struct GNUNET_TIME_Absolute tmp;
(void) cls;
if (SQLITE_INTEGER != sqlite3_column_type (result,
(int) column))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_assert (NULL != dst);
if (sizeof (struct GNUNET_TIME_Absolute) != *dst_size)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
tmp.abs_value_us = sqlite3_column_int64 (result,
(int) column);
GNUNET_break (GNUNET_OK ==
GNUNET_TIME_round_abs (&tmp));
*udst = tmp;
return GNUNET_OK;
}
/**
* Rounded absolute time expected.
* In contrast to #GNUNET_SQ_query_param_absolute_time_nbo(),
* this function ensures that the result is rounded and can
* be converted to JSON.
*
* @param[out] at where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_absolute_time (struct GNUNET_TIME_Absolute *at)
{
struct GNUNET_SQ_ResultSpec res = {
.conv = &extract_round_time,
.dst = (void *) at,
.dst_size = sizeof (struct GNUNET_TIME_Absolute),
.num_params = 1
};
return res;
}
/**
* Extract amount data from a SQLite database
*
* @param cls closure
* @param result where to extract data from
* @param column column to extract data from
* @param[in,out] dst_size where to store size of result, may be NULL
* @param[out] dst where to store the result
* @return
* #GNUNET_YES if all results could be extracted
* #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
*/
static int
extract_round_time_nbo (void *cls,
sqlite3_stmt *result,
unsigned int column,
size_t *dst_size,
void *dst)
{
struct GNUNET_TIME_AbsoluteNBO *udst = dst;
struct GNUNET_TIME_Absolute tmp;
(void) cls;
if (SQLITE_INTEGER != sqlite3_column_type (result,
(int) column))
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
GNUNET_assert (NULL != dst);
if (sizeof (struct GNUNET_TIME_AbsoluteNBO) != *dst_size)
{
GNUNET_break (0);
return GNUNET_SYSERR;
}
tmp.abs_value_us = sqlite3_column_int64 (result,
(int) column);
GNUNET_break (GNUNET_OK ==
GNUNET_TIME_round_abs (&tmp));
*udst = GNUNET_TIME_absolute_hton (tmp);
return GNUNET_OK;
}
/**
* Rounded absolute time expected.
* In contrast to #GNUNET_SQ_result_spec_absolute_time_nbo(),
* this function ensures that the result is rounded and can
* be converted to JSON.
*
* @param[out] at where to store the result
* @return array entry for the result specification to use
*/
struct GNUNET_SQ_ResultSpec
TALER_SQ_result_spec_absolute_time_nbo (struct GNUNET_TIME_AbsoluteNBO *at)
{
struct GNUNET_SQ_ResultSpec res = {
.conv = &extract_round_time_nbo,
.dst = (void *) at,
.dst_size = sizeof (struct GNUNET_TIME_AbsoluteNBO),
.num_params = 1
};
return res;
}
/* end of sq/sq_result_helper.c */

227
src/sq/test_sq.c Normal file
View File

@ -0,0 +1,227 @@
/*
This file is part of TALER
Copyright (C) 2020 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 sq/test_sq.c
* @brief Tests for SQLite3 convenience API
* @author Jonathan Buchanan
*/
#include "platform.h"
#include "taler_sq_lib.h"
/**
* Run actual test queries.
*
* @return 0 on success
*/
static int
run_queries (sqlite3 *db)
{
struct TALER_Amount hamount;
struct TALER_AmountNBO namount;
json_t *json;
struct GNUNET_TIME_Absolute htime = GNUNET_TIME_absolute_get ();
struct GNUNET_TIME_AbsoluteNBO ntime;
sqlite3_stmt *test_insert;
sqlite3_stmt *test_select;
struct GNUNET_SQ_PrepareStatement ps[] = {
GNUNET_SQ_make_prepare ("INSERT INTO test_sq ("
" hamount_val"
",hamount_frac"
",namount_val"
",namount_frac"
",json"
",htime"
",ntime"
") VALUES "
"($1, $2, $3, $4, $5, $6, $7)",
&test_insert),
GNUNET_SQ_make_prepare ("SELECT"
" hamount_val"
",hamount_frac"
",namount_val"
",namount_frac"
",json"
",htime"
",ntime"
" FROM test_sq",
&test_select),
GNUNET_SQ_PREPARE_END
};
int ret = 0;
GNUNET_assert (GNUNET_OK ==
TALER_string_to_amount ("EUR:1.23",
&hamount));
TALER_amount_hton (&namount,
&hamount);
json = json_object ();
json_object_set_new (json, "foo", json_integer (42));
GNUNET_assert (NULL != json);
GNUNET_TIME_round_abs (&htime);
ntime = GNUNET_TIME_absolute_hton (htime);
GNUNET_assert (GNUNET_OK == GNUNET_SQ_prepare (db,
ps));
{
struct GNUNET_SQ_QueryParam params_insert[] = {
TALER_SQ_query_param_amount (&hamount),
TALER_SQ_query_param_amount_nbo (&namount),
TALER_SQ_query_param_json (json),
TALER_SQ_query_param_absolute_time (&htime),
TALER_SQ_query_param_absolute_time_nbo (&ntime),
GNUNET_SQ_query_param_end
};
GNUNET_SQ_reset (db,
test_insert);
GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_insert,
params_insert));
GNUNET_assert (SQLITE_DONE == sqlite3_step (test_insert));
sqlite3_finalize (test_insert);
}
{
struct TALER_Amount result_amount;
struct TALER_AmountNBO nresult_amount;
struct TALER_Amount nresult_amount_converted;
json_t *result_json;
struct GNUNET_TIME_Absolute hresult_time;
struct GNUNET_TIME_AbsoluteNBO nresult_time;
struct GNUNET_SQ_QueryParam params_select[] = {
GNUNET_SQ_query_param_end
};
struct GNUNET_SQ_ResultSpec results_select[] = {
TALER_SQ_result_spec_amount ("EUR",
&result_amount),
TALER_SQ_result_spec_amount_nbo ("EUR",
&nresult_amount),
TALER_SQ_result_spec_json (&result_json),
TALER_SQ_result_spec_absolute_time (&hresult_time),
TALER_SQ_result_spec_absolute_time_nbo (&nresult_time),
GNUNET_SQ_result_spec_end
};
GNUNET_SQ_reset (db,
test_select);
GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_select,
params_select));
GNUNET_assert (SQLITE_ROW == sqlite3_step (test_select));
GNUNET_assert (GNUNET_OK == GNUNET_SQ_extract_result (test_select,
results_select));
TALER_amount_ntoh (&nresult_amount_converted,
&nresult_amount);
if ((GNUNET_OK != TALER_amount_cmp_currency (&hamount,
&result_amount)) ||
(0 != TALER_amount_cmp (&hamount,
&result_amount)) ||
(GNUNET_OK != TALER_amount_cmp_currency (&hamount,
&nresult_amount_converted)) ||
(0 != TALER_amount_cmp (&hamount,
&nresult_amount_converted)) ||
(1 != json_equal (json,
result_json)) ||
(htime.abs_value_us != hresult_time.abs_value_us) ||
(ntime.abs_value_us__ != nresult_time.abs_value_us__))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Result from database doesn't match input\n");
ret = 1;
}
GNUNET_SQ_cleanup_result (results_select);
sqlite3_finalize (test_select);
}
json_decref (json);
return ret;
}
int
main (int argc,
const char *const argv[])
{
struct GNUNET_SQ_ExecuteStatement es[] = {
GNUNET_SQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_sq ("
" hamount_val INT8 NOT NULL"
",hamount_frac INT8 NOT NULL"
",namount_val INT8 NOT NULL"
",namount_frac INT8 NOT NULL"
",json VARCHAR NOT NULL"
",htime INT8 NOT NULL"
",ntime INT8 NOT NULL"
")"),
GNUNET_SQ_EXECUTE_STATEMENT_END
};
sqlite3 *db;
int ret;
GNUNET_log_setup ("test-pq",
"WARNING",
NULL);
if (SQLITE_OK != sqlite3_open ("talercheck.db",
&db))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to open SQLite3 database\n");
return 77;
}
if (GNUNET_OK != GNUNET_SQ_exec_statements (db,
es))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to create new table\n");
if ((SQLITE_OK != sqlite3_close (db)) ||
(0 != unlink ("talercheck.db")))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to close db or unlink\n");
}
return 1;
}
ret = run_queries (db);
if (SQLITE_OK !=
sqlite3_exec (db,
"DROP TABLE test_sq",
NULL, NULL, NULL))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to drop table\n");
ret = 1;
}
if (SQLITE_OK != sqlite3_close (db))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to close database\n");
ret = 1;
}
if (0 != unlink ("talercheck.db"))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to unlink test database file\n");
ret = 1;
}
return ret;
}
/* end of sq/test_sq.c */

View File

@ -61,6 +61,7 @@ libtalertesting_la_SOURCES = \
testing_api_cmd_refund.c \
testing_api_cmd_refresh.c \
testing_api_cmd_revoke.c \
testing_api_cmd_rewind.c \
testing_api_cmd_serialize_keys.c \
testing_api_cmd_signal.c \
testing_api_cmd_sleep.c \

View File

@ -353,7 +353,6 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-ok",
MHD_HTTP_OK,
"EUR:5",
"EUR:0.01",
"deposit-refund-1"),
/**
* Spend 4.99 EUR of the refunded 4.99 EUR coin (1ct gone

View File

@ -346,6 +346,8 @@ run (void *cls,
NULL,
0,
MHD_HTTP_NOT_FOUND),
TALER_TESTING_cmd_sleep ("sleep-before-aggregator",
1),
/* Run transfers. Note that _actual_ aggregation will NOT
* happen here, as each deposit operation is run with a
* fresh merchant public key, so the aggregator will treat
@ -528,18 +530,15 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-ok",
MHD_HTTP_OK,
"EUR:5",
"EUR:0.01",
"deposit-refund-1"),
TALER_TESTING_cmd_refund ("refund-ok-double",
MHD_HTTP_OK,
"EUR:5",
"EUR:0.01",
"deposit-refund-1"),
/* Previous /refund(s) had id == 0. */
TALER_TESTING_cmd_refund_with_id ("refund-conflicting",
MHD_HTTP_CONFLICT,
"EUR:5",
"EUR:0.01",
"deposit-refund-1",
1),
/**
@ -573,7 +572,6 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-fail",
MHD_HTTP_GONE,
"EUR:4.99",
"EUR:0.01",
"deposit-refund-2"),
TALER_TESTING_cmd_check_bank_empty ("check-empty-after-refund"),
/**
@ -607,7 +605,6 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-ok-fast",
MHD_HTTP_OK,
"EUR:5",
"EUR:0.01",
"deposit-refund-1b"),
/**
* Run transfers. This will do the transfer as refund deadline

View File

@ -173,12 +173,6 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-currency-mismatch",
MHD_HTTP_BAD_REQUEST,
"USD:5",
"USD:0.01",
"deposit-refund-1"),
TALER_TESTING_cmd_refund ("refund-fee-above-amount",
MHD_HTTP_BAD_REQUEST,
"EUR:5",
"EUR:10",
"deposit-refund-1"),
TALER_TESTING_cmd_flip_upload ("flip-upload",
CONFIG_FILE,
@ -186,7 +180,6 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-bad-sig",
MHD_HTTP_FORBIDDEN,
"EUR:5",
"EUR:0.01",
"deposit-refund-1"),
/* This next deposit CMD is only used to provide a
@ -207,17 +200,10 @@ run (void *cls,
TALER_TESTING_cmd_refund ("refund-deposit-not-found",
MHD_HTTP_NOT_FOUND,
"EUR:5",
"EUR:0.01",
"deposit-refund-to-fail"),
TALER_TESTING_cmd_refund ("refund-insufficient-funds",
MHD_HTTP_PRECONDITION_FAILED,
"EUR:50",
"EUR:0.01",
"deposit-refund-1"),
TALER_TESTING_cmd_refund ("refund-fee-too-low",
MHD_HTTP_BAD_REQUEST,
"EUR:5",
"EUR:0.000001",
"deposit-refund-1"),
TALER_TESTING_cmd_end ()
};

View File

@ -108,6 +108,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -126,6 +127,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -134,6 +136,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -153,6 +156,7 @@ run (void *cls,
&dbc,
"bob",
"4",
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -160,6 +164,7 @@ run (void *cls,
&dbc,
"bob",
"5",
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -167,6 +172,7 @@ run (void *cls,
&dbc,
"alice",
"4",
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:1",
"EUR:0.1"),
@ -195,6 +201,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -204,6 +211,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -229,6 +237,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
10),
@ -239,6 +248,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -263,6 +273,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.102",
"EUR:0.1"),
@ -274,6 +285,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.102",
"EUR:0.1"),
@ -281,6 +293,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.102",
"EUR:0.1"),
@ -292,6 +305,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.102",
"EUR:0.1"),
@ -303,6 +317,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.112",
"EUR:0.1"),
@ -319,6 +334,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.109",
"EUR:0.1"),
@ -330,6 +346,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.119",
"EUR:0.1"),
@ -346,6 +363,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.122",
"EUR:0.1"),
@ -362,6 +380,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -375,6 +394,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -390,6 +410,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.122",
"EUR:0.1"),
@ -406,6 +427,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -419,6 +441,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_relative_multiply
(GNUNET_TIME_UNIT_SECONDS,
5),
@ -434,6 +457,7 @@ run (void *cls,
&dbc,
"bob",
USER42_ACCOUNT,
GNUNET_TIME_absolute_get (),
GNUNET_TIME_UNIT_ZERO,
"EUR:0.112",
"EUR:0.1"),

View File

@ -203,6 +203,7 @@ deposit_confirmation_run (void *cls,
const struct TALER_TESTING_Command *deposit_cmd;
struct GNUNET_HashCode h_wire;
struct GNUNET_HashCode h_contract_terms;
const struct GNUNET_TIME_Absolute *exchange_timestamp = NULL;
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_TIME_Absolute refund_deadline;
struct TALER_Amount amount_without_fee;
@ -238,6 +239,11 @@ deposit_confirmation_run (void *cls,
TALER_TESTING_get_trait_exchange_sig (deposit_cmd,
dcs->coin_index,
&exchange_sig));
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_absolute_time (deposit_cmd,
dcs->coin_index,
&exchange_timestamp));
GNUNET_assert (NULL != exchange_timestamp);
keys = TALER_EXCHANGE_get_keys (dcs->is->exchange);
GNUNET_assert (NULL != keys);
spk = TALER_EXCHANGE_get_signing_key_info (keys,
@ -309,7 +315,7 @@ deposit_confirmation_run (void *cls,
dcs->dc = TALER_AUDITOR_deposit_confirmation (dcs->auditor,
&h_wire,
&h_contract_terms,
timestamp,
*exchange_timestamp,
refund_deadline,
&amount_without_fee,
&coin_pub,

View File

@ -81,6 +81,11 @@ struct AdminAddIncomingState
*/
struct TALER_ReservePrivateKeyP reserve_priv;
/**
* Whether we know the private key or not.
*/
bool reserve_priv_known;
/**
* Reserve public key matching @e reserve_priv.
*/
@ -271,6 +276,7 @@ admin_add_incoming_run (void *cls,
struct TALER_TESTING_Interpreter *is)
{
struct AdminAddIncomingState *fts = cls;
bool have_public = false;
(void) cmd;
/* Use reserve public key as subject */
@ -278,6 +284,7 @@ admin_add_incoming_run (void *cls,
{
const struct TALER_TESTING_Command *ref;
const struct TALER_ReservePrivateKeyP *reserve_priv;
const struct TALER_ReservePublicKeyP *reserve_pub;
ref = TALER_TESTING_interpreter_lookup_command
(is, fts->reserve_reference);
@ -292,11 +299,23 @@ admin_add_incoming_run (void *cls,
0,
&reserve_priv))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
if (GNUNET_OK != TALER_TESTING_get_trait_reserve_pub (ref,
0,
&reserve_pub))
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
have_public = true;
fts->reserve_pub.eddsa_pub = reserve_pub->eddsa_pub;
fts->reserve_priv_known = false;
}
else
{
fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv;
fts->reserve_priv_known = true;
}
fts->reserve_priv.eddsa_priv = reserve_priv->eddsa_priv;
}
else
{
@ -349,6 +368,7 @@ admin_add_incoming_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
fts->reserve_priv_known = true;
GNUNET_free (keys);
GNUNET_free (section);
GNUNET_CONFIGURATION_destroy (cfg);
@ -358,10 +378,12 @@ admin_add_incoming_run (void *cls,
/* No referenced reserve, no instance to take priv
* from, no explicit subject given: create new key! */
GNUNET_CRYPTO_eddsa_key_create (&fts->reserve_priv.eddsa_priv);
fts->reserve_priv_known = true;
}
}
GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
&fts->reserve_pub.eddsa_pub);
if (! have_public)
GNUNET_CRYPTO_eddsa_key_get_public (&fts->reserve_priv.eddsa_priv,
&fts->reserve_pub.eddsa_pub);
fts->reserve_history.type = TALER_EXCHANGE_RTT_CREDIT;
fts->reserve_history.amount = fts->amount;
fts->reserve_history.details.in_details.sender_url
@ -432,30 +454,58 @@ admin_add_incoming_traits (void *cls,
unsigned int index)
{
struct AdminAddIncomingState *fts = cls;
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_bank_row (&fts->serial_id),
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT,
fts->payto_debit_account),
/* Used as a marker, content does not matter */
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT,
"payto://void/the-exchange"),
TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL,
fts->exchange_credit_url),
TALER_TESTING_make_trait_amount_obj (0, &fts->amount),
TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp),
TALER_TESTING_make_trait_reserve_priv (0,
&fts->reserve_priv),
TALER_TESTING_make_trait_reserve_pub (0,
&fts->reserve_pub),
TALER_TESTING_make_trait_reserve_history (0,
&fts->reserve_history),
TALER_TESTING_trait_end ()
};
if (fts->reserve_priv_known)
{
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_bank_row (&fts->serial_id),
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT,
fts->payto_debit_account),
/* Used as a marker, content does not matter */
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT,
"payto://void/the-exchange"),
TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL,
fts->exchange_credit_url),
TALER_TESTING_make_trait_amount_obj (0, &fts->amount),
TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp),
TALER_TESTING_make_trait_reserve_priv (0,
&fts->reserve_priv),
TALER_TESTING_make_trait_reserve_pub (0,
&fts->reserve_pub),
TALER_TESTING_make_trait_reserve_history (0,
&fts->reserve_history),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
else
{
struct TALER_TESTING_Trait traits[] = {
TALER_TESTING_make_trait_bank_row (&fts->serial_id),
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_DEBIT,
fts->payto_debit_account),
/* Used as a marker, content does not matter */
TALER_TESTING_make_trait_payto (TALER_TESTING_PT_CREDIT,
"payto://void/the-exchange"),
TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BANK_ACCOUNT_URL,
fts->exchange_credit_url),
TALER_TESTING_make_trait_amount_obj (0, &fts->amount),
TALER_TESTING_make_trait_absolute_time (0, &fts->timestamp),
TALER_TESTING_make_trait_reserve_pub (0,
&fts->reserve_pub),
TALER_TESTING_make_trait_reserve_history (0,
&fts->reserve_history),
TALER_TESTING_trait_end ()
};
return TALER_TESTING_get_trait (traits,
ret,
trait,
index);
}
}
@ -548,7 +598,7 @@ TALER_TESTING_cmd_admin_add_incoming (const char *label,
* @param payto_debit_account which account sends money
* @param auth authentication data
* @param ref reference to a command that can offer a reserve
* private key.
* private key or public key.
* @return the command.
*/
struct TALER_TESTING_Command

View File

@ -228,5 +228,27 @@ TALER_TESTING_cmd_batch_get_current (const struct TALER_TESTING_Command *cmd)
{
struct BatchState *bs = cmd->cls;
GNUNET_assert (cmd->run == &batch_run);
return &bs->batch[bs->batch_ip];
}
/**
* Set what command the batch should be at.
*
* @param cmd current batch command
* @param new_ip where to move the IP
*/
void
TALER_TESTING_cmd_batch_set_current (const struct TALER_TESTING_Command *cmd,
unsigned int new_ip)
{
struct BatchState *bs = cmd->cls;
/* sanity checks */
GNUNET_assert (cmd->run == &batch_run);
for (unsigned int i = 0; i < new_ip; i++)
GNUNET_assert (NULL != bs->batch[i].label);
/* actual logic */
bs->batch_ip = new_ip;
}

View File

@ -52,6 +52,11 @@ struct DepositState
*/
struct TALER_Amount amount;
/**
* Deposit fee.
*/
struct TALER_Amount deposit_fee;
/**
* Reference to any command that is able to provide a coin.
*/
@ -91,9 +96,9 @@ struct DepositState
struct TALER_EXCHANGE_DepositHandle *dh;
/**
* Timestamp of the /deposit operation.
* Timestamp of the /deposit operation in the wallet (contract signing time).
*/
struct GNUNET_TIME_Absolute timestamp;
struct GNUNET_TIME_Absolute wallet_timestamp;
/**
* Interpreter state.
@ -126,6 +131,11 @@ struct DepositState
*/
int deposit_succeeded;
/**
* When did the exchange receive the deposit?
*/
struct GNUNET_TIME_Absolute exchange_timestamp;
/**
* Signing key used by the exchange to sign the
* deposit confirmation.
@ -198,6 +208,7 @@ do_retry (void *cls)
*
* @param cls closure.
* @param hr HTTP response details
* @param exchange_timestamp when did the exchange receive the deposit permission
* @param exchange_sig signature provided by the exchange
* (NULL on errors)
* @param exchange_pub public key of the exchange,
@ -206,6 +217,7 @@ do_retry (void *cls)
static void
deposit_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct GNUNET_TIME_Absolute exchange_timestamp,
const struct TALER_ExchangeSignatureP *exchange_sig,
const struct TALER_ExchangePublicKeyP *exchange_pub)
{
@ -254,6 +266,7 @@ deposit_cb (void *cls,
if (MHD_HTTP_OK == hr->http_status)
{
ds->deposit_succeeded = GNUNET_YES;
ds->exchange_timestamp = exchange_timestamp;
ds->exchange_pub = *exchange_pub;
ds->exchange_sig = *exchange_sig;
}
@ -305,7 +318,7 @@ deposit_run (void *cls,
ds->coin_index = ods->coin_index;
ds->wire_details = json_incref (ods->wire_details);
ds->contract_terms = json_incref (ods->contract_terms);
ds->timestamp = ods->timestamp;
ds->wallet_timestamp = ods->wallet_timestamp;
ds->refund_deadline = ods->refund_deadline;
ds->amount = ods->amount;
ds->merchant_priv = ods->merchant_priv;
@ -366,6 +379,7 @@ deposit_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
ds->deposit_fee = denom_pub->fee_deposit;
GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
&coin_pub.eddsa_pub);
@ -379,39 +393,27 @@ deposit_run (void *cls,
}
else
{
ds->refund_deadline = ds->timestamp;
wire_deadline = GNUNET_TIME_relative_to_absolute
(GNUNET_TIME_UNIT_ZERO);
ds->refund_deadline = ds->wallet_timestamp;
wire_deadline = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_ZERO);
}
GNUNET_CRYPTO_eddsa_key_get_public (&ds->merchant_priv.eddsa_priv,
&merchant_pub.eddsa_pub);
(void) GNUNET_TIME_round_abs (&wire_deadline);
{
struct TALER_DepositRequestPS dr;
struct GNUNET_HashCode h_wire;
memset (&dr, 0, sizeof (dr));
dr.purpose.size = htonl
(sizeof (struct TALER_DepositRequestPS));
dr.purpose.purpose = htonl
(TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
dr.h_contract_terms = h_contract_terms;
GNUNET_assert (GNUNET_OK ==
TALER_JSON_merchant_wire_signature_hash (ds->wire_details,
&dr.h_wire));
dr.timestamp = GNUNET_TIME_absolute_hton (ds->timestamp);
dr.refund_deadline = GNUNET_TIME_absolute_hton
(ds->refund_deadline);
TALER_amount_hton (&dr.amount_with_fee,
&ds->amount);
TALER_amount_hton (&dr.deposit_fee,
&denom_pub->fee_deposit);
dr.merchant = merchant_pub;
dr.coin_pub = coin_pub;
GNUNET_CRYPTO_eddsa_sign (&coin_priv->eddsa_priv,
&dr,
&coin_sig.eddsa_signature);
&h_wire));
TALER_EXCHANGE_deposit_permission_sign (&ds->amount,
&denom_pub->fee_deposit,
&h_wire,
&h_contract_terms,
coin_priv,
ds->wallet_timestamp,
&merchant_pub,
ds->refund_deadline,
&coin_sig);
}
ds->dh = TALER_EXCHANGE_deposit (is->exchange,
&ds->amount,
@ -421,7 +423,7 @@ deposit_run (void *cls,
&coin_pub,
denom_pub_sig,
&denom_pub->key,
ds->timestamp,
ds->wallet_timestamp,
&merchant_pub,
ds->refund_deadline,
&coin_sig,
@ -532,8 +534,14 @@ deposit_traits (void *cls,
ds->contract_terms),
TALER_TESTING_make_trait_merchant_priv (0,
&ds->merchant_priv),
TALER_TESTING_make_trait_amount_obj (0,
&ds->amount),
TALER_TESTING_make_trait_amount_obj (
TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_VALUE,
&ds->amount),
TALER_TESTING_make_trait_amount_obj (
TALER_TESTING_CMD_DEPOSIT_TRAIT_IDX_DEPOSIT_FEE,
&ds->deposit_fee),
TALER_TESTING_make_trait_absolute_time (0,
&ds->exchange_timestamp),
TALER_TESTING_trait_end ()
};
@ -599,12 +607,12 @@ TALER_TESTING_cmd_deposit (const char *label,
label);
GNUNET_assert (0);
}
ds->timestamp = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&ds->timestamp);
ds->wallet_timestamp = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&ds->wallet_timestamp);
json_object_set_new (ds->contract_terms,
"timestamp",
GNUNET_JSON_from_time_abs (ds->timestamp));
GNUNET_JSON_from_time_abs (ds->wallet_timestamp));
if (0 != refund_deadline.rel_value_us)
{
ds->refund_deadline = GNUNET_TIME_relative_to_absolute (refund_deadline);
@ -687,12 +695,12 @@ TALER_TESTING_cmd_deposit_with_ref (const char *label,
label);
GNUNET_assert (0);
}
ds->timestamp = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&ds->timestamp);
ds->wallet_timestamp = GNUNET_TIME_absolute_get ();
(void) GNUNET_TIME_round_abs (&ds->wallet_timestamp);
json_object_set_new (ds->contract_terms,
"timestamp",
GNUNET_JSON_from_time_abs (ds->timestamp));
GNUNET_JSON_from_time_abs (ds->wallet_timestamp));
if (0 != refund_deadline.rel_value_us)
{
ds->refund_deadline = GNUNET_TIME_relative_to_absolute (refund_deadline);

View File

@ -81,29 +81,17 @@ struct TrackTransactionState
*
* @param cls closure.
* @param hr HTTP response details
* @param exchange_pub public key of the exchange
* @param wtid wire transfer identifier, NULL if exchange did not
* execute the transaction yet.
* @param execution_time actual or planned execution time for the
* wire transfer.
* @param coin_contribution contribution to the total amount of
* the deposited coin (can be NULL).
* @param dd data about the wire transfer associated with the deposit
*/
static void
deposit_wtid_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_WireTransferIdentifierRawP *wtid,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *coin_contribution)
const struct TALER_EXCHANGE_DepositData *dd)
{
struct TrackTransactionState *tts = cls;
struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
(void) coin_contribution;
(void) exchange_pub;
(void) execution_time;
tts->tth = NULL;
if (tts->expected_response_code != hr->http_status)
{
@ -123,7 +111,7 @@ deposit_wtid_cb (void *cls,
switch (hr->http_status)
{
case MHD_HTTP_OK:
tts->wtid = *wtid;
tts->wtid = dd->wtid;
if (NULL != tts->bank_transfer_reference)
{
const struct TALER_TESTING_Command *bank_transfer_cmd;
@ -151,7 +139,7 @@ deposit_wtid_cb (void *cls,
}
/* Compare that expected and gotten subjects match. */
if (0 != GNUNET_memcmp (wtid,
if (0 != GNUNET_memcmp (&dd->wtid,
wtid_want))
{
GNUNET_break (0);
@ -159,6 +147,8 @@ deposit_wtid_cb (void *cls,
return;
}
}
break;
case MHD_HTTP_ACCEPTED:
/* allowed, nothing to check here */

View File

@ -57,6 +57,11 @@ struct InsertDepositState
*/
struct GNUNET_TIME_Relative wire_deadline;
/**
* When did the exchange receive the deposit?
*/
struct GNUNET_TIME_Absolute exchange_timestamp;
/**
* Amount to deposit, inclusive of deposit fee.
*/
@ -210,6 +215,7 @@ insert_deposit_run (void *cls,
(GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
ids->dbc->plugin->insert_deposit (ids->dbc->plugin->cls,
ids->dbc->session,
ids->exchange_timestamp,
&deposit)) ||
(GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
ids->dbc->plugin->commit (ids->dbc->plugin->cls,
@ -275,6 +281,7 @@ insert_deposit_traits (void *cls,
* @param dbc collects database plugin and session handles.
* @param merchant_name Human-readable name of the merchant.
* @param merchant_account merchant's account name (NOT a payto:// URI)
* @param exchange_timestamp when did the exchange receive the deposit
* @param wire_deadline point in time where the aggregator should have
* wired money to the merchant.
* @param amount_with_fee amount to deposit (inclusive of deposit fee)
@ -282,21 +289,24 @@ insert_deposit_traits (void *cls,
* @return the command.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_insert_deposit (const char *label,
const struct
TALER_TESTING_DatabaseConnection *dbc,
const char *merchant_name,
const char *merchant_account,
struct GNUNET_TIME_Relative wire_deadline,
const char *amount_with_fee,
const char *deposit_fee)
TALER_TESTING_cmd_insert_deposit (
const char *label,
const struct TALER_TESTING_DatabaseConnection *dbc,
const char *merchant_name,
const char *merchant_account,
struct GNUNET_TIME_Absolute exchange_timestamp,
struct GNUNET_TIME_Relative wire_deadline,
const char *amount_with_fee,
const char *deposit_fee)
{
struct InsertDepositState *ids;
GNUNET_TIME_round_abs (&exchange_timestamp);
ids = GNUNET_new (struct InsertDepositState);
ids->dbc = dbc;
ids->merchant_name = merchant_name;
ids->merchant_account = merchant_account;
ids->exchange_timestamp = exchange_timestamp;
ids->wire_deadline = wire_deadline;
ids->amount_with_fee = amount_with_fee;
ids->deposit_fee = deposit_fee;

View File

@ -43,11 +43,6 @@ struct RefundState
*/
const char *refund_amount;
/**
* Expected refund fee.
*/
const char *refund_fee;
/**
* Reference to any command that can provide a coin to refund.
*/
@ -81,6 +76,7 @@ struct RefundState
*
* @param cls closure
* @param hr HTTP response details
* @param refund_fee the refund fee the exchange charged
* @param exchange_pub public key the exchange
* used for signing @a obj.
* @param exchange_sig actual signature confirming the refund
@ -88,6 +84,7 @@ struct RefundState
static void
refund_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_Amount *refund_fee,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct TALER_ExchangeSignatureP *exchange_sig)
{
@ -95,6 +92,7 @@ refund_cb (void *cls,
struct RefundState *rs = cls;
struct TALER_TESTING_Command *refund_cmd;
(void) refund_fee;
refund_cmd = &rs->is->commands[rs->is->ip];
rs->rh = NULL;
if (rs->expected_response_code != hr->http_status)
@ -133,7 +131,6 @@ refund_run (void *cls,
struct TALER_CoinSpendPublicKeyP coin;
const json_t *contract_terms;
struct GNUNET_HashCode h_contract_terms;
struct TALER_Amount refund_fee;
struct TALER_Amount refund_amount;
const struct TALER_MerchantPrivateKeyP *merchant_priv;
const struct TALER_TESTING_Command *coin_cmd;
@ -153,19 +150,6 @@ refund_run (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
if (GNUNET_OK !=
TALER_string_to_amount (rs->refund_fee,
&refund_fee))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to parse amount `%s' at %u/%s\n",
rs->refund_fee,
is->ip,
cmd->label);
TALER_TESTING_interpreter_fail (is);
return;
}
coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
rs->coin_reference);
if (NULL == coin_cmd)
@ -211,7 +195,6 @@ refund_run (void *cls,
}
rs->rh = TALER_EXCHANGE_refund (rs->exchange,
&refund_amount,
&refund_fee,
&h_contract_terms,
&coin,
rs->refund_transaction_id,
@ -254,7 +237,6 @@ refund_cleanup (void *cls,
* @param label command label.
* @param expected_response_code expected HTTP status code.
* @param refund_amount the amount to ask a refund for.
* @param refund_fee expected refund fee.
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
*
@ -264,7 +246,6 @@ struct TALER_TESTING_Command
TALER_TESTING_cmd_refund (const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *refund_fee,
const char *coin_reference)
{
struct RefundState *rs;
@ -273,7 +254,6 @@ TALER_TESTING_cmd_refund (const char *label,
rs->expected_response_code = expected_response_code;
rs->refund_amount = refund_amount;
rs->refund_fee = refund_fee;
rs->coin_reference = coin_reference;
{
struct TALER_TESTING_Command cmd = {
@ -295,7 +275,6 @@ TALER_TESTING_cmd_refund (const char *label,
* @param label command label.
* @param expected_response_code expected HTTP status code.
* @param refund_amount the amount to ask a refund for.
* @param refund_fee expected refund fee.
* @param coin_reference reference to a command that can
* provide a coin to be refunded.
* @param refund_transaction_id transaction id to use
@ -308,7 +287,6 @@ TALER_TESTING_cmd_refund_with_id
(const char *label,
unsigned int expected_response_code,
const char *refund_amount,
const char *refund_fee,
const char *coin_reference,
uint64_t refund_transaction_id)
{
@ -317,7 +295,6 @@ TALER_TESTING_cmd_refund_with_id
rs = GNUNET_new (struct RefundState);
rs->expected_response_code = expected_response_code;
rs->refund_amount = refund_amount;
rs->refund_fee = refund_fee;
rs->coin_reference = coin_reference;
rs->refund_transaction_id = refund_transaction_id;
{

View File

@ -0,0 +1,222 @@
/*
This file is part of TALER
Copyright (C) 2014-2020 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 testing/testing_api_cmd_rewind.c
* @brief command to rewind the instruction pointer.
* @author Marcello Stanisci
* @author Christian Grothoff
*/
#include "platform.h"
#include <taler/taler_exchange_service.h>
#include <taler/taler_testing_lib.h>
#include "taler_testing_lib.h"
/**
* State for a "rewind" CMD.
*/
struct RewindIpState
{
/**
* Instruction pointer to set into the interpreter.
*/
const char *target_label;
/**
* How many times this set should take place. However, this value lives at
* the calling process, and this CMD is only in charge of checking and
* decremeting it.
*/
unsigned int counter;
};
/**
* Only defined to respect the API.
*/
static void
rewind_ip_cleanup (void *cls,
const struct TALER_TESTING_Command *cmd)
{
(void) cls;
(void) cmd;
}
/**
* Seek for the @a target command in @a batch (and rewind to it
* if successful).
*
* @param is the interpreter state (for failures)
* @param cmd batch to search for @a target
* @param target command to search for
* @return #GNUNET_OK on success, #GNUNET_NO if target was not found,
* #GNUNET_SYSERR if target is in the future and we failed
*/
static int
seek_batch (struct TALER_TESTING_Interpreter *is,
const struct TALER_TESTING_Command *cmd,
const struct TALER_TESTING_Command *target)
{
unsigned int new_ip;
#define BATCH_INDEX 1
struct TALER_TESTING_Command *batch;
struct TALER_TESTING_Command *current;
struct TALER_TESTING_Command *icmd;
const struct TALER_TESTING_Command *match;
current = TALER_TESTING_cmd_batch_get_current (cmd);
GNUNET_assert (GNUNET_OK ==
TALER_TESTING_get_trait_cmd (cmd,
BATCH_INDEX,
&batch));
match = NULL;
for (new_ip = 0;
NULL != (icmd = &batch[new_ip]);
new_ip++)
{
if (current == target)
current = NULL;
if (icmd == target)
{
match = icmd;
break;
}
if (TALER_TESTING_cmd_is_batch (icmd))
{
int ret = seek_batch (is,
icmd,
target);
if (GNUNET_SYSERR == ret)
return GNUNET_SYSERR; /* failure! */
if (GNUNET_OK == ret)
{
match = icmd;
break;
}
}
}
if (NULL == current)
{
/* refuse to jump forward */
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return GNUNET_SYSERR;
}
if (NULL == match)
return GNUNET_NO; /* not found */
TALER_TESTING_cmd_batch_set_current (cmd,
new_ip);
return GNUNET_OK;
}
/**
* Run the "rewind" CMD.
*
* @param cls closure.
* @param cmd command being executed now.
* @param is the interpreter state.
*/
static void
rewind_ip_run (void *cls,
const struct TALER_TESTING_Command *cmd,
struct TALER_TESTING_Interpreter *is)
{
struct RewindIpState *ris = cls;
const struct TALER_TESTING_Command *target;
unsigned int new_ip;
(void) cmd;
if (0 == ris->counter)
{
TALER_TESTING_interpreter_next (is);
return;
}
target
= TALER_TESTING_interpreter_lookup_command (is,
ris->target_label);
if (NULL == target)
{
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
ris->counter--;
for (new_ip = 0;
NULL != is->commands[new_ip].label;
new_ip++)
{
const struct TALER_TESTING_Command *cmd = &is->commands[new_ip];
if (cmd == target)
break;
if (TALER_TESTING_cmd_is_batch (cmd))
{
int ret = seek_batch (is,
cmd,
target);
if (GNUNET_SYSERR == ret)
return; /* failure! */
if (GNUNET_OK == ret)
break;
}
}
if (new_ip > is->ip)
{
/* refuse to jump forward */
GNUNET_break (0);
TALER_TESTING_interpreter_fail (is);
return;
}
is->ip = new_ip - 1; /* -1 because the next function will advance by one */
TALER_TESTING_interpreter_next (is);
}
/**
* Make the instruction pointer point to @a new_ip
* only if @a counter is greater than zero.
*
* @param label command label
* @param target_label label of the new instruction pointer's destination after the jump;
* must be before the current instruction
* @param counter counts how many times the rewinding is to happen.
*/
struct TALER_TESTING_Command
TALER_TESTING_cmd_rewind_ip (const char *label,
const char *target_label,
unsigned int counter)
{
struct RewindIpState *ris;
ris = GNUNET_new (struct RewindIpState);
ris->target_label = target_label;
ris->counter = counter;
{
struct TALER_TESTING_Command cmd = {
.cls = ris,
.label = label,
.run = &rewind_ip_run,
.cleanup = &rewind_ip_cleanup
};
return cmd;
}
}

View File

@ -40,7 +40,6 @@ stat_cleanup (void *cls,
(void) cls;
(void) cmd;
/* nothing to clean. */
return;
}

View File

@ -121,37 +121,18 @@ track_transfer_cleanup (void *cls,
*
* @param cls closure.
* @param hr HTTP response details
* @param exchange_pub public key the exchange used for signing
* the response.
* @param h_wire hash of the wire transfer address the transfer
* went to, or NULL on error.
* @param execution_time time when the exchange claims to have
* performed the wire transfer.
* @param total_amount total amount of the wire transfer, or NULL
* if the exchange could not provide any @a wtid (set only
* if @a http_status is "200 OK").
* @param wire_fee wire fee that was charged by the exchange.
* @param details_length length of the @a details array.
* @param details array with details about the combined
* transactions.
* @param ta transfer data returned by the exchange
*/
static void
track_transfer_cb (void *cls,
const struct TALER_EXCHANGE_HttpResponse *hr,
const struct TALER_ExchangePublicKeyP *exchange_pub,
const struct GNUNET_HashCode *h_wire,
struct GNUNET_TIME_Absolute execution_time,
const struct TALER_Amount *total_amount,
const struct TALER_Amount *wire_fee,
unsigned int details_length,
const struct TALER_TrackTransferDetails *details)
const struct TALER_EXCHANGE_TransferData *ta)
{
struct TrackTransferState *tts = cls;
struct TALER_TESTING_Interpreter *is = tts->is;
struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
struct TALER_Amount expected_amount;
(void) exchange_pub;
tts->tth = NULL;
if (tts->expected_response_code != hr->http_status)
{
@ -193,14 +174,14 @@ track_transfer_cb (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (total_amount,
if (0 != TALER_amount_cmp (&ta->total_amount,
&expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Total amount mismatch to command %s - "
"%s vs %s\n",
cmd->label,
TALER_amount_to_string (total_amount),
TALER_amount_to_string (&ta->total_amount),
TALER_amount_to_string (&expected_amount));
json_dumpf (hr->reply,
stderr,
@ -219,7 +200,7 @@ track_transfer_cb (void *cls,
return;
}
if (0 != TALER_amount_cmp (wire_fee,
if (0 != TALER_amount_cmp (&ta->wire_fee,
&expected_amount))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@ -266,7 +247,7 @@ track_transfer_cb (void *cls,
TALER_JSON_merchant_wire_signature_hash (wire_details,
&h_wire_details));
if (0 != GNUNET_memcmp (&h_wire_details,
h_wire))
&ta->h_wire))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Wire hash missmath to command %s\n",
@ -301,7 +282,7 @@ track_transfer_cb (void *cls,
TALER_TESTING_interpreter_fail (is);
return;
}
if (0 != TALER_amount_cmp (total_amount,
if (0 != TALER_amount_cmp (&ta->total_amount,
total_amount_from_reference))
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,

View File

@ -69,6 +69,7 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
#define BATCH_INDEX 1
struct TALER_TESTING_Command *batch;
struct TALER_TESTING_Command *current;
struct TALER_TESTING_Command *icmd;
const struct TALER_TESTING_Command *match;
current = TALER_TESTING_cmd_batch_get_current (cmd);
@ -79,15 +80,15 @@ TALER_TESTING_interpreter_lookup_command (struct TALER_TESTING_Interpreter *is,
/* We must do the loop forward, but we can find the last match */
match = NULL;
for (unsigned int j = 0;
NULL != (cmd = &batch[j])->label;
NULL != (icmd = &batch[j])->label;
j++)
{
if (current == cmd)
if (current == icmd)
break; /* do not go past current command */
if ( (NULL != cmd->label) &&
(0 == strcmp (cmd->label,
if ( (NULL != icmd->label) &&
(0 == strcmp (icmd->label,
label)) )
match = cmd;
match = icmd;
}
if (NULL != match)
return match;

View File

@ -30,6 +30,7 @@
#define TALER_TESTING_TRAIT_WIRE_DETAILS "wire-details"
#define TALER_TESTING_TRAIT_EXCHANGE_KEYS "exchange-keys"
#define TALER_TESTING_TRAIT_JSON "json"
/**
* Obtain serialized exchange keys from @a cmd.
@ -120,4 +121,45 @@ TALER_TESTING_make_trait_wire_details
}
/**
* Obtain json from @a cmd.
*
* @param cmd command to extract the json from.
* @param index index number associate with the json on offer.
* @param[out] json where to write the json.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_json (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const json_t **json)
{
return cmd->traits (cmd->cls,
(const void **) json,
TALER_TESTING_TRAIT_JSON,
index);
}
/**
* Offer json in a trait.
*
* @param index index number associate with the json
* on offer.
* @param json json to offer.
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_json (unsigned int index,
const json_t *json)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_JSON,
.ptr = (const json_t *) json
};
return ret;
}
/* end of testing_api_trait_json.c */

View File

@ -28,7 +28,9 @@
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_UINT "uint"
#define TALER_TESTING_TRAIT_UINT32 "uint-32"
#define TALER_TESTING_TRAIT_UINT64 "uint-64"
#define TALER_TESTING_TRAIT_INT64 "int-64"
#define TALER_TESTING_TRAIT_BANK_ROW "bank-transaction-row"
@ -72,6 +74,45 @@ TALER_TESTING_make_trait_uint (unsigned int index,
}
/**
* Obtain a "number" value from @a cmd, 32-bit version.
*
* @param cmd command to extract the number from.
* @param index the number's index number.
* @param[out] n set to the number coming from @a cmd.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_uint32 (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const uint32_t **n)
{
return cmd->traits (cmd->cls,
(const void **) n,
TALER_TESTING_TRAIT_UINT32,
index);
}
/**
* Offer number trait, 32-bit version.
*
* @param index the number's index number.
* @param n number to offer.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_uint32 (unsigned int index,
const uint32_t *n)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_UINT32,
.ptr = (const void *) n
};
return ret;
}
/**
* Obtain a "number" value from @a cmd, 64-bit version.
*
@ -111,6 +152,45 @@ TALER_TESTING_make_trait_uint64 (unsigned int index,
}
/**
* Obtain a "number" value from @a cmd, 64-bit signed version.
*
* @param cmd command to extract the number from.
* @param index the number's index number.
* @param[out] n set to the number coming from @a cmd.
* @return #GNUNET_OK on success.
*/
int
TALER_TESTING_get_trait_int64 (const struct TALER_TESTING_Command *cmd,
unsigned int index,
const int64_t **n)
{
return cmd->traits (cmd->cls,
(const void **) n,
TALER_TESTING_TRAIT_INT64,
index);
}
/**
* Offer number trait, 64-bit signed version.
*
* @param index the number's index number.
* @param n number to offer.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_int64 (unsigned int index,
const int64_t *n)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_INT64,
.ptr = (const void *) n
};
return ret;
}
/**
* Obtain a bank transaction row value from @a cmd.
*

View File

@ -29,6 +29,7 @@
#include "taler_testing_lib.h"
#define TALER_TESTING_TRAIT_TIME_ABS "time-abs"
#define TALER_TESTING_TRAIT_TIME_REL "time-rel"
/**
* Obtain a absolute time from @a cmd.
@ -73,4 +74,47 @@ TALER_TESTING_make_trait_absolute_time
}
/**
* Obtain a relative time from @a cmd.
*
* @param cmd command to extract trait from
* @param index which time to pick if
* @a cmd has multiple on offer.
* @param[out] time set to the wanted WTID.
* @return #GNUNET_OK on success
*/
int
TALER_TESTING_get_trait_relative_time (
const struct TALER_TESTING_Command *cmd,
unsigned int index,
const struct GNUNET_TIME_Relative **time)
{
return cmd->traits (cmd->cls,
(const void **) time,
TALER_TESTING_TRAIT_TIME_REL,
index);
}
/**
* Offer a relative time.
*
* @param index associate the object with this index
* @param time which object should be returned
* @return the trait.
*/
struct TALER_TESTING_Trait
TALER_TESTING_make_trait_relative_time (
unsigned int index,
const struct GNUNET_TIME_Relative *time)
{
struct TALER_TESTING_Trait ret = {
.index = index,
.trait_name = TALER_TESTING_TRAIT_TIME_REL,
.ptr = (const void *) time
};
return ret;
}
/* end of testing_api_trait_time.c */

View File

@ -42,6 +42,7 @@ libtalerutil_la_SOURCES = \
payto.c \
url.c \
util.c \
yna.c \
os_installation.c
libtalerutil_la_LIBADD = \

88
src/util/yna.c Normal file
View File

@ -0,0 +1,88 @@
/*
This file is part of TALER
Copyright (C) 2020 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 yna.c
* @brief Utility functions for yes/no/all filters
* @author Jonathan Buchanan
*/
#include "platform.h"
#include "taler_util.h"
/**
* Convert query argument to @a yna value.
*
* @param connection connection to take query argument from
* @param arg argument to try for
* @param default_val value to assign if the argument is not present
* @param[out] value to set
* @return true on success, false if the parameter was malformed
*/
bool
TALER_arg_to_yna (struct MHD_Connection *connection,
const char *arg,
enum TALER_EXCHANGE_YesNoAll default_val,
enum TALER_EXCHANGE_YesNoAll *yna)
{
const char *str;
str = MHD_lookup_connection_value (connection,
MHD_GET_ARGUMENT_KIND,
arg);
if (NULL == str)
{
*yna = default_val;
return true;
}
if (0 == strcasecmp (str, "yes"))
{
*yna = TALER_EXCHANGE_YNA_YES;
return true;
}
if (0 == strcasecmp (str, "no"))
{
*yna = TALER_EXCHANGE_YNA_NO;
return true;
}
if (0 == strcasecmp (str, "all"))
{
*yna = TALER_EXCHANGE_YNA_ALL;
return true;
}
return false;
}
/**
* Convert YNA value to a string.
*
* @param yna value to convert
* @return string representation ("yes"/"no"/"all").
*/
const char *
TALER_yna_to_string (enum TALER_EXCHANGE_YesNoAll yna)
{
switch (yna)
{
case TALER_EXCHANGE_YNA_YES:
return "yes";
case TALER_EXCHANGE_YNA_NO:
return "no";
case TALER_EXCHANGE_YNA_ALL:
return "all";
}
GNUNET_assert (0);
}